# 第三章 UnityShader 基础
正式进入 shader 学习了,好耶!
使用 UnityShader 的流程:
- 创建一个材质
- 创建一个 UnityShader,为材质添加
- 把材质赋给对象
- 调整 shader 属性
# 创建 Shader
Unity 包含了多种 shader 模板供我们使用。
Standard Surface Shader | 标准光照模型 |
---|---|
Unlit Shader | 不包含光照(但包含雾效) |
mage Effect Shader | 实现各种屏幕后处理效果 |
Unity Shader 的导入面板还可以方便地查看其使用的渲染队列(Render queue)、是否关闭批处理(Disablebatching)、属性列表(Properties)等信息。
# ShaderLab
ShaderLab 是 Unity 为我们抽象的一种 shader 语言,可以更方便的通用。
# 基本结构
Shader "ShaderName"{ | |
Properties{ | |
// 属性 | |
Name("display name", PropertyType)=DefaultValue | |
} | |
SubShader{ | |
//A 着色器 | |
} | |
SubShader{ | |
//B 着色器 | |
} | |
Fallback "VertexLit" | |
} |
# 属性类型
根据以上属性得到的 Shader 放置在材质上效果:
# SubShader
每个 Unityshader 可以包含多个 SubShader,
最少要有一个
。
加载时会自动选择第一个可以执行的 SubShader,如果都不支持的话,使用 FallBack 指定的 shader。(为了解决不同显卡的差异)
SubShader{ | |
// 可选的 | |
[Tages] | |
// 可选的 | |
[RenderSetup] | |
Pass { | |
} | |
//other Passes | |
} |
每个 Pass 定义了一次完整的渲染流程,Pass 过多,性能下降。
# 状态设置
在 SubShader 中我们可以设置渲染的一些状态。
在 SubShader 中设置的状态会应用到所有的 Pass,也可以在 Pass 中单独设置。
Tags{"TagName1"="Value1" "TagName2"="CC"} //Tags 是一些 str 键值对 |
# SubShader 标签类型
这些标签只能在 SubShader 中申明不可在 Pass 中申明
# Pass
Pass{ | |
[Name] | |
[Tags] | |
[RenderSetup] | |
//Other | |
} |
可以通过 Name 来使用 Pass,例如:
UsePass "MyShader/PASSNAME"
默认 Unity 会将所有 Pass 名称转换为大写,所以使用时需要使用大写名称
# Pass 标签类型
・UsePass:如我们之前提到的一样,可以使用该命令来复用其他 Unity Shader 中的 Pass;
・GrabPass:该 Pass 负责抓取屏幕并将结果存储在一张纹理中,以用于后续的 Pass 处理(详见 10.2.2 节)。
# FallBack
在 SubShader 后定义,如果上述的都不支持,用这个最低级的 Shader 吧!
Fallback "name" | |
// 或者 | |
Fallback off |
FallBack 还会影响阴影的透射
# Unity Shader 形式
着色器代码可以写在 SubShader 中(表面着色器做法),也可以写在 Pass 语义块中(顶点 / 片元着色器和固定函数着色器的做法)。
Shader "MyShader"{ | |
Properties{ | |
// 所需的各种属性 | |
} | |
SubShader{ | |
// 真正意义上的 shader 代码会放在这里 | |
// 表面着色器(Surface Shader) | |
// 顶点 / 片元着色器(Vertex/Fragment Shader) | |
// 固定函数着色器(Fixed Function Shader) | |
} | |
SubShader{ | |
// 和上一哥 SubShader 类似 | |
} | |
} |
# 表面着色器 (Surface Shader)
是 Unity 自己创造的一种着色器代码类型,需要的代码量少,渲染代价比较大
在背后,任然转换为顶点片元着色器。
Shader "Custom/Simple"{
SubShader{
Tags{"RenderType"= "Opaque"}
CGPROGRAM
#pragma suface surf lambert
struct Input{
float4 color: COLOR;
}
void surf(Input In,in out SurfaceOutput o){
o.Albedo=1;
}
ENDCG
}
Fallback "Diffuse"
}
表面着色器不需要关心,有多少个 Pass,每个 Pass 如何渲染
CGPROGRAM 和 ENDCG 之间的代码是使用 CG/HLSL 编写的,也就是说,我们需要把 CG/HLSL 语言嵌套在 ShaderLab 语言中。
# 顶点片元着色器(Vertex/Fragment Shader)
Shader "Custom/Simple VertexFragment Shader"{ | |
SubShader{ | |
Pass{ | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag float4 vert(float4 v: POSITION):SV_POSITION{ | |
return mul(UNITY_MATRIX_MVP,v); | |
} | |
fixed4 frag():SV_Target{ | |
return fixed4(1.0,0.0,0.0,1.0) | |
} | |
ENDCG | |
} | |
} | |
} |
顶点 / 片元着色器的代码也需要定义在 CGPROGRAM 和 ENDCG 之间,但不同的是,顶点 / 片元着色器是写在 Pass 语义块内,而非 SubShader 内的.
# 固定函数着色器
对于一些比较老旧的设备,不支持可编程管线着色器,就需要使用固定函数着色器
# 使用哪种形式?
- 如果你想和各种光源打交道,你可能更喜欢使用表面着色器,但需要小心它在移动平台的性能表现。
- 如果你需要使用的光照数目非常少,例如只有一个平行光,那么使用顶点 / 片元着色器是一个更好的选择。
- 最重要的是,如果你有很多自定义的渲染效果,那么请选择顶点 / 片元着色器。
至此,第三章完!