原文链接
版权声明:本文为CSDN博主「_delta」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
该系列主要是根据 Unity 官方文档和个人搜集的知识对 Unity ShaderLab 框架进行总结,比较基础,用于提高知识学习、复习的效率。对于文中给到的文章链接的作者表示感谢
1、Properties
Properties
属性是 ShaderLab 当中比较容易理解的,在 Shader 当中定义了变量之后,它可以 将 Shader 当中所用到的变量暴露出来,方便我们在材质面板进行调节和在代码中进行调用 。
下面展示一个 ShaderLab 当中比较全面的属性代码:
Properties{
//数值和滑动条
_Num1("Num1Name",Range(8,256)) = 20
_Num2("Num2Name",Float) = 1
_Num3("Num3Name",Int) = 1
//颜色和向量
_Color("ColorName",Color) = (1,1,1,1)
_Vector("VectorName",Vector) = (1,1,1,1)
//纹理
_Texture1("Texture1Name",2D) = "defaulttexture"{}
_Texture2("Texture2Name",Cube) = "defaulttexture"{}
_Texture3("Texture3Name",3D) = "defaulttexture"{}
}
对应的材质 inspector
更多细节
其中 2D 纹理可以设定 Unity 内置的默认纹理,比如将上面的 defaulttexture 写为
white(RGBA: 1,1,1,1),black(RGBA: 0,0,0,0),gray(RGBA: 0.5,0.5,0.5,0.5),bump(RGBA: 0.5,0.5,1,0.5)- 或者
red(RGBA: 1,0,0,0)。
Cube 和 3D 纹理在材质没有被外部赋值时,则默认采用 gray (RGBA: 0.5,0.5,0.5,0.5)。
属性特性
在每一个属性代码的前面加 [特性名] 可以设置属性在面板上面的显示和配置方式,Unity 可以辨识的属性特性有:
[HideInInspector]- 在材质面板上不显示该属性[NoScaleOffset]- 材质面板不显示纹理的缩放和偏移控制输入框,也就是上图中的 Tilling 和 Offset。[Normal]- 表示该纹理需要一个法线纹理输入[HDR]- 表示需要高动态范围的纹理或者颜色[Gamma]- 表示该向量或者浮点数值在 UI 中以 sRGB 来指定的,需要采用 Gamma 矫正。
- 具体了解参考这位大佬文章:Gamma、Linear、sRGB 和 Unity Color Space,你真懂了吗?
- 还有这篇:Gamma & Linear Color Space
[PerRendererData]- 表示纹理属性将以 MaterialPropertyBlock 的形式来自每个渲染器数据。
在 MaterialPropertyBlock 中设置的
Per-Renderer值。表示这通常是 “每个实例” 的数据(例如,为许多共享相同材质的对象个性化定义色调颜色)。具体可参看相关官方文档链接 Accessing shader properties in Cg/HLSL
上面特性的具体效果可以自行测试。
2、SubShader、Fallback
SubShader
ShaderLab 是由一个或者多个 SubShader 组成的,当 Unity 渲染网格模型时,会在 Shader 文件即 ShaderLab 框架当中寻找 能在当前显卡上运行的最高级的效果最好的 SubShader 来使用,SubShader 由一系列的 Pass 组成。Pass 当中 包含着真正的 Shader 的具体代码。ShaderLab 或者说 Shader 文件的框架大概如下:
Shader "Shader文件路径名"{
Properties{
……
}
SubShader{
Pass{
……
}
Pass{
……
}
……
}
SubShader{
Pass{
……
}
Pass{
……
}
……
}
……
}
SubShader Tag
SubShader 使用 Tag 来 控制自身何时怎样被引擎使用来渲染。
Tag 使用的基本方法如下:
SubShader{
Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
Pass{
……
}
Pass{
……
}
……
}
在大括号内可以指定任意数量的键值对,Unity 有一些 预设的键值对:
Rendering Order
渲染顺序键,分别对应着几种预设的值,每个顺序值在内部实现上都是整数
Background- 对象最先渲染,值为 1000Geometry(default) - 是渲染顺序的缺省值,渲染绝大多数不透明的网格,值为 2000AlphaTest- 通过 alpha 测试的 geometry 使用此队列。这是与“Geometry ” 相分离的队列,因为在绘制所有实体(Solid)后,可以更有效地渲染通过 Alpha 测试的对象。值为 2450Transparent- 在 “Geometry” 和 “AlphaTest” 之后渲染,当中的网格按照从后往前的顺序渲染,所有使用alpha-blended的( shader 当中不进行深度写入的 )在这个渲染队列当中(比如玻璃,粒子),值为 3000Overlay- 最后渲染的对象,比如光晕、UI,值为 4000
因为队列值为整数,所以可以自定义非预设的值,比如 "Queue" = "Geometry+1" ,表示在所有的 Geometry 对象之后渲染。
RenderType
用来给 Shader 进行分类,比如 Unity 内置的一些常用 RenderType,如Opaque, Transparent, Background, Overlay,另外还可以自定义 RenderType,可以用来进行材质替代渲染,具体实现参考之前一篇文章:Unity材质替代渲染相关: RenderWithShader & SetReplacementShader
ForceNoShadowCasting
如果对应值为真,则使用该 Subshader 则不会进行阴影投射,不执行 Shadow Pass
IgnoreProjector
如果对应值为真,则忽略 Projector 的影响,Projector 具体参照官方文档:Projector,比较容易理解。
CanUseSpriteAtlas
如果值为假,则 shader 使用 sprite,那么当 sprite 装入图集之后 shader 将不起作用。
PreviewType
控制材质球在预览面板的显示方式,默认展示球形,可以设置为 Plane、 Skybox
Fallback
在所有的 Subshader 定义之后,可以定义 Fallback,表示如果没有 Subshader 可以运行在当前的硬件环境上,那么就使用一个已定义的shader (这不就是传说中的 备胎 嘛, T_T):
Fallback "name"
使用下面的语句表示即使所有的 Subshader 都不能运行,也不使用其他的 shader 并且不允许报错。
Fallback Off
3、Custom Shader GUI
有时 shader 的一些属性数据 无法在编辑器面板上很好的展示出来,所有 Unity 允许用户自定义 shader 的显示面板,比如增加其他的控制方式。
在 ShaderLab 的 Subshader 外写下如下语句:
CustomEditor "name"
Unity 会自动寻找名为 name 的类,该类需要继承自 ShaderGUI 类,每一个定义上述语句的 shader 都会实例化一个 name 类的实例并执行里面的代码。
下面根据个人理解解释一下官方案例:
自定义的表面着色器:
Shader "Custom/Redify" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
__SubShader__ {
Tags { "RenderType"="Opaque" }
__LOD__ 200
CGPROGRAM
#pragma surface surf Lambert addshadow
#pragma shader_feature REDIFY_ON //通过 shader_feature 编译指令定义关键字
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
#if REDIFY_ON // 如果定义关键字,那么就改变输出的 Albedo 颜色
o.Albedo.gb *= 0.5;
#endif
}
ENDCG
}
CustomEditor "CustomShaderGUI"
}
自定义的 CustomShaderGUI 类
继承自 ShaderGUI:
using UnityEngine;
using UnityEditor;
using System;
public class CustomShaderGUI : ShaderGUI
{
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
// render the default gui
base.OnGUI(materialEditor, properties);
Material targetMat = materialEditor.target as Material;
// see if redify is set, and show a checkbox
bool redify = Array.IndexOf(targetMat.shaderKeywords, "REDIFY_ON") != -1;
EditorGUI.BeginChangeCheck();
redify = EditorGUILayout.Toggle("Redify material", redify);
if (EditorGUI.EndChangeCheck())
{
// enable or disable the keyword based on checkbox
if (redify)
targetMat.EnableKeyword("REDIFY_ON");
else
targetMat.DisableKeyword("REDIFY_ON");
}
}
}
和之前说的一样,在 shader 文件中写下如下语句:
CustomEditor "CustomShaderGUI"
Unity 查找到对应的类并执行代码,另外 关键的是,shader 中使用编译指令定义了关键字 —— REDIFY_ON (这部分和编译指令以及 shader 变体有关,具体参考这篇文章:Shader入门(二十一)多重变体(Multiple Variants))。
总之,通过控制该关键字的定义,可以 产生不同效果 的 shader。在 CustomShaderGUI 类的 OnGUI 函数中进行检测,如果 GUI 发生变动(即 checkbox 被手动更改),那么根据 redify 的值来控制是否定义 shader 当中的 REDIFY_ON 关键字,从而达到控制输出的目的。
目前还没有应用到这方面知识,之后会进行更复杂的实现。