《Unity Shader 入门摘要》阅读笔记
最近想要系统的学习一下unity shader相关的东西,根据别人的推荐看到了这本书。于是决定边看边把一些比较重要或者原本不知道的知识点记录一下。
原书讲解十分详细,这里只记录本人相关,欲知详情,请移步至作者github
第3章 Unity Shader基础
ShaderLab:Unity为shader开发者专门设计的语言,详情见官网
我们手动开发的Shader都是用ShaderLab实现的,当然中间需要内置CG/HLSL
代码,完成实际的过程,ShaderLab的作用更像是一个框架。
shader的完整结构如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24Shader "ShaderName" {
Properties {
// 属性
}
SubShader {
// 显卡A使用的着色器
[Tags] // 标签:可选的
[RenderSetup] // 状态:可选的
Pass {
[Name]
[Tags]
[RenderSetup]
// other code
}
// other Passes
}
SubShader {
// 显卡B使用的着色器
}
Fallback "default shader name" // Fall Off
}
Properties
支持如下类型:
Unity支持的Propertiies类型Subshader
的RenderSetup
支持如下类型,其对内部Pass
来说是全局存在的
常见的渲染状态设置选项Subshader
的Tags
支持如下类型:
SubShader的标签类型Pass
的Tags
支持如下类型:
Pass的标签类型UsePass
复用其他shader里的代码,GrabPass
抓取屏幕存储在一张纹理中供后续Pass使用
第4章
坐标空间与变换
- 坐标空间
- 模型空间:又称“对象空间”,顾名思义是与模型本身有关的空间
- 世界空间:广义上“最大的空间”
- 观察空间:摄像机所在的空间,并且摄像机的位置是原点
- 裁剪空间:摄像机实际上真正能看到的空间,由视锥体决定
- 屏幕空间:最终显现的二维屏幕(空间)
- 坐标空间变换
- 模型变换:模型空间 -> 世界空间
- 观察变换:世界空间 -> 观察空间
- 投影变换:观察空间 -> 裁剪空间
- 屏幕映射:裁剪空间 -> 屏幕空间
转换关系图如下:
Unity内置的变换矩阵
Unity内置的变换矩阵见下图:(对应上述的空间变换好好研究下)
Unity内置的摄像机和屏幕参数
第5章
- ShaderLab属性类型与CG变量类型的匹配关系
| ShaderLab属性类型 | CG变量类型 |
|—-|—-|
| Color, Vector | float4, half4, fixed4 |
| Range, Float | float, half, fixed |
| 2D | sampler2D |
| Cube | SamplerCube |
| 3D | sampler3D | - UnityCG.cginc中一些常用的结构体
| 名称 | 描述 | 包含的变量 |
| —- | —- | —- |
| appdata_base | 可用于顶点着色器的输入 | 顶点位置、顶点法线、第一组纹理坐标 |
| appdata_tan | 可用于顶点着色器的输入 | 顶点位置、顶点切线、顶点法线、第一组纹理坐标 |
| appdata_full | 可用于顶点着色器的输入 | 顶点位置、顶点切线、顶点法线、四组(或更多)纹理坐标 |
| appdata_img | 可用于顶点着色器的输入 | 顶点位置、第一组纹理坐标 |
| v2f_img | 可用于顶点着色器的输出 | 裁剪空间中的位置、纹理坐标 | UnityCG.cginc中一些常用的帮助函数
UnityCG.cginc中一些常用的帮助函数可用于顶点着色器的输入的语义
| 语义 | 描述 |
| —- | —- |
| POSITION | 模型空间中的顶点位置,通常是float4类型 |
| NORMAL | 顶点法线,通常是float3类型 |
| TANGENT | 顶点切线,通常是float4类型 |
| TEXCOORDn | 该顶点的第n组纹理坐标,通常是float2或float4类型 |
| COLOR | 顶点颜色,通常是float4或fixed4类型 |- 系统数值语义:System-Value sematics,以
SV
开头- SV_POSITION: 顶点着色器的输出pos
- SV_TARGET: 片元着色器的输出(输出到颜色缓冲)
- 系统数值语义:System-Value sematics,以
第6章
光照模型的计算
- 逐像素光照
- 主要计算过程在片元着色器中;
- 以每个像素为基础,得到其法线(顶点发现插值或法线纹理采样),再进行光照模型的计算。这种在面片之间对顶点法线进行插值的技术被称为Phong着色(Phong Shading)
- 逐顶点光照
- 主要计算过程在顶点着色器中;
- Gouraud shading(高洛德着色),在每个顶点上计算光照,然后在渲染图元内部进行线性插值,最后输出像素颜色。
由于顶点数往往小于像素数量,因此逐顶点光照的计算量更小;但是如果模型中有非线性插值的计算,逐顶点光照的结果会有问题(棱角现象)。
第7章
纹理映射:将纹理图附在模型表面,逐纹素的控制模型的颜色
凹凸映射:使用一张纹理图(法线纹理/高度纹理)来改变模型表面的法线,为模型提供更多细节
凹凸映射
- 两种实现方式:
- 高度映射:使用一张高度纹理模拟表面位移,以得到一个修改后的法线值
- 法线映射:使用一张法线纹理直接存储表面法线
- 模型空间的法线纹理:修改后的模型空间的表面法线存储在一张纹理中
- 切线空间的法线纹理:对每个顶点,求出顶点在其切线空间内的法线,将其存储到法线纹理中。这种方式由于存储的是相对法线,效果更好,更受欢迎。
第8章
透明度测试
设置一个阈值,当片元的透明度不满足条件时,该片元会被舍弃(不再做任何处理),否则就视为不透明物体处理。因此它并不能实现真正的半透明效果。
在片元着色器中调用clip(float/float2/float3/float4 x)
函数进行透明度测试,当传入的参数有任何一个分量是负数,就会舍弃当前像素的输出颜色
透明度混合
可以实现真正的半透明效果。他会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。
需要关闭深度写入,但不关闭深度检测,即计算半透明物体时,深度缓冲是只读的。这会导致渲染的顺序十分重要,因为这时深度缓冲无法完全正常工作。
解决思路:先渲染所有的不透明物体,这里深度关系会一切正常;再将半透明物体加入渲染(半透明物体之间的遮挡关系仍有问题,先不考虑)。
混合命令:Blend,开启混合后,对alpha的修改才有意义。
渲染队列
Unity提供了渲染队列以解决渲染顺序的问题。使用Subshader
里的Queue标签决定模型归于哪个队列。
Unity内部使用整数索引来表示每个渲染队列,索引号越小表示越早渲染。
Cull命令
命令 | 含义 |
---|---|
Cull Off | 关闭剔除 |
Cull Back | 剔除背面,只渲染正面,default |
Cull Front | 提出正面,只渲染背面 |
第12章 屏幕后处理效果
屏幕后处理:通常指的是在渲染完整个场景得到屏幕图像之后,再对这个图像进行一系列操作,实现各种屏幕特效(景深、运动模糊等)。
函数接口:
- OnRenderImage函数:
1
MonoBehaviour.OnRenderImage(RenderTexture src, RenderTexture dest)
src
是Unity当前渲染得到的源渲染纹理;dest
是经过OnRenderImage
函数处理之后的目标渲染纹理,之后会被渲染到屏幕上 - Graphics.Blit函数
1
2
3public static void Blit(Texture src, RenderTexture dest);
public static void Blit(Texture src, RenderTexture dest, Material mat, int pass=-1);
public static void Blit(Texture src, Material mat, int pass=-1);src
对应了源纹理,通常就是当前屏幕的渲染纹理或是上一步处理后得到的渲染纹理;
dest
对应了目标渲染纹理,若为null则直接渲染到屏幕上;
mat
是我们使用的材质,它使用的shader会进行一系列屏幕后处理操作,而src
纹理将会被传递给shader中_MainTex
的纹理属性;
pass
的默认值为-1,表示会一次调用shader内的所有Pass;否则只调用给定索引的Pass;