在Unity中,材质(Material)、着色器(Shader)和纹理(Texture)三者的关系非常密切。一个材质指定使用一个着色器,着色器则决定了材质可以使用的属性;一个着色器可以设置一个或多个纹理。
属性列表(properties)里的属性会显示在材质面板。HLSL代码需要访问列表中的属性时,要先根据变量类型映射进行声明。
每一个着色器(shader)有一个或多个子着色器(subshader)组成,至少有一个。Unity会遍历所有的子着色器并选择第一个被机器支持的着色器进行渲染,如果所有子着色器均不符合,则使用备用着色器(fallback shader)。属性值可以通过三种方式传递到着色器
每个子着色器中有一个或多个Pass,每个Pass表示用块区域内的顶点着色器和片段着色器渲染使用该着色器的对象。其中,UsePass指令可以调用其它着色器的Pass。
语义(semantics)用于声明变量的用途。
表面着色器(surface shader)
定义一个表面函数(surface function),输入为uv坐标等数据,输出为一个结构体SurfaceOutput,函数接口为void surf (Input IN, inout SurfaceOutput o)。表面着色器编译器会根据输入输出生成真正的顶点/片段着色器。
表面着色器代码在CGPROGRAM..ENDCG块中编写,并且只能写在SubShader块中,而不能写在Pass块区域内(与其它着色器不同)。
指定表面函数的语句为 #pragma surface surfaceFunction lightModel [optionalparams],lightModel可以使用Unity内建光照模型或自定义光照模型,具体参数含义查阅官方文档。
顶点/片段着色器(vertex/fragment shader)
着色器程序用HLSL语言编写,写在Pass块区域中,并且一般写在关键字CGPROGRAM和ENDCG之间。每一个程序片段至少包含一个顶点着色器和一个片段着色器程序,即必须有#pragma vertex和#pragma fragment指令。
固定函数着色器(fixed function shader)
使用替换着色器进行渲染(Rendering with Replaced Shaders)
通过调用Camera.RenderWithShader或者Camera.SetReplacementShader可以进行着色器替换,这两个函数都需要一个着色器(shader)和一个替换标签(replacementTag)。
相机会如常渲染场景,所有对象仍然使用自己的材质,但真正起作用的着色器会发生变化:
- 如果替换标签为空,则场景中所有对象会使用替换着色器进行渲染。
- 如果替换标签不为空,则对象按照以下规则进行渲染:
- 查询对象着色器的标签值;
- 如果对象的着色器没有替换标签,则对象不被渲染;
- 如果替换着色器的某个子着色器的替换标签的值跟对象着色器的替换标签的值一致,则用该子着色器对对象标签进行渲染,否则对象不被渲染。
- (替换着色器的LightMode要设置为ForwardBase?)
相机的深度纹理(Camera's Depth Texture)
DepthTextureMode.Depth texture
如果使用surface着色器,增加一个addshadow指令。
子着色器标签(SubShader Tags)
子着色器使用标签来告诉渲染引擎渲染某个对象的时间和方式。便签主要以键值对的形式存在,它们决定了子着色器的渲染顺序以及其它参数。要注意的是以下标签必须位于子着色器的域内,而不能放到Pass的域内。
- Queue标签
Queue标签决定了对象所处的渲染队列。一个着色器指定了其对象的渲染队列,比如,所有Transparent的着色器确保其对象在非透明对象之后渲染。Unity中有4个预定义的渲染队列,但在实际使用中可以指定更多的渲染队列。在Unity内部实现中,每个标签用一个整形索引值表示。
- Background - 索引值为1000,该渲染队列在所有其它队列之前,该队列适合用于渲染背景
- Geometry(default) - 索引值为2000,大部分对象处于该渲染队列,尤其是不透明的几何体
- AlphaTest - 索引值为2450,进行alpha test的对象处于该渲染队列,这类对象在不透明的物体后进行统一渲染效率更高
- Transparent - 索引值为3000,在Geometry和AlphaTest之后,按从后到前的顺序渲染。所有进行alpha混合(alpha-blended),即着色器不写深度缓存(depth buffer)的物体应该放在此渲染队列,比如玻璃、粒子特效
- Overlay - 索引值为4000,该渲染队列用于渲染覆盖效果,所有最后渲染的物体应该放到此队列,比如镜头光晕
Tags{ "Queue" = "Geometry+1" }
通过这种方式指定一个渲染队列,该队列的索引值为2001。在某些情况下,希望某批对象总在另外一批对象之后渲染,比如水要在不透明的物体之后,但在透明的物体之前渲染,可以通过这种方式进行控制。
索引值在2500("Geometry+500")前的队列被视为“不透明”队列,其渲染顺序会被优化以提高性能。索引值高于2500的队列则被视为"透明"队列,其对象根据距离排序,按照从远到近的顺序渲染。天空盒的渲染顺序位于不透明和透明队列之间。
TODO: Stencil ZTest
参考资料
Unity_Shader初级篇_5_Unity Shader入门精要
Unity 制作类似RadiusFill 的SpriteRanderer Shader
Unity Shader教程(一)Toon shader(卡通着色)
Shader error: errorcannot map expression to vs_4_0 instruction
unity shader Offset Factor, Units详解
Unity Shader: 理解Stencil buffer并将它用于一些实战案例(描边,多边形填充,反射区域限定,阴影体shadow volume阴影渲染)
学习进度:Accessing shader properties in Cg/HLSL