3D数学 学习笔记(6)图形管道(渲染流水线)

发表于2018-01-03
评论0 2.3k浏览

3D渲染管线也称为渲染流水线,可以将其理解为一个流程,就是我们准备一些数据,让GPU对这些数据做一些处理,最后得出一张二维图像。图形渲染的流水线包括三个阶段:应用阶段、几何阶段、光栅化阶段。下面就来学习下渲染流水线的知识。


概念性三个阶段

概念性是指按照渲染流程进行的功能划分提出的。GPU流水线才是硬件真正用于实现这个概念的流水线。

  • 应用阶段(Application Stage):(CPU。输出渲染所需几何信息:渲染图元(点、线、三角面))
  • 几何阶段(Geometry Stage):(GPU。绘制几何相关图元。)
  • 光栅化阶段(Rasterizer Stage):(GPU。决定每个渲染图元哪些像素绘制在屏幕。)

应用阶段

即通过软件实现的阶段。最后输出渲染图元(点线面等)到几何阶段。 
划分几个阶段: 
1. 把数据加载到显存中:场景信息(相机位置、视锥体、场景物体、光照与雾化、z-缓冲。 
2. 设置模型渲染状态:材质(漫反射颜色、高光反射颜色)、纹理、shader等。 
3. 调用Draw Call:告诉GPU开始渲染,指向本次需要渲染的图元列表。

超标量体系结构(superscalar):多个并行处理器同时执行。

硬盘(Hard Disk Drive, HDD) -> 系统内存(Random Access Memory, RAM) -> 显存(Video Random Access Memory, VRAM)。 

Draw Call: 

几何阶段

决定绘制什么图元、怎么绘制、在哪绘制。最后输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等信息到光栅化阶段。 
划分几个阶段: 
1. 顶点着色器:顶点空间变化、逐顶点光照。顶点坐标从模型空间转换到其次裁剪空间,接着硬件做透视除法,得到归一化的设备坐标(Normalized Device Coordinates, NDC)。 
2. 曲面细分着色器:可选。细分图元。 
3. 几何着色器:可选。逐图元着色、产生图元。 
4. 投影:正交投影或透视投影。 
5. 裁剪:剔除不再屏幕的顶点,添加必要顶点。 
6. 屏幕映射:将图元坐标转到屏幕坐标。

光栅化阶段

将上一阶段传来的数据,产生屏幕上的像素,并渲染出最终图像。 
划分为几个阶段: 
1. 三角形设置:计算三角形表面数据、边界像素坐标等。 
2. 三角形遍历(扫面变换):找到哪些像素在三角形中,生成片元(包含了屏幕坐标、深度信息、顶点信息、法线、纹理坐标等)。 
3. 片元着色器(像素着色器):从顶点着色器输出的数据插值得到颜色值。通过顶点对应的纹理坐标,用纹理采样进行插值,可以得到覆盖片元的纹理坐标。 
4. 逐片元操作(输出合并阶段):决定片元的可见性(模版测试、深度测试),然后将通过测试的片元颜色值和已经在颜色缓冲区的颜色混合(合并)。

模版测试(Stencil Test) 

不透明物体可以开启混合操作: 

为了避免看到正在光栅化的图元,GPU会使用双重缓冲(Double Buffering)的策略。就是在渲染在背后执行(后置缓冲),渲染好了之后切换另一个缓冲(前置缓冲)内容,保证看到的图像是连续的。 


图形管道伪代码

// 设置场景的视图
setupTheCamera();
// 清除z-缓冲
clearZBuffer();
// 设置光源和雾化
setGlobalLightingAndFog();
// 得到场景视图中可见物体列表
potentiallyVisibleObjectList = highLevelVisibilityDetermination(scene);
// 渲染所有可见物体
for (all objects in potentiallyVisibleObjectList)
{
    // 包尾盒检测,跳过包围盒内不可见物体。执行低级VSD(visible surface determination)检测。
    if (!object.isBoundingVolumeVisible()) continue;
    // 提取或程序化生成几何体
    triMesh = object.getGeometry();
    // 裁切和渲染面
    for (each triangle in the geometry)
    {
        // 转换顶点到裁切空间,执行顶点级光照
        clipSpaceTriangle = transformAndLighting(triangle);
        // 裁切三角形(如果在边界)。
        clippedTriangle = clipToViewVolume(clipSpaceTriangle);
        if (clippedTriangle.isEmpty()) continue;
        // 投影三角形到屏幕空间
        screenSpaceTriangle = clippedTriangle.projectToScreenSpace();
        // 背面剔除
        if (screenSpaceTriangle.isBackFacing()) continue;
        // 光栅化每个三角形
        for (each pixel in the triangle)
        {
            // 跳过在屏幕后面的
            if (pixel is off−screen) continue;
            // 插值颜色,z-缓冲值和纹理映射坐标
            // 执行zbuffering
            if (!zbufferTest()) continue;
            // 执行透明度检测
            if (!alphaTest()) continue;
            // 写入帧缓存和z-缓冲
            writePixel(color, interpolatedZ);
        }
    }
}


代码和图形API在不同步骤下的关系:

数据流在图形管道的流程:

CPU、OpenGL/DirectX、显卡驱动和GPU之间的关系:

3D数学 学习笔记系列教程:

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引