《Unity Shader入门精要》 第五章 开始学习Unity Shader之旅 笔记

开始学习Unity Shader之旅

最简单的片元着色器

最简单的片元着色器代码

#pragma vertex vert
#pragma fragment frat

告诉Unity那个函数包括顶点着色器的代码,哪个函数包括片元着色器的代码。

float4 vert(float4 v : POSITION) SV_POSITION{
    return mul(UNITY_MATRIX_MVP,v);
}

vert 中包含了这个顶点的位置,这是通过 POSITION 语义定义的。
输出值为裁切空间中的顶点坐标,这是通过 SV_POSITION 语义定义的。

//使用一个结构体来定义顶点着色器的输入
	struct a2v {
			//POSITION 语义告诉 Unity,用模型空间的顶点坐标填充vertex变量
			float4 vertex : POSITION;
			//NORMAL 语义告诉Unity,用模型空间的法线方向填充normal变量
			float3 noremal : NORMAL;
			//TEXCOORD0语义告诉Unity,用模型的第一套纹理坐标填充texcoord变量
			float4 texcoord : TEXCOORD0;
	};

	float4 vert(a2v v) : SV_POSITION {
			return UnityObjectToClipPos(v.vertex);
	}

需要获取多个顶点信息,需要创建一个结构,并且作为参数传递给顶点着色器代码块。

顶点着色器与片元着色器通信

在顶点着色器代码块中,返回值输出一个与片元着色器参数类型相同的结构体。

v2f vert(a2v v)
{
	v2f o;
	o.pos = UnityObjectToClipPos (v.vertex);
	o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
	return o;

fixed4 frag(v2f i) : SV_Target
{
	return fixed4(i.color, 1.0);
}

如何使用属性

Properties语义块中可以定义一些参数由编辑器的材质面板调节。

  • 首先我们添加了Properties语义块,并在其中声明一个属性_Color,它的类型是Color,初始值是(1.0,1.0,1.0,1.0);
  • 为了在CG代码中访问他,我们需要在CG代码中提前定义一个变量,变量名称和类型必须与Properties语义块中的属性定义相匹配。

ShaderLab属性类型和CG变量类型的匹配关系

ShaderLab属性类型 CG变量类型
Color,Vector float4,half4,fixed4
Range,Float float,half,fixed
2D sampler2D
Cube samplerCube
3D sampler3D

Unity 提供的内置文件和变量

包含文件,类似c++中的头文件的一种。在Unity中它们的后缀为.cginc。使用#include可以把它们包含进来。
这些文件可以在官方网站上选择下载->内置着色器来直接下载。这些文件是非常好的参考资料,在我们想要学习内置着色器的实现或是寻找内置函数的实现时,都可以在这里找到内部实现。
我们可以直接在Unity的应用程序中找到
Mac:/Application/Unity/Unity.app/Content/CGIncludes
Windows:Unity安装路径/Data/CGIncludes

Unity中一些常用的包含文件

文件名 描述
UnityCG.cginc 包含了最常使用的帮助函数、宏和结构体
UnityShaderVariables.cginc 在编译Unity Shader时,会被自动包含进来。包含了许多内置的全局变量,如UNITY_MATRIX_MVP等
Lighting.cginc 包含了各种内置的光照模型,如果编写的是Surface Shader的话,会自动包含进来
HLSLSupport.cginc 在编译Unity Shader时,会自动包含进来。声明了很多用于跨平台编译的宏和定义

UnityCG.cginc 是最常接触的一个包含文件。

UnityCG.cginc中一些常用的结构体

名称 描述 包含的变量
appdata_base 可用于顶点着色器的输入 顶点位置、顶点法线、第一组纹理坐标
appdata_tan 可用于顶点着色器的输入 顶点位置、顶点切线、顶点法线、第一组纹理坐标
appdata_full 可用于顶点着色器的输入 顶点位置、顶点切线、顶点法线、四组(或更多)纹理坐标
appdata_img 可用于顶点着色器的输入 顶点位置、第一组纹理坐标
v2f_img 可用于顶点着色器的输出 裁剪空间中的位置、纹理坐标

UnityCG.cginc中一些常用的帮助函数

函数名 描述
float3 WorldASpaceViewDir(float4 v) 输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
float3 ObjSpaceViewDir(float4 v) 输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向
float3 WorldSpaceLightDir(float4 v) 仅可用于前向渲染。输入一个模型空间中的额顶点位置,返回世界空间中从该点到光源的光照方向。没有被归一化。
float3 ObjSpaceLightDir(float4 v) 仅可用于前向渲染中。输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向。没有被归一化。
float3 UnityObjectToWorldNormal(float3 norm) 把法线方向从模型空间转换到世界空间中
float3 UnityObjectToWorldDir(in float3 dir) 把矢量方向从模型空间变换到世界空间中
float3 UnityWorldToObjectDit(float3 dir) 把矢量方向从世界空间变换到模型空间中

Unity 提供的CG/HLSL语义

什么是语义

语义可以让Shader知道从哪里读取数据,并把数据输出到哪
之前代码中的POSITION NORMAL SV_POSIONT 等就是语义

Unity 支持的语义

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

语义 描述
POSITION 模型空间中的顶点位置,通常是float4类型
NORMAL 顶点法线,通常是float3类型
TANGENT 顶点切线,通常是float4类型
TEXCOORDn,如TEXCOORD0、TEXCOORD1 该顶点的纹理坐标,TEXCOORD0表示第一组纹理坐标,以此类推。通常是float2或float4类型
COLOR 顶点颜色,通常是fixed4或float4类型

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

语义 描述
SV_POSITION 裁剪空间中的坐标顶点,结构体中必须包含一个用该语义修饰的变量。等同于DirectX9中的POSITION,但最好使用SV_POSITION
COLOR0 通常用于输出第一组顶点颜色,但不是必须的
COLOR1 通常用于输出第二组顶点颜色,但不是必须的
TEXCOORD0~TEXCOORD7 通常用于输出纹理坐标,但不是必须的

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

语义 描述
SV_Target 输出值将会存储到渲染目标(render target)中。等同于DirexX 9中的COLOR语义,但最好使用SV_Target

调试

可以将法线,切线向量值对应到RGB颜色值,然后在画面颜色显示中来判断。
使用Unity中的帧调试器(Window->Frame Debugger)可以更方便进行调试。

帧调试器

  • 最上面的区域可以控制帧调试器开关,开启时滑动条可以重放渲染事件。如果选中的Draw Call是对一个纹理的渲染操作,这个渲染纹理就会显示在Game视图中。而且右侧面板上方可以选择只显示R、G、B、A之一。
  • 左侧的区域显示了所有事件的树状图,这个树状图中,每个叶子节点就是一个事件。我们可以从事件的命名了解这个事件的操作,例如Draw开头的事件通常就是一次Draw Call。
  • 右侧窗口显示事件的细节,例如几何图形的细节以及使用了哪个shader。

如果想要得到更多信息,还是需要使用外部工具。

渲染平台的差异

Unity的有点之一就是跨平台性很强,写一份代码就可以在多平台上运行。但绝大多数情况下,Unity为我们隐藏了这些细节,但有时候我们还是需要自己处理。

渲染纹理的坐标差异

  • OpenGL和DirectX中的屏幕坐标存在差异,OpenGL中坐标(0,0)点在左下角,而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来判断是否打开了抗锯齿。