重新浅析 “渲染管线”

本文转自Unity Connect博主 北京琳云信息科技有限责任公司

“渲染管线”扫盲篇

        所谓的渲染管线,实际上是渲染过程的流水线,指的不是具体某一样东西,而是一个流程。渲染管线,也称为渲染流水线,渲染流水线可以分为三个概念性阶段:应用阶段,几何阶段,光栅化阶段。

        游戏场景中的物体渲染都是基于可编程流水线实现的,其实就是把绘制的 3D 物体通过可编程流水线绘制在 2D 的屏幕上的过程。

        什么是渲染(Rendering):渲染简单的理解可能可以是这样,就是将三维物体或三维场景的描述转化为一幅二维图像,生成的二维图像能很好的反应三维物体或三维场景。

        从宏观概念上来说:从硬盘上 - 出来  ->  一个三角面片 - 走进  -> CPU - 三角面片 -> GPU - 光栅化 -> 显示器(屏幕)

        真正的渲染管线简单分为三部分,1)应用阶段    2)几何阶段    3)光栅化阶段

        接着,我们从具体的每一个阶段进行进一步的浅析哈!1、应用程序阶段(由CPU负责)

        其实这个阶段相对比较好理解,就比如我们在 Unity3D 里开发了一个游戏,其实很多底层的东西 Unity3D 都帮我们实现好了,例如碰撞检测、视锥剪裁等等,这个阶段主要是和 CPU 、内存打交道,在把该计算的都计算完以后,在这个阶段的末端,这些计算好的数据(顶点坐标、法向量、纹理坐标、纹理)就会通过数据总线传给图形硬件,从而作为我们进一步处理的源数据。

一般主要有三个执行的步骤:

(1) 准备好场景数据,例如摄像机的位置,视锥体,场景中的模型以及使用的光源等;

(2) 为了提高渲染性能,通常需要做一个粗粒度剔除( Culling )工作,把那些在场景中不可见的物体剔除出去,这样这些物体就不需要再移交给几何阶段处理;

(3) 需要设置好每个模型的渲染状态,这些渲染状态包括但不限于它使用的材质纹理 Shader等。

        这一阶段的输出是渲染所需要的几何信息,即渲染图元。其中每个渲染图元里包含了该图元所对应的所有顶点数据。例如,在 OpenGL 中,如果我们绘制一个三角形,我们需要在程序中制定顶点位置,指定图元类型,这些都是需要 CPU 先准备好。

        其中,里面特别需要提到的是 Draw Call(别名: DP 、批次)这个概念,我们都知道在游戏里面批次太多会导致 CPU 的性能开销过大,当然我们通常也会有一些解决的方法:

A. 合批,就是把能合并的合并起来,减少 Draw Call ;

B. Instance,CPU 硬件算法,当使用与大量需要重复绘制的模型,例如草地,减少 Draw Call 。2、几何阶段 (由GPU负责)

        几何阶段用于处理所有和我们需要绘制的几何相关的工作。例如:决定需要绘制的图元是什么,怎样绘制,在哪绘制。主要负责顶点坐标变换、光照、裁剪、投影以及屏幕映射,该阶段基于 GPU 进行运算,在该阶段的末端得到了经过变换和投影之后的顶点坐标、颜色、以及纹理坐标。简而言之,几何阶段的主要工作就是“变换三维顶点坐标”和“光照计算”。

        问题随之而来,为什么要变换顶点坐标?我是这么理解的,比如你有一个三维游戏场景,场景中的每个模型都可以用一个向量来确定它的位置,但如何让计算机根据这些坐标把模型正确的、有层次的画在屏幕上?这就是我们需要变换三维顶点坐标的原因,最终目的就是让 GPU 可以将这些三维数据绘制到二维屏幕上。

        几何阶段最重要的部分就是进行坐标的转换,如果想了解这其中具体的转换步骤,可以参考:顶点着色器内的矩阵变换。该阶段的重要任务就是:把顶点坐标变换到屏幕空间中,再交给光栅器进行处理,所以该阶段还是侧重与几何计算部分,而不是给物体“上色”。通过对输入的渲染图元进行多步处理后,这一阶段将会输出:屏幕空间的二维顶点坐标、每个坐标对应的深度值、着色等相关信息,并传递给下一阶段。

官方的细节文档参考:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/3、光栅化阶段(由GPU负责)

        这一阶段会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像。

        光栅化的任务:主要是决定每个渲染图元中的哪些像素应该被绘制到屏幕上。它需要对上一个阶段得到的逐顶点数据进行插值,然后再进行逐像素处理。光栅化其实是一种将几何图元变为二维图像的过程,该过程主要包含了两部分的工作,光栅化和片元着色。

        光栅化:决定窗口坐标中的哪些整型栅格区域被基本图元占用,这一部分主要对应着三角形设置和三角形遍历两个阶段通过插值计算完成,这一阶段输出一个片元序列,之后的片元着色器就会对该片元序列进行处理。

        片元着色:分配一个颜色值和一个深度值到各个区域。

        经过上面的步骤之后,我们得到了每个点的屏幕坐标值,和我们需要绘制的图元,但此时还有两个问题:

(1)  屏幕坐标是浮点数,但像素是用整数来表示的,如何确定屏幕坐标值所对应的像素?

(2)  如何根据已确定位置的点,在屏幕上画出线段或者三角形?

        对于问题 1 ,绘制的位置只能接近两指定端点间的实际线段位置,例如,一条线段的位置是(10.48, 20.51),转换为像素位置就是(10,21)。

        问题 2 ,我们所要解决的问题就是根据三角形的三个顶点来判断哪些像素在这个三角形内,这就转化为判断一个顶点是否在三角形内的问题,这也是 OpenGL 三角形遍历中一个重要的算法。

        这个过程结束后,顶点和图元已经对应到像素,之后的流程就是如何处理像素,即给像素赋予颜色值。 给像素赋予颜色的阶段称为 Pixel Operation ,也就是逐片元操作,是在更新帧缓存之前,执行最后一系列针对每个片段的操作,其目的是计算出每个像素的颜色值。

        在这个阶段,主要进行的就是一些逐片元操作,先后是模板测试,深度测试,混合,最后写入帧缓存。

Pixel Operation 包含下面这些流程:

        (1) 消除遮挡面,深度测试可以消除遮挡面;

        (2) Texture Operation ,纹理操作也就是文理采样,根据像素的纹理坐标,查询对应的纹理值;

        (3) Blending,通常称为 Alpha Blending ,根据目前已经画好的颜色,与正在计算的颜色的 Alpha 值混合,形成新的颜色。

        (4) Filtering ,将正在计算的颜色经过某种滤镜后输出。

        该阶段之后,像素的颜色值被写入帧缓存中。 好了,这样,一个三角面片就完成了由硬盘中冷冰冰的数据,到屏幕上华丽丽的图像的全部冒险了。

原文链接:https://connect.unity.com/p/zhong-xin-qian-xi-xuan-ran-guan-xian?app=true

欢迎戳上方原文链接,下载Unity官方技术社区app,在线技术答疑,发现更多资源干货!

你可能感兴趣的:(重新浅析 “渲染管线”)