Unity Shader学习笔记(二) - 纹理与透明效果
本文最后更新于 2025年1月30日 下午
实用且有趣,美味又易燃。
纹理
在Properties语义块中使用2D、3D或Cube类型定义纹理属性:
1 |
|
随后在CG块中使用sampler
定义纹理变量:
1 |
|
对于需要进行平移、缩放变换的纹理(即,在检视器上可以操作Tilling和Offset),需要在Properties块中定义属性的基础上,在CG块中定义float4 _变量名_ST
变量。该变量xy
分量表示Tilling
值,zw
分量表示Offset
值。
应用平移/缩放时,进行如下操作:
uv = uv.xy * _变量名_ST.xy + _变量名_zw
也可以直接使用
TRANSFORM_TEX
宏来操作。该宏的第一个参数为纹理坐标,第二个参数为纹理名。
使用tex2D
、tex3D
、texCUBE
函数分别对2D、3D和Cube纹理采样。前两者使用纹理坐标采样,CubeMap则使用方向向量采样。
导入属性
如图。常用的导入属性如下:
属性 | 作用 |
---|---|
Texture Type | 纹理类型。包含Default、Normal Map、Sprite等 |
Texture Shape | 纹理形状,包括2D、3D、2DArray、Cube等 |
sRGB | 是否启用sRGB。对于颜色贴图需要勾选,对于数据贴图(如法线贴图)则取消勾选 |
Alpha Source | Alpha值的来源。包括Input Texture Alpha和From Grey Scale。前者直接导入透明通道,后者从灰度值生成透明通道 |
Wrap Mode | 环绕模式。当纹理坐标超出[0,1]范围时,纹理该如何平铺。Repeat则重复平铺,Clamp则边缘像素拉伸,Mirror则镜像平铺,Mirror Once则镜像平铺一次随后边缘拉伸 |
Filter Mode | 过滤模式。Point则最邻近,Linear则双线性,Trilinear则三线性。 |
Aniso Level | 各向异性等级。越高,则斜视下清晰度越好,但性能开销越大 |
Max Size | 纹理的最大尺寸。超过此大小时进行缩放 |
Resize Algorithm | 尺寸超过MaxSize时,缩放使用的算法 |
Format | 纹理存储格式。一般为Automatic |
Compression | 压缩质量 |
Use Crunch Compression | 启用Crunch压缩,进一步减少文件大小,但加载速度减慢 |
除此之外,还有一些高级导入选项位于Advanced下拉栏内。常用的有Generate Mipmaps
,启用纹理的Mipmap,提高占用空间的同时改善纹理缩小时的过滤效果。
纹理的大小最好为2的幂,否则加载速度会下降。
凹凸映射
凹凸映射(Bump Mapping)包含位移贴图(Displacement Map,使用Height Map模拟顶点偏移)和法线贴图(Normal Map)。
对于位移贴图技术,其使用的高度图为灰度图,存储强度值,表示模型表面局部的高度。颜色越浅则越凸。该技术由于无法直接得到偏移后的发现,如果要得到发现则需要复杂计算,消耗性能。因此,往往将其与法线贴图结合。
法线贴图
法线贴图上采样得到的三维向量取值范围为[0,1],而实际法线的范围为[-1,1]。因此,需要通过乘以二,减去一,然后通过TBN矩阵变换的方式处理采样值。
直接采样法线贴图得到的法线位于切线空间(Tangent Space)。切线空间的优点是,在制作法线贴图时,无需考虑顶点本身的模型空间坐标,让所有像素在同一坐标系内;同时,顶点无关性则能让同一张法线贴图应用到不同的物体。
切线空间的原点为顶点;Z轴为顶点原本的法线;X轴为顶点切线;Y轴为顶点副切线。
为了从法线贴图采样值推导出对应的真实法线,我们通常在片段着色器中使用TBN矩阵进行变换。具体操作如下:
首先,在appdata
结构体中,新增float4
类型变量tagent
,通过TAGENT
语义标记;
tagent.w
用于决定副切线的方向性
然后,在v2f
结构体中,新增三个float4
类型变量TtoWx
,以TEXCOORDx
语义标记,用于:首先在VS中计算TBN矩阵,然后将其传递给FS。
TBN矩阵为三维矩阵,为了充分利用寄存器空间,我们将三个空闲的
w
用于存储worldPos
。
VS中计算TBN矩阵的代码如下:
1 |
|
FS中对法线贴图采样值变换的代码如下:
1 |
|
若要将高度图作为法线贴图,需要在导入后,在导入设置内,将纹理类型改为Normal Map,同时勾选Create From GrayScale。
渐变纹理
渐变纹理类似于一张LUT,将光照强度映射到LUT的不同颜色区域上,主要用于NPR。关键代码如下:
1 |
|
渐变纹理的环绕模式应当设置为Clamp。
遮罩纹理
遮罩纹理(Mask Texture)用于精细地控制各光分量,例如,可以让物体某些特定区域的高光减弱。
透明效果
实现透明效果有两种方案:透明度测试(Alpha Test)和透明度混合(Alpha Blend)。
- 透明度测试中,会设置一个阈值。只要某个片段的alpha值小于该阈值,就会被剔除。它不是一种真正的半透明渲染。使用透明度测试实现透明效果的材质无需关闭深度写入。
Alpha Test使用内置函数
clip
实现。当其参数小于0时,裁剪该像素。此外,使用Alpha Test的着色器需要使用
Transparent/Cutoff/VertexLit
作为Fallback。
- 透明度混合中,会使用当前片段的alpha值作为混合因子,与颜色缓冲中的颜色进行Lerp。但是,Alpha Blend需要关闭深度写入,即使用该方法的半透明材质的片段不会更新深度缓冲,只会读取深度缓冲并进行深度剔除。
之所以关闭深度写入,是因为如果不关闭,那么当半透明物体比不透明物体距离相机更近时,由于深度缓冲已经被半透明物体更新,不透明物体会由于深度测试而被直接剔除。
也就是说,我们始终应该在渲染完所有不透明物体之后渲染半透明物体,这样既能保证位于不透明物体之后的透明物体被深度剔除,又能保证位于半透明物体之后的不透明物体都能被正常混合。
在透明度混合中,半透明物体的渲染顺序十分重要。我们必须先渲染距离摄像机远的半透明物体,再渲染离摄像机近的半透明物体。
ShaderLab中的透明度混合
ShaderLab中开启混合的语义如下:
语义 | 描述 |
---|---|
Blend Off | 关闭混合 |
Blend SrcFactor DstFactor | 开启混合,设置混合因子。此片段产生的颜色会乘以SrcFactor,颜色缓冲中的颜色会乘以DstFactor。该语义最常用。 |
Blend SrcFactor DstFactor, SrcFactorA DstFactorA | 使用不同因子混合RGB通道和A通道 |
BlendOp BlendOperation | 使用Blend Operation对源、目标颜色进行其他操作。 |
使用透明度混合时,最终输出的颜色为:
Alpha值也类似。
通常,开启透明度混合的语义为:
Blend SrcAlpha OneMinusSrcAlpha
此外,开启透明度混合的着色器应当设置Tag:
- “Queue” = “Transparent”
- “IgnoreProjector” = “True”
- “RenderType” = “Transparent”
- “LightMode” = “ForwardBase”
同时,还需要设置ZWrite Off
除了SrcAlpha
、OneMinusSrcAlpha
等,还有其他混合因子。如下:
- One 、Zero:混合因子固定为0/1。
- SrcColor/DstColor/OneMinusSrcColor/OneMinusDstColor:混合因子为RGB值(但混合Alpha时,使用颜色值的Alpha作为混合因子)
- SrcAlpha/DstAlpha/OneMinusSrcAlpha/OneMinusDstAlpha:混合因子为alpha值。
对于BlendOp
,有以下混合操作:
- Add:最常用,将乘以各自混合因子的源/目标颜色值相加得到结果。
- Sub:源-目标
- RevSub:目标-源
- Min:min(目标,源),逐分量比较
- Max:max(目标,源)
常见的混合类型
这里给出常见的混合语义类型及其效果。
Blend SrcAlpha OneMinusSrcAlpha
:正常的透明度混合Blend OneMinusDstColor One
:柔和相加Blend DstColor Zero
:正片叠底(等效于目标*源)Blend DstColor StcColor
:两倍相乘BlendOp Min
Blend One One
:变暗BlendOp Max
Blend One One
:变亮Blend OneMinusDstColor One
:滤色Blend One One
:线性减淡
开启深度写入的半透明
对于相互重叠的半透明物体,在ZWrite Off的情况下,始终是无法正常渲染的。此时,我们需要使用双Pass渲染来规避这个问题。
所谓双Pass渲染,即:
- 在渲染半透明物体时,首先进行仅写入深度的Pass,将该模型的深度值写入深度缓冲。
- 随后进行第二个Pass,此Pass利用第一个Pass的深度信息对半透明物体的片段进行剔除。
大致的代码如下:
1 |
|
该方法的本质是,针对半透明物体组进行一次单独的深度写入和深度测试。
双面渲染的半透明
对于部分透明的物体,有时需要透过透明部分看到物体内部的需求。此时,需要用到双面渲染。
对于AlphaTest,直接将默认的Cull Back
改为Cull Off
即可。
对于Alpha Blend,直接Cull Off
会导致混合无法工作,因为我们无法保证背面始终在正面之前渲染。因此,我们使用双Pass,第一个Pass渲染背面,第二个Pass渲染正面。
代码很简单,第一个Pass进行Cull Front
,第二个Pass进行Cull Back
即可。