Shader入门精要(三)UnityShader

第三章 UnityShader基础

正式进入shader学习了,好耶!

使用UnityShader的流程:

  1. 创建一个材质
  2. 创建一个UnityShader,为材质添加
  3. 把材质赋给对象
  4. 调整shader属性

image-20211130223524763

创建Shader

Unity包含了多种shader模板供我们使用。

Standard Surface Shader 标准光照模型
Unlit Shader 不包含光照(但包含雾效)
mage Effect Shader 实现各种屏幕后处理效果

Unity Shader的导入面板还可以方便地查看其使用的渲染队列(Render queue)、是否关闭批处理(Disablebatching)、属性列表(Properties)等信息。

ShaderLab

ShaderLab是Unity为我们抽象的一种shader语言,可以更方便的通用。

image-20211130224529408

基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Shader "ShaderName"{
Properties{
//属性

Name("display name", PropertyType)=DefaultValue
}
SubShader{
//A着色器
}
SubShader{
//B着色器

}
Fallback "VertexLit"
}

属性类型

image-20211130225328452

根据以上属性得到的Shader放置在材质上效果:

image-20211201122154053

SubShader

每个Unityshader可以包含多个SubShader,最少要有一个

加载时会自动选择第一个可以执行的SubShader,如果都不支持的话,使用FallBack指定的shader。(为了解决不同显卡的差异)

1
2
3
4
5
6
7
8
9
10
11
SubShader{
//可选的
[Tages]
//可选的
[RenderSetup]

Pass {

}
//other Passes
}

每个Pass定义了一次完整的渲染流程,Pass过多,性能下降。

状态设置

在SubShader中我们可以设置渲染的一些状态。

image-20211201123019333

在SubShader中设置的状态会应用到所有的Pass,也可以在Pass中单独设置。

1
Tags{"TagName1"="Value1" "TagName2"="CC"} //Tags是一些str键值对

SubShader标签类型

image-20211201123359479

这些标签只能在SubShader中申明不可在Pass中申明

Pass

1
2
3
4
5
6
Pass{
[Name]
[Tags]
[RenderSetup]
//Other
}

可以通过Name来使用Pass,例如:

UsePass "MyShader/PASSNAME"

默认Unity会将所有Pass名称转换为大写,所以使用时需要使用大写名称

Pass标签类型

image-20211201124000121

  • ·UsePass:如我们之前提到的一样,可以使用该命令来复用其他Unity Shader中的Pass;

  • ·GrabPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理(详见10.2.2节)。

FallBack

在SubShader后定义,如果上述的都不支持,用这个最低级的Shader吧!

1
2
3
Fallback "name"
//或者
Fallback off

FallBack 还会影响阴影的透射

Unity Shader形式

着色器代码可以写在SubShader中(表面着色器做法),也可以写在Pass语义块中(顶点/片元着色器和固定函数着色器的做法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Shader "MyShader"{
Properties{
//所需的各种属性

}
SubShader{
//真正意义上的shader代码会放在这里
//表面着色器(Surface Shader)
//顶点/片元着色器(Vertex/Fragment Shader)
//固定函数着色器(Fixed Function Shader)
}
SubShader{
//和上一哥SubShader类似
}
}

表面着色器(Surface Shader)

是Unity自己创造的一种着色器代码类型,需要的代码量少,渲染代价比较大

在背后,任然转换为顶点片元着色器。

简单表面着色器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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内的.

固定函数着色器

对于一些比较老旧的设备,不支持可编程管线着色器,就需要使用固定函数着色器

使用哪种形式?

  • 如果你想和各种光源打交道,你可能更喜欢使用表面着色器,但需要小心它在移动平台的性能表现。
  • 如果你需要使用的光照数目非常少,例如只有一个平行光,那么使用顶点/片元着色器是一个更好的选择。
  • 最重要的是,如果你有很多自定义的渲染效果,那么请选择顶点/片元着色器。

至此,第三章完!