44.Shader开发入门总结
44.1 核心要点速览
光照模型
光照模型概述
知识基础与学习方向:
已掌握渲染管线、数学基础(向量、矩阵等)、Shader语法(ShaderLab、CG),后续将学习光照、纹理、透明等基础渲染效果。光照模型定义:
模拟光照效果的数学公式与算法,用于计算3D物体表面对光的反射和散射,决定物体呈现的亮度与颜色(由入射光、材质及交互规律共同决定)。核心学习内容:
- 漫反射模型:光线被粗糙表面无规则反射(如墙壁、纸张),各方向反射均匀。
- 高光反射模型:考虑视角与光源方向,产生高光点(如金属、塑料)。
- 综合模型:结合漫反射与高光反射。
- Unity内置光照函数:简化常见光照效果实现。
核心要点:光照模型是前辈总结的计算公式,通过数学计算实现物体受光照的视觉效果,是Shader开发中模拟真实光照的基础。
兰伯特漫反射
核心原理:
漫反射强度由表面法线与光源方向的夹角决定,夹角越小(余弦值越大),反射光越强。公式:
漫反射颜色 = 光源颜色 × 材质漫反射颜色 × max(0, 归一化世界空间法线 · 归一化光源方向)
(其中,归一化世界空间法线 · 归一化光源方向
即cosθ
,θ为法线与光源方向的夹角;max(0, cosθ)
用于过滤背面光照,避免负数)关键参数:
- 光源颜色:
_LightColor0
(需引用Lighting.cginc
)。 - 光源方向:
_WorldSpaceLightPos0.xyz
(归一化后使用)。 - 法线转换:模型空间法线通过
UnityObjectToWorldNormal
转为世界空间。 - 环境光:
UNITY_LIGHTMODEL_AMBIENT.rgb
(避免阴影全黑,增强真实感)。
- 光源颜色:
半兰伯特漫反射
核心原理:
基于兰伯特模型的改进,无物理依据,是一种视觉增强技术。解决兰伯特模型背光面全黑的问题,通过公式调整让背光面也呈现明暗变化,原理仍基于入射光与法线夹角的余弦值。公式:
漫反射颜色 = 光源颜色 × 材质漫反射颜色 × ((归一化法线 · 归一化光源方向) × 0.5 + 0.5)
(将法线与光源方向点乘结果(范围-11)映射到01,避免背光面全黑,背光区域也有明暗层次)
Phong式高光反射
核心原理:
基于光的反射行为和观察者位置,高光反射颜色与观察者方向向量和反射光线方向向量夹角的余弦值成正比,通过对余弦值取幂次(光泽度)控制高光锐利度,幂次越高,高光越集中。公式:
高光反射颜色 = 光源颜色 × 材质高光颜色 × max(0, 归一化视角方向 · 归一化反射方向)^n
(n
为光泽度,控制高光锐利度;反射方向
由reflect(-光源方向, 法线)
计算,视角方向
为摄像机位置减顶点世界坐标并归一化)关键参数获取:
- 观察者位置:
_WorldSpaceCameraPos
(世界空间摄像机位置)。 - 反射向量:
reflect(入射向量, 法线)
(入射向量为-光源方向
,因光源方向是从顶点指向光源,入射方向相反)。 - 幂次计算:
pow(底数, 指数)
(指数即光泽度n
)。
- 观察者位置:
Blinn-Phong式高光反射
核心原理:
对Phong模型的改进,通过半角向量(视角方向与光源方向的角平分线)计算高光,而非反射向量。高光颜色与顶点法线和半角向量夹角的余弦值成正比,对余弦值取幂次(光泽度)控制高光集中程度,幂次越高,高光越锐利、集中。公式:
高光反射颜色 = 光源颜色 × 材质高光颜色 × max(0, 归一化法线 · 归一化半角向量)ⁿ
(n
为光泽度;半角向量由视角单位向量与光源单位向量相加后归一化得到:half = normalize(viewDir + lightDir)
)关键参数获取:
- 半角向量:
normalize(viewDir + lightDir)
(viewDir
为摄像机到顶点的归一化方向,lightDir
为光源到顶点的归一化方向)。 - 观察者位置:
_WorldSpaceCameraPos
(用于计算viewDir
)。 - 幂次计算:
pow(底数, 指数)
(指数即光泽度n
)。 - 法线转换:
UnityObjectToWorldNormal
(模型空间法线转世界空间)。
- 半角向量:
Phong光照模型
核心原理:
综合光照模型,由越南裔计算机学家裴祥风提出,认为物体表面的反射光由环境光、漫反射光(兰伯特模型)、高光反射光(Phong式高光) 三部分组成,最终颜色为三者之和(颜色相加,往白色靠拢,符合光的叠加特性)。公式:
物体表面颜色 = 环境光颜色 + 兰伯特漫反射颜色 + Phong式高光颜色
- 环境光颜色:
UNITY_LIGHTMODEL_AMBIENT.rgb
(可在Unity的Lighting设置中调整,支持天空盒、纯色或渐变)。 - 兰伯特漫反射颜色:
光源颜色 × 材质漫反射颜色 × max(0, 归一化世界法线 · 归一化光源方向)
。 - Phong式高光颜色:
光源颜色 × 材质高光颜色 × max(0, 归一化视角方向 · 归一化反射方向)^n
(n
为光泽度,控制高光锐利度)。
- 环境光颜色:
关键参数获取:
- 环境光:
UNITY_LIGHTMODEL_AMBIENT
(或渐变环境光的unity_AmbientSky
等)。 - 漫反射:世界空间法线(
UnityObjectToWorldNormal
转换)、光源方向(_WorldSpaceLightPos0.xyz
归一化)。 - 高光:视角方向(
normalize(_WorldSpaceCameraPos - 顶点世界坐标)
)、反射方向(reflect(-光源方向, 世界法线)
)、光泽度(_SpecularNum
)。
- 环境光:
Blinn-Phong光照模型
核心原理:
由Jim Blinn提出,是Phong模型的改进版,同样认为物体表面反射光由环境光、漫反射光(兰伯特模型)、高光反射光三部分组成。核心改进是高光计算使用半角向量(视角方向与光源方向的角平分线)替代Phong模型的反射向量,计算更高效,高光效果更均匀自然。公式:
物体表面颜色 = 环境光颜色 + 兰伯特漫反射颜色 + Blinn-Phong式高光颜色
- 环境光颜色:
UNITY_LIGHTMODEL_AMBIENT.rgb
(可通过Unity Lighting设置调整)。 - 兰伯特漫反射颜色:
光源颜色 × 材质漫反射颜色 × max(0, 归一化世界法线 · 归一化光源方向)
。 - Blinn-Phong式高光颜色:
光源颜色 × 材质高光颜色 × max(0, 归一化世界法线 · 归一化半角向量)^n
(n
为光泽度,半角向量 = normalize(视角方向 + 光源方向)
)。
- 环境光颜色:
关键参数获取:
- 环境光:
UNITY_LIGHTMODEL_AMBIENT
(或渐变环境光的unity_AmbientSky
等)。 - 漫反射:世界空间法线(
UnityObjectToWorldNormal
转换)、光源方向(_WorldSpaceLightPos0.xyz
归一化)。 - 高光:
- 视角方向:
normalize(_WorldSpaceCameraPos.xyz - 顶点世界坐标)
。 - 半角向量:
normalize(视角方向 + 光源方向)
。 - 光泽度:
_SpecularNum
(控制高光锐利度)。
- 视角方向:
- 环境光:
光照模型对比表格
模型名称 | 核心原理 | 核心公式(简化版) | 关键参数/特点 |
---|---|---|---|
兰伯特漫反射 | 漫反射强度由表面法线与光源方向夹角的余弦值决定,夹角越小反射越强 | 漫反射色 = 光源色 × 材质漫反射色 × max(0, 法线·光源方向) | 依赖世界空间法线、光源方向;过滤背面光照(避免负数);背光面可能全黑 |
半兰伯特漫反射 | 兰伯特改进版,无物理依据,通过映射余弦值范围解决背光面全黑问题 | 漫反射色 = 光源色 × 材质漫反射色 × (0.5×(法线·光源方向) + 0.5) | 将法线与光源方向点积(-1 |
Phong式高光反射 | 高光强度与观察者方向和反射光线方向夹角的余弦值成正比,幂次控制高光锐利度 | 高光色 = 光源色 × 材质高光色 × max(0, 视角方向·反射方向)^n(n为光泽度) | 需计算反射方向(reflect(-光源方向, 法线))、视角方向;n越大,高光越集中;依赖观察者位置 |
Blinn-Phong式高光反射 | 高光计算用“半角向量”(视角与光源方向角平分线)替代Phong的反射方向,更高效 | 高光色 = 光源色 × 材质高光色 × max(0, 法线·半角向量)^n(n为光泽度,半角向量=normalize(视角方向+光源方向)) | 无需计算反射方向,效率更高;高光更均匀自然;n控制锐利度 |
Phong光照模型 | 综合环境光、兰伯特漫反射、Phong式高光反射的整体光照模型 | 总颜色 = 环境光 + 兰伯特漫反射色 + Phong式高光色 | 包含环境光(避免阴影全黑)、漫反射、高光三部分;高光基于反射方向计算;经典但高光计算稍复杂 |
Blinn-Phong光照模型 | 综合环境光、兰伯特漫反射、Blinn-Phong式高光反射的改进模型 | 总颜色 = 环境光 + 兰伯特漫反射色 + Blinn-Phong式高光色 | 核心改进是高光用半角向量计算,效率优于Phong模型;高光效果更自然;广泛应用于实时渲染 |
- 补充说明:
- “高光反射”与“光照模型”的区别:前两者(Phong/Blinn-Phong式高光反射)仅计算高光部分,而后两者(Phong/Blinn-Phong光照模型)是包含环境光、漫反射、高光的完整光照计算模型。
- 环境光作用:在完整光照模型中,环境光用于模拟间接光照,避免物体背光区域完全黑暗,增强场景真实感。
- 效率对比:Blinn-Phong系列(高光/光照模型)因无需计算反射向量,效率高于对应的Phong系列,更适合实时渲染(如游戏)。
逐顶点 vs 逐片元光照的差异
1. 逐顶点光照表现粗糙的原因
顶点以外的像素颜色,是 插值运算的结果,无法精准模拟光照细节,因此效果不够真实。
2. 逐片元光照表现平滑的原因
每个像素点会 利用自身的法线、位置等数据 独立计算颜色,能更精准还原光照效果,因此表现更平滑、真实。
3. 性能消耗对比
- 片元着色器:因需逐像素计算,性能消耗更大;
- 顶点着色器:仅需处理顶点,性能消耗更小。
Unity内置常用光照函数
作用:位于
UnityCG.cginc
中,用于简化光照计算中的坐标转换、方向获取等操作,避免手动数学计算。常用函数:
观察方向:
WorldSpaceViewDir(float4 v)
:输入模型空间顶点,返回世界空间从该点到摄像机的方向。UnityWorldSpaceViewDir(float4 v)
:输入世界空间顶点,返回世界空间从该点到摄像机的方向。ObjSpaceViewDir(float4 v)
:输入模型空间顶点,返回模型空间从该点到摄像机的方向。
光照方向(仅向前渲染):
WorldSpaceLightDir(float4 v)
:输入模型空间顶点,返回世界空间从该点到光源的方向(未归一化)。UnityWorldSpaceLightDir(float4 v)
:输入世界空间顶点,返回世界空间从该点到光源的方向(未归一化)。ObjSpaceLightDir(float4 v)
:输入模型空间顶点,返回模型空间从该点到光源的方向(未归一化)。
空间转换:
UnityObjectToWorldNormal(float3 normal)
:模型空间法线 → 世界空间法线。UnityObjectToWorldDir(float3 dir)
:模型空间方向向量 → 世界空间方向向量。UnityWorldToObjectDir(float3 dir)
:世界空间方向向量 → 模型空间方向向量。UnityObjectToClipPos(float4 v)
:模型空间顶点 → 裁剪空间顶点。
核心要点:这些函数用于快速处理光照计算中的空间转换和方向获取,简化Shader开发。
纹理
纹理概念
纹理作用:模型的“皮”,通过纹理映射技术将图片与模型关联,控制模型外观颜色表现。
纹理映射:美术在建模软件中,将纹理坐标(二维UV坐标,U为水平轴、V为垂直轴)存储在每个顶点,导出的模型数据包含各顶点对应的UV坐标。
UV坐标对Shader的意义:在顶点着色器中可获取模型UV坐标,结合纹理贴图,用UV坐标从图片中取出对应颜色用于渲染,使模型呈现“皮”的效果。
UV坐标注意事项:
- 取值范围归一化到[0,1],适配不同尺寸纹理(如256x256、1024x1024)。
- 仅顶点记录UV坐标,片元的UV坐标经插值计算得到,可获取非顶点位置的颜色。
纹理设置
关键纹理导入设置:
- 纹理类型(Texture Type)与形状(Texture Shape):决定Shader能否正确获取数据,定义纹理用途与形态。
- 循环模式(Wrap Mode):控制纹理缩放/偏移表现,包括Repeat(重复)、Clamp(拉伸边缘)、Mirror(镜像重复)、Mirror Once(镜像一次后拉伸)、Per-axis(单独控制U/V轴)。
- 过滤模式(Filter Mode):影响纹理放大/缩小的质量,Point(块状)、Bilinear(模糊)、Trilinear(类似Bilinear,且在Mip级别间模糊),需结合MipMaps选择。
Shader中纹理处理:
- Properties声明纹理(如
_MainTex("MainTex", 2D) = ""{}
),通过sampler2D _MainTex
获取纹理数据,_MainTex_ST
存储缩放(xy)和平移(zw)信息。 - 顶点着色器中处理UV:
uv = texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw
(或用TRANSFORM_TEX
宏),实现缩放后平移。 - 片元着色器用
tex2D(_MainTex, uv)
从纹理采样颜色。
- Properties声明纹理(如
单张纹理
单张纹理颜色采样
核心采样函数:
fixed4 tex2D(sampler2D tex, float2 s)
,传入纹理和UV坐标,返回对应位置颜色。Shader编写步骤:
- 基础结构:创建基本Shader框架,确保无报错(含顶点/片元着色器声明,引用
UnityCG.cginc
)。 - 变量声明:
- ShaderLab属性:声明2D纹理(如
_MainTex("MainTex", 2D) = ""{}
)。 - CG变量:
sampler2D _MainTex
(映射纹理数据)、float4 _MainTex_ST
(xy为缩放,zw为平移)。
- ShaderLab属性:声明2D纹理(如
- UV坐标处理:
- 顶点着色器中,从
appdata_base.texcoord.xy
获取原始UV。 - 计算方式:先缩放后平移,公式为
uv = texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw
,或用内置宏TRANSFORM_TEX(texcoord.xy, _MainTex)
。
- 顶点着色器中,从
- 颜色采样:片元着色器中,用
tex2D(_MainTex, uv)
根据处理后的UV采样颜色并返回。
- 基础结构:创建基本Shader框架,确保无报错(含顶点/片元着色器声明,引用
核心要点:需通过两个CG变量映射纹理属性(数据+缩放平移),UV坐标经缩放平移处理后,片元着色器通过
tex2D
完成颜色采样。
单张纹理结合BlinnPhong光照模型
关键注意点:
- 纹理颜色与漫反射色相乘叠加(
albedo = 纹理色 × 漫反射色
)。 - 兰伯特漫反射计算用
albedo
作为漫反射材质色。 - 环境光与
albedo
相乘叠加,避免渲染偏灰。
- 纹理颜色与漫反射色相乘叠加(
核心实现:
- 属性含纹理(
_MainTex
)、漫反射色、高光色、光泽度。 - 顶点着色器处理UV(缩放+平移),转换法线和顶点至世界空间。
- 片元着色器:
- 采样纹理并计算
albedo
。 - 按BlinnPhong公式算漫反射(用
albedo
)和高光。 - 最终色 = 环境光×albedo + 漫反射色 + 高光色。
- 采样纹理并计算
- 属性含纹理(
核心逻辑:纹理与漫反射色结合,融入BlinnPhong光照计算,调整材质色和环境光叠加方式实现融合效果。
凹凸纹理
凹凸纹理基本概念
1. 凹凸纹理的作用
通过 高度纹理或法线纹理,在 不添加顶点(不增加模型面数) 的前提下,让模型呈现凹凸细节。
原理:计算“视觉上的凹凸面”时,复用“真实凹凸面的法线”参与光照计算,模拟细节。
2. 高度纹理贴图
- 存储方式:RGB通道均存储 高度值,视觉表现为 灰度图;
- 缺陷:需额外计算高度对法线的影响,性能开销大,因此 实际使用较少。
3. 法线纹理贴图
- 存储方式:RGB通道分别存储 法线的X、Y、Z分量;
- 分类:
- 模型空间法线:颜色偏“彩色”(法线直接基于模型坐标系);
- 切线空间法线:颜色偏“蓝色”(法线基于模型表面的切线坐标系)。
4. 切线空间法线的优势
切线空间法线贴图虽会增加 计算开销,但能让同一张法线贴图 适配不同朝向、变形的模型表面,实现 更高的通用性和灵活性(无需因模型变换重新烘焙法线)。
凹凸纹理切线空间与世界空间的计算对比
1. 两种主流计算方式
- 切线空间计算:效率高,但 全局性差(仅针对模型表面局部);
- 世界空间计算:效率低,但 全局性好(基于全局坐标系统一计算)。
2. 切线空间下的计算核心
重点推导 模型空间 → 切线空间的变换矩阵(让法线从模型空间适配到切线空间)。
3. 世界空间下的计算核心
重点推导 切线空间 → 世界空间的变换矩阵(让切线空间的法线转换到全局坐标系)。
4. 关键知识点补充
- 切线数据类型为
float4
; - 法线解码需用
UnpackNormal
函数; - 可通过
_BumpScale
控制凹凸强度; - 高度纹理也可用于类似计算(需额外处理高度→法线的转换)。
凹凸纹理切线空间下计算
核心属性:漫反射色(
_MainColor
)、主纹理(_MainTex
)、法线纹理(_BumpMap
)、凹凸程度(_BumpScale
)、高光色(_SpecularColor
)、光泽度(_SpecularNum
)。结构体设计:
- 顶点输入:使用
appdata_full
(含顶点、法线、切线、纹理坐标)。 - 片元输入(
v2f
):裁剪空间坐标、主纹理UV(xy)、法线纹理UV(zw)、切线空间光照方向、切线空间视角方向。
- 顶点输入:使用
顶点着色器逻辑:
- 顶点坐标转裁剪空间,计算主纹理和法线纹理的UV(缩放+偏移)。
- 计算副切线:切线与法线叉乘,乘切线的
w
确定方向。 - 构建模型→切线空间变换矩阵(行:切线、副切线、法线)。
- 将模型空间的光照方向(
ObjSpaceLightDir
)和视角方向(ObjSpaceViewDir
)通过变换矩阵转换到切线空间。
片元着色器逻辑:
- 采样法线纹理,用
UnpackNormal
解压得到切线空间法线,乘_BumpScale
控制凹凸。 - 计算漫反射叠加色:
albedo = 主纹理色 × _MainColor
。 - 按Blinn-Phong模型计算:
- 漫反射:
_LightColor0 × albedo × max(0, dot(切线法线, 切线空间光向))
。 - 高光:
_LightColor0 × _SpecularColor × pow(max(0, dot(切线法线, 半角向量)), 光泽度)
(半角向量=切线空间视角方向+光向)。
- 漫反射:
- 最终颜色:环境光×albedo + 漫反射色 + 高光色。
- 采样法线纹理,用
核心逻辑:通过模型→切线空间变换矩阵,将光照和视角方向转换到切线空间,结合法线纹理计算光照,实现凹凸效果。
凹凸纹理世界空间下计算
核心思路:在顶点着色器中构建切线空间→世界空间的变换矩阵,片元着色器中采样法线并转换到世界空间进行光照计算。
关键步骤:
属性与结构体:
- 属性同切线空间(漫反射色、主纹理、法线纹理、凹凸系数等)。
- 顶点输入用
appdata_full
(含顶点、法线、切线、UV);片元输入(v2f
)含裁剪坐标、UV(主纹理xy/法线纹理zw)、世界顶点位置、切线→世界变换矩阵。
顶点着色器:
- 顶点转裁剪空间,计算主纹理和法线纹理的UV(缩放+偏移)。
- 模型空间顶点、法线、切线转换到世界空间,计算副切线(世界空间切线与法线叉乘,乘切线w确定方向)。
- 构建变换矩阵(行:世界空间切线、副切线、法线)。
片元着色器:
- 计算世界空间光照方向(定向光:
_WorldSpaceLightPos0
;点光源:_WorldSpaceLightPos0 - 世界顶点位置
)和视角方向。 - 采样法线纹理,
UnpackNormal
解压,处理凹凸:tangentNormal.xy *= _BumpScale
,z = sqrt(1 - saturate(dot(xy, xy)))
(保证单位向量)。 - 切线空间法线通过变换矩阵转换到世界空间。
- 计算Blinn-Phong光照:漫反射(主纹理色×漫反射色×兰伯特项)+ 高光(高光色×pow(半角项, 光泽度)),叠加环境光。
- 计算世界空间光照方向(定向光:
性能优化:用3个
float4
存储变换矩阵(每行xyz为矩阵元素,w为世界顶点坐标),适配GPU的4分量并行计算,提升效率。
核心差异:法线在世界空间进行光照计算,支持点光源等更复杂光照,需注意变换矩阵的构建与法线转换。
渐变纹理
渐变纹理的基本概念
1. 渐变纹理的作用
让模型呈现 插画、卡通风格的渲染效果(通过颜色渐变模拟风格化光照)。
2. 渐变纹理的基本原理
- 核心逻辑:利用 半兰伯特光照公式后半段计算的0~1值,构建 与模型UV对齐的采样坐标,从渐变纹理中提取对应颜色并叠加;
- 明暗控制:漫反射的明暗不再由原始的0~1数值直接决定,而是 由渐变纹理的采样颜色叠加结果决定(通过纹理色彩定制风格化明暗过渡)。
渐变纹理基础实现
核心属性:漫反射色(
_MainColor
)、渐变纹理(_RampTex
)、高光色(_SpecularColor
)、光泽度(_SpecularNum
)。结构体设计:
- 顶点输入:使用
appdata_base
(含顶点、法线)。 - 片元输入(
v2f
):裁剪空间坐标、世界空间顶点位置、世界空间法线。
- 顶点输入:使用
顶点着色器:
- 顶点坐标转裁剪空间,顶点和法线从模型空间转换到世界空间。
片元着色器关键步骤:
- 计算半兰伯特值:
(dot(世界法线, 光向) * 0.5 + 0.5)
(将法线与光向的点积映射到[0,1]范围)。 - 从渐变纹理采样:用半兰伯特值作为UV(
fixed2(halfLambertNum, halfLambertNum)
),获取对应颜色。 - 漫反射计算:
_LightColor0 * _MainColor * 渐变纹理采样色
。 - 高光计算(Blinn-Phong):
_LightColor0 * _SpecularColor * pow(max(0, dot(世界法线, 半角向量)), 光泽度)
。 - 最终颜色:环境光 + 漫反射色 + 高光色。
- 计算半兰伯特值:
注意事项:渐变纹理的
Wrap Mode
需设为Clamp
,避免浮点数计算误差(如1.00001)在Repeat
模式下取小数部分(0.00001)导致的黑点。
渐变纹理综合实现
核心融合思路:在切线空间下的凹凸纹理Shader基础上,将漫反射计算替换为基于渐变纹理的半兰伯特方式,保留凹凸效果与高光计算。
关键修改:
- 属性新增:添加渐变纹理(
_RampTex
)及缩放偏移(_RampTex_ST
,实际少用)。 - 结构体与顶点着色器:沿用切线空间凹凸纹理的设计,顶点输入为
appdata_full
,片元输入(v2f
)含裁剪坐标、UV(主纹理xy/法线纹理zw)、切线空间光照/视角方向;顶点着色器计算裁剪坐标、UV偏移、副切线,构建模型→切线空间矩阵,转换光照和视角方向到切线空间。 - 片元着色器核心调整:
- 保留法线处理(采样法线纹理→
UnpackNormal
→调整凹凸系数_BumpScale
,保证法线单位化)。 - 漫反射计算改为:
- 计算半兰伯特值:
dot(切线法线, 切线光向) * 0.5 + 0.5
。 - 从渐变纹理采样:
tex2D(_RampTex, fixed2(半兰伯特值, 半兰伯特值))
。 - 漫反射色:
_LightColor0 * albedo(主纹理色×漫反射色) * 渐变纹理采样色
。
- 计算半兰伯特值:
- 高光仍用Blinn-Phong:
_LightColor0 * _SpecularColor * pow(max(0, dot(切线法线, 半角向量)), 光泽度)
。 - 最终颜色:环境光×albedo + 漫反射色 + 高光色。
- 保留法线处理(采样法线纹理→
- 属性新增:添加渐变纹理(
效果差异:相比单纯凹凸纹理,融合渐变纹理后明暗过渡更明显,呈现卡通感;凹凸纹理则更写实。
遮罩纹理
遮罩纹理基本概念
1. 遮罩纹理的作用
用于 控制或限制特定效果的显示范围(如光照强度、透明度变化、特效覆盖区域等)。
2. 高光遮罩纹理的实现逻辑
通过三步运算定制高光表现:
- 提取掩码值:从高光遮罩纹理中,读取对应像素的 RGB值(任意通道均可作为掩码);
- 计算遮罩值:将掩码值与 自定义的遮罩系数 相乘,得到最终遮罩权重;
- 叠加高光颜色:将遮罩值与 高光反射计算的颜色 相乘,调整高光的显示强度。
最终,高光遮罩纹理 + 遮罩系数 共同决定高光反射的表现效果(如局部高光强弱、范围控制)。
遮罩纹理综合实现
基础复用:基于切线空间下的凹凸纹理Shader,保留顶点输入(
appdata_full
)、结构体(v2f
含裁剪坐标、UV、切线空间光照/视角方向)及顶点着色器逻辑(坐标转换、副切线计算、模型→切线空间矩阵构建)。新增属性与映射:
- 高光遮罩纹理(
_SpecularMask
)及缩放偏移(_SpecularMask_ST
)。 - 遮罩系数(
_SpecularScale
),用于调整遮罩强度。 - 映射变量:
sampler2D _SpecularMask
、float4 _SpecularMask_ST
、float _SpecularScale
。
- 高光遮罩纹理(
片元着色器核心修改:
- 采样遮罩纹理获取掩码值(如取r通道:
tex2D(_SpecularMask, uv).r
)。 - 计算遮罩值:
遮罩值 = 掩码值 × _SpecularScale
。 - 高光反射计算时乘以遮罩值:
specularColor = 光色 × 高光色 × pow(...) × 遮罩值
,实现局部高光抑制。
- 采样遮罩纹理获取掩码值(如取r通道:
遮罩纹理的高效利用:RGBA通道可分别存储不同遮罩数据(如R=高光、G=透明、B=特效),多张纹理可扩展存储更多信息,提升灵活性。
透明
半透明效果的前置知识回顾
1. 回顾的核心目的
Unity Shader 中,半透明效果通过 混合物体与背景(或其他物体)的颜色 模拟透明感。本节回顾的渲染规则(队列、深度、测试、混合),是实现颜色混合的核心前提。
2. 关键渲染概念解析
- 渲染队列:定义物体的 渲染顺序,决定半透明物体与其他物体的绘制先后(如透明物体需晚于不透明物体绘制,保证混合正确)。
- 深度缓冲:记录像素的 深度值,用于后续深度测试,判断片元是否“更靠近摄像机”。
- 深度测试:对比当前片元与深度缓冲的深度值,决定片元是否保留(控制半透明物体的遮挡逻辑,如“后面的透明物体不遮挡前面的”)。
- 混合方式:将通过深度测试的片元颜色,与颜色缓冲区已有颜色 按算法混合(如alpha混合),最终更新颜色缓冲区(实现透明叠加效果)。
这些规则共同控制半透明效果的 绘制顺序、遮挡关系、颜色叠加逻辑,是实现真实半透明的基础。
透明渲染顺序的重要性
深度测试和深度写入带来的好处:
无需关心不透明物体的 渲染顺序;透明混合为什么需要关闭深度写入:
能够得到背后物体的颜色进行 颜色混合 计算;关闭深度写入带来的问题:
物体的 渲染顺序 会影响最终的渲染效果;如何解决渲染顺序可能带来的问题:
引擎底层指定了通用的 渲染顺序 规则,特殊情况时可以根据需求选择性处理。
设置深度写入和渲染队列
Unity Shader中设置深度写入:
Pass { Tags { "LightMode"="ForwardBase" } // 关闭深度写入我们一般放在Pass渲染通道中 // 如果存在多个Pass渲染通道,它仅仅会影响该Pass渲染通道 ZWrite Off }
Unity Shader中设置渲染队列:
- 示例一:
SubShader { // 渲染队列的标签一般定义在SubShader语句块中 // 影响下面的所有Pass渲染通道 Tags{ "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"} }
- 示例二:
SubShader { // 渲染队列的标签一般定义在SubShader语句块中 // 影响下面的所有Pass渲染通道 Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"} }
- 示例一:
设置混合命令
混合的基本原理:
用当前片元颜色(源颜色)和颜色缓冲区中颜色(目标颜色)进行混合得到新颜色;混合的计算规则(混合因子、混合操作):
- 计算方式一:
( \text{O}_\text{rgb} = \text{源因子} \times \text{S}_\text{rgb} + \text{目标因子} \times \text{D}_\text{rgb} )
( \text{O}_\text{a} = \text{源透明因子} \times \text{S}_\text{a} + \text{目标透明因子} \times \text{D}_\text{a} ) - 计算方式二:
( \text{O}_\text{rgb} = \text{源因子} \times \text{S}_\text{rgb} + \text{目标因子} \times \text{D}_\text{rgb} )
( \text{O}_\text{a} = \text{源因子} \times \text{S}_\text{a} + \text{目标因子} \times \text{D}_\text{a} )
(Shader 代码逻辑示例:
Pass { Tags { "LightMode"="ForwardBase" } ZWrite Off // 设定混合计算方式(BlendOp、Blend 语法) // Blend 源因子 目标因子, 源透明因子 目标透明因子(完整格式) // 简化示例:Blend 源因子 目标因子 } ```)
- 计算方式一:
常见的混合类型:
透明度混合、柔性相加、正片叠底、两倍相乘、变暗、变亮、滤色、线性减淡。
透明度测试效果实现
应用场景:处理物体部分完全透明、部分完全不透明的需求(无半透明),如树叶、栅栏、草等。
基本原理:
通过阈值判断片元透明度(A值),小于阈值的片元被丢弃(不参与渲染),大于等于阈值的片元按不透明物体处理。
用clip(参数)
函数实现:若参数任一分量为负,通过discard
指令剔除片元(内部逻辑:if(any(参数 < 0)) discard;
)。实现步骤:
- 基础Shader:基于单张纹理结合Blinn-Phong光照的Shader(无需法线贴图)。
- 属性与映射:添加阈值
_Cutoff
(范围0~1),映射为fixed _Cutoff
。 - Tags设置:
"Queue"="AlphaTest"
(渲染顺序:不透明后、透明前)、"IgnoreProjector"="True"
(忽略投影)、"RenderType"="TransparentCutout"
(指定渲染类型)。 - 片元着色器:
- 采样纹理获取
texColor
(含A值)。 - 用
clip(texColor.a - _Cutoff)
判断,剔除透明度不足的片元。 - 保留的片元按原光照逻辑计算(漫反射、高光等),返回颜色。
- 采样纹理获取
关键特点:无需关闭深度写入,片元要么完全显示(按不透明处理),要么完全丢弃,适合非半透明的镂空效果。
透明度混合效果实现
应用场景:实现半透明效果(非完全透明/不透明),如玻璃、水、buff效果等。
基本原理:
关闭深度写入,开启颜色混合,通过混合因子计算目标颜色:目标颜色 = 源颜色透明度 × 源颜色 + (1 - 源颜色透明度) × 目标颜色
核心是使用混合因子Blend SrcAlpha OneMinusSrcAlpha
。实现步骤:
- 基础Shader:基于单张纹理结合Blinn-Phong光照的Shader。
- 属性与映射:添加
_AlphaScale
(0~1,控制整体透明度),映射为fixed _AlphaScale
。 - Tags设置:
"Queue"="Transparent"
(透明队列,在不透明物体后渲染)、"IgnoreProjector"="True"
(忽略投影)、"RenderType"="Transparent"
(指定透明渲染类型)。 - 渲染设置:关闭深度写入
ZWrite Off
,设置混合因子Blend SrcAlpha OneMinusSrcAlpha
。 - 片元着色器:
- 采样纹理获取
texColor
(含alpha值)。 - 计算光照颜色(漫反射+高光+环境光)。
- 返回颜色时,透明度设为
texColor.a × _AlphaScale
。
- 采样纹理获取
核心特点:片元不被丢弃,而是与颜色缓冲区混合,通过
_AlphaScale
调节透明度,需注意渲染顺序以保证正确叠加。
开启深度写入的半透明效果
需求背景:普通透明度混合(关闭深度写入)在复杂模型上会因片元渲染顺序错误导致前后关系混乱,此效果用于解决该问题。
核心原理:通过两个Pass实现
- 第一个Pass:开启深度写入(
ZWrite On
),不输出颜色(ColorMask 0
),仅将模型各片元的深度值写入深度缓冲,确定片元前后关系。 - 第二个Pass:执行正常透明度混合(关闭深度写入
ZWrite Off
,混合因子Blend SrcAlpha OneMinusSrcAlpha
),此时深度测试会剔除被遮挡的片元,保证正确叠加。
- 第一个Pass:开启深度写入(
实现步骤:
- 基于透明度混合Shader,在
SubShader
中新增第一个Pass,设置ZWrite On
和ColorMask 0
。 - 保留原透明度混合的Pass(含光照计算、透明度控制)。
- 基于透明度混合Shader,在
特性与注意事项:
- 模型内部片元不会互相混合(仅最靠前的片元参与混合)。
- 因多一个Pass,存在一定性能开销。
- 同一物体的Pass执行顺序:先所有片元执行第一个Pass,再执行第二个Pass。
双面渲染的透明效果
应用场景:实现半透明物体的双面可见性,既可以透过物体看到外部,也能看到其内部结构(解决默认背面剔除导致的内部不可见问题)。
核心原理:通过
Cull
剔除指令控制渲染面:Cull Back
(默认):剔除背面,只渲染正面。Cull Front
:剔除正面,只渲染背面。Cull Off
:关闭剔除,同时渲染正背面。
透明度测试的双面渲染
- 基础:基于透明度测试Shader(片元要么完全显示,要么剔除)。
- 实现:在Pass中添加
Cull Off
,关闭剔除,同时渲染正背面。 - 特点:无需混合,直接显示双面,适合镂空且需双面可见的物体(如网格状半透明结构)。
透明度混合的双面渲染
- 基础:基于透明度混合Shader(需颜色混合)。
- 实现:
- 使用两个Pass,第一个
Cull Front
(渲染背面),第二个Cull Back
(渲染正面)。 - 其他设置与普通透明度混合一致(关闭深度写入
ZWrite Off
,混合因子Blend SrcAlpha OneMinusSrcAlpha
)。
- 使用两个Pass,第一个
- 特点:先渲染背面再渲染正面,确保混合顺序正确,适合需双面半透明的物体(如薄玻璃、塑料袋)。
- 关键区别:透明度测试通过关闭剔除实现双面;透明度混合需分两个Pass分别渲染正背面,保证混合效果正确。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com