一、游戏图像处理的基础:现代GPU可编程管线
GPU(显卡)制约着游戏图像效果的呈现,同时游戏图像的需求也反向促进着显卡的发展。
开始的时候GPU不能编程,也叫固定管线,只能实现固定的图像效果。而现在的显卡发展为可编程管线,通常使用的语言被称为高级着色语言(Shading Language),这才是现在游戏画面效果可以自由开发的前提。当前所有的游戏引擎,都以GPU提供的标准接口为基础实现画面渲染,只不过每家在中间的实现上各有侧重,有些引擎会包装一层更易用的界面,提供常用的材质效果,简化图像渲染开发流程。
渲染管线基本概念及流程
1,模型基础信息
模型的基本单位:顶点。顶点储存着坐标信息,编号索引,以此来标示出线、面、体的形状信息;同时有uv信息,标识此顶点平铺在贴图上的坐标;法线信息,标识出当前顶点所在平切面的垂直方向,一般用于平滑度的处理、光照、反射等计算。
2,引擎中的摄像机
引擎中的一次渲染流程,是以摄像机为单位。场景中必须有一个主摄像机,为屏幕上显示基本内容。场景可以有多个摄像机,分别渲染不同的物体,最后进行分层叠加处理。如UI部分可由单独的摄像机渲染,最后叠加到最上方。
3,顶点着色器
顶点着色器:可编程管线中第一个可编程的地方,主要处理顶点的信息,也是能使模型渲染在屏幕上的基础。
GPU运算特点:可同时并行进行巨量的运算。所以顶点的处理是独立进行的,顶点着色编程时,顶点相互之间没有关联,只能使用顶点自身包含的信息,和传入的全局信息(比如光照信息)。
坐标转换:顶点所在模型坐标空间 -> 世界空间(顶点在世界中的位置) -> 观察空间(顶点相对于摄像机的位置) -> 裁剪空间 -> 屏幕空间(最终体现到屏幕上的像素点位置)
其中裁剪空间转换是体现立体透视效果的基础,较难理解,以下图为例:
其中锥形区域是摄像机的可视区域,由6个面组成,只有包含在其中的内容才可在屏幕上显示(如前面的球太近超出了近裁切面的内容不会被渲染,透出了后面的背景,远端同理)。相机的可视角度(FOV)决定了锥形区域的斜度,某些引擎会用接近真实摄像机的焦距等信息来进行表示再进行换算,FOV越大,则可视范围越大,同样的屏幕尺寸下会显示更多远处的物体,类似于广角镜头的效果;反之则类似于长焦镜头的效果,远端裁切面变小,而远处的物体在屏幕中所占的比例增大。而裁剪空间的转换,可想象成把锥形六面体的区域拉伸成正六面体,近端放大,里面的物体根据位置等比例放大,从摄像机的前后端平面大小一致,映射在平面上,即体现出了近大远小的透视关系。而正交镜头不存在可视角度,则为正六面体,近端、远端平面同样大,屏幕上物体就不存在透视关系。
4,光栅化和差值处理
经过顶点处理,已经可以把模型的顶点信息,映射到屏幕的像素坐标上,但只有顶点还不能完全填充屏幕,每三个顶点可以确认一个面,三角面才是真正渲染到屏幕上的内容。
这里为GPU 自动完成,上面的裁剪空间到屏幕空间转换一般也在这个阶段自动处理。
三个顶点构成的三角面映射到屏幕上,内部填充像素即为光栅化,其中每个像素的坐标信息、UV、信息、法线信息,都根据其距离三个顶点的位置,通过三个顶点的信息进行差值计算得来。注意:因为有透视关系的存在,差值并不等比例于屏幕坐标距离,而是由裁剪空间中的其次坐标计算。
5,片段着色器
顶点着色器:可编程管线中第二个可编程的地方,在光栅化完成后,对每个像素进行处理。所有需要精细到像素的渲染内容,都会在这一步进行处理。如颜色贴图、法线贴图等,就是在这一步获取到当前像素的UV坐标,去对应贴图上找到对应的像素点颜色即可。比如光照处理,如果没有法线贴图,那么光照的处理在上面的订单着色器计算,光照信息存在顶点中即可,这里每个像素会得到光照的平滑差值;如果需要用到法线贴图,那么光照的处理则要在片段着色器进行计算,因为需要根据每个像素的法线进行计算光照。注意,这里同样是GPU在进行大量的并行计算,每个像素处理都是独立进行没有关联(除了各种缓存),所以有些视觉效果用到了其他颜色信息、或者要处理到物体外部的内容(如外发光),需要结合后处理(Post-process)实现。最后的返回处理结果即为屏幕上像素的实际颜色(不考虑后面半透明物体、后处理效果、其他摄像机内容的叠加等情况)。
光照
不考虑最新的光线追踪,主流有两种渲染模式:前向渲染(Forward rendering)和延迟渲染(Deferred rendering),各有利弊。
1,前向渲染(Forward rendering)
最基础的渲染模式,只能处理较少的光源,性能较高,移动平台主流模式。
把物体根据离摄像机的距离由近到远排序依次渲染,在进行片段着色时不透明物体会记录z-buffer,后面的物体再进行渲染时被遮挡的像素可跳过。最后进行半透明物体的渲染进行颜色叠加。
但没有被遮挡的物体,几乎所有的顶点都会同时进行光照计算,所以场景内可见的顶点数量和光照数量(引擎会自动限制为之考虑距离最近的几个光源)是影响渲染性能的主要因素。
所以适合较少光源、较少顶点的场景,如大部分手机游戏。
2,延迟渲染(Deferred rendering)
延迟渲染为了避免前向渲染的缺点,在其基础上进行优化:
第一步,在顶点或片段着色器计算时先不进行光照计算,只处理基础的位置信息,并转换到屏幕空间,储存为G-Buffer。
第二部,当处理完所有物体信息,完成G-Buffer后,再根据G-Buffer中的信息进行光照计算。注意此时G-Buffer中的基础单位为屏幕空间中像素,这样需要计算光照的数量只为当前屏幕分辨率像素数量x光照数量,场景中可容纳更多的顶点和光照,而不会严重影响性能。
但其有一定的限制:比如需要更大的缓存空间和GPU的支持(G-Buffer),半透明物体需要进行额外的单独处理等。
延迟渲染模式下除了有顶点和光照的效果提升外,还因为其增加了G-Buffer,可以根据其储存的信息增加另外的画面优化效果,如屏幕空间反射(SSR)、环境光遮蔽(AO),这两者对于画面的整体质感提升有非常大的影响。
二、后期处理(Post-process)
主要应用有扩散光(Bloom)、模糊、描边的某些算法、去锯齿的某些算法等。
后期处理是在GPU渲染完一帧后,以一幅整体图像进行处理,可以想象成PS里的滤镜,可进行整体颜色风格的转换,而且还可以结合Z-Buffer或G-Buffer中的场景信息达成更多的效果,如AO,镜头景深效果等。
前面说过,GPU编程管线是以GPU并行计算的特点为基础,只能单独处理当前独立的信息,而涉及到整体画面效果的调整,就需要用到后期处理来实现。而后期处理的大部分运算会在CPU而非GPU上,所以大量使用也会对帧率有较大影响。
了解上面的基本知识,是为了能在调整游戏画面和风格时,知道从哪个方向着手才能达到自己想要的效果,还可以有能力在画面效果与性能上进行平衡取舍和优化。
三、画面效果呈现思路事例
1,卡通渲染思路:调高贴图饱和度;减少光照比例,使用贴图自发光颜色;光照暗部处理为非线性,而以阈值为界进行硬处理;光照暗部颜色增加互补色;增加描边;简化阴影等。
2,Low Poly风格思路:贴图使用中性偏高的饱和度;减少光照影响比例,使用自发光颜色;使用非常浅的软阴影;重点一定要使用AO效果并调优;灵活使用扩散光等。
3,写实风格:最简单的途径是使用引擎提供的PBR材质(Physically-Based Rendering 基于真实物理的渲染),配合材质编辑器。PBR材质本质也遵循上述的基本渲染流程,只不过暴露出的参数以真实世界的材质效果为基础,如基础颜色或贴图(漫反射颜色)、金属性或贴图(镜面反射率),粗糙度或贴图(表面平滑度),其中参数都可以用一个数值或一个贴图来表示,数值为物体整体参数设置,而贴图可以表示物体不同位置拥有不同的数值(黑0,白1,灰过度)。美术可以根据实际的生活经验调整数值来制作自己想要的材质,而不用考虑其内部的算法实现。
四、总结
游戏最终的画面效果,是从模型、贴图、灯光布局开始,结合材质(bpr或自定义shader或材质编辑器),和后期处理,三者相辅相成,最终共同作用的结果。其中对画面影响最大,可操作性最强的部分是材质部分,即GPU可编程管线的部分。无论BPR材质,还是UE中的材质编辑器,都基于上述的渲染流程,只不是过从具体的shader编程变成了更加易用的可视化界面。