Unity入门精要学习笔记(第五章)

时间:2024-03-22 14:16:50

一、Unity提供的内置文件和变量

1、内置的包含文件:类似于C++中头文件的一种文件。后缀为.cginc。我们可以使用#include指令把文件包含进来:    

        #include "UnityCG.cginc"。   

     路径为 Mac:  /Applications/Unity/Unity.app/Contents/CGIncludes;        Windows:  Unity安装路径/Data/CGIncludes

     CGIncludes中主要的包含文件及作用:

     Unity入门精要学习笔记(第五章)

    UnityCG.cginc是我们最常接触的一个包含文件,该文件提供的结构体和函数为我们的编写提供方便,例如我们可以直接使用UnityCG.cginc中预定义的结构体作为顶点着色器的输入和输出。UnityCG.cginc中常用的结构体如下:

    Unity入门精要学习笔记(第五章)

    UnityCG.cginc中常用的帮助函数如下

    Unity入门精要学习笔记(第五章)

二、Unity提供的CG/HLSL语义

1、什么是语义:语义实际上就是一个赋给Shader输入和输出的字符串,这个字符串表达了这个参数的含义,通俗地讲,这些语义可以让shader知道从哪里读取数据,并把数据输出到哪里。他们是在CG/HLSL的Shader流水线中是不可或缺的。

        通常情况下,这些输入输出变量并不需要有特别的意义,也就是说我们可以执行决定这些变量的用途。在DirectX 10以后,有了一种新的语义类型-系统数值语义。这类语义以SV开头,SV代表系统数值。这些语义在渲染流水线中有特殊的含义。用这些语义描述的变量不可以随便赋值。

        在绝大多数平台上,SV_POSITION和POSITION语义是等价的,但某些平台上必须使用SV_POSITION来修饰顶点着色器的输出,否则无法让Shader正常工作。同样的例子还有COLOR和SV_Target。因此,对于这些有特殊含义的变量,我们最好使用以SV开头的语义进行修饰。

2、Unity支持的语义

                                从应用阶段传递模型数据给顶点着色器时Unity支持的常用语义   

Unity入门精要学习笔记(第五章)

        TEXCOORDn中n的数目是和Shader Model有关的,一般在Shader Model2和Shader Model3中,n等于8,而在Shader Model4和Shader Model 5中,n等于16.通常一个模型的纹理坐标组数不超过2,即我们往往只使用TEXCOORD0和TEXCOORD1.

                               从顶点着色器传递   数据给片元着色器时Unity使用的常用语义

Unity入门精要学习笔记(第五章)

        上面的语义中,除了SV_POSITION有特别含义外,其他语义对变量的含义没有明确要求。即我们可以存储任意值到这些语义描述变量中。通常如果我们需要把一些自定义的数据从顶点着色器传递给片元着色器,一般选用TEXCOORD0等。

                                片元着色器输出时Unity支持的常用语义

                 语        义                                                                        描      述
    SV_Target     输出值会存储到渲染目标中,等同于DirectX 9中的COLOR语义,但最好使用SV_Target
二、Debug

1、使用假彩色图像

        假彩色图像指的是用假彩色技术生成的一种图像。与假彩色图像对应的是真彩色图像。假彩色图像可以用于可视化一些数据。主要思想:把需要调试的变量映射到[0,1]之间作为颜色输出到屏幕上,然后通过屏幕上显示的像素颜色来判断值是否正确。

2、使用Visual Studio

        在Visual Stidio 2012 中提供了对UnityShader的调试-Graphics Debugger。通过Graphics Debugger,我们不仅可以查看每个像素的最终颜色、位置等信息,还可以对顶点着色器和片元着色器进行单步调试。限制:需要运行在DirectX 11 平台。

三、渲染平台的差异

1、渲染纹理的坐标差异

        OpenGL和DirectX的屏幕空间坐标差异:在水平方向上,两者的数值变化方向是相同的,但在竖直方向上,两者是相反的。在OpenGL中,原点对应了屏幕的左下角,而在DirectX中,原点对应了左上角。

        当我们需要同时处理多张渲染图像(前提是开了抗锯齿)例如同时处理屏幕图像和法线纹理,这些图像在竖直方向的朝向就可能是不同的(DirectX平台)这时,我们就需要自己在顶点着色器中翻转某些渲染纹理的纵坐标。例如:

        Unity入门精要学习笔记(第五章)      

        UNITY_UV_START_AT_TOP用于判断当前平台是否是DirectX类型的平台,当这样的平台下开启了抗锯齿后,主纹理的纹素大小在竖直方向上会变成负值,以方便我们对主纹理进行正确的采样。因此,我们可以通过判断_MainTex_TexelSize.y是否小于0来检验是否开启了抗锯齿。如果是,我们就需要对除主纹理外的其他纹理的采样坐标进行竖直方向上的翻转。

2、语义差异

        一些语义(SV_POSITION和POSITION)在某些平台下是等价的,但在另一些平台上不等价。我们应该尽量:

        1)使用SV_POSITION来描述顶点着色器输出的顶点位置。

        2)使用SV_Target来描述片元着色器的输出颜色。

四、shader代码规范

1、float、half和fixed

        在CG/HLSL中,有三种精度的数值类型:float,half和fixed。

        Unity入门精要学习笔记(第五章)

        通常来说,大多数现代的桌面GPU会把所有计算按最高浮点精度进行计算,也就是说float,half,fixed在这些平台上实际是等价的。但在移动平台的GPU上,的确会有不同的精度范围,而且不同精度的浮点值的运算速度也会有所差异。fixed精度实际上只在一些旧的移动平台上有用,多数现代的GPU上,它们内部把fixed和half当成同等精度来对待。

        尽可能使用精度较低的类型(优化shader的性能),例如使用fixed类型来存储颜色和单位矢量,如果要存储更大范围的数据可以选择half类型,最差情况下再选择使用float。如果目标平台是移动平台,一定要确保在真实的手机上测试shader。

2、规范语法

        如果要发布到DirectX平台上就需要使用更严格的语法,例如使用和变量类型相匹配的参数数目来对变量进行初始化。

3、避免不必要的计算

        temporary register limit of 8 exceeded 或者

        Arithmetic instruction limit of 64 exceeded;65 arithmethic instructions needed to compile program

        这些错误大多是因为在shader中进行了过多的运算,使得需要的临时寄存器数目或指令数目超过了当前可支持的数目。通常我们可以通过指定更高等级的Shader Target来消除这些错误。Unity目前支持的Shader Target如下

        Unity入门精要学习笔记(第五章)

        注意:所有类似OpenGL的平台(包括移动平台)被当成是支持到Shader Model 3.0的,而WP8/WinRT平台则只支持到Shader Model 2.0。Shader Model等级越高,shader能力越大。

4、慎用分支和循环语句

        if-else、for和while这种流程控制指令在GPU上的实现和在CPU上有很大的不同。大体说,GPU使用了不同于CPU的技术来实现分支语句,流程控制语句会降低GPU的并行处理操作。如果在Shader中使用了大量的流程控制语句,该性能可能会成倍下降。解决办法:尽量把计算向流水线上端移动,如放在片元着色器中的计算放到顶点着色器中,或直接在CPU中进行预计算再把结果传递给Shader。如果必须使用分支语句进行运算,那么:分支判断语句中使用的条件变量最好是常数;每个分支包含的操作指令数尽可能少;分支的嵌套层数尽可能少。

5、不要除以0:对于除数可能为0的情况,强制截取到非0范围。