1、渲染纹理的坐标差异
2、渲染纹理
我们不仅可以把渲染结果输出到屏幕上,还可以输出到不同的渲染目标(Render Target)中。这时,我们需要使用渲染纹理(Render Texture)来保存这些渲染结果。大多数情况下,这样的差异并不会对我们造成任何影响。但当我们要使用渲染到纹理技术,把屏幕图像渲染到一张渲染纹理中时,如果不采取任何措施的话,就会出现纹理翻转的情况。幸运的是,Unity在背后为我们处理了这种翻转问题——当在DirectX平台上使用渲染纹理技术时,Unity会为我们翻转屏幕图像纹理,以便在不同平台上达到一致性。
在一种特殊情况下Unity不会为我们进行这个翻转操作,这种情况就是我们开启了抗锯齿(在Edit->Project Settings->Quality->Anti Aliasing中开启)并在此时使用了渲染到纹理技术。在这种情况下,Unity首先渲染得到屏幕图像,再由硬件进行抗锯齿处理后,得到一张渲染纹理来供我们进行后续处理。此时,在DirectX平台下,我们得到的输入屏幕图像并不会被Unity翻转,也就是说,此时对屏幕图像的采样坐标是需要符合DirectX平台规定的。如果我们的屏幕特效只需要处理一张渲染图像,我们仍然不需要在意纹理的翻转问题,这是因为在我们调用Graphics.Blit()函数时,Unity已经为我们对屏幕图像的采样坐标进行了处理,我们只需要按正常的采样过程处理屏幕图像即可。但如果我们需要同时处理多张渲染图像(前提是开启了抗锯齿),例如需要同时处理屏幕图像和法线纹理,这些图像在竖直方向的朝向就可能是不同的(只有在DirectX这样的平台上才有这样的问题)。这种时候,我们就需要自己在顶点着色器中翻转某些渲染纹理(例如深度纹理或其他由脚本传递过来的纹理)的纵坐标,使之都符合DirectX平台的规则。例如:
#if UNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y < 0)
uv.y = 1 - uv.y;
#endif
其中,UNITY_UV_STARTS_AT_TOP用于判断当前平台是否是DirectX类型的平台,而当在这样的平台下开启了抗体锯齿后,主纹理的纹素大小在竖直方向上会变成负值,以方便我们对主纹理进行正确的采样。因此,我们可以通过判断_MainTex_TexelSize.y是否小于0来检验是否开启了抗体锯齿。如果是,我们就需要对除主纹理外的其他纹理的采样坐标进行竖直方向上的翻转。
3、Shader的语法差异
在OpenGL中可以这样写
float4 v = float4(0.0);
在DirectX11平台上必须这样写
float4 v = float4(0.0, 0.0, 0.0, 0.0);
否则,报错: incorrect number of arguments to numeric-type constructor (compiling for d3d11)
在表面着色器的顶点函数中必须要对out修饰的参数进行初始化
void vert (inout appdata_full v, out Input o) { //使用Unity内置的UNITY_INITIALIZE_OUTPUT宏对输出结构体o进行初始化 UNITY_INITIALIZE_OUTPUT(Input, o); // ... }否则,报错:output parameter 'o' not completely initialized (compiling for d3d11)
除了上述两点语法不同外,DirectX 9/11也不支持在顶点着色器中使用tex2D函数。tex2D是一个对纹理进行采样的函数。之所以DirectX 9/11不支持顶点阶段中的tex2D运算,是因为在顶点着色器阶段Shader无法得到UV偏导,而tex2D函数需要这样的偏导信息(这和纹理采样时使用的数学运算有关)。如果我们的确需要在顶点着色器中访问纹理,需要使用tex2Dlod函数来替代,如:
tex2Dlod(tex, float4(uv, 0, 0));
而且我们还需要添加#pragma target 3.0,因为tex2Dlod是Shader Model 3.0中的特性。
4、Shader的语义差异
为了让Shader能够在所有平台上正常工作,我们应该尽可能使用下面的语义来描述Shader的输入输出变量:
5、其他平台差异
如果读者发现一些Shader在平台A下工作良好,而在平台B下出现了问题,可以去Unity官方文档(http://docs.unity3d.com/Manual/SL-PlatformDifferences.html)中寻找更多资料。