本篇教程中,我们将在前面基于光照的地形与水面程序里面加上纹理映射,而且我们会基于时间动态改变水面的纹理坐标,实现水面纹理波动的效果。
地形(山谷)以及水面都是基于网格的平面。
对于地形,修改顶点类型为:
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR3 normal;
D3DXVECTOR2 texture; //纹理坐标
D3DXVECTOR4 Kd; //材质的漫反射系数
D3DXVECTOR4 Ks; //材质的高光系数
};
假设m,n为网格行数、列数,则纹理坐标计算如下:
float du = 10.0f / (n-1);
float dv = 10.0f / (m-1)
…
// 计算纹理坐标.
vertices[i*n+j].texture.x = j*du;
vertices[i*n+j].texture.y = i*dv;
其中du,dv中的10.0f,是我们把纹理坐标扩大10倍,大于[0,1]的纹理坐标会用wrap的方式使用纹理,这样在一个grid内,可以多次使用贴图,避免出现网格很大,而我们的纹理图片比较小,从而避免过度magnification的情形。
下面2个图是用10.0和2.0的比较结果,第一个图是2.0:
水面的纹理坐标计算方式和山谷基本相同,但是我们会在update函数中根据dt动态改变坐标,这样会实现水面漂移的效果。
// 更新顶点缓冲.
float du = 5.0f / (m_NumCols-1);
float dv = 5.0f / (m_NumRows-1);
//我们根据时间动态计算纹理坐标
static float mWaterTexOffsetX = 0;
static float mWaterTexOffsetY = 0;
mWaterTexOffsetY += 0.1f*dt;
mWaterTexOffsetX = 0.25f*sinf(4.0f*mWaterTexOffsetY);
…
vertices[i*m_NumCols+j].texture.x = j*du + mWaterTexOffsetX;
vertices[i*m_NumCols+j].texture.y = i*dv + mWaterTexOffsetY;
当然,我们也通过矩阵的方式,来缩放、平移以及旋转纹理坐标,这样的话,就需要定义个shader常量矩阵,在shader中用它来乘以纹理坐标,实现我们想要的效果。纹理旋转经常用来实现动态粒子的效果,比如火焰等等。
最后,我们在GraphicsClass中,调用光照纹理渲染类,就可以得到最终结果:
//用light shader texture渲染
result = m_LightTexShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
light, material, camera,m_TexManager->createTex(m_D3D->GetDevice(),string("grass.dds")));
…
result = m_LightTexShader->Render(m_D3D->GetDeviceContext(), m_WaterModel->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
light, material, camera,m_TexManager->createTex(m_D3D->GetDevice(),string("water2.dds")));
完整的代码请参考:
工程文件myTutorialD3D11_38
代码下载: