US5L10——表面着色器编译指令
US5L10——表面着色器编译指令
本章代码关键字
1 |
|
表面着色器中的编译指令
表面着色器中最为关键的几个部分为:
- 编译指令
- 结构体
- 自定义函数
编译指令是表面着色器中用来和 Unity 沟通的重要方式,通过编译指令,我们可以告诉 Unity 需要让他做什么和不做什么
因为通过上节课我们知道表面着色器只需要实现少量代码,大部分代码(比如光照、阴影、反射、折射等)都交给 Unity 自动生成
表面着色器中编译指令的基本构成
表面着色器中编译指令的基本构成如下
1 |
它是由 4 个部分构成的:
- 固定声明部分
#pragma surface
- 表面函数名
- 光照模型
- 可选额外参数
示例:
1 |
固定写法
在表面着色器中编译指令的基本构成中,#pragma surface
是固定写法,是用于指明该编译指令是用于表面着色器的
在他后面我们必须填写表面函数和光照模型,还可以填写一些可选参数来控制表面着色器的行为
表面函数名
在表面着色器中编译指令的基本构成中,表面函数名可以随意取名,但是需要在后面的代码中有对应名字的函数
函数的参数列表有三种固定格式
-
void 表面函数名(Input IN, inout SurfaceOutput o)
-
void 表面函数名(Input IN, inout SurfaceOutputStandard o)
-
void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
其中 Input
为输入结构体,其中可以得到各种表面属性:SurfaceOutput
、SurfaceOutputStandard
和 SurfaceOutputStandardSpecular
它们是 Unity 内置的写好的用于输出的结构体,他们分别用于不同的工作流,可以配合不同的光照模型使用
我们之后就可以利用 Input
结构体中的数据进行计算,计算得到的结构赋值给输出结构体 o
中的成员
之后会自动传递给光照函数进行下一步计算(如果我们不自定义,Unity 会自动生成计算代码)
1 |
|
光照模型
在表面着色器中编译指令的基本构成中,光照模型是用来计算物体表面的光照效果的
Unity 内置了基于物理的光照模型函数 Standard
和 StandardSpecular
,还有简单的非基于物理的光照模型函数 Lambert
和 BlinnPhong
我们可以直接填写他们来应用对应的光照计算,同样我们也可以自定义光照模型计算相关,只需要在代码中按照格式书写以下函数
-
不依赖视角的光照模型,比如漫反射(函数名固定以
Lighting
开头)1
half4 Lighting自定义名字 (SurfaceOutput s, half3 lightDir, half atten)
-
依赖视角的光照模型,比如高光反射(函数名固定以
Lighting
开头)1
half4 Lighting自定义名字 (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
然后只需要在光照模型处填写 自定义名字
即可(不需要固定的 Lighting
),就会自动调用函数中的逻辑来处理光照相关逻辑了
1 |
|
可选额外参数
可选额外参数包含了很多非常有用的指令类型,如果要同时使用,直接编译指令在后面追加额外参数即可
关于所有的额外参数,可见:编写表面着色器 - Unity 手册
这里着重提及几个可选参数:
-
自定义顶点修改函数
vertex:顶点函数名
一般来说,
surf
函数主要用于处理物体表面颜色计算,如果需要处理顶点相关内容,例如顶点动画等,就需要自定义顶点函数
顶点函数要求无返回值,有一个由inout
修饰的 appdata_full 参数,
若要对顶点进行处理,直接修改传入参数的v.vertex
属性即可,不需要返回顶点1
2
3
4
5
6
7
void vert(inout appdata_full v)
{
// TODO.. 处理顶点相关逻辑
v.vertex = v.vertex * float4(1, 1, 1, 1);
} -
最终颜色修改函数
finalcolor:自定义最终颜色修改函数名
如果在纹理采样和光照计算等都计算完毕得到最终颜色后,还要在此基础上再做额外处理,就可以定义这个自定义最终颜色函数,
自定义最终颜色函数要求无返回值,参数列表为:Input IN, SurfaceOutput o, inout fixed4 color
,注意!
finalcolor
函数的第二个参数的类型取决于surf
表面函数的 inout
参数的结构体类型!- 若
surf
函数的函数列表为void 表面函数名(Input IN, inout SurfaceOutput o)
,
则对应的finalcolor
函数列表为Input IN, SurfaceOutput o, inout fixed4 color
- 若
surf
函数的函数列表为void 表面函数名(Input IN, inout SurfaceOutputStandard o)
,
则对应的finalcolor
函数列表为Input IN, SurfaceOutputStandard o, inout fixed4 color
- 若
surf
函数的函数列表为void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
,
则对应的finalcolor
函数列表为Input IN, SurfaceOutputStandardSpecular o, inout fixed4 color
其中,
color
就是我们最终会输出的颜色,若要对最终颜色进行处理,直接修改color
参数的值即可1
2
3
4
5
6
7
8
9
10
// 表面着色器的函数,inout 的参数类型为 SurfaceOutputStandard
void surf (Input IN, inout SurfaceOutputStandard o) { /*...*/ }
void FinalColorProcess(Input IN, SurfaceOutputStandard o, inout fixed4 color)
{
// TODO.. 对已经计算完的颜色再次进行处理
color = fixed4(o.Albedo, 1);
} - 若
-
阴影相关
-
addshadow
:为一些进行了顶点动画、透明度测试的物体显式的进行阴影投射,避免通过 FallBack 无法准确处理也就说相比使用
FallBack
阴影投射,使用此可选参数可以让阴影投射适配顶点动画、透明度测试等1
-
fullforwardshadows
:在前向渲染路径中支持所有光源类型和阴影1
-
noshadow
:禁用阴影1
-
-
透明相关
-
alphatest:变量名
利用它可以使用指定名字的变量来剔除不满足条件的片元注意!如果属性定义的透明测试剔除属性如果用在了这里
alphatest
可选参数上,
则不能再在着色器代码块内声明这个变量,否则就会报错,因为alphatest
这个可选参数会自动声明此变量!1
2
3
4
5
6
7
8
9
10
11
12
13Properties
{
// ...
_Cutoff("Cutoff", Range(0, 1)) = 0 // 透明测试阈值
}
SubShader
{
CGPROGRAM
// ...
ENDCG
}
-
-
光照
-
noambient
:不使用任何环境光和光照探针 -
novertexlights
:不使用任何逐顶点光照 -
noforwardadd
:去掉所有前向渲染中的额外Pass
,只会支持一个逐像素平行光,其他光源按照逐顶点/SH来计算 -
nofog
:关闭雾效处理 -
nolightmap
:关闭光照烘焙处理
-
-
控制代码生成
默认情况下,表面着色器自动生成的代码包含前向渲染路径、延迟渲染路径使用的
Pass
这会让Shader
文件较大,如果我们明确表面着色器只会在某些渲染路径中使用,可以使用如下可选额外参数-
exclude_path:deferred
:排除延迟渲染路径 -
exclude_path:forward
:排除前向渲染路径 -
exclude_path:prepass
:排除预通道渲染路径
这些来可选参数会明确告诉 Unity,不需要为某些渲染路径生成代码
-