19.4 纹理映射
回顾8.10节,我们在地形网格上平铺了一幅草地纹理,以此来提高分辨率(即,提高地形网格三角形上的纹理元素采样数量)。这里,我们要做类似的工作,只是不再局限于一幅简单的草地纹理。我们要创建同带有沙滩、草地、泥土、岩石、雪地的地形。但是,如果你认为我们将要做的工作就是创建一幅包含些地貌特征的超大尺寸纹理,并在地形网格上拉伸该纹理,那就错了。这会使我们重新陷入到分辨率的困扰之中——因为地形网格非常巨大,如果我们想要得到一个令人满意的分辨率,使网格得到足够多的采样颜色,那么就必须创建一幅非常巨大的纹理。这样行不通,我们必须转变思路。一种可行的做法是使用多重纹理映射(multitexturing),它的工作原理与透明alpha混合相似。
实现思路是:为每个地形图层准备一幅单独的纹理(例如,为草地、泥土、岩石等图层各准备一幅纹理)。为提高分辨率,我们会在地形网格上平铺这些纹理。举一个例子,假设我们有3个地形图层(草地、泥土和岩石);这些图层会按照如下方式组合在一起,如图19.11所示。
上述过程与透明alpha混合非常相似。混合贴图存储了源alpha分量,指定了源图层的不透明度,我们以以此来控制有多少源图层颜色混合为当前的地形颜色。这样,在地形中会有一部分是草地、一部分是泥土、还有一部分是雪地或者这三个图层的混合颜色。
图19.11说明了3个彩色贴图的混合过程。在代码中,我们使用了5个彩色贴图。为了混合这5个彩色贴图,我们需要4个灰阶混合贴图。我们可以把这4个灰阶混合贴图封装为一个单个的RGBA纹理(红色通道存储第1个混合贴图,绿色通道存储第2个混合贴图,蓝色通道存储第3个混合贴图,alpha通道存储第4个混合贴图)。这样,用6个纹理就可以完成一工作。下面的地形像素着色器代码展示了纹理混合的实现方式:
// 采样纹理数组中的层. float4 c0 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 0.0f) ); float4 c1 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 1.0f) ); float4 c2 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 2.0f) ); float4 c3 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 3.0f) ); float4 c4 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 4.0f) ); // 采样混合贴图. float4 t = gBlendMap.Sample( samLinear, pin.Tex ); // 混合各层. float4 texColor = c0; texColor = lerp(texColor, c1, t.r); texColor = lerp(texColor, c2, t.g); texColor = lerp(texColor, c3, t.b); texColor = lerp(texColor, c4, t.a);
混合贴图通常由地形的高度图来决定。因此,我们没有把混合贴图嵌入到彩色贴图的alpha通道中。如果我们这样做了,那么彩色贴图就只能用于一个特定的高度图。通过让彩色贴图和混合贴图分离,我们可以在许多不同的地形中重复使用彩色贴图。另一方面,当创建不同的地形时,我们使用的混合贴图也不一样。
我们没有在整个地形表面上平铺混合贴图,而是将它们直接拉伸在了整个地形表面上,这一点与彩色贴图的用法不同。这样做是必须的,因为我们使用混合贴图标记地形区域,决定地形的哪个部分显示哪种纹理,所以混合贴图是一个全局性的纹理,它们必须覆盖在整个地形表面上。也许你想知道,这样渲染出来的结果是否可以让人接受,或者说,是否会有严重的扩增问题发生。当然,在整个地形表面上拉伸混合贴图必然会导致扩增问题的发生,由于纹理过滤的原因,混合贴图难免会出现扭曲变形,但是混合贴图不是用来表现细节的(我们从平铺的彩色贴图中获取细节)。混合贴图只是用来标记在地形的哪个区域显示(多少)特定的纹理。即使混合贴图出现一些扭曲和模糊,也不会对最终的渲染结果产生多大影响——例如,泥土和草地的交界处会有一些混合,这其实是件好事,因为这会使两个图层之间产生较为平滑的过渡。
注意:你可以通过将法线贴图平铺在地形上添加法线映射。诸如岩石之类的材质可以通过更高细节的法线贴图获得更好地视觉效果。在19.3节中已经介绍了如何逐像素地计算切线空间。
文件下载(已下载 621 次)发布时间:2014/8/26 下午6:46:13 阅读次数:4009