D3D里面有两个容易混淆的概念,环绕纹理寻址模式(wrap texture address mode)和纹理环绕(texture wrapping)。
环绕纹理寻址模式,是一种纹理坐标寻址模式,指定了纹理坐标超出[0,1]的部分的处理方法,环绕纹理寻址模式就是简单的重复,如果用代码来描述它相当于对uv做了
u = fmod(u,1.0);
v = fmod(v,1.0);
纹理环绕,这个描述起来更麻烦些,它其实是改变了每个顶点的纹理坐标在光栅化时的插值方法。举个例子,如果有一个三角形的两个顶点u值分别等于0.9和0.1,在不启用纹理环绕时,他们之间的插值将在0,9~0.1上进行,而有的时候我们希望它在0.9~1.0连上0.0~0.1上进行,这时就可以通过指定WRAP_U实现。
环绕纹理寻址模式是默认的寻址模式,而纹理环绕默认是不启用的。
今天我做了个例子,在例子中球面的纹理需要启用u方向上的纹理环绕,然而我发现它并不工作。
球面的纹理坐标是在VertexShader中通过球的顶点位置坐标计算出来的,计算方法如下
const float HALF_PI = 3.1415926/2;
Out.UV.x = atan2(In.Position.z,In.Position.x) /HALF_PI;
Out.UV.y = In.Position.y;
事实上这是一个单位球,这个的计算使得 u∈[0,4), v∈[-1,1],然后利用了wrap寻址模式进行寻址。然而,在边界处左右的顶点的u值分别是3.x和0.x,我期望在启用纹理环绕的情况下可以在3.x~4.0连上0.0~0.x上插值,但事实情况似乎是在3.x~0.x上直接做了插值才导致了上面图中的问题。
解决方法是将Shader改为
const float HALF_PI = 3.1415926/2;
Out.UV.x = atan2(In.Position.z,In.Position.x) /HALF_PI;
Out.UV.x = fmod(Out.UV.x, 1); //手动实现了wrap寻址模式
Out.UV.y = In.Position.y;
让u在区间[0,1]上,这时纹理环绕就正常工作了。
我用颜色输出了uv,可以看到明显的1.0和0.0的边界,说明插值是按照我需要的wrapping方法,从贴图的结果也可以看到是正常的。
结论:纹理环绕要求纹理坐标在[0,1]范围才能正常工作。也可以认为wrap寻址和texture wrapping不能同时工作。