当我们查看别人的shader,如果没有在代码里找到声明那多半是使用了UNITY内置的文件和变量。
一、包含文件
UNITY可以使用#include 来包含部分文件,文件后缀.cginc,类似C++头文件/java的包
例如
CGPROGRAM
...
#include "UnityCG.cginc"
...
ENDCG
通过这种方式可以引用UNITY已经封装好的函数/变量我们可以通过 http://unity3d.com/cn/get-unity/download/archive 下载(虽然网站没法访问)
常用的UNITY内置文件
UnityCG.cginc 包含了最常用的函数结构体和宏等
UnityShaderVariables.cginc 在编译UNITY SHADER时 会自动被包含进来 ,包含了很多全局变量 如 UNITY_MATRIX_MVP转换矩阵
Lighting.cginc 包含了各种光照模型,如果编写Surface Shader的话 会被自动包含进来
HLSLSupport.cginc 在编译UNITY SHADER时被自动包含进来,声明了很多用于跨平台编译的宏和定义
UnityStandardBRDF.cginc、UnityStandardCore.cginc 这些文件里面包含了用于基于物理的渲染
-----------------------------------------------------------
其中UnityCG.cginc是最常用的组件 他里面提供了很多常用的定义和文件
常用结构体名 | 包含变量 |
appdata_base | 顶点位置 顶点法线 第一组纹理坐标 |
appdata_tan | 顶点位置 顶点法线 顶点切线 第一组纹理坐标 |
appdata_full | 顶点位置 顶点法线 顶点切线 第四组(或者更多)纹理坐标 |
appdata_img | 顶点位置 第一组纹理坐标 |
v2f_img | 裁剪空间中的位置 纹理坐标 |
UnityCG.cginc常用的函数
函数名 | 描述 |
float3 WordSpaceViewDir(float4 v) | 输入一个模型空间的顶点位置,返回在世界空间中这个点到摄像机的方向 |
float3 ObjSpaceViewDir(flaot4 v) | 输入一个模型空间的顶点位置,返回在模型空间中这个点到摄像机的方向 |
float3 WordSpaceLightDir(float4 v) | 仅可用于前向渲染,输入一个模型空间的顶点,返回世界空间中该点到光源的光照方向,没有被归一化 |
flaot3 ObjSpaceLightDir(float4 v) | 仅可用于前向渲染,输入一个模型空间的顶点,返回模型空间中该点到光源的光照方向,没有被归一化 |
float3 UnityObjToWorldNormal(flaot3 normal) | 把法线方向从模型空间转换到世界空间 |
float3 UnityObjToWorldDir(int float3 dir) | 把方向矢量从模型空间转换到世界空间中 |
float3 UnityWorldToObjDir(int float3 dir) | 把方向矢量从世界空间转换到模型空间中 |
二、内置变量
除了上述常用的函数和文件,unity还提供了很多常用的变量,如用于访问环境光、雾效、时间、光照等目的的变量,这些变量大都在UnityShaderVariables.cginc中
光照的变量还位于 Lighting.cginc 和AutoLight.cginc
三、Unity支持的语义
SV_POSITION/SV_Target/POSITION/COLOR0等等都是CG/HLSL提供的语义
可以在微软的DirectX的文档里找到说明(网址倒是能打开 ,就是没看懂)
http://msdn.microsoft.com/en-us/library/windows/desktop/bb509674(v=vs8.5).aspx#VS
语义其实就是赋给shader的一个输入输出的字符串,这个字符串表达了参数的含义,这些语义可以让shader知道从哪里读取数据,并把数据输出到哪里
他们在CG/HLSL的shader流水线中是不可获取的,需要注意的是 Unity并没有支持所有的语义
通常情况下 这些输入输出的参数并不需要特别有意义,我们可以自行决定这些变量的用途.变量本身存储什么 shader并不关心
UNITY为了方便数据传输,对一些特殊的语义进行了特殊的含义规定,例如TEXCOORD0,在顶点着色器的入参结构体a2v的内部我们用它描述texcoord,UNITY会识别该语义并把第一组纹理坐标填充给texcoord,需要注意即使语义一样出现的位置不同,意义也可能不同,如TEXCOORD0出现在输出结构体内时,这个修饰的变量由我自己定义。
在DirectX10之后出现了一种新语义,系统语义,SV开头 System Value Semantics
例如上面的SV_POSITION修饰pos,那么就表示pos包含了可以用于光栅化的变换后的顶点坐标,即齐次裁剪空间坐标,这些语义修饰的变量是不可随便赋值的,因为流水线需要靠他们去实现一些特殊的目的,例如渲染引擎会把SV_POSITION的坐标经过光栅化后显示在屏幕上,有的时候我们会看到同样的变量在不同的shader里用不同的语义修饰,例如一些shader会使用POSITION而不是SV_POSITION来修饰顶点的着色器输出,SV_POSITION 和POSITION在大多数平台上是等价的,但在某些平台上必须使用SV_POSITION来修饰输出,同样的情况还会在COLOR0和SV_Target上出现,所以我们尽量使用SV开头的修饰符。
语义名称 | 描述 |
POSITION | 模型空间中的顶点位置,通常是float4类型 |
NORMAL | 顶点法线,通常是float3类型 |
TANGENT | 顶点切线,通常是float4类型 |
TEXCOORDn | 第n组纹理坐标,通常是float2和float4类型 |
COLOR | 顶点颜色,通常是float4或者fixed4 |
TEXCOORDn的n是由shader model决定的 ,在shader model2 的时候 n最大为4,在shader model 3的时候为8,在shader model 4的时候为16
通常情况下 一个模型的纹理坐标不超过2,即我们往往只使用TEXCOORD0/TEXCOORD1,在UNITY内置的appdata_full中,最多使用6个坐标纹理组
从顶点着色器传递到片元着色器的常用语义
语义 | 描述 |
SV_POSITION | 裁剪空间里的顶点坐标,输出结构体必须包含这个修饰的变量,DirectX9里面是POSITION,但最好使用SV_POSITION |
COLOR0 | 通常用于输出的第一组颜色 但是不是必须的 |
COLOR1 | 通常用于输出的第二组颜色,但是不是必须的 |
TEXCOORD0~TEXCOORD7 | 通常用于输出纹理坐标,但是不是必须的 |
上面的语义 除了SV_POSITION之外,其他语义对变量没有明确的要求,也就是说我们可以存储任意值到语义描述的变量中,通常我们把一些自定义数据从顶点着色器传递到片元着色器,使用TEXCOORD0修饰。
片元着色器输出时使用的常用语义 SV_Target:输出值会存储到渲染目标Render Target中,等同于DirectX9中的COLOR语义,但最好使用SV_Target。
四、如何定义复杂的变量类型
上面提到的语义多半用来描述矢量或者标量类型的变量,例如fixed2 float float4 fixed4
下面的代码给出了一些使用语义来修饰不同变量的例子
struct v2f{
float4 pos:SV_POSITION;
fixed3 color0:COLOR0;
fixed4 color1:COLOR1;
half value0:TEXCOORD0;
float2 value1:TEXCOORD1;
}
需要注意 一个语义可以使用的寄存器最多只能使用四个浮点值,因此如果我们想要定义矩阵类型,如float3*4,float4*4等变量就需要使用更多的空间,另一种方法就是把这些变量拆分成多个变量,如float4*4 可以拆分成4个float4,每个变量存储一行矩阵的元素。
来源:oschina
链接:https://my.oschina.net/u/3678539/blog/4279581