深度测试
多个三角形覆盖同一个像素时,需要确定最靠近相机的表面。画家算法 Painter’s Algorithm 先按深度从远到近排序,再依次覆盖颜色,排序成本为 O ( n log n ) O(n\log n) O ( n log n ) ,而且循环遮挡的三角形无法得到一致的全局顺序。
深度缓冲 Z-buffer 为每个样本保存当前最近深度。这里比较的是经过投影和规范深度映射后的样本深度 d d d ,并非相机空间中带符号的 z z z 。第三篇采用近裁剪面 z n d c = 1 z_{\mathrm{ndc}}=1 z ndc = 1 、远裁剪面 z n d c = − 1 z_{\mathrm{ndc}}=-1 z ndc = − 1 的推导;为了使用数值越小越近的约定,可再映射
d = 1 − z n d c 2 . d
=
\frac{1-z_{\mathrm{ndc}}}{2}.
d = 2 1 − z ndc .
此时近处 d = 0 d=0 d = 0 、远处 d = 1 d=1 d = 1 。缓冲区初始化为无穷大;每产生一个覆盖该样本的片元,就执行
d < d b u f f e r ( x , y ) ⟹ { d b u f f e r ( x , y ) ← d , C ( x , y ) ← C f r a g m e n t . d<d_{\mathrm{buffer}}(x,y)
\quad\Longrightarrow\quad
\begin{cases}
d_{\mathrm{buffer}}(x,y)\leftarrow d,\\
C(x,y)\leftarrow C_{\mathrm{fragment}}.
\end{cases}
d < d buffer ( x , y ) ⟹ { d buffer ( x , y ) ← d , C ( x , y ) ← C fragment .
它的正确性来自一个简单不变量:处理完任意数量片元后,
d b u f f e r = min { d i ∣ i 已处理且覆盖该样本 } . d_{\mathrm{buffer}}
=
\min
\left\{
d_i\mid
i\text{ 已处理且覆盖该样本}
\right\}.
d buffer = min { d i ∣ i 已处理且覆盖该样本 } .
初始集合为空,对应 + ∞ +\infty + ∞ 。加入新片元时,若它更远,不变量不变;若它更近,用新值替换后仍得到集合最小值。归纳到所有片元,留下的就是最近表面,因此结果与三角形提交次序无关。
算法成本与实际产生的覆盖片元数成正比:
O ( ∑ i coverage ( T i ) ) . O\left(
\sum_i\operatorname{coverage}(T_i)
\right).
O ( i ∑ coverage ( T i ) ) .
当平均覆盖范围有限时,它常近似随图元数线性增长,也适合按像素并行。Z-buffer 只解决相机可见性;一个点是否被光源照到,还需要阴影算法。
已经推导出透视深度含有 1 / z 1/z 1/ z ,远处分到的深度值更密集。有限位数或浮点量化会让两个近乎共面的表面落到同一深度区间,形成深度冲突 z-fighting。反向深度 Reversed-Z 会改变远近数值和比较方向,但维护“当前最近值”的不变量没有改变。
深度测试使用哪种大小关系,取决于投影后的深度约定;不能直接拿相机空间的负 z z z 与 Z-buffer 比较。
局部着色
局部着色 Local Shading 计算表面某点沿观察方向离开的光。简单局部模型使用:
单位表面法线 n \mathbf n n ;
从着色点指向光源的单位方向 l \mathbf l l ;
从着色点指向相机的单位方向 v \mathbf v v ;
光源强度 I I I 与距离 r r r ;
表面的颜色、光泽等材质参数。
从位置相减得到光线或视线方向后,进入点积前必须归一化;法线 Normal 是描述表面朝向的方向约束,不是一个可直接代入的 position。法线插值、变换或贴图解码后也要重新归一化,否则点积会同时受到向量长度影响。
局部模型只处理当前点和直接光源,不追踪光在其他表面上的多次反射。它可以产生明暗和高光,却不会自动产生遮挡阴影与间接光照。
漫反射
理想漫反射 Lambertian Reflection 向各方向均匀散射,因此结果与观察方向无关。课程的 Blinn–Phong 经验着色式常把材质比例、单位换算和 1 / π 1/\pi 1/ π 一并吸收到系数 k d k_d k d 中:
L d = k d I r 2 max ( 0 , n ⋅ l ) . L_d=k_d\frac{I}{r^2}\max(0,\mathbf n\cdot\mathbf l).
L d = k d r 2 I max ( 0 , n ⋅ l ) .
若 ρ \rho ρ 明确表示物理反照率、I I I 表示点光源的辐射强度,则 I / r 2 I/r^2 I / r 2 是到达表面的入射照度尺度。能量归一化的 Lambertian BRDF 为 f r = ρ / π f_r=\rho/\pi f r = ρ / π ,相应出射辐亮度贡献应写成
L d = ρ π I r 2 max ( 0 , n ⋅ l ) . L_d
=
\frac{\rho}{\pi}
\frac{I}{r^2}
\max(0,\mathbf n\cdot\mathbf l).
L d = π ρ r 2 I max ( 0 , n ⋅ l ) .
因此不能在遗漏 1 / π 1/\pi 1/ π 的同时把经验系数 k d k_d k d 直接解释为反照率。余弦项来自投影面积。面积为 d A \mathrm dA d A 的斜面面对光线时,有效截面只有
d A ⊥ = d A cos θ = d A ( n ⋅ l ) . \mathrm dA_\perp
=
\mathrm dA\cos\theta
=
\mathrm dA(\mathbf n\cdot\mathbf l).
d A ⊥ = d A cos θ = d A ( n ⋅ l ) .
点光源向固定立体角发出的能量落在半径为 r r r 的球面上,球面面积随 r 2 r^2 r 2 增长,所以单位面积能量按 1 / r 2 1/r^2 1/ r 2 衰减。k d k_d k d 是漫反射系数或表面颜色。夹角超过 90 ∘ 90^\circ 9 0 ∘ 时,光从表面背面到达,余弦项必须截断为零;方向光通常不随场景中的位置改变。
镜面反射
镜面高光出现在观察方向接近理想反射方向的位置。Blinn–Phong 反射模型 Blinn–Phong Reflection Model 使用半程向量 Half Vector
h = v + l ∥ v + l ∥ \mathbf h=\frac{\mathbf v+\mathbf l}{\lVert\mathbf v+\mathbf l\rVert}
h = ∥ v + l ∥ v + l
当 v + l = 0 \mathbf v+\mathbf l=\mathbf0 v + l = 0 时,半程向量没有定义;这对应光照方向与观察方向完全相反,镜面项应跳过或置零,不能继续做归一化。
比较法线与光线、视线的中间方向:
L s = k s I r 2 max ( 0 , n ⋅ h ) p . L_s=k_s\frac{I}{r^2}\max(0,\mathbf n\cdot\mathbf h)^p.
L s = k s r 2 I max ( 0 , n ⋅ h ) p .
理想镜面反射时,法线恰好平分光照方向 l \mathbf l l 与观察方向 v \mathbf v v ,两个单位方向的归一化和正是角平分线,所以 n ⋅ h \mathbf n\cdot\mathbf h n ⋅ h 能衡量观察方向离理想高光还有多远。p p p 越大,反射瓣越窄,高光越集中;k s k_s k s 控制高光颜色和强度。这里描述的是经验模型,并不是后续基于微表面的物理 BRDF。
环境光项 L a = k a I a L_a=k_aI_a L a = k a I a 用一个常量近似所有未计算的间接光。最终模型为
L = L a + L d + L s . L=L_a+L_d+L_s.
L = L a + L d + L s .
环境光能避免无直接光区域完全变黑,但没有几何依据,也不能表达真实的颜色互染和遮挡关系。
Blinn–Phong 是经验性的局部光照模型;BRDF Bidirectional Reflectance Distribution Function 描述给定入射和出射方向下的反射分布,BSDF Bidirectional Scattering Distribution Function 还把透射、折射等散射纳入同一描述,后续可以用它们讨论比经验项更一般的材质响应。
着色频率
三角网格可以在不同频率上执行着色:
Flat shading:每个三角形使用一个面法线和一个颜色,棱角清晰。
Gouraud shading:在顶点计算颜色,再把颜色插值到三角形内部;小而尖锐的高光可能被顶点采样漏掉。
Phong shading:插值顶点法线,在每个像素重新计算着色,平滑效果和高光质量更好。
Phong shading 指法线插值策略,Phong 或 Blinn-Phong reflectance model 指反射公式,两者含义不同。
顶点法线可以由相邻面法线加权平均得到。若三角形 i i i 的法线为 n i \mathbf n_i n i 、面积或夹角权重为 w i w_i w i ,则
n v = ∑ i w i n i ∥ ∑ i w i n i ∥ . \mathbf n_v=
\frac{\sum_iw_i\mathbf n_i}
{\left\lVert\sum_iw_i\mathbf n_i\right\rVert}.
n v = ∥ ∑ i w i n i ∥ ∑ i w i n i .
三角形内部插值得到的法线一般不再是单位向量,送入点积前必须重新归一化。硬边两侧不应共享同一顶点法线,否则本应尖锐的棱边会被错误地抹平。
图形管线
图形管线 Graphics Pipeline 把实时光栅化拆成一条流水线:
应用阶段准备场景、相机、材质和绘制命令。
顶点处理执行模型、视图与投影变换,并输出顶点属性。
图元装配把顶点组成三角形,随后进行裁剪等处理。
光栅化计算覆盖样本,并对顶点属性做插值,产生片元。
片元处理执行着色和纹理查询。
帧缓冲阶段完成深度、模板、混合等测试,再写入图像。
GPU 以大量并行处理单元执行顶点和片元程序,同时保留光栅化、纹理过滤等固定功能硬件。顶点着色器描述单个顶点如何变换,片元着色器描述单个候选像素如何计算颜色;同一程序会并行应用于大批数据。
重心坐标
重心坐标 Barycentric Coordinates 用三个顶点的仿射组合表示三角形平面上的点:
P = α A + β B + γ C , α + β + γ = 1. P=\alpha A+\beta B+\gamma C,
\qquad
\alpha+\beta+\gamma=1.
P = α A + βB + γ C , α + β + γ = 1.
令有向三角形面积为
[ A B C ] = 1 2 ( ( B − A ) × ( C − A ) ) z . [ABC]
=
\frac12
\big((B-A)\times(C-A)\big)_z.
[ A BC ] = 2 1 ( ( B − A ) × ( C − A ) ) z .
由于面积对顶点位置是线性的,
α = [ P B C ] [ A B C ] , β = [ P C A ] [ A B C ] , γ = [ P A B ] [ A B C ] . \alpha=\frac{[PBC]}{[ABC]},
\qquad
\beta=\frac{[PCA]}{[ABC]},
\qquad
\gamma=\frac{[PAB]}{[ABC]}.
α = [ A BC ] [ PBC ] , β = [ A BC ] [ PC A ] , γ = [ A BC ] [ P A B ] .
三个子三角形的有向面积之和等于原三角形面积,所以权重和为 1。点位于内部时,三个子三角形与原三角形朝向一致,因而 α , β , γ ≥ 0 \alpha,\beta,\gamma\ge0 α , β , γ ≥ 0 ;越过某条边后,对应权重会变成负数。
若变换是仿射的,顶点颜色、法线、纹理坐标等属性可按相同权重插值:
V ( P ) = α V A + β V B + γ V C . V(P)
=
\alpha V_A+\beta V_B+\gamma V_C.
V ( P ) = α V A + β V B + γ V C .
透视投影 Perspective Projection 含有齐次除法,因此不保持普通仿射权重。设三角形上一个点在投影前的重心权重为 λ A , λ B , λ C \lambda_A,\lambda_B,\lambda_C λ A , λ B , λ C ,顶点裁剪坐标的第四分量为 w A , w B , w C w_A,w_B,w_C w A , w B , w C 。MVP 变换在齐次坐标中仍是线性的:
P ~ = λ A P ~ A + λ B P ~ B + λ C P ~ C . \widetilde P
=
\lambda_A\widetilde P_A
+\lambda_B\widetilde P_B
+\lambda_C\widetilde P_C.
P = λ A P A + λ B P B + λ C P C .
令 P A , P B , P C P_A,P_B,P_C P A , P B , P C 表示除法后的屏幕点。因为 P ~ i = w i P i \widetilde P_i=w_iP_i P i = w i P i ,再做齐次除法可得
P s c r e e n = λ A w A P A + λ B w B P B + λ C w C P C λ A w A + λ B w B + λ C w C . P_{\mathrm{screen}}
=
\frac{
\lambda_Aw_AP_A
+\lambda_Bw_BP_B
+\lambda_Cw_CP_C
}{
\lambda_Aw_A
+\lambda_Bw_B
+\lambda_Cw_C
}.
P screen = λ A w A + λ B w B + λ C w C λ A w A P A + λ B w B P B + λ C w C P C .
屏幕空间计算出的重心坐标记为 α , β , γ \alpha,\beta,\gamma α , β , γ 。与上式逐项比较:
α = λ A w A λ A w A + λ B w B + λ C w C , \alpha
=
\frac{\lambda_Aw_A}
{\lambda_Aw_A+\lambda_Bw_B+\lambda_Cw_C},
α = λ A w A + λ B w B + λ C w C λ A w A ,
其余两项同理。反解得到
λ i = α i / w i α / w A + β / w B + γ / w C . \lambda_i
=
\frac{\alpha_i/w_i}
{\alpha/w_A+\beta/w_B+\gamma/w_C}.
λ i = α / w A + β / w B + γ / w C α i / w i .
真正应当插值的是投影前的权重,所以
V ( P ) = α V A / w A + β V B / w B + γ V C / w C α / w A + β / w B + γ / w C . V(P)=
\frac{
\alpha V_A/w_A
+\beta V_B/w_B
+\gamma V_C/w_C
}{
\alpha/w_A
+\beta/w_B
+\gamma/w_C
}.
V ( P ) = α / w A + β / w B + γ / w C α V A / w A + β V B / w B + γ V C / w C .
这就是 透视正确插值 Perspective-Correct Interpolation 。硬件实现通常分别线性插值 V i / w i V_i/w_i V i / w i 与 1 / w i 1/w_i 1/ w i ,再用前者除以后者。
一个一维例子能看出差异。设屏幕线段中点的权重为 α = β = 0.5 \alpha=\beta=0.5 α = β = 0.5 ,两端 w A = 1 , w B = 4 w_A=1,w_B=4 w A = 1 , w B = 4 ,则投影前的权重为
λ A = 0.5 / 1 0.5 / 1 + 0.5 / 4 = 0.8 , λ B = 0.2. \lambda_A
=
\frac{0.5/1}{0.5/1+0.5/4}
=0.8,
\qquad
\lambda_B=0.2.
λ A = 0.5/1 + 0.5/4 0.5/1 = 0.8 , λ B = 0.2.
若属性从 V A = 0 V_A=0 V A = 0 变到 V B = 1 V_B=1 V B = 1 ,中点正确值是 0.2 0.2 0.2 ,并非屏幕线性插值得到的 0.5 0.5 0.5 。远端在透视后被压缩,屏幕距离已经不能直接代表原三角形上的距离。
屏幕重心坐标适合判断覆盖;属性插值还必须除以齐次分量 w w w ,否则纹理会随透视发生“游动”和扭曲。
纹理映射
纹理映射 Texture Mapping 把二维参数域中的数据附着到三维表面。网格顶点保存纹理坐标 ( u , v ) (u,v) ( u , v ) ,光栅化器对其做透视正确插值,片元着色器再查询纹理。纹理既可以保存颜色,也可以保存法线、高度、粗糙度、遮挡或任意供着色器读取的数据。
三种空间必须区分:顶点位置位于模型或世界空间,最终片元位于屏幕空间,纹理样本位于纹理空间。UV 展开决定三维表面如何切开并铺到二维图像上;接缝、拉伸和重复铺贴都由这层映射控制。
纹理放大
当一个纹素覆盖多个屏幕像素时,需要在离散纹素之间重建连续值。最近邻只取距离查询点最近的纹素,边缘会呈块状。双线性插值 Bilinear Interpolation 读取相邻四个纹素:
纹素通常以中心而非边界作为采样位置;对宽高为 W t , H t W_t,H_t W t , H t 的纹理,索引 ( i , j ) (i,j) ( i , j ) 的归一化中心可写成
( i + 0.5 W t , j + 0.5 H t ) . \left(\frac{i+0.5}{W_t},\frac{j+0.5}{H_t}\right).
( W t i + 0.5 , H t j + 0.5 ) .
UV 超出 [ 0 , 1 ] [0,1] [ 0 , 1 ] 时还要明确 address mode:clamp 把坐标截到边界,wrap/repeat 按周期重复,mirror 则交替翻转。采样中心和越界规则不一致,会造成半纹素偏移、接缝或边缘颜色异常。
u 0 = lerp ( s , u 00 , u 10 ) , u 1 = lerp ( s , u 01 , u 11 ) , u_0=\operatorname{lerp}(s,u_{00},u_{10}),
\qquad
u_1=\operatorname{lerp}(s,u_{01},u_{11}),
u 0 = lerp ( s , u 00 , u 10 ) , u 1 = lerp ( s , u 01 , u 11 ) ,
f ( u , v ) = lerp ( t , u 0 , u 1 ) , lerp ( x , a , b ) = a + x ( b − a ) . f(u,v)=\operatorname{lerp}(t,u_0,u_1),
\qquad
\operatorname{lerp}(x,a,b)=a+x(b-a).
f ( u , v ) = lerp ( t , u 0 , u 1 ) , lerp ( x , a , b ) = a + x ( b − a ) .
双三次插值使用更大邻域,过渡更平滑,代价也更高。插值解决的是重建方式,不会凭空恢复原纹理中不存在的细节。
纹理缩小
当一个屏幕像素覆盖很多纹素时,点查询会漏掉大部分纹理信息,产生摩尔纹和闪烁。正确问题从“这个点是什么颜色”变为“这个像素在纹理空间覆盖的区域平均是什么”。屏幕上不同位置的像素 footprint 还会因透视而具有不同大小与形状。
Mipmap 预先把纹理逐层缩小一半。层级 D D D 的宽高约为原纹理的 2 − D 2^{-D} 2 − D ,所有层总存储量为原图的
1 + 1 4 + 1 16 + ⋯ = 4 3 , 1+\frac14+\frac1{16}+\cdots=\frac43,
1 + 4 1 + 16 1 + ⋯ = 3 4 ,
额外开销是原图的三分之一。层级选择要估计一个屏幕像素在纹理空间中的覆盖范围 Footprint。
在当前片元附近,UV 映射可以用一阶 Taylor 展开近似。它的 Jacobian 为
J = ( ∂ u ∂ x ∂ u ∂ y ∂ v ∂ x ∂ v ∂ y ) . J=
\begin{pmatrix}
\frac{\partial u}{\partial x}
&
\frac{\partial u}{\partial y}
\\
\frac{\partial v}{\partial x}
&
\frac{\partial v}{\partial y}
\end{pmatrix}.
J = ( ∂ x ∂ u ∂ x ∂ v ∂ y ∂ u ∂ y ∂ v ) .
屏幕沿 x x x 或 y y y 移动一个像素,乘以 J J J 后得到纹理空间中的两条边。若原纹理宽高为 W t , H t W_t,H_t W t , H t ,把归一化 UV 转成纹素坐标后,两条边的长度为
ρ x = ( W t ∂ u ∂ x ) 2 + ( H t ∂ v ∂ x ) 2 , \rho_x
=
\sqrt{
\left(W_t\frac{\partial u}{\partial x}\right)^2
+
\left(H_t\frac{\partial v}{\partial x}\right)^2
},
ρ x = ( W t ∂ x ∂ u ) 2 + ( H t ∂ x ∂ v ) 2 ,
ρ y = ( W t ∂ u ∂ y ) 2 + ( H t ∂ v ∂ y ) 2 . \rho_y
=
\sqrt{
\left(W_t\frac{\partial u}{\partial y}\right)^2
+
\left(H_t\frac{\partial v}{\partial y}\right)^2
}.
ρ y = ( W t ∂ y ∂ u ) 2 + ( H t ∂ y ∂ v ) 2 .
取
L = max ( ρ x , ρ y ) L=\max(\rho_x,\rho_y)
L = max ( ρ x , ρ y )
作为方形 footprint 的近似尺度。当 L ≈ 2 D L\approx2^D L ≈ 2 D 个原始纹素时,第 D D D 层把这段范围缩成约一个纹素。取数值下限 ε > 0 \varepsilon>0 ε > 0 以避免退化 footprint 导致 log 2 0 \log_2 0 log 2 0 ,连续层级为
D = log 2 max ( L , ε ) . D=\log_2\max(L,\varepsilon).
D = log 2 max ( L , ε ) .
实际查询还要按可用层数截断:
D q u e r y = clamp ( D , 0 , D max ) . D_{\mathrm{query}}
=
\operatorname{clamp}(D,0,D_{\max}).
D query = clamp ( D , 0 , D m a x ) .
D ≤ 0 D\le0 D ≤ 0 表示纹理正在放大,应使用最高分辨率层;D q u e r y D_{\mathrm{query}} D query 非整数时,在 ⌊ D q u e r y ⌋ \lfloor D_{\mathrm{query}}\rfloor ⌊ D query ⌋ 和 ⌈ D q u e r y ⌉ \lceil D_{\mathrm{query}}\rceil ⌈ D query ⌉ 两层分别做双线性查询,再沿层级方向线性插值。这一过程称为三线性过滤 Trilinear Filtering。
若直接在相邻层级之间取整,物体移动或视角变化时会出现明显的层级切换 level transition;三线性过滤按 D q u e r y D_{\mathrm{query}} D query 的小数部分混合两层,能把这种突变变成平滑过渡,但不会改变每一层近似方形 footprint 的限制。
Mipmap 的 level 0 是原始纹理,level 越大,分辨率越低;第 D D D 层的一个纹素大致汇总原图 2 D × 2 D 2^D\times2^D 2 D × 2 D 的区域。L L L 的含义是“一个屏幕像素跨过多少原始纹素”:近处正视表面通常 L L L 小,使用较低 level;远处或斜视表面通常 L L L 大,使用较高 level。真正决定 level 的是 UV Jacobian,单看相机距离并不充分,纹理重复次数和表面朝向同样会改变 footprint。
Jacobian 实际把屏幕像素映射为平行四边形,局部更准确的形状常近似为椭圆。Mipmap 只用最长边构造方形范围,斜视表面的 footprint 很细长时会包含过多无关纹素,造成过度模糊。Anisotropic Filtering 沿 footprint 的长、短方向分解尺度,在长轴方向使用更多样本或不同层级,减少这种过度模糊;Ripmap 和 Summed-Area Table 适合轴对齐矩形,Elliptical Weighted Average 使用椭圆核处理更一般的 footprint。
环境贴图
环境贴图 Environment Mapping 按方向存储远处环境光。球面参数化容易在极点产生强烈扭曲,立方体贴图 Cube Map 使用六张方形纹理:给定方向后,选择绝对值最大的坐标分量确定立方体面,再计算面内坐标。只要环境可视为无限远,查询只依赖方向而不依赖物体位置。
几何细节
Bump Mapping 用高度函数的局部导数扰动法线,却不改变轮廓和真实交点。设切线空间高度为 h ( u , v ) h(u,v) h ( u , v ) ,局部切平面沿 u , v u,v u , v 的斜率为 h u , h v h_u,h_v h u , h v 。高度场上的两条切向量可写成
t u = ( 1 , 0 , h u ) , t v = ( 0 , 1 , h v ) . \mathbf t_u=(1,0,h_u),
\qquad
\mathbf t_v=(0,1,h_v).
t u = ( 1 , 0 , h u ) , t v = ( 0 , 1 , h v ) .
二者叉积给出局部法线
n ′ = normalize ( − ∂ h ∂ u , − ∂ h ∂ v , 1 ) . \mathbf n'=\operatorname{normalize}
\left(-\frac{\partial h}{\partial u},
-\frac{\partial h}{\partial v},1\right).
n ′ = normalize ( − ∂ u ∂ h , − ∂ v ∂ h , 1 ) .
贴图只给出了切线空间 Tangent Space 中的法线,还要变换到着色使用的坐标系。设表面沿 UV 的单位切线为 T \mathbf T T ,副切线为 B \mathbf B B ,宏观法线为 N \mathbf N N ,则
M T B N = ( T B N ) , M_{\mathrm{TBN}}
=
\begin{pmatrix}
\mathbf T&\mathbf B&\mathbf N
\end{pmatrix},
M TBN = ( T B N ) ,
n s h a d e = normalize ( M T B N n ′ ) . \mathbf n_{\mathrm{shade}}
=
\operatorname{normalize}
\left(
M_{\mathrm{TBN}}\mathbf n'
\right).
n shade = normalize ( M TBN n ′ ) .
T \mathbf T T 应由表面位置对 UV 的变化求得,B \mathbf B B 再与 T , N \mathbf T,\mathbf N T , N 正交化;随意取一条垂直于 N \mathbf N N 的方向虽然能构造坐标系,却可能让贴图方向在三角形之间跳变。
Normal Mapping 直接保存切线空间扰动法线。若 RGB 通道位于 [ 0 , 1 ] [0,1] [ 0 , 1 ] ,常先解码为
n t a n = 2 c − 1 , \mathbf n_{\mathrm{tan}}
=
2\mathbf c-\mathbf1,
n tan = 2 c − 1 ,
再乘 TBN。模型带非均匀缩放时,法线使用逆转置矩阵,切线按普通方向变换,之后重新正交化。
Displacement Mapping 则真实移动表面:
p ′ = p + s h ( u , v ) N . \mathbf p'
=
\mathbf p+s\,h(u,v)\mathbf N.
p ′ = p + s h ( u , v ) N .
它能改变轮廓、交点和阴影,但几何必须足够细密,或者在细分阶段动态增加顶点。
Bump/Normal Mapping 只改着色法线,轮廓仍是原几何;Displacement Mapping 真正改变表面位置。
程序化噪声可以生成木纹、岩石等实体纹理;环境光遮挡等预计算结果也能存入纹理;三维纹理把标量或颜色场存到体素网格中,可用于医学数据和体渲染。