常用渲染管线介绍

发布于:2024-07-03 ⋅ 阅读:(18) ⋅ 点赞:(0)

1、光栅化渲染管线(Raster pipeline)

1.1、光栅化概述

光栅化图形渲染管线是实时渲染的核心组件。渲染管线的功能是通过给定虚拟相机、3D场景物体以及光源等场景要素来产生或者渲染一副2D的图像。如上图所示,场景中的3D物体通过管线转变为屏幕上的2D图像。渲染管线是实时渲染的重要工具,实时渲染离不开渲染管线。光栅化图形渲染管线主要包括两个功能:一是将物体3D坐标转变为屏幕空间2D坐标,二是为屏幕每个像素点进行着色。渲染管线的一般流程如下图所示。分别是:顶点数据的输入、顶点着色器、曲面细分过程、几何着色器、图元组装、裁剪剔除、光栅化、片段着色器以及混合测试。我们会在后文对管线的各个阶段进行详细的介绍。

渲染管线的一个特点就是每个阶段都会把前一个阶段的输出作为该阶段的输入。例如,片段着色器会将光栅化后的片段(以及片段的数据块)作为输入进行光照计算。除了图元组装和光栅化几个阶段是由硬件自动完成之外,管线的其他阶段管线都是可编程/可配置的。其中顶点着色器、曲面细分相关着色器、几何着色器和片段着色器是可编程的阶段,而混合测试是可高度配置的阶段。管线的可编程/可配置是渲染管理的另一个特点。因为早期的渲染管线采用的是立即渲染模式(Immediate mode,也就是固定渲染管线),不允许开发人员改变GPU渲染的方式,而核心渲染默认(Core-profile mode)允许开发人员定制化GPU的渲染方式。

1.2、光栅化图形管线

我们接下来简单介绍管线各个阶段的功能:

1.2.1、顶点数据

顶点数据用来为后面的顶点着色器等阶段提供处理的数据。是渲染管线的数据主要来源。送入到渲染管线的数据包括顶点坐标、纹理坐标、顶点法线和顶点颜色等顶点属性。为了让OpenGL明白顶点数据构成的是什么图元,我们需要在绘制指令中传递相对应的图元信息。常见的图元包括:点(GL_POINTS)、线(GL_LINES)、线条(GL_LINE_STRIP)、三角面(GL_TRIANGLES)。

1.2.2、顶点着色器

顶点着色器主要功能是进行坐标变换。将输入的局部坐标变换到世界坐标、观察坐标和裁剪坐标。虽然我们也会在顶点着色器进行光照计算(称作高洛德着色),然后经过光栅化插值得到各个片段的颜色,但由于这种方法得到的光照比较不自然,所以一般在片段着色器进行光照计算。

1.2.3、曲面细分着色器

曲面细分是利用镶嵌化处理技术对三角面进行细分,以此来增加物体表面的三角面的数量,是渲染管线一个可选的阶段。它由外壳着色器(Hull Shader)、镶嵌器(Tessellator)和域着色器(Domain Shader)构成,其中外壳着色器和域着色器是可编程的,而镶嵌器是有硬件管理的。我们可以借助曲面细分的技术实现细节层次(Level-of-Detail)的机制,使得离摄像机越近的物体具有更加丰富的细节,而远离摄像机的物体具有较少的细节。

1.2.4、几何着色器

几何着色器也是渲染管线一个可选的阶段。我们知道,顶点着色器的输入是 单个顶点(以及属性), 输出的是经过变换后的顶点。与顶点着色器不同,几何着色器的输 入是完整的图元(比如,点),输出可以是一个或多个其他的图元(比如,三角面),或者不输 出任何的图元。几何着色器的拿手好戏就是将输入的点或线扩展成多边形。下图展示了几 何着色器如何将点扩展成多边形。

1.2.5、图元组装

图元组装将输入的顶点组装成指定的图元。图元组装阶段会进行裁剪和背面剔 除相关的优化,以减少进入光栅化的图元的数量,加速渲染过程。在光栅化之前,还会进 行屏幕映射的操作:透视除法和视口变换。 关于透视除法和视口变换到底属于流水线的那个阶段并没有一个权威的说法,某些资料将 这两个操作归入到图元组装阶段,某些资料将它归入到光栅化过程,但对我们理解整个渲 染管线并没有太大的影响,我们只需要知道在光栅化前需要进行屏幕映射就可以了,所以 我们这里将屏幕映射放到了图元组装过程。这两个操作主要是硬件实现,不同厂商会有不 同的设计。

1.2.6、光栅化

经过图元组装以及屏幕映射阶段后,我们将物体坐标变换到了窗口坐标。光栅化 是个离散化的过程,将3D连续的物体转化为离散屏幕像素点的过程。包括三角形组装和三 角形遍历两个阶段。光栅化会确定图元所覆盖的片段,利用顶点属性插值得到片段的属性 信息,然后送到片段着色器进行颜色计算,我们这里需要注意到片段是像素的候选者,只 有通过后续的测试,片段才会成为最终显示的像素点。

1.2.7、片段着色器

片段着色器在DirectX中也成为像素着色器(Pixel Shader)。片段着色器用来 决定屏幕上像素的最终颜色。在这个阶段会进行光照计算以及阴影处理,是渲染管线高级 效果产生的地方。

1.2.8测试混合阶段

管线的最后一个阶段是测试混合阶段。测试包括裁切测试、Alpha测试、模 板测试和深度测试。没有经过测试的片段会被丢弃,不需要进行混合阶段;经过测试的片 段会进入混合阶段。Alpha混合可以根据片段的alpha值进行混合,用来产生半透明的效 果。Alpha表示的是物体的不透明度,因此alpha=1表示完全不透明,alpha=0表示完全透 明。测试混合阶段虽然不是可编程阶段,但是我们可以通过OpenGL或DirectX提供的接口 进行配置,定制混合和测试的方式。

值得注意的是,半透明物体的绘制需要遵循画家算法(painter Algorithm)由远及近进行绘 制,因为半透明的混合跟物体的顺序有严格的对应关系。从下面两张图我们可以看到,先 绘制红色还是先绘制绿色对最终颜色的有这很大的影响。所以,绘制半透明物体之前,我 们需要按照距离远近对场景中的物体进行严格排序,然而这是一个非常棘手的问题。比 如,我们如何排序下面几个三角形呢?所以当进行半透明物体渲染时,一般会使用顺序无 关的半透明渲染技术(Order-independent transparency,OIT)

渲染管线并非严格这样划分,不同的教材会有不同的划分方法。《Real Time Rendering》一书将渲染管线划分为以下四个阶段:应用程序阶段(Application)、几何处 理阶段(Geometry Processing)、光栅化(Rasterization)和像素处理阶段(Pixel Processing)。应用阶段通常是在CPU端进行处理,包括碰撞检测、动画物理模拟以及视椎 体剔除等任务,这个阶段会将数据送到渲染管线中;几何处理阶段主要执行顶点着色器、 投影变换、裁剪和屏幕映射的功能;光栅化阶段和我们上面讨论的差不多,都是将图元离 散化片段的过程;像素处理阶段包括像素着色和混合的功能。我们可以发现,虽然管线的 划分粒度不一样,但是每个阶段的具体功能其实是差不多的,原理也是一样的,并没有太 大的差异。

2、光线追踪渲染管线(Raytracing pipeline)

2.1、 光线追踪概述

2.1.1、 光线追踪是什么

与传统的扫描线或光栅化渲染方式不同,光线追踪(Ray tracing)是三维计算机图形学中的特殊渲染算法,追踪从摄像机发出的光线而不是光源发出的光线,通过这样一项技术生成编排好的场景的数学模型显现出来。

与传统方法的扫描线技术相比,这种方法有更好的光学效果,例如对于反射与折射有更准确的模拟效果,并且效率非常高,所以当追求高质量的效果时经常使用这种方法。

在物理学中,光线追迹可以用来计算光束在介质中传播的情况。在介质中传播时,光束可能会被介质吸收,改变传播方向或者射出介质表面等。我们通过计算理想化的窄光束(光线)通过介质中的情形来解决这种复杂的情况。

在实际应用中,可以将各种电磁波或者微小粒子看成理想化的窄波束(即光线),基于这种假设,人们利用光线追迹来计算光线在介质中传播的情况。光线追迹方法首先计算一条光线在被介质吸收,或者改变方向前,光线在介质中传播的距离,方向以及到达的新位置,然后从这个新的位置产生出一条新的光线,使用同样的处理方法,最终计算出一个完整的光线在介质中传播的路径。

2.1.2、 光线追踪的特点

运用光线追踪技术,有以下渲染特性:

  • 更精确的反射、折射和透射。
  • 更准确的阴影。包括自阴影、软阴影、区域阴影、多光源阴影等。
  • 更精准的全局光照。
  • 更真实的环境光遮蔽(AO)。

2.1.3、 光线追踪的历史

光线追踪渲染技术从自然界中的光线简化、光线投射算法、光线追踪算法一步步演变而来。

  1. 光线投射算法(1968年)

由Arthur Appel提出用于渲染的光线投射算法。光线投射的基础就是从眼睛投射光线到物体上的每个点,查找阻挡光线的最近物体,也就是将图像当作一个屏风,每个点就是屏风上的一个正方形。

根据材料的特性以及场景中的光线效果,这个算法可以确定物体的浓淡效果。其中一个简单假设就是如果表面面向光线,那么这个表面就会被照亮而不会处于阴影中。

光线投射超出扫描线渲染的一个重要优点是它能够很容易地处理非平面的表面以及实体,如圆锥和球体等。如果一个数学表面与光线相交,那么就可以用光线投射进行渲染。复杂的物体可以用实体造型技术构建,并且可以很容易地进行渲染。

  1. 光线追踪算法(1979年)

最先由Turner Whitted于 1979 年做出的突破性尝试。以前的算法从眼睛到场景投射光线,但是并不追踪这些光线。而光线追踪算法则追踪这些光线,并且每次与物体表面相交时,计算一次所有光影的贡献量。

  1. 光线追踪API及硬件集成(2018年)

在早些年,NV就联合Microsoft共同打造基于硬件的新一代光线追踪渲染API及硬件。在2018年,他们共同发布了RTX(Ray tracing X)标准。Direct X 12支持了RTX,而NV的RTX系列显卡支持了RTX技术,从而宣告光线追踪实时化的到来。

  1. UE集成光线追踪(2019年)

UE于2019年4月发布了4.22版本,该版本最耀眼的新特性无疑是支持了光线追踪技术。这将助力广大启用UE的个人或团队更加有效地渲染出照片级的画面。

2.2、光线追踪的原理

2.2.1、 光线追踪的物理原理

在几何光学中,可以忽略光线的波动性而直接简化成直线,从而研究光线的物理特性。同样地,在计算机图形学,也可以利用这一特点,以简化光照着色过程。

此外,人类的眼睛接收到的光照信息是有限的像素,大多数人的眼睛在5亿像素左右。人类接收到的图像信息可以分拆成5亿个像素,也就是说,可以分拆成5亿条非常微小的光线,以相反的方式去逆向追踪这些光线,就可以检测出这些光线对应的场景物体的信息(位置、朝向、表明材质、光照颜色和亮度等等)。

光线追踪技术就是利用以上的物理原理衍生出来。将眼睛抽象成摄像机,视网膜抽象成显示屏幕,5亿个像素简化成屏幕像素,从摄像机位置与屏幕的每个像素连成一条射线,去追踪这些射线与场景物体交点的光照信息。

当然,实际的光线追踪算法会更加复杂,下一小节会详细描述。

 

2.2.2、 光线追踪算法

以视点为起点,向场景发射N条光线,然后根据碰撞点的材质进行BXDF、BRDF的运算,然后再进行漫反射、镜面反射或者折射,如此递归循环直到光线逃离场景或者到达最大反射次数,最后对N条光线进行蒙特卡洛积分即可获得结果。

 

2.2.3、 RTX和DXR

2.2.3.1、 RTX(NV)

NV作为世界级的图形学界的探索先锋队,在光线追踪方面有着深入的研发,最终抽象成技术标准RTX平台。

随着DirectX 12的DXR和Vulkan的支持,使得支持硬件级的光线追踪技术渐渐普及。NV最先在Turing架构的GPU支持了RTX技术:

由上图可见,最上层是用户层(MDL和USD),包含了深度学习和普通应用开发;中间层是图形API层,支持RTX的有OptiX、DXR、Vulkan,OpenGL并不支持RTX;最底层就是RTX平台,它又包含了4个部分:传统的光栅化器、光线追踪(RT Core)、CUDA计算器、AI核心。

当然,除了Turing架构的GPU,还有PASCAL、VOLTA、TURING RTX等架构的众多款GPU支持RTX技术。(下图)

下图是若干款支持RTX技术的GPU运行同一个Demo(Battlefield)的性能对比:

此外,对于光线追踪,每种光线追踪的特性都会有不同的负载:

上图涉及的BVH(Bounding volume hierarchy)是层次包围盒,是一种加速场景物体查找的算法和结构体。

对于开发者,需要根据质量等级,做好各类指标预选项,以便程序能够良好地运行在各个画质级别的设备中。

2.2.3.2、 DXR(Microsoft)

在DX12的全新图形API中,加入了可编程的光线追踪渲染管线(上图),简称DXR。和传统光栅化管线一样,光线追踪的管线有固定的逻辑,也有可编程的部分。新管线中新增了5种着色器(Shader),分别是:

  • Ray Generation:用于生成射线。在此shader中可以调用TraceRay()递归追踪光线。
  • IntersectionAny Hit:当TraceRay()内检测到光线与物体相交时,会调用此shader,以便使用者检测此相交的物体是否特殊的图元(球体、细分表面或其它图元类型)。
  • Closest HitMiss:当TraceRay()遍历完整个场景后,会根据光线相交与否调用这两个Shader。Cloesit Hit可以执行像素着色处理,如材质、纹理查找、光照计算等。Cloesit Hit和Miss都可以继续递归调用TraceRay()。

光线追踪渲染管线中,还涉及到加速结构(Acceleration Structure)。它的作用是保存场景的所有几何物体信息,在GPU内提供物体遍历、相交测试、光线构造等等的极限加速算法,使得光线追踪达到实时渲染级别。它可以在应用程序通过BuildRaytracingAccelerationStructure()接口构建。

如上图,对于场景中的每个几何体,在GPU内部都存在两个级别的加速结构。底层加速结构(Bottom-Level AS)从输入的图元信息构建而成,如三角形、四边形。顶层加速结构(Top-Level AS)从底层加速结构创建而来,相当于是底层加速结构的实例,保存了底层结构的变换矩阵和shader偏移。

Shader映射表(Shader Table)描述了shader与场景的哪个物体关联,也包含了shader中涉及的所有资源(纹理、buffer、常量等)。

在GPU底层,Shader映射表是一个等尺寸的记录体(record),每个记录体关联着带着一组资源的shader(或相交组(Hit group))。通常每个几何体存在一个记录体。

由上图可见,每个记录体由shader编号起始,随后存着CBV、UAV、常量、描述表等shader资源。

这种双层架构的好处是将资源和实例化分离,加速实例创建和初始化,降低带宽和显存占用。

光线步进渲染管线(RayMatching pipeline)

1、光线步概述

RayMarching是一个比较好玩的技术,不需要什么顶点数据纹理数据就能画出很多东西。如果你对raymarching的概念还不是很清楚可以去看看下面的文章:Ray Marching and Signed Distance Functions​jamie-wong.com

Ray Marching and Signed Distance Functions​jamie-wong.com

从观察点发射一条射线, 顺着这条射线每次延伸一段固定距离, 判断是否碰撞到物体. 通常延伸的步数是有限的, 并且延伸的单步距离太大会导致穿透很薄的物体. 那么有了射线, 我们如何去跟物体做碰撞检测呢? 一般来说我们会使用SDF(Signed Distance Function)。

2、光线步流程

2.1、构建距离场(Signed Distance Field)

构建距离场的方法有很多,我们可以用公式或者用3Dtexture都可以,或者用2Dtexture的序列图3Dtexture拟合。

2.1.1、SDF公式

我们假设空间中有一球体,同时空间中还存在另一点A,当点A在球体内部时,则A点到球心的距离小于球的半径,即length(A) - r < 0;当点A在球体表面上时,则A点到球心的距离等于球的半径,即length(A) - r = 0; 当点A远离球体表面时,则A点到球心的距离大于球的半径,即length(A) - r > 0;上述的表述可用以下公式表示(假设球体的半径为1):

float sphereSDF(float3 p) {    

return length(p) - 1.0;

}

上式是球体的SDF公式,更多的SDF公式可查阅SDF距离函数

2.1.2、暴力计算法

假设我们定义保存距离场的3Dtexture大小时32*32*32,暴力建场的方法,就是直接迭代32*32*32个点,然后在每个点上向周围发射一大堆光线,然后保存交到的最小距离,作为距离场在这个点上的值。

值得一提的几个细节是,一般距离场的范围是比物体的AABB大一圈,我这里取的是扩大40%大小。

然后,在物体内部的点,距离存的是负值,然而不是所有曲面都是闭合的,有的时候我们没法区分内和外,虚幻用的是一个heuristic,如果50%以上的光线和三角形的正面相交,那么我们认为这个点在物体外面,如果50%以上的光线与三角形的背面相交,我们认为这个点在曲面内部,再借用一张虚幻的图:

2.1.3、JumpFlood计算法

JumpFlood类似做blur的方法,只是第一步走半分辨的步长,采样周围8个点,如果这8个点中包含Seed,那么比较该点的坐标到当前像素坐标的距离与当前像素所存储的坐标到当前像素坐标的距离场,将更近的坐标存到当前像素上,具体做clamp或warp可视需求定。使用pingpong的办法切换RT,将上一步结果供下个pass采样,每个pass步长减半,直到执行完步长一像素的pass就能得到Voronoi的结果,拿Voronoi中的坐标和当前像素求距离就能拿到距离场贴图。详细参考JumpFloodAlgorithm生成Voronoi和距离场贴图

2.1.4、NV的DeepSDF

       基于神经网络 SDF(有向距离场)——计算机图形学中,常见的一种隐式曲面表示方法。详细参考英伟达首次实现SDF实时3D渲染,还是细节超清晰的那种

2、RayMarching算法
通过上述SDF的简单分析后我们已经有了建立各种基础形状的SDF函数,那么我们如何渲染他们呢?
RayMarching要介绍它就需要先介绍下RayTracing(光线追踪),从字面上看这类型的技术与光线相关,在现实世界中光线的行进方向从光源开始,在经过一系列的反射,散射或折射的过程后进入眼睛。但是在计算机世界中,如果要从光源位置开始计算光线经过的一系列路径,最后进入人眼的全过程,会变得异常复杂,因为所有行进的光线不会全部进入人眼,这往往增加了巨大的工作量,因为我们最终想要的仅仅是被光源影响后的直接或间接光照后的结果图像,那么为了避免无谓的运算,通过从眼睛或相机发出射线来反方向追踪光线就变成了最直接的处理方式。


上图是来自维基百科RayTracing的图示,与RayTracing一样,我们为相机选择一个位置,在相机前放置一个网格,通过网格中的每个点发送来自相机的射线,每个网格点对应于输出图像中的一个像素。在光线行进中,整个场景是根据SDF函数定义的。为了找到视线和场景之间的交点,我们从摄影机开始,沿着视线一点一点地移动。在每个步骤中,我们会获取点与物体中心的距离,直到获取到的点与物体中心的距离小于物体表面到物体中心的距离时,代表此时的点已经在物体的内部了。反之就将沿着射线不断增加最大数量。

 


网站公告

今日签到

点亮在社区的每一天
去签到