首页 理论教育虚拟现实应用开发实践:硬件层分析

虚拟现实应用开发实践:硬件层分析

【摘要】:硬件层在渲染流水线中最为复杂,也最为重要。图2-2固定渲染流水线的几个阶段图2-3可编程渲染流水线硬件层对比上面的两个图我们发现,在可编程渲染流水线中,固定渲染流水线中的顶点变换与片段纹理映射和着色被分离出来,作为可编程顶点处理器与可编程片段处理器。裁剪这部分不可见物体的过程称为视域体裁剪。视域体是一个3D的梯形,我们要在3D的空间里对顶点进行剔除,是一件非常难以实现的事,所以才将顶点变换到投影平面上。

硬件层在渲染流水线中最为复杂,也最为重要。前面已经提到,可编程渲染流水线与固定渲染流水线的区别在于是否对着色器进行编程。

首先我们先了解固定渲染流水线主要分为以下几个阶段:顶点变换→图元转配与光栅化→片段纹理映射和着色→光栅化操作,如图2-2所示。

下面,我们再看一下可编程渲染流水线硬件层的流程图(见图2-3)。

图2-2 固定渲染流水线的几个阶段

图2-3 可编程渲染流水线硬件层

对比上面的两个图我们发现,在可编程渲染流水线中,固定渲染流水线中的顶点变换与片段纹理映射和着色被分离出来,作为可编程顶点处理器与可编程片段处理器。而如前面所述,假如我们使用DirectX或者OpenGL自带着色程序,那么两条流水线其实是一样的[19]。所以,下面我们将对可编程渲染流水线进行讲解。

下面,我们主要从可编程顶点处理器、图元装配、光栅化插值、可编程片段处理器、光栅化操作来讲解硬件层的渲染流水线。

1)可编程顶点处理器(下面的流水线都是指硬件层部分)

顶点变换:在固定渲染流水线或者可编程渲染流水线中这都是第一个处理阶段。这个阶段对顶点进行了一系列的数学变换。包括了世界变换、取景变换、投影变换、视口变换。另外,贴图纹理坐标的产生、顶点照亮以及顶点颜色的决定,都在这个阶段进行。

在这里,世界变换、取景变换、视口变换在这里我不多加赘述,但是投影变换还是很想多说几句,投影和裁剪到底哪一个先进行。下面我们先介绍几个概念。

投影:把一个物体从n维变换到n-1维的过程称为投影。所以我们三维的世界转换到2D的屏幕上的过程也叫做投影。

视域体裁剪:在以摄像机为中心,由视线方向、视角和远近平面共同构成的一个梯形体,在梯形体内的物体可见,梯形体外的物体不可见。裁剪这部分不可见物体的过程称为视域体裁剪。

图2-4 视域体裁剪案例

在图2-4中,梯形体为三维空间中的一部分,超出梯形体部分的物体将会被剔除。而在视域体中的物体,最终会形成一幅图像,展示在近平面上。这里默认裁剪平面与投影片面重合。

投影矩阵:将3D世界里顶点变换到投影平面上的矩阵。即一个三维的顶点与这个矩阵相乘,能够将空间中的顶点变换到投影平面上(最终的顶点依然具有四个维度,第四个维度只是用来区分三维的向量是点还是普通的数学向量)。具体,大家可以参考http://blog.csdn.net/popy007/article/details/1797121#comments。

回归我们的问题,到底是投影先还是裁剪先呢?

实际上,当我们把空间中的点变换到投影平面的时候,假如一个点不在视域体的范围内,那么变换后的点的坐标不会在(-1,-1,-1)到(1,1,1)的范围内(OpenGL以这个为标准),说明这个点是被裁剪的顶点。而假如变换后的坐标在这个范围内,其(x,y)坐标就是对应的视口坐标系的坐标了。所以,实际上我们做投影变换的时候其实也同时为裁剪做了准备,也就是裁剪跟投影其实是同步的,只是裁剪的周期会比较长一点。

这里其实有个问题刚好被我跳过了,就是为什么不直接在3D的空间里面进行裁剪,要转换到投影屏幕上才进行裁剪。这里涉及实现的难度的问题。视域体是一个3D的梯形,我们要在3D的空间里对顶点进行剔除,是一件非常难以实现的事,所以才将顶点变换到投影平面上。

下面让我们看一下可编程顶点着色器的工作流程(见图2-5)。

图2-5 可编程顶点着色器的工作流程(www.chuimin.cn)

2)图元装配

图元:实际上就是点,线,面。

图元装配阶段的工作:根据伴随顶点序列的集合图元分类信息把顶点装配几何图元。产生一系列的三角形、线段和点(之前的流水线只是对顶点进行处理)。

挑选:光栅器根据多边形的朝前或朝后来丢弃一些多边形。多边形经过挑选后,进入光栅化插值。

3)光栅化插值

首先解释一下,为什么要用光栅化插值而不用光栅化。我学习渲染流水线主要是通过图2-5的流程图来学习的,而其他的作者将光栅化分为了光栅化插值与光栅化操作,所以我也继续沿用了他的方法。下面先介绍几个概念。

光栅化:一个决定哪些像素被几何图元覆盖的过程。光栅化的结果是像素位置的集合和片段位置的集合。

片段:是更新一个特定像素潜在需要的一个状态。

术语片段是因为光栅化会把每个几何图元,例如三角形,所覆盖的像素分解成像素大小的片段。一个片段有一个与之相关联的像素位置,深度值和经过插值的参数,例如颜色,第二(反射)颜色和一个或多个纹理坐标集。这些各种各样的经过插值的参数是来自变换过的顶点,这些顶点组成了某个用来生成片段的几何图元。如果一个片段通过了各种各样的光栅化测试,这个片段将被用于跟新帧缓存中的像素。

在这个阶段,光栅器还可以根据多边形的朝前或朝后来丢弃一些多边形(挑选Culling)。

实际裁减是一个较大的概念,为了减少需要绘制的顶点个数,而识别指定区域内或区域外的图形部分的算法都称为裁减。裁减算法主要包括:视域剔除(view frustum culling)、背面剔除(back-face culling)、遮挡剔除(occlusing culling)和视口裁减等。在上面提到的裁剪是指背面剔除。遮挡剔除在下一个光栅化操作阶段进行。

4)可编程片段处理器

可编程片段处理器需要许多和可编程顶点处理器一样的数学操作,但是它们还支持纹理操作,纹理操作使得处理器可以通过一组纹理坐标存取纹理图像,然后返回一个纹理图像过滤的采样(注意了,纹理在这个地方进行映射。假如没有编写着色器程序,也是在这里映射,只不过使用的是默认的着色器)。关于什么是纹理过滤,请参考http://blog.csdn.net/poem_qianmo/article/details/8567848。

前面有讲到,可编程与固定渲染流水线的区别在于是否对着色器进行编程。第一个可能进行编程的地方在可编程顶点处理器,第二个可能进行编程的在可编程片段处理器这里。

可编程片段处理器,因为纹理操作等都在这里进行,所以假如我们希望我们的游戏非常绚丽,特效非常丰富,这部分需要我们投下很大的精力。不过因为我们今天是解释渲染流水线,所以在这个地方我们不做太多的解释,其内部流程,如图2-6所示。

5)光栅化操作

在前面,光栅化与可编程片段处理器会提供给我们片段以及一些相关的数据,比如片段的ALPHA,其深度等(片段的概念见前面,忘了可以认为是没有存在屏幕上的像素)。在这一步我们将会对其进行各种测试,而假如它通过了所有的测试,片段将会显示在屏幕上(见图2-7)。

抖动显示:一种能够使用较少的颜色种类模拟较多颜色的显示模式。

图2-6 可编程片段处理器的内部流程

图2-7 片段测试的过程

在这个阶段,光栅器根据多边形的朝前或朝后来丢弃一些多边形(挑选Culling)。光栅操作阶段将根据许多测试来检查每个片段,这些测试包括剪切、Alpha、模板和深度等测试。这些测试设计了片段最后的颜色或深度,像素的位置和一些像素值(如像素的深度值和颜色值)。如果任何一项测试失败了,片段就会在这个阶段被丢弃,而更新像素的颜色值,虽然一个模板写入的操作也许会发生。通过了深度测试就可以用片段的深度值代替像素的深度值了。