渲染路径

Forward

渲染流程

待渲染几何体 → 顶点着色器 → 片元着色器 → 渲染目标

优缺点

缺点

  • 光源数量对计算复杂度影响巨大
  • 访问深度等数据需要额外计算

优点

  • 支持半透明渲染
  • 支持使用多个光照pass
  • 支持自定义光照计算方式(延迟渲染因为是用整个Light Pass去计算所有的光照,所以不支持每一个物体用单独的光照方式计算)

Deferred

渲染流程

首先将场景渲染一次,获取到的待渲染对象的各种几何信息存储到G-buffer中,然后第二个pass再遍历所有G-buffer中的位置、颜色、法线等参数,执行一次光照计算。

一个典型的G-Buffer:

优缺点

缺点

  • 对MSAA支持不友好
  • 透明物体渲染存在问题
  • 占用大量的显存带宽

优点

  • 大量光照场景优势明显
  • 只渲染可见像素,节省计算量
  • 对后处理支持良好
  • 用更少的shader

Forward+

渲染流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 伪代码
// Depth PrePass
foreach object in sceen
        get depth
// Light Culling
foreach tile in screen:
        get max min depth
        Frustum  Intersection test
        Generate a list of light 
// Final Shading
foreach pixel in screen:
    foreach light in light_list_of_this_tile:
        pixelLighting += light_contribution_to_pixel(light,pixel)

Depth PrePass

只渲染深度,相当于 Z-Prepass,因此其实也可以结合 HiZ 或者 Early-Z 来。

Light Culling

全程在 GPU Compute Shader 中完成,分为屏幕分块 → Tile 视锥构建 → 光源视锥相交测试 → 光源列表生成四步。

最容易影响整体Forward+效率的阶段也就是Light Culling阶段。

  1. 屏幕分块(Tile Grid)
  • 每个 Tile 对应一个线程组(Thread Group),线程数 = Tile 内像素数(如 16×16=256 线程)
  • 为每个 Tile 分配:LightList[MaxLightPerTile](光源索引数组)、LightCount(光源数量)
  1. 构建 Tile 视锥体(Tile Frustum)
  • 每个 Tile 对应一个 3D 视锥体,包含空间范围和深度范围
  • 根据 Tile 内最大最小深度,把 Tile 的 2D 屏幕矩形,拉伸为 3D 视锥体
  1. 光源与 Tile 视锥相交测试
  • 点光(Point Light):用球体(Sphere) 表示,中心 = 光源位置,半径 = 光源影响范围。
  • 聚光(Spot Light):用圆锥体(Cone)+ 球体 表示。
  • 方向光(Directional Light):通常不参与剔除(影响全屏幕),直接加入所有 Tile 的 Light List。
  1. 光源列表生成与存储

Final Shading

从Light List中获取到影响该像素的光源。然后进行相应的着色计算。

优缺点

缺点

  • Light Culling效果不稳定,Light Culling是基于深度u去做的,场景每帧的深度信息都不一样,导致每帧最终生成的light List也是不一样的。
  • 寻找minZ和maxZ需要遍历Tile中的像素,有一定的性能消耗,由其是在移动端。并且现在的实现是强依赖Computer Shader,可能兼容性没那么好
  • 强制的 early depth test,在某些三角形数量特别多的场景,这个可能会成为瓶颈

优点

  • 对多光源的支持
  • 有Forward的一切好处(透明物体的支持以及MSAA,复杂材质的支持)

TBR TBDR

渲染流程

优缺点

缺点

优点

参考

https://zhuanlan.zhihu.com/p/408238134

https://zhuanlan.zhihu.com/p/553907076

https://zhuanlan.zhihu.com/p/672881713

https://zhuanlan.zhihu.com/p/112120206