gta5 画面模糊,《侠盗猎车手5》作为一款游戏大作,一直以其细腻的画面和流畅的游戏性备受玩家们的喜爱。不过在游戏中,不时会出现一些画面模糊现象,让游戏画面有些许失真。那么到底是什么原因导致了这一现象的出现呢?为了更好地理解,我们需要对GTA5游戏画面的渲染技术进行深入解析。
GTA5游戏画面的渲染技术解析
所写的对渲染过程的图形学分析,并大量引用了里面的内容。之前也有前辈做了翻译,这里我介绍了更多的细节与优化。读者需要一定的图形学基础去理解其中的概念,而我也在其中加入了一些自己的理解,若有错误希望不吝指正。
剖析GTA5(侠盗猎车手5)中一帧的渲染流程开始之前首先要指出,GTA5里许多渲染所用的缓冲都是HDR缓冲(每个channel大于8位,无法在显示器上正常显示),直到每帧结尾才转化为常见的RGB图像。原作者Adrian Courrèges为了演示方便,已经将这些HDR缓冲都转化为8位的正常图像。所以下文你看到的图片都是经过HDR处理过的。文末附上了原作者使用的HDR处理方式的简介以供参考。
好了,下面进入正题,开始详细的渲染流程介绍!
Step 1. 生成环境贴图每一帧开始时,GTA5首先渲染出一张环境立方体贴图(Cubemap)。这张Cubemap用于后面计算池塘,河流等物体的反射,并不需要很高的精度, 只有地形,天空,摩天大楼等大型景致的低精度模型被渲染到Cubemap中,并且每一面的贴图大小只有128*128,只要约30个draw call(绘图指令)即可完成。由下图可以发现,只有那些大型模型在水面里有倒影,无论行人、载具还是装饰的静态物体都是没有倒影的,这就是提高渲染效率的代价。
人物,围栏,树木都是没有倒影的环境立方体贴图(Cubemap):一个由6张纹理组成各个面的立方体,渲染方式为将摄像机置于你所在的位置,朝着6个方向各进行一次渲染(每次将摄像头角度旋转90°)。Cubemap也可以用于实现简单的天空盒,使远景一直与人物保持相对静止,实现天空的效果。
环境立方体贴图(引用自Adrian)然而GTA5在这里还加上了一个优化:将立方体贴图(Cubemap,6个面。128*128)转化为双抛物面贴图(Dual-Paraboloid Map,2个面,128*128),转化后的结果如下所示:
GTA5使用的Dual-Paraboloid Map(引用自Adrian)这两张图可以看成把原来的立方体贴图压缩成球体,然后映射到上半球和下半球上。这样做有什么好处呢?首先,它降低了显存开销(从6张到2张);其次,Cubemap是用于后续的反射计算的,考虑到多数情况中反射体正面朝上(水池,车窗等),只有那些高处的景物会被反射进我们眼中,所以大部分都是对上半球的贴图(上图右边部分)进行采样;再次,使用立方体贴图的话,我们在使用mipmap技术采样贴图时很容易在不同的面之间产生seams,而使用双抛物面贴图则会减轻这个问题的影响。
至此,用于后面计算反射的贴图已经生成好了。
Step 2. 背面剔除和LOD技术这一步的操作都由计算着色器(Compute Shader,CS)完成。原文没有提及很多细节,我稍作一些补充。
剔除(Culling)是一种很常见的优化技术,旨在提前去掉不需要渲染的面,比如背面消隐,视锥体剔除等。何为背面消隐?比如将镜头对准一张纸板,只有纸板的正面需要渲染,背面则不需要(因为你也看不到背面的内容),这样可以减轻渲染的压力。一般都是通过传入的网格面片的顶点顺序来定义哪一面是正面。何为视锥体裁剪?摄像头同我们眼睛一样,能看到的通常都在一个平截头体中(太近、太远或太偏的物体都看不到),所以我们只要渲染在平截头体内的物体。
LOD技术(Level of Detail)则是为了处理模型的精度问题,精度越高的话,模型的表现越精细。但对于离摄像机很远的物体,用高精度模型渲染没有明显的画质提升,反而会消耗大量的显存(因为远处往往包含更多的物体,使用高精度模型的话,需要的模型面片数量将十分巨大)。常用的解决方案是 对同一个物体预先生成不同精度的网格,当物体离摄像机近时就用高精度的网格,离的远就用低精度的网格(后面介绍的Cascaded Shadow Map技术也是基于类似的原理,来保证近处的阴影质量更高),这样能尽量保证玩家感受高质量的模型。
LOD:根据距离选择不同的模型精度可以观察GTA5对于植被运用的LOD技术处理,当植被与摄像机超过一定距离后不再渲染就不再渲染,即模型精度为0。
用人物手机拍照,拉近镜头时更多植被被渲染GTA5对LOD技术的运用十分高超。我们知道GTA5的场景庞大而复杂,每一帧都需要对大模型进行处理,但它却能在显存只有256MB的PS3上渲染很多大型场景,并且将细节表现得十分逼真,足见它在LOD上下了很大的功夫。
Step 3. G-Buffer Generation主要的渲染过程就是在这一步进行,因此该步骤将做重点介绍。GTA5运用了延迟渲染技术(与之相对的是:之前绘制cubemap时运用的是前向渲染),利用MRT技术将同时渲染到5张渲染对象(贴图)。这里首先对名词稍作解释。
前向渲染(Forward Rendering):在每一帧中轮流渲染各个物体,渲染每个物体时调用它的顶点着色器(VS)和像素着色器(PS),在PS中结合光源信息利用光照模型进行计算,将物体最终绘制在纹理上。该渲染方式存在一些不足:首先,如果很多物体在摄像机方向前后重叠,则很可能同一个像素有多个物体调用各自的PS进行光照计算(最终只保留一个),这就造成PS运算的浪费,即over-drawing问题(采用由近及远的渲染方式可以减轻这个问题);其次,当场景中具有大量光源时(如下图所示),每个物体需要向PS传入所有临近光源的信息并一一计算光照,考虑到庞大的物体数目和光源数量,这将严重影响PS的效率,形成GPU Bound。
GTA5夜间场景存在数以百计的光源和大量的物体综合前面所说,前向渲染存在重要的运算浪费:一个像素可能被渲染了数百次(每次都会进行光照运算)但最终只有深度最低的那次运算结果得以保留,其他光照计算都被浪费掉了。
延迟渲染(Deferred Rendering):针对前向渲染的缺点,延迟渲染换了一种思路。它利用MRT(multiple rendering target)技术将每个物体的漫反射、法线、镜面反射等信息分开绘制到4~5张主要的纹理(也就是G-Buffer)上,暂不进行光照运算。当所有物体的信息绘制完成,光源 再利用G-Buffers中的数据进行运算(”延迟“的由来)。 由此可见,场景的复杂度对光照计算量的影响大大降低(因为无论一个像素点被物体覆盖多少次,光源只对该点渲染了一次),大量光源的渲染得以实现。
GTA5延迟渲染示意图下面将主要介绍一下G-Buffer里面几张贴图的具体内容(需要另外指出,这里所用的缓冲都是LDR而非HDR)。一张贴图有RGBA这四个8位通道,而像深度等信息只占一个通道,颜色也只占三个通道,如果只存一种信息必然会浪费空间,因此GTA5会将不同的信息合同到同一个贴图内以充分利用所有通道)。
Diffuse Map:漫反射贴图,保存物体的原本的颜色。但与其他游戏不同在于,GTA5在这里顺便加入了阳光对物体的光照计算。考虑太阳的平行光特性,所有物体都会被照射到,这点代价(相较于在结合G-Buffer时进行阳光计算)还是可以接受的。下图中可以看出引擎盖上的对于阳光的光照计算。引擎盖上的阳光光照计算(引用自Adrian)剩下的alpha通道用于保存”Blending“(混合)信息,标志出需要抖动平滑处理的像素点。我们将在介绍Dither Smoothing的时候介绍它是如何使用的。
2. Normal Map:法线贴图,用于存储每个像素点的法线,可以在光照计算时模拟表面的变化,从而提高真实度。
这里做一个比较:延迟渲染中法线贴图存储的是每一点的法线在世界空间(World Space)的向量坐标,而前向渲染为了方便在PS中计算光照,一般存储的是每一点法线在自己的切线空间(Tangent Space)的向量坐标。这就解释了前向渲染的法线贴图为何偏蓝色:贴图中每个通道只能存储正数,因此需要将法线向量的每一位从 (-1, 1) 线性映射到 (0, 1) 中,切线空间中常见法线向量 (0, 0, 1) 将会被映射到 (0.5, 0.5, 1) ,正好对应蓝色。
GTA5中使用延迟渲染,因此normal map需要存储法线的世界空间坐标,可以看出其色调与前向渲染的法线贴图不太一样。但主要色调仍为蓝色,因为大部分物体都是表面朝上(如地面),法向量的世界空间坐标仍会从(0, 0, 1) 映射到 (0.5, 0.5, 1)。
注意:这里要注意两张贴图的来源区别:延迟渲染的法线贴图是G-Buffer的中间产物; 而前向渲染中的法线贴图则是预先提供的美术素材。这里比较是为了更清晰地展示两张贴图的取值空间的不同。
GTA5(延迟渲染)的法线贴图 切线空间下的法线贴图法线贴图的alpha可能是用于判断植被与摄像机的距离(决定是否渲染),原文不甚详细。
3. Specular Map:高光贴图,但这里并不直接存储每个像素的镜面反射光颜色,而是存储每个像素点最上面的物体的材质信息以供后续计算,原因在于:GTA5采用的延迟渲染,所以得在G buffer渲染结束后才会进行光照计算(否则光源很多时,你需要对一个物体进行上百次镜面光计算)。这里各个通道存储的信息为:
Red:高光强度(specular intensity)
Green:光泽度(glossiness)
Blue:菲涅尔系数(Fresnel Intensity,同一材质所渲染的像素点都采用相同的菲涅尔系数)
高光贴图并非直接存储高光颜色(引用自Adrian)4. Irradiance Map:辐照贴图(这个我不是很了解),主要用于保存每个点的辐照度。各个通道存储的信息为:
Red:每个像素点接收到的来自太阳的辐照度(基于该像素的位置,法线与阳光角度进行计算)。
Green:原作者猜测该通道记录了来自另一个光源的辐照度
Blue:记录物体的自发光属性(emissive),对于霓虹灯等物体占据的像素,该通道值非0。
Alpha:该通道主要用于标记皮肤、植被等需要特殊处理的像素点(后续将介绍如何处理)。
辐照贴图(引用自Adrian)由上面容易看出,在白天时,Red通道基本都被填充,Blue和Alpha通道大部分为0。所以该贴图明显偏红。城市部分对应的绿色可能是做了特殊处理(我猜测是考虑了热岛效应?)
5. Depth & Stencil Map:深度贴图和模板贴图,用于存储每个像素点的深度和模板值。存储深度时GTA5做了一个优化:存储对数深度缓冲。公式为: d=\frac{log(C*w+1)}{log(C*far+1)}*w , C 通常取小于1的常数, w 是观察空间下的深度, d 是实际被写入缓冲的深度。
为什么要做这个优化?正常情况下写入的深度与实际深度的关系为 d=a\frac{1}{z}+b ,可以看出在近平面时 d 的精度可以对应大部分深度;而当 z 很远时,需要距离较大改变,才能被 d 感受到(因为 d 的精度是有限的),因此在远处相近的两个物体深度会重叠,这就是Z Fighting现象。GTA5由于地图庞大,经常出现far plane很远的情况,因此必须要解决这一问题。
1/z中远平面的变化无法准确反馈给d(图片来自NVIDIA)我们可以在存储d时使用一个拟对数分布来保证far部分能对应到大量的d值,near部分也能获得足够的精度,实际上正好可以用对数去模拟这样的分布,这就是公式的由来。我利用Mathematics简单绘制了对数缓冲公式的图像,并举例进行计算:下图右边可见,当far plane为 10^{7}m 时,假设物体从 10^{5}m 移动到 10^{5}+10m ,对数缓冲的变化仍在精度范围内,而倒数函数的变化十分小,已经无法在24位精度下被计算机准确反应了。
对数缓冲函数能更精确地记录远处物体的深度数据模板值被用来标志每个像素点渲染的网格类型,同一种网格所渲染的像素都用同一个值进行标志。之所以需要标志出来,是因为GTA5将对一些网格做后续处理(如皮肤,载具等),读取模板值可以决定应该用哪种方式进行处理。一些典型的模板值如下:
0x89:玩家所控制的人物0x82:玩家驾驶的载具0x01:游戏中的NPC0x07:天空至此,G buffer中所有的buffer都被介绍完了,这些buffer一共调用了约1900个draw call。但是注意该part还没有结束,下面将列出一些渲染G-Buffer的其他细节;在这些操作完成之后,才能进行G-Buffer的合并计算。
I. 场景中物体都是”从前往后“渲染的,这样做的好处在于:像素被近处的物体先渲染了,后面的物体渲染这些像素时就无法通过深度测试,在调用PS渲染之前就被GPU丢弃了,从而提高了渲染效率。“从前往后”渲染能节省大量不必要的PS运算开销。从下图可以看出,人和车会占据屏幕的一大部分,而后面的物体则省去了渲染的开销。
II. 前面介绍diffuse map时提到diffuse map的alpha通道,被用来标志出需要抖动平滑处理的像素点。如果你将diffuse map放大查看,会发现远处的树占据的像素只有一半被渲染,就像国际象棋的棋盘一样。GTA5这样做并非因为渲染效率的限制,而是有意为之(在像素着色器中读取屏幕坐标判断是否渲染)。
这种抖动的渲染方式可以使得物体在不同的LOD之间的转化更为自然。在原文后面的Dither Smoothing介绍了后续操作:利用一个后处理像素着色器,读取alpha通道来确定被抖动处理的像素(远处的树木),然后对周围进行采样混合,计算出该像素点平滑处理后的颜色值。
处理前后图像对比(图像经过放大,引用自Adrian)这里有一篇关于该技术(Alpha Stippling)的问答。GTA4也使用了该技术。
III. 阴影技术:GTA5采用了级联式阴影贴图(CSM,Cascaded Shadow Map),这也是大型游戏中常用的技术。对于普通的shadow map,很容易出现摄像机近处多个点对应到同一深度的情况,为了解决这一问题,通常会根据距离生成不同精度的shadow map,来保证近处的阴影的准确性。可以看到左图中大块区域会在shadow map中对应到同一深度,而右图中则可以保证近处的深度划分更加细致。
基本的阴影贴图和级联式阴影贴图GTA5将4张阴影贴图合成了一张1024*4096的贴图,每个阴影贴图都对应了不同的视锥体(从光源出发)。注意到阴影贴图是从太阳发射的平行光的角度进行渲染的,结合下图和Diffuse Map可以看出,从左到右视锥体覆盖的范围越来越大,并且离摄像机越来越远(即不同距离生成不同精度的贴图)。
GTA5中的阴影贴图(引用自Adrian)渲染这几张贴图可能会带来很大的开销,因此同一场景被渲染了四次。但值得庆幸的是渲染每张贴图时都可以用视锥裁剪裁去大部分不需要的物体,因此CSM只需要约1000个draw call就可以生成。最后太阳光和云投射的阴影经过平滑处理后进行模糊混合,存储到Specular Map的alpha通道内。
混合后的阴影贴图(引用自Adrian)由于模糊操作需要从多个纹理中采样进行计算,代价十分高昂。GTA5在模糊混合之前还进行了一个优化:它首先生成原贴图1/8大小的贴图并进行低质量的近似模糊处理,通过结果估计出哪些像素点将会被完全点亮;然后对原大小的贴图进行高质量模糊处理时,如果该点被估计为完全点亮,则跳过模糊计算直接输出1(完全点亮的情况)。由上图可以看出,白天场景中大部分像素都会被完全点亮,因此这个优化可以节省大量昂贵的模糊计算。
IV. SSAO(Screen Space Ambient Occlusion,屏幕空间环境光遮蔽):在渲染场景时,墙角等地方往往会由于周围建筑的遮挡而显得比较暗。这是因为光线在角落里有更小的概率反射出去,因此墙角的光更难射入人眼中。用Path Tracing可以较好地去感受光线的传播路径,这里放一张我写的路径追踪的渲染结果(噪点太多,请忽视。。):
Path Tracing的生成结果可以明显看出墙角和墙沿的光线更加昏暗。而只计算太阳光是无法模拟出这样的效果的,因此提出了SSAO技术。考虑到G-Buffer中已经存有Normal Map, Depth Map等信息,我们可以在每个像素点对其法线方向的上半球进行采样,然后根据深度贴图确定周围有多少部分是高于当前深度,即确定有多少光会被遮蔽。计算结果会生成一张SSAO贴图,然而这个贴图一般噪音很强,因此还需要两个pass去做水平和竖直方向的平滑,更新SSAO贴图。
另外需要注意:为了提高效率,SSAO Map都是以原图一半的分辨率去实现的。
未平滑的SSAO Map噪音十分严重(引用自Adrian)细节介绍完了,终于是时候把这些G-Buffer的信息合并计算了!
一个像素着色器将从这些缓冲收集信息并最终计算出HDR下的渲染结果。对于夜间场景则会在这里额外渲染那些灯光,不做赘述。
G-Buffer集合(引用自Adrian)这是合并后的结果:
G-Buffer Combination(引用自Adrian)至此,渲染结果已经大致成型!
此处附上HDR处理简介,感兴趣的读者可以了解一二:
这里简单介绍一下Adrian使用的HDR处理方式(详情参考Reinhard的论文Photographic Tone Reproduction for Digital Images):
对于每个像素点都根据RGB值计算出其亮度(符号为L,L=0.27R+0.67G+0.06B),然后利用以下方程更新该点的 L :L_{d}(x, y)=\frac{L(x,y)(1+\frac{L(x, y)}{L_{white}^{2}})}{1+L(x, y)}L_{white} 代表能映射到纯白色的最小亮度。这一步可以将 L 全部映射到 [0, 1] 区间内,但对于色彩范围极大的图片效果仍然不好,因此需要进一步处理。
2. 对于图片中的每个像素,找出适当的尺寸 s 使得:以该点为中心, s 为半径的区域与周围对比足够强(利用他定义的公式计算对比)。然后结合 s 利用公式
L_{d}(x, y)=\frac{L(x, y)}{1+V_{1}(x, y, s_{m}(x, y))}
重新计算 L 。 这里V_{1} 是基于每个像素的 s 利用卷积计算出平均亮度。
然后按照色彩空间中的定义进行 L 与 RGB 的转化(这一块不太了解),即可得到处理后的图片,此时每个通道都可以用8位存储。处理结束。
GTA5作为一款出色的开放世界游戏,在画面模糊方面采用了许多优秀的技术手段来实现其细节丰富、逼真绝伦的画面效果。我们可以期待,在未来的游戏开发中,这些技术不断完善和创新,让游戏画面更加逼真,给玩家带来更加震撼的视觉体验。