44.Shader开发入门总结

  1. 44.Shader开发入门总结
    1. 44.1 核心要点速览
      1. 光照模型
        1. 漫反射光照模型
          1. 兰伯特光照模型
          2. 半兰伯特光照模型
        2. 高光反射光照模型
          1. Phong 式高光反射
          2. Blinn-Phong 式高光反射
        3. 综合光照模型
          1. Phong 光照模型
          2. Blinn-Phong 光照模型
        4. 光照模型对照表
        5. 逐顶点与逐片元光照
        6. Unity 内置常用函数
      2. 纹理
        1. 纹理与 UV 在干什么
        2. 导入设置:Wrap 与 Filter
        3. 单张纹理 + Blinn-Phong
        4. 凹凸纹理(法线贴图)
        5. 渐变纹理(Ramp)
        6. 遮罩纹理
      3. 透明
        1. 前置概念:队列、深度、测试、混合
        2. 深度写入、队列与混合(Shader 里怎么写)
        3. 透明度测试(Alpha Test)
        4. 透明度混合
        5. 双 Pass:先钉深度再混合
        6. 双面透明
    2. 44.2 面试题精选
      1. 基础题
        1. 1. 兰伯特与半兰伯特漫反射的区别
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. Phong 高光与 Blinn-Phong 高光的差异
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. 透明度测试与透明度混合怎么选
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      2. 进阶题
        1. 1. 逐顶点与逐片元光照的取舍
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. 半透明为何常关闭深度写入
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. 切线空间法线贴图为何更常用
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      3. 深度题
        1. 1. 法线贴图:切线空间与世界空间两条算路
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. 双 Pass 深度预写半透明
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. 渐变纹理与 Wrap Mode
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章

44.Shader开发入门总结


44.1 核心要点速览

光照模型

光照模型是用公式近似「光与材质如何作用」的算法。学习主线:漫反射 → 高光 → 综合模型 → Unity 辅助函数。

漫反射光照模型

漫反射强度由法线与光源方向夹角决定:夹角越小、余弦越大、越亮。

兰伯特光照模型
  • 公式
漫反射颜色 = 光源颜色 × 材质漫反射颜色 × max(0, 法线方向 · 光源方向)

\[
C_d = \mathbf{L} \odot \mathbf{M}_d \cdot \max(0, \mathbf{n}\cdot\mathbf{l})
\]

  • 各项含义:\( \mathbf{L} \) 光源颜色,\( \mathbf{M}_d \) 漫反射色,\( \odot \) 按分量乘(R×R, G×G, B×B);\( \max(0, \mathbf{n}\cdot\mathbf{l}) \) 法线与光方向余弦,背光截为 0,须单位向量
  • 实践_LightColor0 取光色;_WorldSpaceLightPos0.xyznormalize;法线 UnityObjectToWorldNormal 转世界空间;UNITY_LIGHTMODEL_AMBIENT.rgb 避免阴影死黑。
半兰伯特光照模型

纯兰伯特背光面整块为 0;半兰伯特把 \( \mathbf{n}\cdot\mathbf{l}\in[-1,1] \) 映射到 \( [0,1] \),背光仍有灰阶,是观感技巧而非物理模型。

  • 公式
漫反射颜色 = 光源颜色 × 材质漫反射颜色 × ((法线方向 · 光源方向) / 2 + 1/2)

\[
C_d^{\mathrm{half}} = \mathbf{L} \odot \mathbf{M}_d \cdot \left(\frac{\mathbf{n}\cdot\mathbf{l}}{2}+\frac{1}{2}\right)
\]

  • 各项含义:与兰伯特相同,仅把 \( \max(0, \mathbf{n}\cdot\mathbf{l}) \) 换为 \( \dfrac{\mathbf{n}\cdot\mathbf{l}}{2}+\dfrac{1}{2} \),映射到 \( [0,1] \),不做硬截断。

高光反射光照模型

镜面感来自「视线方向」与「反射方向 / 半角方向」的对齐程度。

Phong 式高光反射

先算反射方向 \( \mathbf{r}=\mathrm{reflect}(-\mathbf{l},\mathbf{n}) \),再看 \( \mathbf{v} \) 与 \( \mathbf{r} \) 是否接近;pow 指数 \( n \) 控制高光锐利度。

  • 公式
高光颜色 = 光源颜色 × 材质高光颜色 × max(0, 视角方向 · 反射方向) ^ 光泽度
反射方向 = reflect(-光源方向, 法线方向)

\[
C_s = \mathbf{L} \odot \mathbf{M}_s \cdot \max(0,\mathbf{v}\cdot\mathbf{r})^{n},\quad \mathbf{r}=\mathrm{reflect}(-\mathbf{l},\mathbf{n})
\]

  • 各项含义:\( \mathbf{M}_s \) 材质高光色;\( \max(0,\mathbf{v}\cdot\mathbf{r})^n \) 视线与反射方向对齐度,\( n \) 越大高光越尖;\( \mathbf{r} \) 由 reflect(-l, n) 得到。
  • 实践reflect 入射符号用 -光源方向;视角方向 _WorldSpaceCameraPos - worldPosnormalize
Blinn-Phong 式高光反射

半角向量 \( \mathbf{h}=\mathrm{normalize}(\mathbf{v}+\mathbf{l}) \) 代替反射方向,看 \( \mathbf{n} \) 与 \( \mathbf{h} \) 是否接近;通常更快、高光略柔和

  • 公式
高光颜色 = 光源颜色 × 材质高光颜色 × max(0, 法线方向 · 半角方向) ^ 光泽度
半角方向 = normalize(视角方向 + 光源方向)

\[
C_s = \mathbf{L} \odot \mathbf{M}_s \cdot \max(0,\mathbf{n}\cdot\mathbf{h})^{n},\quad \mathbf{h}=\mathrm{normalize}(\mathbf{v}+\mathbf{l})
\]

  • 各项含义:与 Phong 同,仅把 \( \mathbf{v}\cdot\mathbf{r} \) 换为 \( \mathbf{n}\cdot\mathbf{h} \);\( \mathbf{h} \) 为视线与光方向的角平分方向。

综合光照模型

环境光 + 漫反射 + 高光,三项 RGB 相加

Phong 光照模型
  • 公式
最终颜色 = 环境光 + 兰伯特漫反射 + Phong 高光

\[
C = C_{\mathrm{amb}} + C_d^{\mathrm{Lambert}} + C_s^{\mathrm{Phong}}
\]

  • 各项含义:\( C_{\mathrm{amb}} \) 环境光(UNITY_LIGHTMODEL_AMBIENT / 天空盒),\( C_d \) 兰伯特漫反射,\( C_s \) Phong 高光。
Blinn-Phong 光照模型

与 Phong 相同的三项结构,仅高光换成 Blinn-Phong 项;实时渲染里更常见。

  • 公式
最终颜色 = 环境光 + 兰伯特漫反射 + Blinn-Phong 高光

\[
C = C_{\mathrm{amb}} + C_d^{\mathrm{Lambert}} + C_s
\]

  • 各项含义:与 Phong 光照模型相同,仅 \( C_s \) 换为 Blinn-Phong 高光项。

光照模型对照表

名称 核心思路 公式骨架 关键参数与特点 白话一行
兰伯特漫反射 法线与光方向夹角越小越亮 \( \mathbf{L}\odot\mathbf{M}_d\cdot\max(0,\mathbf{n}\cdot\mathbf{l}) \) 世界空间法线、光向;背光可全黑 漫反 = 光×漫材×max(0, n·l)
半兰伯特 点积映射到 \( [0,1] \),背光有层次 上式中把 \( \max(0,\cdot) \) 换为 \( \frac{\mathbf{n}\cdot\mathbf{l}}{2}+\frac{1}{2} \) 风格化友好,非能量守恒物理模型 漫反 = 光×漫材×((n·l)/2+1/2)
Phong 高光 视线与反射方向对齐程度 \( \propto \max(0,\mathbf{v}\cdot\mathbf{r})^{n} \) reflect(-光向, 法线);\( n \) 越大越尖 高光 = 光×高光材×max(0,v·r)^n
Blinn-Phong 高光 法线与半角对齐程度 \( \propto \max(0,\mathbf{n}\cdot\mathbf{h})^{n} \) reflect,常更快、略柔和 高光 = 光×高光材×max(0,n·h)^n
Phong 光照模型 环境 + 兰伯特 + Phong 高光 三项相加 经典完整模型 最终 = 环境 + 漫反 + Phong高光
Blinn-Phong 光照模型 环境 + 兰伯特 + Blinn-Phong 高光 三项相加 游戏与引擎中更常用 最终 = 环境 + 漫反 + BP高光
  • 名词分清:「Phong/Blinn-Phong 高光」只负责镜面项;「Phong/Blinn-Phong 光照模型」指 整碗汤(环境 + 漫反射 + 高光)。
  • 效率:同条件下 Blinn-Phong 高光常省掉显式反射向量,更适合逐像素计算密集的实时场景。

逐顶点与逐片元光照

  • 类比:顶点光照像在 几个角 算好明暗再插值涂满三角面,省算力但易糊;片元光照是 每个像素 用自身插值后的法线、位置再算,更准更贵。
  • 原因:顶点间颜色由光栅化 插值 得到,无法表现细尺度明暗;片元阶段可再用高精度法线(如法线贴图)参与计算。
  • 性能:顶点着色器按顶点计费,片元着色器按覆盖像素计费;同模型下移计算到片元通常更耗。

Unity 内置常用函数

函数 作用
WorldSpaceViewDir(v) 模型空间顶点 → 世界空间观察方向(未归一化)
UnityWorldSpaceViewDir(v) 世界空间顶点 → 世界空间观察方向
ObjSpaceViewDir(v) 模型空间顶点 → 模型空间观察方向
WorldSpaceLightDir(v) 模型空间顶点 → 世界空间光照方向(Forward)
UnityWorldSpaceLightDir(v) 世界空间顶点 → 世界空间光照方向
ObjSpaceLightDir(v) 模型空间顶点 → 模型空间光照方向
UnityObjectToWorldNormal 法线模型→世界(含非均匀缩放修正)
UnityObjectToWorldDir 方向向量模型→世界
UnityWorldToObjectDir 方向向量世界→模型
UnityObjectToClipPos 模型空间 → 裁剪空间

返回方向常需 normalize;光照方向函数仅在 Forward 渲染有效。

纹理

纹理与 UV 在干什么

  • 问题:模型只有几何,如何贴上「皮」?
  • 分析:美术在 DCC 里为顶点写入 UV;GPU 光栅化时对 UV 插值,片元里用 tex2D 取色。
  • 公式
纹理坐标取值范围 [0, 1]²,与贴图像素数无关

\[
(u,v) \in [0,1]^2
\]

  • 各项含义:\( u,v \) 为纹理上横向、纵向归一化坐标,与贴图分辨率无关。
  • 实践sampler2D _MainTex + float4 _MainTex_ST(xy 缩放、zw 偏移);TRANSFORM_TEX 或手动 uv * _ST.xy + _ST.zw

导入设置:Wrap 与 Filter

  • Wrap ModeRepeat 像瓷砖重复;Clamp 钳住边缘防绕回;还有镜像等变体,按美术需求选。
  • Filter ModePoint 硬边像素风;Bilinear / Trilinear 缩放更柔,常配合 Mipmap 减闪烁与走样。

单张纹理 + Blinn-Phong

  • 问题:贴图颜色与光照如何合成才不假灰、不假白?
  • 分析:把采样结果当作 反照率 albedoalbedo = texColor × 漫反射色;兰伯特项用 albedo;环境光也乘 albedo,避免「环境一块灰蒙在模型上」。
  • 综合最终 ≈ 环境光×albedo + 漫反射项 + Blinn-Phong 高光项(与系列课文一致即可)。

凹凸纹理(法线贴图)

  • 问题:不加三角面,如何做出细小凹凸?
  • 分析:用贴图存 扰动后的法线,光照仍按新法线算,眼睛感到起伏。高度图 灰度存高度,常需额外求导变正法线,开销大;法线贴图 直接存法线向量更常用。
  • 空间选择切线空间 法线偏蓝紫,易复用到不同朝向表面;模型空间 法线彩色、直观但换模型常要重烘焙。世界空间 算法线光照利于点光等,但矩阵与变体更多。
  • 实践套路appdata_full 带切线;副切线 bitangent = cross(n, t) * tangent.w;构建 TBNUnpackNormal 解码;_BumpScale 控制强度。切线空间算法:光向、视向转到切线空间再 dot;世界空间算法:法线转到世界空间再与光向、半角算。

渐变纹理(Ramp)

  • 问题:如何把连续明暗变成 色带、插画感
  • 分析:用半兰伯特标量当作 UV(如 float2(t,t))采样 _RampTex,用纹理颜色 重映射 明暗,而非直接用标量当亮度。
  • 公式
明暗标量 = (法线方向 · 光源方向) / 2 + 1/2,用该值采样渐变纹理做明暗重映射

\[
t=\frac{\mathbf{n}\cdot\mathbf{l}}{2}+\frac{1}{2}\in[0,1]
\]

  • 各项含义:\( t \) 为半兰伯特标量 \( \in[0,1] \),_RampTex 是渐变查找表。
  • 实践:渐变图 Wrap ModeClamp,避免浮点误差在 Repeat 下绕回采到黑点。

遮罩纹理

  • 问题:同一材质上,有的区域要更强高光、有的区域要压住特效?
  • 分析:用贴图某一通道(如 R)作 局部权重,再乘自定义系数 _SpecularScale,最后乘进高光项。RGBA 四路可分别存高光、透明、特效等,减少贴图张数。

透明

前置概念:队列、深度、测试、混合

  • 渲染队列:由 SubShader Tags 中的键 Queue 指定;决定大致绘制顺序。透明物体通常排在不透明之后,才能读到背后颜色做混合。
  • 深度缓冲:每像素记「目前最近深度」;深度测试决定新片元是否通过;混合决定通过后的颜色如何与颜色缓冲已有值合成。
  • 直觉:不透明物体常开深度写入,顺序不敏感;半透明常要 ZWrite Off(关深度写入)才能混合背后,但引入 排序敏感(前后穿插时可能错)。

深度写入、队列与混合(Shader 里怎么写)

  • ZWrite:写在 Pass 内,只影响该 Pass。半透明 Pass 常见 ZWrite Off
  • 队列与渲染类型QueueRenderType 写在 SubShaderTags 中,影响其下所有 Pass。
Pass
{
    Tags { "LightMode"="ForwardBase" }
    ZWrite Off
}
SubShader
{
    Tags{ "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
}
SubShader
{
    Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
}
  • 混合通式(RGB 与 A 可分别指定因子;具体以目标平台文档为准)

  • 公式

混合 RGB = 源因子 × 片元色 + 目标因子 × 缓冲色

\[
O_{\mathrm{rgb}} = F_s \cdot S_{\mathrm{rgb}} + F_d \cdot D_{\mathrm{rgb}}
\]

  • 各项含义:\( F_s,F_d \) 为源/目标混合因子(Blend 指令配置),\( S \) 片元色,\( D \) 缓冲已有色。

常见 透明度混合Blend SrcAlpha OneMinusSrcAlpha,即

  • 公式
输出色 = α × 片元色 + (1 − α) × 缓冲色

\[
\mathbf{C}_{\mathrm{out}} = \alpha \mathbf{C}_S + (1-\alpha)\mathbf{C}_D
\]

  • 各项含义:\( \alpha \) 片元透明度,\( \mathbf{C}_S \) 片元色,\( \mathbf{C}_D \) 缓冲色;对应 Blend SrcAlpha OneMinusSrcAlpha

透明度测试(Alpha Test)

  • 场景:树叶、栅栏、草等 镂空,不要真半透明。
  • 做法clip(texColor.a - _Cutoff),低于阈值直接丢弃片元;通过的可按不透明写深度。Queue=AlphaTestRenderType=TransparentCutout不必关深度写入。

透明度混合

  • 场景:玻璃、水、特效半透明层。
  • 做法Queue=TransparentZWrite OffBlend SrcAlpha OneMinusSrcAlpha;返回颜色的 alpha 常用 texColor.a * _AlphaScale。注意整体渲染顺序与穿插。

双 Pass:先钉深度再混合

  • 问题:复杂网格自穿插时,单纯关 ZWrite 易导致片元顺序错乱。
  • 解决:第一 Pass ZWrite OnColorMask 0 只写深度;第二 Pass 正常半透明混合。代价是多一遍几何;模型内部一般不互相混合,仅最前层参与混合。

双面透明

  • CullCull Back 默认只正面;Cull Front 只背面;Cull Off 双面。
  • Alpha Test:常 Cull Off 即可双面镂空。
  • Alpha Blend:常用两 Pass——先 Cull Front 画背,再 Cull Back 画前,保证混合顺序与薄壳玻璃观感。

44.2 面试题精选

基础题

1. 兰伯特与半兰伯特漫反射的区别

题目

兰伯特漫反射与半兰伯特漫反射各如何计算?半兰伯特解决了什么视觉问题?它是否更「物理正确」?

深入解析
  • 兰伯特:\( C_d \propto \max(0, \mathbf{n}\cdot\mathbf{l}) \),背光侧点积为负时被截断为 0,整块背光可显得死黑;白话可记 C_d ∝ max(0, n·l)
  • 半兰伯特:把 \( \mathbf{n}\cdot\mathbf{l} \) 从 \( [-1,1] \) 线性映射到 \( [0,1] \),背光仍有明暗渐变,利于卡通、手绘感;白话可记 C_d ∝ (n·l)/2+1/2(代替 max(0,n·l))。
  • 物理性:半兰伯特是视觉近似,不是能量守恒的物理模型;面试里要能说清「为什么好看」和「为什么不等于真实漫反射」。
答题示例

兰伯特用 \( \max(0, \mathbf{n}\cdot\mathbf{l}) \),背光容易全黑。半兰伯特用 \( \frac{\mathbf{n}\cdot\mathbf{l}}{2}+\frac{1}{2} \) 把点积拉到 0~1,背光也有层次。它是风格化技巧,不强调物理正确。

参考文章
  • 3.光照模型-漫反射光照模型-兰伯特光照模型-必备知识点
  • 6.光照模型-漫反射光照模型-半兰伯特光照模型-必备知识点

2. Phong 高光与 Blinn-Phong 高光的差异

题目

Phong 高光与 Blinn-Phong 高光在方向量选择上差在哪里?为何实时渲染里更常见 Blinn-Phong?

深入解析
  • Phong:用反射方向 \( \mathbf{r}=\mathrm{reflect}(-\mathbf{l},\mathbf{n}) \),高光项看 \( \mathbf{v}\cdot\mathbf{r} \);白话可记 C_s ∝ max(0, v·r)^nr = reflect(−l, n)
  • Blinn-Phong:用半角向量 \( \mathbf{h}=\mathrm{normalize}(\mathbf{v}+\mathbf{l}) \),高光项看 \( \mathbf{n}\cdot\mathbf{h} \);白话可记 C_s ∝ max(0, n·h)^nh = normalize(v+l)
  • 工程上:避免显式求反射向量、高光形状往往更稳;代价是「在相同指数 \( n \) 下」与 Phong 高光宽度并不完全一致,调参时要心里有数。
答题示例

Phong 用视角和反射方向的夹角;Blinn-Phong 用法线和半角向量的夹角。Blinn-Phong 少算反射方向、GPU 友好,游戏里更常用,但光泽度要和 Phong 一一对应需要重新调。

参考文章
  • 9.光照模型-高光反射光照模型-Phong式高光反射模型-必备知识点
  • 12.光照模型-高光反射光照模型-BlinnPhong式高光反射模型-必备知识点

3. 透明度测试与透明度混合怎么选

题目

AlphaTest(clip)与半透明 Alpha Blend 各适合什么效果?对深度写入、排序各有什么影响?

深入解析
  • 透明度测试:像素要么保留要么 discard,一般可保持 ZWrite On,排序压力小,适合镂空、树叶、栅栏。
  • 透明度混合:真半透明,常配 ZWrite Off + Blend SrcAlpha OneMinusSrcAlpha,依赖渲染顺序,玻璃、水体、特效常用。
  • 易错点:把需要柔和渐变的半透明硬做成 clip,或把镂空当半透明又开深度写入导致层次错误。
答题示例

Clip 是硬切,一般还能写深度,适合镂空。半透明要混合背后颜色,通常关深度写入,所以同一批透明物体排序很重要;玻璃、雾状用混合,草叶栅栏多用 AlphaTest。

参考文章
  • 40.透明-效果实现-透明度测试
  • 41.透明-效果实现-透明度混合

进阶题

1. 逐顶点与逐片元光照的取舍

题目

同样一套光照公式,放在顶点着色器和片元着色器里效果差在哪里?为什么片元更贵?

深入解析
  • 逐顶点:光照在顶点上算完,颜色在三角形内插值,高光、法线变化快时容易「糊、条带」。
  • 逐片元:每像素用插值后的法线、位置再算,细节好,但指令数随分辨率线性放大。
  • 选用:移动端或大量简单物体可顶点光照;角色、法线贴图、锐利高光优先片元。
答题示例

顶点光照是算在顶点上再插值,中间像素不重新算光照,所以高光和法线细节容易糊。片元光照每像素算,贵但准。有法线贴图或要高光质量就上片元。

参考文章
  • 2.光照模型-逐顶点光照和逐片元光照
  • 21.光照模型-为什么逐片元比逐顶点平滑

2. 半透明为何常关闭深度写入

题目

不透明物体可以乱序画,半透明为什么往往要关 ZWrite?关了以后带来什么问题、引擎一般怎么缓解?

深入解析
  • 关 ZWrite 的目的:同一像素要累积「背后已经画好的颜色」,若先写上近处深度,远处半透明片元可能被深度测试挡掉,混合无从谈起。
  • 副作用:透明物体之间依赖绘制顺序,交叉、自穿插模型容易穿帮。
  • 缓解:按队列排序、拆模型、双 Pass 深度预写、或接受限制并分材质排序。
答题示例

半透明要和 framebuffer 里已有颜色混合,如果还写深度,后面的透明片元可能被深度测试直接扔掉。所以常关深度写入,代价是透明物体谁先画谁后画会影响结果,要靠队列和排序,复杂情况用双 Pass 或拆 mesh。

参考文章
  • 36.透明-必备知识点-重要知识回顾
  • 37.透明-必备知识点-渲染顺序的重要性

3. 切线空间法线贴图为何更常用

题目

法线贴图为什么多做成切线空间?和模型空间法线比,各有什么代价?

深入解析
  • 切线空间:纹素存的是相对表面的扰动,同一张贴图可跨不同朝向、可动画蒙皮复用,美术流水线友好。
  • 模型空间:每个纹素对应绝对方向,物体变形、复用差,但实现上有时少一套 TBN 变换。
  • Shader 侧:切线空间要在顶点构造 TBN,把光方向或法线转到同一空间再算点积;世界空间法则在世界空间算光照。
答题示例

切线空间里法线是相对表面局部坐标,贴图可以重复用在不同朝向的模型上,动画顶点也不会轻易「法线乱飞」。模型空间法线跟模型绑定,复用差。代价是 Shader 里要 TBN 变换,多一点顶点与矩阵工作。

参考文章
  • 27.纹理-凹凸纹理-基本概念
  • 28.纹理-凹凸纹理-法线贴图的计算方式
  • 29.纹理-凹凸纹理-切线空间下计算

深度题

1. 法线贴图:切线空间与世界空间两条算路

题目

在切线空间采样法线贴图与在世界空间用 TBN 把法线转出来再光照,各适合什么光源与性能权衡?

深入解析
  • 切线空间算光照:顶点把 \( \mathbf{l} \)、\( \mathbf{v} \) 变到切线空间,片元里与解包法线直接 dot,纹理采样与光照同空间,定向光下很顺。
  • 世界空间:片元把切线法线乘 TBN 到世界空间,与 _WorldSpaceLightPos0 等统一算;点光、聚光、多光源时少重复变换逻辑。
  • 性能:切线空间常减少片元里矩阵组合次数;世界空间可能增加插值与寄存器(传 TBN 三向量)。选型看光源种类与变体数量。
答题示例

切线空间是把光向和视角转到切线空间,在片元里和法线贴图解包结果直接点积,定向光很省事。世界空间是把法线用 TBN 转到世界再算,点光源多的时候更自然。性能上要看是少算片元矩阵还是少插值数据,项目里会按光源类型选一条路或做 Shader 变体。

参考文章
  • 29.纹理-凹凸纹理-切线空间下计算
  • 30.纹理-凹凸纹理-世界空间下计算

2. 双 Pass 深度预写半透明

题目

ColorMask 0 + ZWrite On 的第一个 Pass 加上正常透明混合的第二个 Pass,解决了什么问题?仍有哪些现象是这套方案解决不了的?

深入解析
  • 机制:Pass1 只写深度不写颜色,把「模型最前表面」钉在深度缓冲里;Pass2 半透明混合时深度测试能挡住被其它物体遮挡的片元,减轻凸包内部错误混合。
  • 局限:同物体内部多层半透明仍不会像真实介质那样层层混合(近似成只有最前层参与);多 Pass 增加带宽与 Overdraw。
  • 与排序关系:不能替代所有排序问题,只是减少「同物体内部」一类错误。
答题示例

第一遍只写深度不输出颜色,相当于先把物体最前面的深度定下来;第二遍再半透明混合,深度测试能去掉被挡住的片元,减轻比如头发、树叶那种内部乱序。但它不能让模型内部多层透明都物理正确混合,只是近似,还多一次 Pass 成本。

参考文章
  • 42.透明-效果实现-开启深度写入的半透明效果

3. 渐变纹理与 Wrap Mode

题目

用半兰伯特标量去采样渐变纹理做卡通明暗时,为什么常把渐变纹理设为 ClampRepeat 下会出现什么现象?

深入解析
  • 半兰伯特输出:理论在 \( [0,1] \),浮点误差可能略大于 1 或略小于 0。
  • Repeat:UV 取小数部分,略大于 1 会绕回纹理左侧,出现错误条带或黑点。
  • Clamp:越界钳在 0 或 1,明暗稳定在条带两端;与渐变纹理「一维查找表」语义一致。
答题示例

半兰伯特算出来应该是 0~1,但浮点会有 1.0001 这种。Repeat 会取小数部分,采样会跳到纹理另一端,出现噪点或黑条。Clamp 把坐标钉在 0~1 边界,卡通 ramp 一般都用它。

参考文章
  • 31.纹理-渐变纹理-基本概念
  • 32.纹理-渐变纹理-基础实现

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏