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

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

参考书籍:
《3D数学基础:图形与游戏开发》
《Unity Shader 入门精要》
浅墨_毛星云:【《Real-Time Rendering 3rd》 提炼总结】(二) 第二章 · 图形渲染管线 The Graphics Rendering Pipeline

3D数学 学习笔记(6) 图形管道(渲染流水线)_第1张图片


概念性三个阶段

概念性是指按照渲染流程进行的功能划分提出的。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)。

3D数学 学习笔记(6) 图形管道(渲染流水线)_第2张图片

Draw Call:

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

几何阶段

决定绘制什么图元、怎么绘制、在哪绘制。最后输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等信息到光栅化阶段。
划分几个阶段:

  1. 顶点着色器:顶点空间变化、逐顶点光照。顶点坐标从模型空间转换到其次裁剪空间,接着硬件做透视除法,得到归一化的设备坐标(Normalized Device Coordinates, NDC)。
  2. 曲面细分着色器:可选。细分图元。
  3. 几何着色器:可选。逐图元着色、产生图元。
  4. 投影:正交投影或透视投影。
  5. 裁剪:剔除不再屏幕的顶点,添加必要顶点。
  6. 屏幕映射:将图元坐标转到屏幕坐标。

光栅化阶段

将上一阶段传来的数据,产生屏幕上的像素,并渲染出最终图像。
划分为几个阶段:

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

模版测试(Stencil Test)

3D数学 学习笔记(6) 图形管道(渲染流水线)_第4张图片

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

3D数学 学习笔记(6) 图形管道(渲染流水线)_第5张图片

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

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


图形管道伪代码

// 设置场景的视图
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在不同步骤下的关系:

3D数学 学习笔记(6) 图形管道(渲染流水线)_第7张图片

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

3D数学 学习笔记(6) 图形管道(渲染流水线)_第8张图片

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

3D数学 学习笔记(6) 图形管道(渲染流水线)_第9张图片


你可能感兴趣的:(图形学,Unity,3D数学,学习笔记)