最开始,我想到最直接的办法就是利用湖面顶点在View-Projection空间的X、Z坐标值进行插值,来映射纹理,纹理坐标=ViewProj空间X、Z坐标 * 0.5 + 0.5。
但是发现反射图像出现奇怪的扭曲现象,一开始以为是纹理坐标环绕设置有问题( 设置成D3DTADDRESS_WRAP或者D3DTADDRESS_CLAMP等值后发现没有任何改观 )。同时,又发现一个奇怪的现象:如果湖面的顶点数量越多,则扭曲程度越小。由于我一直在纹理坐标寻址上转不过弯,所以始终没有找到解决办法。
后来,通过参考微软DX SDK的程序,发现这么一段话
If D3DPTEXTURECAPS_PROJECTED is set, projected textures are computed per pixel,
so this sample will work fine with just a quad for the water model. If it's
not set, textures are projected per vertex rather than per pixel, so distortion
will be visible unless we use more vertices.
这段话揭示了我发现的顶点数量越多,则扭曲程度越小的现象,同时指出如果“D3DPTEXTURECAPS_PROJECTED”标志被设置,则纹理是按照逐像素方式计算投影坐标,逐像素方式可以避免扭曲的出现。
再通过查找DX的Help档,发现这么一段针对PixelShader中的_dw/_da/_dz/_db操作符的重要的说明:
For pixel shader version 1.4, the D3DTTFF_PROJECTED flag under D3DTSS_TEXTURETRANSFORMFLAGS
is ignored because a projective divide is accomplished by the source register modifiers
listed previously.( 在PS1.4中,由于在纹理坐标寄存器具有预先除法的功能,所以,“D3DTTFF_PROJECTED”标志可以忽略。)
所以,结论是通过利用PS.1.4中的texcrd指令和dw操作符组合,可以解决扭曲现象。
至此,终于明白了扭曲的原因,因为纹理坐标 U = X / w * 0.5 + 0.5, V = Y / w * 0.5 + 0.5,其中:由于齐次坐标中的w分量是一个变化量而非一个常数,所以U与X并非一个线性变化( 比如,X扩大2倍,U并非同时扩大2倍,因为w也变化了 ),所以,如此通过per-Vertex方式插值出来的UV坐标必然是不对的( U = x / w并非一个线性变化 ),所以,只能把X、Y、W统统送到PS中去运算才对,而在VS中运算好后再通过硬件插值的方案是行不通的。
最终,可以很容易得到如下逐像素投影纹理坐标的解决方案:
PS.1.4
texcrd r0.rg, t0_dw.xyw
...
phase
texld r0, r0
....