1. 图形渲染管线

应用阶段 (CPU)-> 几何处理 (GPU)-> 光栅化 -> 像素处理(后处理?)

几何处理阶段

顶点着色 -> 投影 -> 裁剪 (clip) -> 屏幕映射

每个顶点储存各种材质数据,比如位置、法线、颜色,计算之后的着色结果可能是颜色、向量、纹理坐标发送到光栅化阶段

曲面细分的作用:生成具有相当数量三角形的曲面来节省时间和内存,根据距离相机的远近来合并顶点

几何着色器:功能类似于反向细分,比如一个顶点渲染为一个正方形来达成某些视觉效果(Geometry Modifier?)

光栅化

三角形设置 -> 遍历 -> 像素着色 -> 合并

合并阶段处理可见性

帧缓冲区 :使用双缓冲,避免屏幕上看到颜色缓冲区的内容(垂直同步?)

GPU数据并行

一个warp包含多个线程,一个线程负责一个图元的渲染,GPU一次执行一个warp,遇到需要读取的数据的时候会stall(比如纹理),就可以先切换执行下一个warp,等待第一个warp的数据读取完成。

xyzw 坐标,w是perspective的参数

流式输出

在GPU渲染管线中,流式输出(Stream Output,在OpenGL/Vulkan中通常被称为转换反馈 Transform Feedback)是一个非常强大且灵活的机制。

简单来说,它的核心作用是:允许GPU将几何阶段处理完的顶点数据,跳过后续的光栅化和像素着色阶段,直接重定向(写回)到显存中的缓冲区(Buffer)中。

2. 变换

如果要让物体绕某个轴,旋转中心为某个点p,可以先将这个物体平移,使p点与原点重合,运用旋转矩阵之后再平移回其原始位置。

如果要在某一方向上进行缩放,则需要进行一个复合变换,为了构建正交矩阵 F,我们需要另外两个相互正交的单位向量,使得它们构成一个右手坐标系。也就是说,这个逆矩阵在进行basis的转换,将原来方向vec 变为以缩放方向为basis,最后再转换回x,y,z的basis

复合矩阵的通常顺序为C = TRS

是物体在相机这个“新世界”里的相对坐标,达到了改变相机朝向的目的,也就是“移动了整个世界”。M_view x P_world = (x', y',z')

顶点混合

一个用来解决类似于“手臂弯曲伸展的真实性”的算法,简单理解为给skin的矩阵变换添加了一个权重。使用线性混合蒙皮可能会让关节处的处理没有那么自然,用双四元数可以改善外观,但也可能会导致隆起效应。

几何体缓存回放

解决类似过场动画内存处理效率的问题(精细的动画对读取速率的要求高)。使用量化技术,例如用16位整数储存每个顶点的位置和纹理坐标,或是进行空间和时间维度上的预测(预测n+1帧的位置,预测下一个顶点的位置)。

正交vs透视投影

透视本质是用数学方法来模拟人眼观察的近大远小的效果,一个带”透视“一个不带。除以 "w"

视场(field of view) 越小 -> 透视效果越小。可以这么理解(所谓的广角镜头):你的视野越大,你看到的东西就越多,然而却依然要投影到有限的一个屏幕范围内,所以透视效果自然而然就变强了。

书中还介绍了更高级的透视算法实现,以及对这些算法进行优化以提升精度,从而影响最终的渲染效果,当然在实际代码中还有更具体的操作,比如对于颜色和深度buffer的清理和处理。

3. 着色基础

Gooch着色模型

如果法线指向光源,则使用较暖的颜色,反之则用较冷的颜色。这其实给风格化渲染提供了一种思路,用某一些属性来座位渲染效果的参数

Lambertian shading model

Lambertian shading model formula
Lambertian shading model formula

该模型适用于理想的漫反射表面,即完全没有光泽的表面

光源

本质上是一种用程序思维来进行艺术效果的模拟,可以通过调整各种光源对物体颜色的影响程度来达到想要的视觉效果,比如,我希望不被光照到的部分为纯黑色等等。这也给风格化渲染提供了一种思路,重点是要思考“我想要什么样的效果”,先确定思路再实行工程。

点光源的模拟上很重要的一点是考虑性能,运用一些衰减函数来设定一个r_max这样不至于太远之后光线还在渲染。

材质系统

材质模板 + 材质实例(instance)

模块化和可组合性(?)是材质系统的重要任务

抗锯齿

进行更高精度的渲染 -> 采样(滤波器)

由于对于每个像素的采样率不足而导致的失真画面是我们常说的锯齿,可以在一个像素内增加采样点。

FSAA = SSAA (full screen antialiasing)

概念上最简单,细分为2x2采样,但更需要性能。在实践中,可以增加物体的大小,比如电线,让它尽可能的覆盖确定的像素。

MSAA multisampling antialiasing

一个像素内有四个点,看哪个样本(要渲染的物体)覆盖的点位多,就渲染哪个物体。这样的好处是性能更好。

Nvidia TXAA 帧间抗锯齿

FXAA fast approximate antialiasing

SMAA subpixel morphological antialiasing

这些算法的开销基本都在1-2毫秒,所以很受欢迎

累计缓冲区accumulation buffer

与顺序无关的透明度

under运算符的用法是将所有透明物体绘制到一个单独的颜色缓冲区,然后用over运算符将该颜色缓冲区合并到场景的不透明视图之上。然而这种算法性能消耗较大,学者们尝试了不同的数据结构进行优化,来将算法更好的映射到GPU上(1984)。

A缓冲区是另一种多重采样的形式,每个被渲染的三角形都会为其每个覆盖的屏幕网格单元创建一个覆盖蒙版。每个像素都储存了一个所有相关片元的列表。

常见的 Pass 类型:

  • Shadow Pass(阴影通道):这个 Pass 不画任何颜色。它把摄像机放在光源的位置看世界,只记录物体的深度信息,生成一张“阴影图(Shadow Map)”。
  • Base Pass / Forward Pass(基础/前向渲染通道):真正开始画物体的颜色。它会读取上一步生成的阴影图,结合当前的光照计算,把物体的色彩和细节画出来。
  • Post-processing Pass(后处理通道):当场景全部画完后,整个画面被当作一张普通的 2D 图片。后处理 Pass 针对这张全屏图片进行二次加工,实现如辉光(Bloom)、景深(Depth of Field)、色彩校正(Color Grading)等电影级特效。

透明度算法的领域十分深且广阔,更复杂的算法包括加权重,加权平均,随机透明度......由于有很多不同类型的透明问题,渲染方法和GPU功能也多种多样,因此目前还没有一种能完美渲染透明物体的结局方案(2018)。更主要的是,作为技术美术,你应该去更加全面而概括的了解这些算法,知道他们的优势劣势,以及适用场景,而不是钻研其中的实现以及如何优化。比如,如果在一个很大的场景中,两个彼此很相近的物体的距离权重会基本相同,所以用不用距离权重来渲染这个物体差别不会很大。

显示编码

gamma correction:将着色器输出的颜色值限定在[0,1]范文内,并将其提高1/2.2幂次(非线性!)

总的来说,为了使得让我们计算的值加倍,同时输出的辐射亮度也加倍,我们需要应用显示传输函数的逆函数来抵消其非线性效应,这个抵消显示器响应曲线的过程被称为伽马矫正。

4.纹理

(x,y,z) -> (u,v) 纹理投影(UV mapping)

避免纹理投影的周期性问题的一种解决方法是将纹理值与另一个非平铺纹理组合起来。

纹理放大映射->双线性插值,即在两个方向进行线性插值,类似blender中的color ramp?只管的来说,这个函数的目的是让距离样本位置更近的纹素对最终值产生更多的影响。

在实时渲染中,滤波器(Filter)*的本质是一个*加权求和的数学算子。无论是纹理采样(Texture Sampling)还是抗锯齿(Anti-Aliasing),其核心任务都是连续信号的数字化离散采样,而滤波器就是用来控制“采样点周围的像素如何影响最终输出颜色”的工具。

从信号处理的角度来看,像素是一个个离散的格子,但现实/虚拟世界是连续的。当我们把连续的图像强行塞进像素格子时,就会产生走样(Aliasing,即锯齿或闪烁)。滤波器的作用,就是在空间域上对局部区域进行加权平均,在频域上滤除高频信号(平滑细节),从而消除锯齿。

任何滤波器都由两个核心要素决定:

  • 支持域(Support Window): 滤波器覆盖的像素范围(如 $3 \times 3$ 或 $1 \times 1$ 像素区域)。
  • 权重函数(Weight Function): 决定邻近的像素对中心点贡献多少权重的数学公式。

mipmap (multum in parvo - “在一个小地方有很多东西”) 多级渐远纹理

将一个纹理图片不断的进行下采样,d轴越远,这个新的生成的子纹理就越小,直到只有一个纹素的大小,注意 d 并不是线性的。在实践中,d并不是整数值,我们会对d两侧的mipmap等级分别进行采样(线性插值),再根据每个纹理等级到d的距离,对得到的样本进行线性插值,整个过程成为 三线性插值。

加和表面积(summed area table)

各向异性(Anisotropic) 描述的是一种非均匀的、具有方向性的缩放现象。当一个表面(比如铺着地砖的地面)远离相机并斜向延伸时,它在屏幕上的投影会发生严重的形变:沿视线方向(纵向)被剧烈压缩,而垂直于视线的方向(横向)压缩得很小。与之相对的词语就是各向同性。

各向异性滤波

像素单元的反投影(带有透视效果)形成一个四边形,利用与四边形长边平行的轴来作为采样轴(各向异性线)。可以理解为在透视的情况下,横向(短边)的信息应该被压缩了,而纵向(长边)的信息应该被保留,所以有更多的采样点来沿着这个方向来进行最终的加权平均计算。

纹理在材质表达上的应用

法线贴图和高度场是比较熟悉的用法,这些算法现在还在持续进行着优化来搭配纹理压缩、抗锯齿等功能来继续提高性能,同时让纹理的材质表达更加真实。值得在实现的层面上总结的就是“切线场”的概念,因为既然要改变法线的方向,你必须先建立一个坐标系,所以我们就需要切线来作为第二个向量,这是所有纹理材质表现的底层逻辑。

凹凸映射 、Blinn(高度场)

计算比较繁琐,本质是displacement mapping看,顶点密度过高

法线映射

缺点是在沿着平面观察的时候就不会有凹凸了

视差映射 / 视差遮挡映射(浮雕映射)

img
img

简单来讲,就是我们根据离散的高度场信息,来推算假设这个材料是真的有几何上的凹凸的话,我们的视线所看到的材质是怎样的。最简单的方法就是假设这个真实的几何走势是以A点为圆心,H(A)为坡度的一个圆形坡,与视线的相交点就是最终估算的数值。浮雕映射,视察遮挡等等算法都是对这个思路进行的优化,大致来说就是在视线向量上进行更多的采样,来尽量确定更精确的相交点。还有很多更高级的思路和优化,这里不多研究了。

5. 阴影

阴影贴图

基于深度缓冲区。核心思路是灯光所“看到”的区域会被照亮,而其它地方在阴影中。进行一个阴影的pass,与阴影贴图进行比较。如果一个被渲染的点到光源的距离比阴影贴图中的相应值更远,则该点就在阴影中(灯光看不到这个点)。建立阴影贴图的成本与渲染图元的数量大致呈线性关系,而且访问时间是恒定的。

阴影映射的缺点是,阴影的质量取决于阴影贴图的分辨率和深度缓冲区的数值精度,所以比较容易出现锯齿,一个常见的问题是自阴影。

自阴影

当灯光斜着照射在一个表面上时,阴影贴图中的一个像素(格)会覆盖场景表面的一片区域。

  • 在阴影 Pass 中:这个格子里记录的是这整块区域中心点(或者某个采样点)到光源的深度值 d_map。
  • 在渲染 Pass 中:表面上属于这个格子覆盖范围内的多个连续片元,都会去比对这同一个 d_map。由于表面是有倾角的,这些片元的实际深度值 d_real 在连续变化。

这就导致在同一个阴影贴图像素覆盖的区域内,有一半片元的实际深度 d_real 会略小于 d_map(被判定为照亮),而另一半片元的实际深度 d_real 会略大于 $ d_map(被错误地判定为在阴影中)。

这种对错交替的结果,反映在视觉上就是一圈圈、一团团交错的黑白条纹,好像物体自己把阴影投射在了自己身上。

法线偏移 (与恒定深度偏移(Constant Bias)结合使用) -> 每个多边形原理光源一点-> 避免可能出现的精度误差。这样做d_real 永远会小于d_map,这样不会让片元一半在阴影中一半不在阴影中。

分辨率增强

改变光照矩阵,让接近人眼的地方有更高采样率来对阴影进行抗锯齿,例如沿着观察轴放置更多的“层级阴影贴图”(单张2048x2048 -> 四张 1024x1024),这样观察更近(更仔细)的区域的阴影的分辨率不会因为这个场景很大,而导致的阴影贴图在近处不清晰。运用这个方法时,划分阴影层级(最近到最远)十分重要。

光照样本的助阵移动而产生的闪烁效应是一个问题,并且还会随着物体在层级之间移动而变得更糟。当物体跨越两个阴影贴图之间的边界时,阴影质量可能会发生突然变化。一种解决方案是使视域体稍微重叠,在这些重叠区域采集的样本会收集来自两个相邻阴影贴图的结果,并进行混合。

取巧的方法:使用一个低细节层次的模型作为代理来实际投射阴影,另一种技术是将微小的遮挡物从计算中移除。较远的阴影贴图的更新频率可以低于每帧一次。

让阴影更加真实(模拟软阴影)

接近百分比的滤波(PCF,percentage-closer filtering)

从投射阴影点位置反向滤波,找到遮挡光源的物体,近似出光线的软阴影

接近百分比软银鹰(PCSS,percentage-closer soft shadows)

在阴影图上搜索附近的区域来找到所有可能的遮挡物,用这些遮挡物与光源位置的平均距离来确定样本区域的宽度,如果跟没有遮挡物,该位置就会被完全照亮,无需进一步处理。

阴影贴图滤波(方差阴影贴图,VSM,variance shadow map)

非常适合在地形上的阴影,因为没有很多的遮挡物。

最终像素颜色 = 环境光 + 光照可见度 [0 ,1] x 直接光照

VSM的目的就是在解决:该区域内有多少比例的遮挡物深度 x (深度贴图中,以光源视角来看的各个物体的深度)大于等于我们的深度 t (当前正在被着色的场景表面像素点的深度)? 这个比例就作为光照可见度来应先阴影边缘的过度。

平均深度越接近光源 -> 光照可见度减小 -> 阴影更硬(因为被光照的影响更小)

还有很多对软阴影进行模拟的算法,比如利用卷积采样等等,但需要跟高的算力,也许在今年(2026)已经有了一定的普及,也可能跟没有。

自适应体积阴影贴图

透明物体会使光线衰减并改变光的颜色。头发、烟雾等的渲染不能依赖普通和单一的阴影贴图。深层度阴影贴图的概念是,其中每个纹素都储存一个光线如何随深度下降的函数。自适应体积阴影贴图每个纹素都储存了不透明度和层的深度。

锥体追踪/ 体素锥体追踪 (Voxel Cone Tracing, VCT)

既然发射了一个逐渐变宽的圆锥,如何检测圆锥一路上扫过的遮挡物?

在 VCT 中,场景首先被体素化(Voxelized)存入一个 3D 纹理中,并生成类似 Mipmap 的三维层级结构(被称为占用率金字塔 Occupancy Pyramid):

  • Level 0:分辨率最高,体素极小、极精确。
  • Level N:分辨率低,体素很大,代表了一个大区域内体素密度的平均值。

当圆锥从着色点 $P$ 出发向光源延伸时:

  1. 起始阶段(近处):圆锥的横截面积非常小。算法采样的 3D 纹理层级非常低(如 Level 0)。由于体素分辨率极高,遮挡物边界非常精准,此时能算出来极窄、极精确的硬阴影边缘。
  2. 前进阶段(远处):随着行进距离变远,圆锥的半径迅速增大。此时算法会自适应地采样更高、更模糊的 3D 纹理层级(如 Level 3 或 4)。这一层级中的一个体素代表了很大一片区域的平均不透明度。

如何计算阴影的淡出?(步进累积)

圆锥在 3D 体素空间中步进(Marching)时,会在一路上累积不透明度(Opacity/Occlusion)。

设每一个步进点的单步遮挡度为 $\alpha_i$(可以通过当前的 Mipmap 体素直接采样得到),光线透过率 $T$ 初始为 $1.0$:

$$T_{i} = T_{i-1} \times (1.0 - \alpha_i)$$

  • 如果在起始阶段(近处)*碰到了一个实心体素($\alpha = 1.0$),透过率 $T$ 瞬间归 0,形成边缘锋利的*硬阴影
  • 如果在远处碰到了半透的、稀疏的体素($\alpha$ 较小,比如由于高层级 Mipmap 均值化导致的渐变),透过率 $T$ 就会逐渐平滑地衰减(例如 $1.0 \to 0.7 \to 0.4 \to 0.1$),从而形成非常平滑自然的软阴影

算法与硬件的映射具体来讲是怎样的?

6.光照与颜色

可见光波长范围大约 380nm - 780nm

色度学与光度学

光度学与色度学是光学的重要分支,它们研究的核心并非单纯的“物理光”,而是物理光作为刺激源,在人类视觉系统中引发的“亮度”与“色彩”的心理物理学(Psychophysics)感受

人类对光和色彩的感知,始于视网膜上的两类感光细胞:视杆细胞(Rod Cells)与视锥细胞(Cone Cells)。其中,视锥细胞是色度学与光度学研究的生理学基础。

  • 视杆细胞(暗视觉):数量多、对光极度敏感,但不参与色觉。在微弱光线下起主导作用,只让人感知黑白与轮廓。
  • 视锥细胞(明视觉/色觉):工作于较亮环境,负责捕捉细节和色彩。人眼视网膜上拥有三种不同类型的视锥细胞,它们含有不同的视色素,分别对不同波长范围的光产生最大共振响应:

    • L 锥细胞(Long-wave):响应长波长光,敏感峰值在红色光谱区域(约 $564\text{ nm}$)。
    • M 锥细胞(Medium-wave):响应中波长光,敏感峰值在绿色光谱区域(约 $534\text{ nm}$)。
    • S 锥细胞(Short-wave):响应短波长光,敏感峰值在蓝色光谱区域(约 $420\text{ nm}$)。

三种视锥细胞将接收到的光谱辐射转化为神经电信号,传入大脑皮层进行综合处理。这种“三原色式”的生理结构,决定了人类视网膜捕获光信息的方式,也是一切色彩空间与照明标准诞生的源头。

光度学回答了“光有多亮”(将辐射功率加权转化为亮度感知值)。

色度学回答了“光是什么颜色”(将复杂的光谱分布降维成三维的颜色刺激值坐标)。

色度图 vs 颜色空间 (sRGB等编码标准)

如果我们想要一个能够彻底盖住整个马蹄形色度图的三角形,唯一的办法是将三角形的三个顶点设在马蹄形外面(比如 CIE XYZ 颜色空间的原色点)。但问题是,落在马蹄形外部的颜色在物理上是虚构原色(Imaginary Colors)——它们对应的物理光谱是不存在的,人眼根本无法感知。

为什么物理上能发出的光,无法让人眼在三原色混合下达到“绝对纯度”的感知?这源于我们视锥细胞的生理特性:

  • 人眼的 L(红)、M(绿)、S(蓝)三种视锥细胞的光谱响应曲线是高度重叠的
  • 特别是在中波(绿光)和长波(红光)区域,L 和 M 细胞的重叠度极高。
  • 当一个纯度极高的单色绿光(比如 $520\text{ nm}$ 的激光)照进人眼时,它会在几乎不刺激 L 细胞的情况下强烈刺激 M 细胞。此时人眼感知到的是极其纯净、饱和度拉满的绿色(对应色度图最顶端的马蹄形边缘)。
  • 但是,如果我们试图用显示器的物理红光和蓝光去混合绿光:任何物理红光和蓝光在刺激 M 细胞的同时,都会不可避免地刺激到 L 或 S 细胞。这种“顺带的刺激”在视觉上相当于给绿色中混入了杂质,从而降低了颜色的饱和度
  • 因此,通过 RGB 混合出来的颜色,其饱和度永远无法达到单色激光的纯度。这导致 RGB 三角形的边界被迫向内缩进。

物理与工业极限:sRGB 诞生时的妥协

sRGB 标准是由微软和惠普在 1996 年联合制定的。在那个时代,主流的显示设备是庞大的阴极射线管显示器(CRT 显器)

sRGB 选定的红、绿、蓝三个原色顶点,实际上就是当时 CRT 显像管所能使用的荧光粉(P22 荧光粉)在激发时的物理发光极限

  • 绿点妥协:当时的绿色荧光粉纯度很低,发出的绿色带有一点点偏黄的杂质。因此,sRGB 的绿色顶点被迫定在了一个非常靠内(靠近白色中心)的位置,直接导致 sRGB 缺失了大量的纯绿、青色(Cyan)和高饱和度的蓝绿色。
  • 兼容性与成本:虽然现在有了 LED、OLED 和量子点(Quantum Dot)技术,我们完全可以制造出原色纯度极高、色域大得多的显示屏(如 DCI-P3、Rec.2020 色域,如上方对比图所示),但 sRGB 作为互联网、标准网页和大多数普通显示器的“通用语言”,为了保证数十亿台老旧设备的色彩兼容性,依然将这个较窄的三角形范围沿用至今。

色调映射

将渲染中的物理量压缩到硬件可以实现的数值,这样做的目的是为了让最终屏幕的画面和人眼在现实中看到的一样。人眼在现实中遇到强光时,瞳孔会收缩(自带一种动态范围压缩机制)。色调映射就是用数学曲线(如 ACES)去模拟人眼在真实世界中的高光消散、色彩去饱和等视觉习惯,让有限的屏幕展现出“高动态”的艺术美感。

色调映射是第一层滤镜,它解决的是物理维度的限制,即“如何把高动态范围(HDR)的刺眼阳光和深邃阴影,塞进平庸的显示器(LDR)里”。

  • 它的滤镜表现: 比如电影感滤镜(胶片风)。经典的 ACESFilmic 色调映射,本质上就是模拟了胶片对光线的非线性化学反应。它会让高光有一种平滑的“卷边(Roll-off)”效果,使亮部死白变得柔和,让暗部更有厚重感。
  • 处理的时机: 紧接在物理光照计算之后。它不负责把画面“变蓝”或“变暖”,它只负责确定画面的整体反差、曝光亮度和高光收敛方式

场景参考图像显示参考图像有本质上的不同,物理操作只有在对场景参考数据执行时才有效。

颜色分级(Color Grading)

这才是我们狭义上最常说的、能一眼看出来的“调色滤镜”(比如赛博朋克风、日系清新风、黑客帝国绿)。

在三维渲染和现代调色软件(如 DaVinci Resolve)中,这个阶段通常通过一个叫 3D LUT(Look-Up Table,三维查找表) 的文件来实现。

  • 它的滤镜表现: 改变颜色的倾向。比如将阴影染上青蓝色(Teal),将高光染上橙黄色(Orange),或者降低某一特定颜色(如绿色)的饱和度,来制造出某种特定的叙事氛围。

支持HDR显示器的协议:Q 曲线(Perceptual Quantizer,即 ST 2084 标准)或 HLG 曲线

现代显示器的工作原理https://www.bilibili.com/video/BV1Me4y1k72b

7. 基于物理的着色 (Physics Based Rendering)PBR

散射的程度决定了浑浊度,高散射会产生不透明的外观。任何颜色的色调通常是由吸收的波长依赖性决定的。在渲染方面,我们不会对几何形态进行建模,而是对其进行统计学处理,将表面视为一个微表面法线随机分布的整体。

着色方程

前一项是物体自身的发光,而后面的积分项是周围光线对物体的影响(反射情况)。其中f()是scattering function,如果是常数则这个材料就是一个完美的漫反射材料,从各个方向看都是一样的反射。

菲涅尔反射率 Fresnel Reflectance

是物理光学和图形学中的一个核心概念,用来描述**光线在不同折射率的介质交界面上,反射回来的光能量占总入射光能量的比例。

用生活中的一句话来概括它的核心物理现象就是:视线越平视(贴近表面),反射越强;视线越垂直(正对表面),反射越弱。

最典型的例子是平静的水面:当你低头垂直看脚边的水面时,能清澈地看到水底的鹅卵石(反射弱,透射强);而当你抬头看远处的江面时,水底消失了,你只能看到远山和天空完美的倒影(反射极强)。一般来说在实时渲染中使用近似方程,theta是入射角,F_0(基础反射率 / 0度反射率)。

照射角度不同,感受到的粗糙度也不同。

反照率vs 反射率?衍射vs折射?

微平面理论

没太看懂,总之就是用一些函数来从统计的角度模拟微小的平面变化以呈现更真实的光照效果?

Cook-Torrance BRDF

F 是菲涅尔反射率

决定这个光线有多少是折射了有多少是反射了。

D(h) 微平面法线分布:


这一项的意义在于,由于物体表面在微观上是由无数朝向各异的“微小镜面”组成的,因此从统计的角度来看,宏观上反射出的光线会在镜面反射方向周围形成一个圆锥形的波瓣。

描述的就是微观法线朝向半角向量 h 方向的概率密度(即有多少比例的微小平面刚好能把光线反射进观察方向 v)。宏观表面的粗糙程度(Roughness)决定了这些微观法线偏离宏观法线 n 的分散程度:

  • 粗糙程度越低(表面越光滑):微表面的法线高度集中在宏观法线 n 的周围。当 h 极度接近 n 时,D(h) 的概率密度极大,而一旦 h 稍微偏离 n,D(h)就会急剧衰减至 0。在视觉上呈现为极亮、极其锐利且范围很小的“针尖状高光”(如抛光金属、玻璃)。
  • 粗糙程度越高(表面越粗糙):微表面的法线朝向极其分散混乱。即使 h 等于 n,D(h) 的峰值概率也相对较低;但当 h 较大地偏离 n 时,依然有足够数量的微观法线分布在那里。在视觉上呈现为亮感被稀释、但扩散范围很大且带有平缓拖尾的“柔和高光晕”(如磨砂塑料、橡胶)。
G 遮蔽阴影函数

由于微观表面是由凹凸不平的崎岖山谷组成的,光线在入射和反射的过程中,必然会受到周围微表面的互相遮挡。

当视角 $v$ 或光线 $l$ 接近垂直于表面($N\cdot V \approx 1$ 或 $N\cdot L \approx 1$)时:你是在“俯视”这片微观山脉,光线也是“顶光”照下来。你能看到大部分山谷底部的微平面,微平面相互遮挡的概率极低。此时 $G(l, v)$ 非常接近 $1.0$(几乎没有几何损失)。

当视角 $v$ 或光线 $l$ 接近掠射角($N\cdot V \to 0$ 或 $N\cdot L \to 0$)时:你是在“平视”这片山脉,前面的山峰会挡住后面绝大多数的山谷。此时绝大部分反射光线都被微地形拦截,导致 $G(l, v)$ 急剧衰减趋近于 $0$

粗糙度越低(表面越平整):G 项衰减平缓。s

粗糙度越高(表面越崎岖):G 项衰减非常剧烈。

哪些材料是各向异性?是指不同角度观察起来不一样吗?

次表面反射

用一个值ρ_ss (sub surface?)来表示下面分子反射出来的光线颜色,这个颜色和平面反射的颜色是不同的,因为其吸收和反射的过程就和平面反射不同。

经典布料模型

这个可以稍微了解一下。

迪士尼漫反射

这个说实话可以搞的清楚一点。

薄膜干涉

从薄介电质顶部和底部反射的光路相互干涉时发生的一种波动光学现象,随着不同波长的光在相长干涉和相消干涉之间转换,最终结果便会产生彩虹色的色偏。肥皂泡、油渍,甚至是皮革。在实际的实时渲染中,通过薄膜厚度,视角,和折射率可以很高效的计算出产生的颜色,所以也可以做一个一维RGB查找表。

材质分层

多种材质叠在一起,比如一个表面被灰尘、水、冰或雪覆盖,它们之间会有折射、散射、基于路径长度的吸收等等相互作用,来实现一个复杂的几何表面效果。

混合材质

顾名思义。混合材质是将多种材质的特性(BRDF的参数)进行结合的过程,例如带有锈斑的金属板,用一个单独的纹理来控制锈斑的位置,并使用它来混合锈斑和金属的材质属性。

动态混合纹理元素的例子:建筑物、车辆和活体生物身上的动态损伤(生化危机中射击僵尸身上的伤口?)、服装和装备的自定义

材质过滤

材质过滤就是让材质在任何距离、角度和屏幕分辨率下,都既不产生锯齿/闪烁(高质量),又能维持其原有的物理特性(真实)的信号处理技术。由于有些PBR材质会运用微表面技术,法线贴图等不能单纯用线性插值来进行过滤,所以需要其他算法来进行优化。

人类视觉系统的带宽(400nm - 700nm)决定了相干长度大约为1微米,这个数值可以好好记忆一下。

PBR的方法很优美,这个领域也十分的广阔,还是把精力放在知道所有的算法和它们的适用场景,不要硬去深度了解所有的算法。

8.局部光照

区域光源的高光可以通过使用精确光源配合表面粗糙度的增加进行近似,这个技巧在仅使用精确光时也能产生较为可信的结果,但是一旦光照方案改变,以这种方式创建的渲染便会不准确。

区域光源

在渲染方程中,某一点 p 接收到的直接光辐射亮度原本是对入射方向半球进行积分。对于区域光源,我们可以通过变量替换,将对半球方向的积分直接转换为对光源几何表面 A 的面积积分。这一节的技术是改变积分的范围而不是散射方程本身。

大多数实时渲染的区域光照效果的实际近似均基于以下理念:针对每个着色点找到一个等效的精确光设置,以模仿非无穷小光源的效果。

有时可以通过高光阈值化来模拟区域光源(光滑物体上的高光清晰地反映出光源的形状)。

Mittring 粗糙度修改方法、GGX BRDF 的粗糙度微调、代表性点技术(最具代表性点解)

在实时渲染(如游戏引擎)中,计算点光源非常快,因为只需计算“着色点到那一个灯泡点”的光照方向。但对于区域光源,表面上的每个点实际上都在接收发光面无数个点发出的光线。如果按照物理理论去对整个发光面做积分,GPU 的实时帧率会直接瘫痪。

为了打破这个难题,Epic 提出了 MRP(代表点) 方案。它的本质意义在于:把“对整个发光面做复杂积分”的过程,极低成本地简化为“寻找并计算最亮的那一个点”。

MRP 的意义:它在发光面上寻找一个最关键的点——这个点发出的光线经过表面反射后,与你的视线方向(反射向量 $\mathbf{R}$)夹角最小。 换句话说,MRP 就是发光面上对你眼睛贡献亮度最高、最亮的那一点

通过光照形状

没太看懂这一部分。总之核心思路依旧是模拟并简化解析解,来让物体上产生接近于光照形状的高光。还有一些不同的方法比如线性变换余弦(linearly transformed consinces, LTC)

球面与半球函数

没看懂。储存、表示辐射度的一种方法?

环境映射

感觉就是HDRI技术,比较直观,虽然占据更多内存,但是解码方面更加快速。潜在缺陷是,在平坦表面上时,环境查找表的一小部分被映射到相对较大的表面上。

经纬度映射、球面映射、立方体映射

各有优劣,其中立方体映射的信息最均匀,计算也很快速,任何一个向量都可以很快计算出属于立方体的哪个面然后确定(u, v)

预过滤

解决粗糙材质的真实感:把粗糙度带来的“微表面各向散射”转化为“光线在环境图上的锥形模糊/范围采样”。

离线换时间,用空间换性能:把极高开销的实时半球积分,提前预计算(Prefilter)并存入 Cubemap 的 Mipmap 链中,让运行时能够以 $O(1)$ 的单次纹理采样开销,完美呈现不同粗糙度材质的高光模糊与能量守恒效果。

可以通过环境贴图来生成辐射照度贴图 ,以用于漫反射。因为使用极宽的过滤器,所以很难实时生成。

9. 全局光照

光线追踪

记住是从相机位置反向射出射线进行bounce,每个反射点都进行蒙特卡洛采样。

光线追踪和路线追踪是相同的概念吗?

光线追踪是一个宏观的技术统称。它的核心逻辑非常简单:从摄像机(眼睛)向场景发出光线,计算光线与物体的交点,从而决定像素的颜色。

只要利用了“发射光线并计算几何求交”这一手段的技术,都叫光线追踪。

路径追踪是 1986 年由 James Kajiya 提出的一种基于蒙特卡洛积分(Monte Carlo Integration)的全局光照算法。它是光线追踪技术的“完全体”。

环境光遮蔽

最基础的全局光照效果。给着色方程添加一个可见性函数,如果被遮挡则为0,没有则为1.经过归一化的可见性函数的余弦加权积分被称为环境光遮蔽(总的来说就是被遮挡的程度如何?)。这个值范围是[0,1]。

预计算任何光照信息的过程(包括环境光遮蔽),叫做“烘焙”(baking)。环境光遮蔽因子可以通过预计算储存在纹理中,所以在实时渲染中要求的算力较低。

环境光遮蔽(AO)本质上是一种纯几何的近似方法,它假设环境光在半球空间内是全向均匀的,且计算出的遮挡系数与视线无关。因此,它无法捕捉光泽 BRDF 随视角变化且高度方向聚集的波瓣特性,也无法适应 HDR 贴图或强方向光等复杂的非均匀光照,导致在计算高光和复杂光场时出现错误的暗斑或失真。

屏幕空间方法(用来计算AO)

z buffer是唯一的输入,利用像素周围的球形范围内的采样点的深度测试结果来估算每个像素的环境光遮蔽因子k。

方向性遮蔽

支持复杂的非均匀光照:由于 DO 记录了方向(Bent Normal),在计算环境贴图(IBL)或主光源时,它可以只采样未被遮挡的那部分半球区域的光照,不会出现“强光照进缝隙却被传统 AO 错误压暗”的现象。

更好地拟合光泽材质(Glossy BRDF):DO 提供的方向信息可以与材质的镜面反射波瓣(Specular Lobe)进行求交计算(即 Specular Occlusion),从而判断高光方向是否被真正挡住,大幅减少了高光区域的错误暗斑。

漫反射全局光照(预计算光照)

下面这些都是实现漫反射全局光照的优化方法

反射阴影贴图(RSM,reflective shadow maps)

光线传播体积

场景被分解为一个规则的三位单元网格,每个单元维护一个流经该单元的、方向性的辐射亮度分布

基于体素的圆锥体追踪

镜面反射全局光照

屏幕空间反射(SSR):在 G-Buffer 的深度图(Depth Buffer)中沿着反射向量做 2D 步进(Ray Marching)。

平面反射(Planar Reflection):把场景沿着反射平面翻转,重新渲染一遍场景。

反射探针(Reflection Probes / Cubemaps):捕捉周围环境的 360 度贴图进行采样。

预计算光照到底是计算光照的什么?生成阴影贴图和光照贴图吗?

1. 光照贴图(Lightmaps)—— 存“光照能量”

这是预计算光照最基础、最核心的产物。烘焙系统会为场景中的所有静态物体展开一套独立的 UV(Lightmap UV),并将光照信息绘制成 2D 纹理贴图。

  • 计算内容:计算静态光源照射到静态表面上的直接光照 + 漫反射间接光照(多次弹射)
  • 它与普通纹理的区别:普通贴图存的是物体的颜色(Base Color),而 Lightmap 存的是到达该表面的光照能量值(RGB / Radiance)。运行时将 Base Color * Lightmap Value 即可得到完美的全局光照着色。
  • 方向性光照贴图(Directional Lightmaps):现代 Lightmap 不仅存光照强度,还利用球谐函数(SH)或主光照方向矢量,存储光线的主要入射方向。这样即使使用预计算光照,表面依然能展现出法线贴图(Normal Map)的凹凸细节。

2. 预计算阴影 / 阴影贴图(Shadowmaps & Distance Fields)—— 存“遮挡关系”

你提到的“生成阴影贴图”是对的,但预计算光照处理阴影的方式有明确的分工:

  • 预计算静态阴影贴图(Shadowmask / Precomputed Shadowmaps)

    • 对于静态光源照射静态物体产生的阴影,直接烘焙到了上面提到的 Lightmap 中(暗部就是阴影)。
    • 对于混合光源(Stationary Light),引擎会生成一张 Shadowmask 纹理。这张纹理只存 0 到 1 的遮挡系数(表示该点是否被静态挡光物遮挡)。
  • 为什么要把阴影单独存成 Shadowmask?

    • 这样可以实现静态遮挡 + 动态光照。例如,静态建筑物投下的阴影是烘焙好的,但当一个动态角色走进这片阴影时,混合光源依然可以实时地给角色计算高光和动态阴影,同时静态地面依然保留着预计算好的软阴影。

3. 光照探针 / 辐照度体积(Light Probes / Irradiance Volumes)—— 给“动态物体”用

Lightmap 只能贴在静态物体的表面,动态物体(如玩家角色、移动的车辆)在场景中走动时,怎么接收静态场景的间接光?

这就是光照探针(Light Probes) 解决的问题:

  • 计算内容:烘焙系统会在场景的三维空间中布设密集的采样点(探针)。在每个探针位置,向四周半球采样,计算该点来自各个方向的漫反射间接光,并将其压缩存储为球谐函数(Spherical Harmonics, SH)系数
  • 运行时工作:当动态角色移动到某个位置时,引擎找到周围最近的几个光照探针,进行三线性插值,将烘焙好的间接光“套”在动态角色身上。

4. 反射探针(Reflection Probes)—— 存“镜面反射环境”

由于前面提到镜面反射(Specular GI)极度依赖视角,无法直接烘焙到 Lightmap 中,因此预计算系统会单独生成反射探针。

  • 计算内容:在场景特定位置渲染一个 360 度的立方体环境贴图(Cubemap),并进行预过滤(Prefiltering)。
  • 运行时工作:当光滑/金属材质的物体经过时,采样这张预计算好的 Cubemap,用来模拟金属和玻璃表面的环境倒影。

这些贴图为什么在baking之后还是需要实时更新?

其实,严格意义上的“纯 static 光照贴图(Lightmaps)”在烘焙(Baking)完成后是绝对不需要在运行时更新的

为什么镜面反射全局光照要和漫反射全局光照分开讨论?

1. 信号频率与视角依赖性不同(最核心的原因)

  • 漫反射 GI(Diffuse GI)—— 低频、与视角无关

    • 物理特性:光线在粗糙表面向半球所有方向均匀散射。
    • 视角无关(View-Independent):观察者无论站在哪里看,该点的漫反射亮度都是一样的。
    • 数学特征:表现为平滑过渡的低频信号(如红墙投射在白墙上的红晕)。
    • 优势:计算一次后可以全局缓存(如存入 Lightmap、Irradiance Probes 或 Volume),多个帧或多个视角共享使用。
  • 镜面反射 GI(Specular GI)—— 高频、强依赖视角

    • 物理特性:光线在光滑表面沿着狭窄的反射波瓣(Specular Lobe)集中反射。
    • 强依赖视角(View-Dependent):观察者位置稍有移动,看到的倒影和反射波瓣就会彻底改变。
    • 数学特征:包含极高频的信号(清晰的倒影、锐利的边缘、焦散亮斑)。
    • 劣势绝对无法直接缓存。一旦摄像机移动,整个反射图景必须重新计算。

2. 采样与数值积分的难度不同

在蒙特卡洛积分(Monte Carlo Integration)中,两者的采样策略有天壤之别:

  • Diffuse GI 采样

    • 光线向整个半球空间扩散,需要用余弦分布(Cosine-weighted Sampling)在整个半球上投射光线。
    • 由于是低频信号,可以用极少的 Ray/Pixel(甚至不到 1 Ray/Pixel)结合大半径的时空滤波去噪(Spatiotemporal Denoiser) 来得到平滑结果。
  • Specular GI 采样

    • 光线高度集中在反射波瓣内,必须使用针对 BRDF(如 GGX)的重要性采样(Importance Sampling),沿着镜面反射方向集中投光。
    • 如果反射极其光滑(接近镜面),去噪算法不能过度模糊,否则倒影就会变糊;如果去噪不足,又会有严重的噪点和闪烁(Fireflies)。

什么是视察矫正(parallax correction)?局部反射探针?

10. 图像空间特效

图像处理

图像处理中,使用GPU的优势之一是可以使用内置的插值和mipmapping硬件来帮助减少访问的纹素数量。有各种滤波核来对每个像素进行分析、降噪。后处理特效通常在最终图像上运行,并且可以模仿热成像,重现胶片颗粒和色差现象,执行边缘检测,产生热浪和涟漪现象,对图像进行色调分离,帮助渲染云层等等。

重投影

重投影技术(Reprojection)与现今的帧生成技术(Frame Generation)在终极目的上完全一致——即在不付出完整渲染代价的前提下,大幅提升显示帧率并改善画面流畅度。 两者的核心思路都是利用上一帧的已有画面信息加上运动数据,衍生出新的画面,但在应用场景、技术原理和实现机制上有所侧重。

1. 什么是重投影技术(Reprojection)?

重投影技术最早并广泛应用于 VR / AR 领域(例如 Oculus 的 ATW/ASW、SteamVR 的 Motion Smoothing)。

由于 VR 场景对帧率与延迟极其敏感(低于 90fps 或存在延迟抖动容易引发严重眩晕),当 GPU 来不及完整渲染下一帧时,重投影机制就会介入:

  • 工作原理: 利用上一帧已经渲染好的颜色缓冲区(Color Buffer)、深度缓冲区(Depth Buffer)以及传感器最新回传的头部姿态(Pose / Motion Vector),对上一帧画面进行几何上的扭曲与重采样(Warping),从而预测并拼凑出当前姿态下应该看到的画面。
  • 主要变体:

    • 时间扭曲(Timewarp / ATW): 仅针对头部的 3DOF 旋转角度进行二维平移扭曲。
    • 空间扭曲(Spacewarp / ASW): 引入深度和运动矢量,补充头部 6DOF 位移与场景物体的平移运动。
  • 核心目的: 防卡顿与降延迟。确保显示器始终能以固定刷新的频率(如 90Hz/120Hz)输出画面,补齐 GPU 掉帧的缺口。

2. 什么是现代帧生成技术(Frame Generation)?

帧生成技术(如 NVIDIA DLSS 3 Frame Generation、AMD FSR 3 Fluid Motion Frames)主要应用于 传统 PC 及主机游戏渲染

  • 工作原理: 借助深度学习模型或传统光流算法,分析连续两帧(帧 $N$ 和帧 $N+1$)之间的运动矢量(Motion Vectors)、光流场(Optical Flow)、深度与颜色数据,在帧 $N$ 与帧 $N+1$ 中间插值生成 一帧全新的中间帧 $N+0.5$。
  • 核心目的: 突破性能瓶颈。在 CPU 瓶颈或 GPU 渲染负载过高时,无需完整跑一遍光栅化/光追管线,直接“合成”新帧,成倍提升显示帧率(例如从 60fps 提升至 120fps)。

眩光和泛光

各种Bloom效应......可以理解为本来是用相机(透镜镜头)拍照时催生的次级光学效应,渲染的时候用来作为艺术表达效果。在实时渲染中,有时会需要降低这种滤镜的分辨率或采样来避免需要的算力过高。

景深

聚焦的地方清晰,其他地方模糊。

正向映射

弥散圆

各个物体用一个圆圈的范围来表示对周围像素的影响程度,越小则越清晰(聚焦),越大则越模糊,对周围影响小。

运动模糊

实现方法之一是在快速移动物体的前边缘和后边缘加上半透明渐变,运用速度缓冲区储存每个像素的表面速度。

11.多边形以外的其他图形

Billboard

根据观察方向来确定纹理矩形方向的技术叫做公告板特效,而其中的矩形称为公告板。要正确执行合成,重叠的半透明公告板应该按排序顺序进行渲染,烟或雾公告板在与固体穿插时会出现失真,一种解决方法是让像素着色器程序在处理每个公告板时检查底层物体的z缓冲。公告板会测试此深度,但不会使用自身的深度进行替代。如果深度接近,则公告板的片段将变得更加透明,基于S型曲线的淡出函数会让观感更加连续。以这种方式修改透明度的公告板称为“软粒子”

粒子模拟

点渲染

体素

体素 擅长处理动态破坏、体积介质(云/雾/火)、空间光照场计算(GI/AO)以及 procedural 生成的宏观地形

12.体积与半透明渲染

透射与散射

短波长光(蓝色/紫色,$\lambda \approx 400 \sim 450\text{ nm}$):散射率显著高于长波长光。

长波长光(红色,$\lambda \approx 650 \sim 700\text{ nm}$):散射率极低,受散射影响很小。

当太阳位于高处时,蓝色光线会发生更多的散射,从而使天空呈现自然的蓝色。然而,当太阳位于地平线之上时,大气层中有很长的光路,天空会由于更多红色光线的投射而显得更红。

相位函数

在描述各种散射现象(如大气渲染、云雾体积渲染、次表面散射等)时,相位函数(Phase Function),是用于描述光子被介质微粒碰撞后,向各个方向散射的概率密度分布函数

如果把 BRDF(双向反射分布函数)看作是描述光线在二维硬质表面上如何反射的函数,那么相位函数就是描述光线在三维体积介质(Volumetric Media)内部如何偏折的“三维 BRDF”

雾的渲染

深度雾的思路十分简单,基本形式就是根据物体相机的距离在场景上对雾的颜色进行alpha混合,更进阶的方法则是将基于物理的透射率应用到混合公式中让雾更真实。

高度雾(Height Fog / Exponential Height Fog)*是一种假设*雾气密度随海拔高度(Y轴或Z轴)呈指数级衰减的大气雾效渲染方法。高度雾能够模拟出自然界中低洼处浓雾弥漫、山顶晴朗开阔,或者晨雾紧贴地面的层次感与真实空间深度。

雾的密度-> 光学厚度 (有解析解,计算极快)-> 透射率 -> 混合雾气和场景的颜色

介质渲染

溅射法来渲染渲染烟雾、火焰。在考虑介质的情况下,场景中的每一盏灯光都会与参与的介质相互作用,每一盏灯的辐射亮度和阴影贴图均用于累计其散射光照贡献。

天空和云层的渲染

天空可以通过LUT来进行实时的渲染,云的相位函数很复杂,也要考虑其中的多重散射和与大气的相互作用。

屏幕空间扩散、预积分皮肤着色、纹理空间扩散

高效实现此表散射的各种方法

渲染毛发与毛皮

总之就是非常多的散射

13.非真实感渲染

卡通着色

一般情况可以使用二分法区分光照和阴影,如果光照更加复杂,另一种方法是量化最终图像本身,也被称为色调分离,获取连续范围的值并将其转换为少数几种色调的一个过程。

轮廓描边

如果着色法线和眼睛观察方向之间的点积基本为0,则说明着色法线几乎垂直于视图方向,因此很可能在轮廓边缘附近。一种获取几何轮廓的方法是绘制背面的边缘。

还有一种方法是通过后处理pass对周围的邻域进行采样,并根据这些采样输出结果,各种滤波器的最终效果会有些不同,比如边缘更厚或更浅。这种方式不是基于几何的,而是基于图像的,所以它可以处理所有类型的平面或曲面,网格不必相互连通。还有一种方法是使用两个不同的高斯滤波器对图像进行两次处理,然后从一个结果中减去另一个结果。

笔触风格化表面

使用漫反射着色项来选择在表面上使用哪种纹理。还有一个例子是沿表面上的主曲线方向来绘制笔触。

这些思路可以从“画风”的角度来理解,你有自己的风格,或者说画画的方法,在图形技术上如何实现?

14.管线优化

定位管线的瓶颈 -> 以某种方式优化该阶段

应用程序阶段

CPU一直占用率高就是这个阶段瓶颈的一个现象

几何处理

顶点获取和顶点处理是这个阶段可能导致瓶颈的部位,可以试着增加顶点格式的大小,看性能是否下降。

光栅化阶段

包括三角形设置和三角形遍历,对于由于细分生成的或诸如草地或树叶之类的小三角形,这个阶段可能称为瓶颈。

像素处理阶段

确保除了像素着色程序之外的指令没有被降低分辨率之后的编译器优化掉,如果帧渲染时间增加,则像素着色器就是瓶颈。

API

合理利用API,减少冗余的状态切换。使用实例化最大程度减少需要绘制调用的次数。

15.加速算法与高效着色

更高的帧率、更高的分辨率和采样率、更真实的材质和光照以及更高的几何复杂度

场景图

BVH、BSP树和八叉树都使用树作为它们的基本数据结构,它们的不同之处在于如何划分空间和储存几何体。然而渲染一个三维场景不仅仅是关于几何体的问题。动画,可见性和其他元素的控制通常是使用场景图来实现。场景图是一个Tree,并对这个Tree按照一定的顺序遍历以渲染场景。比如一个光源在其中的一个节点上,这样光源只影响该节点sub tree的内容。另一个例子是在树中的一个材质可以应用于sub tree的所有几何体。

LOD

在切换LOD时对两个LOD进行线性混合,避免他们的弹出效应太严重。alpha LOD可以让两个LOD淡入淡出,或者使用Geomorph方法插值一个模型。最常见的LOD选择方法就是与不同的范围关联起来,另一个常用度量是边界框的投影面积。切换LOD的策略很重要,比如玩家视线追踪的地方。临界时间LOD渲染时通过估算来在期望帧率内选择能给我们带来最佳图像的细节层次。

大型场景渲染

稀疏纹理让分配一个巨大的虚拟纹理成为可能。使用纹理流输送可以访问大型图像数据库的高分辨率纹理映射。

着色器通道?

一种降低成本的技术是将每个局部光源的效果限制在一定半径的球体,一种简单的预处理方法是为每个网格创建一个影响它的灯光列表。

延迟着色

核心思想是,在执行任何材质光照计算之前,先执行所有的可见性测试和表面属性求值。透明度和抗锯齿是延迟着色两个重要的技术限制。

延迟着色将渲染过程严谨地拆分为两个阶段(Pass)

阶段 1:G-Buffer Pass(几何通道 / 属性生成)

  • 渲染场景中所有的不透明几何体。
  • 不进行任何光照计算,仅运行简单的片元着色器,将后续光照计算所需的表面属性(如:世界空间法线、漫反射颜色/Albedo、粗糙度、金属度、世界坐标/深度等)渲染并写入到一组多缓冲区目标(Multiple Render Targets, MRT)中。
  • 这组存储表面属性的 2D 纹理贴图集合,就被称为 G-Buffer(Geometry Buffer,几何缓冲区)
  • 利用 Depth Buffer(深度测试): 自动剔除了所有被遮挡的像素。当这个 Pass 完成时,G-Buffer 里保留的100% 是最终屏幕上可见的表面属性

2. Lighting Pass(光照通道 / 属性求值)

  • 关闭深度写入,不再处理任何场景 3D 几何体,而是渲染一个覆盖屏幕的画板(Full-screen Quad)或光源体积(Light Volume,如点光源球体)。
  • 采样 G-Buffer 中对应像素点的表面属性,结合光源参数,仅针对屏幕上当前可见的 $1920 \times 1080$ 个像素计算一次光照

分块着色、集群着色

分块着色的核心思想是将光源分配给像素分块,从而既限制了每个表面需要计算的灯光数量,也限制了所需的工作量和储存量。集群着色则是将视锥体划分为一组三维单元格,集群着色为每个列表分配的灯光数量更少。