短篇图形学——从“打光”开始,聊聊延迟渲染


3楼猫 发布时间:2024-03-08 11:32:27 作者:Hakumen Language

前言

前几天看羽毛直播的切片,里面有一段他提到场景要做得好看需要一个会“打光”的美术,还有弹幕提到用老黄的硬件以后直接配实时全局光照(RTX光追模式)就可以搞定了。
概念上来说这么理解没问题,但由于实时渲染毕竟有着诸多限制,而实际的玩家群体的高端硬件普及率没那么乐观,个人觉得还是有必要聊聊游戏引擎的“打光”是怎么发展到现在的。
图形学里很多概念强行翻译会丢失其准确性,Deffered Shading(或者Deffered Rendering)中的这个deffered翻译成延迟就丢失了很多上下文。硬要说的话,这里的延迟更多的是在一帧内渲染顺序上的变化,是一种不同的渲染思路。
这里面不会展开光照具体的算法,由于上一篇讲纹理其实基本也算介绍了前向渲染(Forward Rendering)的基本思路,这里从前向渲染的光照开始介绍:

1 前向渲染及其光照

前一篇讲纹理渲染的文章已经大概介绍过,对一个物体的前向渲染大致可以理解为多边形几何处理(矩阵变换)、读写深度缓冲和着色几个步骤。着色往往可以多于一次绘制,我们把一次绘制的调用叫做Draw Call(有些绘制的调用可以批处理,被称为Batch)。对于着色上次只提到了纹理映射相关的问题,没有考虑光照。
传统管线中常见的光源中比较有代表性的有平行光、点光源、聚光灯、区域光源等:
1)平行光 Directional light——用于近似抽象地球上接收太阳光的情况,强度不随距离衰减,一般被作为主光源
2)点光源 Point light——覆盖范围是球体的有限范围光源
3)聚光灯 Spotlight——覆盖范围是锥体聚光灯形式,常用来模拟手电筒
4)区域光源 Area light——光线追踪中会主要使用的光源形式,在传统管线中一般只能设置后用于预渲染静态物的光照贴图(Bake)
前3者对于传统渲染管线来说都是实时光源。在前向渲染的着色这一步,如果有多光源的情况,实际上是每个物体逐光源绘制一次,最后一个物体呈现出的是多光源混合的结果。
(图中是我在Unity中摆的2个平行光加2个点光源的示例,并展示了4个drawcall逐步叠加上去的渲染结果)
在较新的实时渲染管线中,前向渲染的光照这一步也进行了调整,例如在一次绘制调用中带入一个主光源(MainLight 一般指平行光)和弱干个补充光源(AdditionalLight)进行计算,增加了单次计算量但减少了drawcall。光照计算还包含逐顶点(Per vertext)逐像素(Per pixel)两种方式,这里不展开了。
假设场景有1000个物体,想要设置大大小小100个光源,如果按照一个物体一个光源绘制一次,最多就会有十万次drawcall——即使能通过距离或者剔除筛掉一些,但是量级不会显著下降,这对于现有的大部分硬件都是一个遥不可及的数字了。所以对前向渲染来说,根本不是会不会“打光”的问题了,而是不可能大规模使用实时局部灯光。

2 延迟渲染(Deffered Shading)

延迟渲染(或延迟着色)的提出在当时主要就是提升可容纳的光源数量的。
假设待渲染的物体都是不透明的,在硬件越来越多的支持帧缓冲(Frame Buffer)后,其中一个想法就是把渲染物体的一些中间结果缓存到几何缓冲区(G-Buffer),后续对这个中间结果计算光照。这个过程被大概可以拆解为几何处理阶段光照处理阶段
几何处理阶段需要缓存的中间结果有以下几类——颜色、深度、法线(Normals)
(从左到右分别为颜色、深度、法线缓冲)
法线并不是在延迟渲染中才提出的概念,但确实是本系列第一次提到。简单的理解法线是一个向量,描述了一个点的方向,在进行光照计算时会影响其结果;三角面和顶点有其对应的法线,如果引入法线贴图(Normal Map)还可以在像素级别来修改其法线,帮助呈现为视觉上更细致的模型表面。
(图中a是法线贴图,c-d是通过法线贴图来提升细节表现的例子)
渲染的过程中,物体法线及法线贴图通过空间变换后,每个像素的法线值就被缓存了下来。
在光照处理阶段,结合帧缓冲和之前提到的材质和灯光信息,就可以做光照着色了。此时每个光源每种材质(Per material per light )会对依据缓冲区的中间结果进行一次绘制。
(Wiki上的例子中Deffered Shading的渲染结果)
前文提到的1000个物体100个灯光的问题,在延迟渲染的管线下,假设都是统一材质,则最少只需要几何1000+光照100次绘制就可以实现了。虽然每次渲染比前向渲染需要更多的渲染带宽和帧缓冲,但至少(进行一些优化后)数量级上在2000年左右已经是可实现的了。
事实上这也是基于物理的渲染(PBR)材质被提出的一个重要原因,因为延迟渲染需要一个参数上有代表性的材质搞定大部分不透明物体渲染的情况——毕竟材质种类太多就会失去延迟渲染的优势了。
*延迟渲染也是硬件定制化的主机在某个阶段画质显得大幅领先于PC游戏的重要原因——既因为可定制化设计的帧缓冲,也因为主机游戏更关注写实化渲染,更少引入那些半透明的亮瞎眼粒子特效。
(图中展示了延迟渲染的一张“秀肌肉”图)

3 优劣比较

简单说一下前线渲染和延迟渲染的对比:

1)前向渲染

优点

  • 1.支持半透明渲染
  • 2.支持使用多个光照pass
  • 3.支持逐物体光照计算(延迟渲染是渲染到帧缓冲,再一起计算光照,所以不支持每一个物体用单独的光照方式计算)

缺点

  • 1.光源数量对计算量影响巨大,太多光源会严重拖慢性能
  • 2.访问深度数据需要额外再渲染一张深度图,深度图对最终渲染结果没有贡献

延迟渲染

优点

  • 1.大量光照场景的情况下,优势明显
  • 2.经过了剔除,只渲染可见像素,节省计算量
  • 3.对后处理支持良好(帧缓冲中有深度、法线等信息)

缺点

  • 1.对多重抗锯齿MSAA支持不友好(原理上导致的,帧缓冲已经丢失了物体原始的几何信息,因此无法正确超采样)
  • 2.透明物体渲染存在问题(透明物体需要按深度着色并混合,和帧缓冲的思路是违背的)
  • 3.占用更多的显存和渲染带宽
  • 4.只能使用同一个光照pass
至于什么是光照pass,可以简单理解为一套预定义的光照渲染计算流程,如数据结构定义、预处理、主函数等,这里不做更多展开了。

4 现代渲染管线

由于光追也已经是“现代”管线了,这里的现代建议理解成传统两种管线的改良版。这部分主要做一个概念上的介绍。
1)延迟光照( Deferred Lighting
延迟光照主要是改进了延迟渲染中光照不够灵活的问题,并减少缓冲区的占用,某种意义上可以理解成是嫁接了前向渲染——因此光照处理部分能支持多Pass了,但也会带来更多drawcall。
2) 分块渲染(Tile-Based Rendering)
基本思路是把屏幕空间分块进行并行的延迟渲染(TBDR),以充分利用GPU的特性。这个方案后续也有迭代和发展,产生了改进版PowerVR TBDR
前向渲染也有其对应的Tiled方案,又被称为Forward Plus(莫名想起某手机的命名方式)。
(图中是TBDR的一个概念示意图,tiled是基于屏幕像素范围的划分)
3) 群组渲染(Clustered Rendering)
简单来说就是对空间进行分块,然后按前向渲染或延迟渲染的管线进行处理,目标也是充分并行计算。(这里面有一个物体跨多个空间的问题处理,不展开了)
(图中是ClusterRendering中对摄像机视锥体空间划分的示意图)

尾声

无论是前向渲染还是延迟渲染,作为表现力的补充还需要引入各种基于预渲染或预采样的光照技术——如光照贴图(LightMap)光探针(Light Probe)环境反射CubeMapIBL ),以及环境光等,这里不展开了。无论实时还是烘焙,都可能面对漏光问题,这就是一个需要“打光”的经验来化解的问题,有兴趣的可以自行搜索了解下。
另外,传统管线的中的阴影实现也是一个大课题。其实阴影的渲染伴随在以上各个管线的过程中,由于篇幅原因这里没法展开讲。
在光线追踪管线中,任何光线传播都会考虑其能量传递和损失,这一点带来的光照算法就完全不同于传统管线了。这也是光追管线无法和传统管线拼接使用的其中一个原因。
游戏开发商即使不开发光追模式,也必须开发传统管线模式,这是现在单机3D游戏的无奈现状(不是高精度画面更不用考虑光追了);基于相信Nvidia硬件采用的RTX全局光照方案,至少5年内都不会真正到来,即使到了下一个主机世代,中小开发者还是会继续使用各种改良后的传统管线作为主要的“现代渲染管线”。
最后是一些相关资料:
中文内容这篇知乎里写得比较详细了,本文也参考了很多
延迟渲染Wiki(包含了延迟光照)
Tiled Rendering
GDC Cluster-Forward-Rendering

© 2022 3楼猫 下载APP 站点地图 广告合作:asmrly666@gmail.com