最爱午后红茶

剖析地形纹理演示程序

日期图标
2023-06-26

地形纹理演示程序:

本程序是基于 带光照的山峰河谷程序 进行修改的。主要改动为:给山峰地形和河谷水体贴了一层纹理。

地形纹理(纹理平铺)

本程序地形网格较大,而地形纹理较小。如果直接使用一张地形纹理去对应整个地形,将出现锯齿、模糊等失真问题(对应 Games-101-Texture Queries 里面纹理过小的问题)。我们可以通过设置纹理的寻址模式(纹理坐标超出 [0, 1] 时应如何采样)以及对纹理进行变换(Direct3D 支持扩展纹理的值域(使得范围大于 [0, 1]))来”放大“纹理。具体方式是:

  • 纹理的寻址模式设置为:重复
  • 把原本纹理的坐标范围 [0, 1] 乘以一个倍数 n,使得区间变为 [0, n]。

这样的话,只要纹理边界设计得好,我们就不容易察觉到纹理的边缘。这就相当于在较少失真的情况下放大了纹理:

复制纹理
图一:复制纹理

本程序对地形纹理坐标值域放大了 5 倍:

TexturedHillsAndWavesApp::TexturedHillsAndWavesApp(HINSTANCE hInstance)
    : D3DApp(hInstance), mLandVB(0), mLandIB(0), mWavesVB(0), mWavesIB(0),
      mGrassMapSRV(0), mWavesMapSRV(0), mWaterTexOffset(0.0f, 0.0f),
      mEyePosW(0.0f, 0.0f, 0.0f), mLandIndexCount(0),
      mTheta(1.3f * MathHelper::Pi), mPhi(0.4f * MathHelper::Pi),
      mRadius(80.0f) {
  ...
  XMMATRIX grassTexScale = XMMatrixScaling(5.0f, 5.0f, 0.0f);
  XMStoreFloat4x4(&mGrassTexTransform, grassTexScale);
  ...
}

纹理的变换在顶点着色器中进行:

VertexOut VS(VertexIn vin) {
	VertexOut vout;
	...
	// Output vertex attributes for interpolation across triangle.
	vout.Tex = mul(float4(vin.Tex, 0.0f, 1.0f), gTexTransform).xy;
	return vout;
}

在采样器对象中设置寻址模式:

SamplerState samAnisotropic {
  Filter = ANISOTROPIC; // 各向异性过滤
  MaxAnisotropy = 4; // 各向异性的最大值 1~16

  AddressU = WRAP; // 将U轴(水平)纹理坐标的寻址模式设置为"WRAP",意味着在采样超出纹理边界时,纹理将环绕。
  AddressV = WRAP; // 将V轴(垂直)纹理坐标的寻址模式设置为"WRAP",与前一行类似。
};

float4 PS(VertexOut pin, uniform int gLightCount, uniform bool gUseTexure)
    : SV_Target {
  ...
  // Default to multiplicative identity.
  float4 texColor = float4(1, 1, 1, 1);
  if (gUseTexure) {
    // Sample texture.
    texColor = gDiffuseMap.Sample(samAnisotropic, pin.Tex);
  }
  ...
}

可以对比下对纹理进行”放大“前后的区别:

使用 DDS Viewer 加载的地形纹理

图二:使用 DDS Viewer 加载的地形纹理

纹理”放大“前
图三:纹理”放大“前
纹理”放大“后
图四:纹理”放大“后

水体纹理(纹理动画)

从演示中可以看到,水体纹理会随着时间的推移朝一个方向缓慢移动。这是因为水体的纹理不仅应用了前面所说的寻找模式以及纹理坐标的缩放变换,还对每一帧的纹理坐标都进行了微小的平移变换,以此得到一个平滑的动画:

void TexturedHillsAndWavesApp::UpdateScene(float dt) {
  ...
  // Tile water texture.
  XMMATRIX wavesScale = XMMatrixScaling(5.0f, 5.0f, 0.0f);

  // Translate texture over time.
  mWaterTexOffset.y += 0.05f * dt;
  mWaterTexOffset.x += 0.1f * dt;
  XMMATRIX wavesOffset =
      XMMatrixTranslation(mWaterTexOffset.x, mWaterTexOffset.y, 0.0f);

  // Combine scale and translation.
  XMStoreFloat4x4(&mWaterTexTransform, wavesScale * wavesOffset);
}
* 未经同意不得转载。