多个纹理图像混合在一起到3D地面上

时间:2009-07-10 17:22:04

标签: opengl terrain

电脑游戏如何发挥作用?我将使用几何高度图(虽然我稍后会对其进行优化)但我想知道最好的技术是什么,例如,“绘制”我的地面;到处都是草,这里和那里的泥路,城镇内的砾石,以及每种材料之间的平滑过渡。

我只是使用巨大的预烘烤质地吗?当我可以平铺现有纹理时,这看起来非常低效。那么我是否为每个现有纹理使用一个巨大的alpha贴图?从理论上讲,这对我来说听起来不错,但我如何实际做到这一点以及后果是什么?我真的不知道从哪里开始,我的谷歌搜索并没有证明非常有效。

我宁愿不必将纹理“捕捉”到网格上(即空间(0,0)是草,空间(0,2)是污垢,空间(0,1)是草尘过渡) ;我宁愿能够任意绘画,以使它看起来更有说服力。当然,这将是一种简单的方法,但它在图形质量和“现实主义”方面的牺牲太大了。

我主要只是在这里寻找理论和选择。我正在使用OpenGL,所以如果你能提供关于OpenGL的工作方式的提示,以及我可能从未听说过的功能,那就太棒了。

只是为了澄清,Oblivion是我正在寻找的一个很好的参考。我不知道地面的几何形状(高度图,静态3D模型等),但它们的地形有不同的地面类型和它们之间的平滑过渡,就像我在说的那样。这是一个示例图像,注意鹅卵石如何融入草丛,不切实际但顺利:http://www.elitistsnob.com/images/Oblivion%202006-05-21%2008-38-25-15.jpg

另外我想我在其中一本游戏编程宝石书中读过这篇文章,但我当时并没有太多关注它,现在是夏天我无法访问我大学的图书馆来检查!我现在正在寻找目录,如果我找到它会编辑,但直到8月中旬我仍然无法阅读。

编辑:啊,游戏编程宝石7有一个标题为“为户外地形渲染绘制大纹理”的章节5.8,听起来就像我需要的那样,但我的U的图书馆甚至没有那本书!我在其他Game Programming Gems书籍中找不到任何完全相同的东西,尽管有一些人有一些地形几何文章。

6 个答案:

答案 0 :(得分:9)

我最近在OpenGL中编写了一个小型地形渲染引擎,它可以执行类似于您所说的内容。我使用的技术最好描述为texture splatting

我使用五种纹理来实现这一目标。这些纹理中有四种是细节纹理:草,岩石,水和沙子。这些纹理很小,512x512纹理,并且它们在地形上平铺。第五个纹理是mixmap。 mixmap是一个覆盖整个地形的巨大纹理,在我的例子中是4096x4096。

MixMap

此mixmap使用所有4个颜色通道(r,g,b,a)来描述在该特定位置显示多少细节纹理。我使用红色通道来确定沙子是多么不透明,绿色是草,蓝色是水,阿尔法是岩石。此mixmap是在初始化时根据高度图计算的,我使用高度来确定这些值。例如,靠近海平面,我主要想要水,所以我在蓝色通道中设置了较高的值,在其他通道中设置了较低的值。当我进入山区时,我将alpha颜色通道设置为高值,因为我需要大量的岩石纹理,但我将所有其他颜色通道设置为较低的值。

片段着色器

然后在片段着色器中使用此mixmap,我在其中使用mixmap的这些颜色通道并使用它们来组合细节纹理。这是我用于片段着色器的GLSL代码:

uniform sampler2D alpha;
uniform sampler2D grass;
uniform sampler2D water;
uniform sampler2D rock;
uniform sampler2D sand;
uniform float texscale;

varying vec3 normal, lightDir ;

void main()
{
   // Get the color information
   vec3 alpha    = texture2D( alpha, gl_TexCoord[0].st ).rgb;
   vec3 texSand  = texture2D( sand, gl_TexCoord[0].st * texscale ).rgb;
   vec3 texGrass = texture2D( grass,  gl_TexCoord[0].st * texscale ).rgb;
   vec3 texWater = texture2D( water, gl_TexCoord[0].st * texscale ).rgb;
   vec3 texRock  = texture2D( rock,  gl_TexCoord[0].st * texscale ).rgb;

   // Mix the colors together
   texSand *= mixmap.r;
   texGrass = mix(texSand,  texGrass, mixmap.g);
   texWater = mix(texGrass, texWater, mixmap.b);
   vec3 tx  = mix(texWater, texRock,  mixmap.a);

   // Lighting calculations
   vec3 dl = gl_LightSource[0].diffuse.rgb;   
   vec3 al = gl_LightSource[0].ambient.rgb;
   vec3 n = normalize(normal);
   vec3 d = tx * (dl * max( dot ( n, lightDir), 0.0 ) + al );   

   // Apply the lighting to the final color   
   vec4 finalColor = vec4( min(d, 1.0), 1.0);
   gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor);
}

统一texscale是一个值,用于确定细节纹理在地形上平铺的次数。较高的值会使细节纹理看起来更清晰,有可能使它们看起来更重复。

答案 1 :(得分:1)

预烘烤肯定不是低效的。事实上它是最有效的,因为您的运行时代码不必进行任何混合或其他计算。它只是渲染纹理。当然,动态技术提供了工具链的简化和更多选项。

答案 2 :(得分:1)

ID似乎已经让单个大"megatexture"(我们说的是32K ^ 2-128K ^ 2)在QuakeWars中工作。

有一个有趣的Q& A与Carmack谈论它

http://web.archive.org/web/20080121072936/http://www.gamerwithin.com/?view=article&article=1319&cat=2

(原始链接目前似乎已经死了,但并非上面没有链接,因为SO似乎在Internet Archive链接上有问题;它们在编辑预览中看起来很好,然后在主页上中断)。

答案 3 :(得分:1)

在源(hl2)引擎中使用的标准方法之一是使每个顶点alpha在两个材质之间进行混合。材料通常至少包括反照率和法线图。在源引擎中,每个“混合纹理”材料只允许使用2种材料,这有点蹩脚。

如果您需要2种以上的材料,则必须通过使用不同的纹理纹理化不同的位移来模拟它,确保任何单个图块最多只包含2种材质。

每个顶点而不是每个像素都会产生非常糟糕的外观。正如你在Oblivion中所提到的那样,你可以将鹅卵石平滑地混合成沙子。有一个解决这个缺陷的方法;你使用另一个纹理来调整每个像素的alpha,这样鹅卵石之间的裂缝很容易填满沙子,但是鹅卵石的顶部几乎保持在1.0 alpha,直到内插的顶点alpha几乎为0材料。这可以使过渡看起来非常好,而不仅仅是一个丑陋的涂抹。

您还可以使用包含alphas的低res纹理(但仍然远高于每个顶点1个alpha)。平铺的terain纹理可能是2048x2048,但用于将它们混合到一起的纹理不需要每纹理分辨率。每个纹理素的256x256 8位纹理只有96 kB,带有mip贴图。

答案 4 :(得分:0)

通常,过渡区域是预先创建的,尽管可以使用Alpha通道和纹理混合来做一些事情。

答案 5 :(得分:0)

你无法摆脱平铺,但也许如果你使用的基础纹理在地面上产生一点噪音,它看起来就像没有平铺...

使用具有相同纹理坐标的alpha纹理的基础纹理。基础纹理可以是任何东西..卫星图片,或草纹理.. 如果它与你的地面形状有关,那就完美了。

并尝试为草,岩石纹理使用无缝纹理...... 它没有解决,但让它看起来没有问题