OpenGL ES手册翻译---3.栅格化(一)

栅格化

栅格化是一个把图元转换成二维图像的过程。图像的每个点都包含了颜色和深度这样的信息。因此,栅格化一个图元包含了两个部分。第一步是要确定图元在窗口坐标中所占的整数网格的方块。第二步,给每个方块分配颜色和深度值。处理的结果传递给GL的下一个阶段(对每个片段的操作),GL使用这些信息更新帧缓冲区中的相应合适的位置。图3.1是栅格化处理过程。碎片的颜色由片段着色器决定(见3.8节),片段着色器使用由栅格化操作(见3.3到3.6节)产生的varying变量来实现。最后的深度值由栅格化操作决定栅格化点,线或者多边形的结果都是通过片段着色器来规定的。

一个网格和z(深度)方向的参数以及varying数据被合称为一个碎片(fragment);这些参数被统称为关联数据。一个碎片基于整数网格坐标系,从左下角开始栅格化操作也有一个指定的碎片的中心,这个中心是从左下角开始的(1/2,1/2)偏移量的地方(也就是基于半整数坐标系)。

在GL中网格不要求完全是方形的。栅格化规则不会受网格实际长宽比的影响。显示非方形的网格会导致栅格化的点和线段呈现出来在一个方向上比另一个方向上胖一点。我们指定碎片是正方形的,这样可以简化抗锯齿和纹理操作。

几个因素会影响栅格化。点可能会有不同的直径,线段会有不同的宽度。多重采样必须被用在栅格化抗锯齿的图元中(见3.2节)。

图3.1 栅格化

3.1 不变性

考虑在窗口坐标系中,把图元p平移(x,y),得到p',其中x和y是整数。只要pp'不会被裁剪,那么每从p'产生的每个碎片f'和相对应的p产生的每个碎片f是相同的,只是f'f的中心偏移(x,y)

3.2 多重采样

多重采样是一种GL图元:点,线和三角形抗锯齿的机制。这个技术是在所有图元上的每个像素进行多次采样。每次像素更新后,颜色采样值会被解析成一个单一的,可以显示的颜色,抗锯齿操作在应用层面会被自动处理。因为每个采样点都包括颜色,深度和模板信息,颜色(包括纹理操作),深度和模板函数和单次采样模式下效果是一样的。

还有另外一个缓冲区,叫做多采样缓冲区,被添加在帧缓冲区上。像素的采样值,包括颜色,深度,模板值,都储存在这个缓冲中。当帧缓冲包含了一个多重采样缓冲,帧缓冲就没有深度和模板缓冲了,甚至多采样缓冲也不会存储深度和模板的值。但是,颜色缓冲和多采样缓冲是共存的。

多重采样抗锯齿对渲染三角形是非常有用的,因为它不需要用排序来消除隐藏的表面,它可以正确的处理相邻的三角形,对象的轮廓甚至是相交的三角形。

如果SAMPLE_BUFFERS的值是一,所有图元的栅格化就变了,而是指多重采样的栅格化。而图元的栅格化是指单采样的栅格化。将GetIntegerv函数的pname设置为SAMPLE_BUFFERS就可以查询到SAMPLE_BUFFERS的值。

一个像素碎片的内容在多重渲染期间有两种方式。第一种,每个碎片都包含一个SAMPLE位数的信息值。这个SAMPLE值是依赖实现的常量,可以通过把函数GetIntegervpname设置为SAMPLES来查询。

第二种,每个碎片都包含了SAMPLES的深度值,一些varying值,而不是在单采样渲染模式下的单一的深度值和一些varying变量。GL实现时可能会选择给多个采样值分配相同的varying变量。评估varying值的位置可以是在像素内的任何位置,包括碎片中心或者采样位置。在同一个位置的varying值不需要评估。每个像素碎片因此由网格坐标的整数x和y,SAMPLES的varying值,和一个最大SAMPLES的位数信息组成。

多重采样的栅格化在GL上下文创建后是不能打开或者关闭的。如果SAMPLE_BUFFER是一就是打开的,不是一,那么就是关闭的。

图元的多采样的栅格化很大程度上和单采样的栅格化是不同的。可以理解为在帧缓冲的每个像素有对应的SAMPLES位置相关联。这些位置不是一个区域或者面积,而是一个准确的位置,每个位置被称为一个采样点。和像素相关联的采样点的位置可能在正方形单元的里面或者外面,这个正方形单元是指像素的边界。不仅如此,采样点的相关位置可能和帧缓冲中的每个像素相同也有可能不同。

如果采样点位置和像素不同,那么采样点位置应该和窗口对齐,而不是屏幕。另外,渲染结果也是通过窗口位置指定的。在3.1节中描述的不变性要求对所有的多重采样栅格化是宽松的,因为采样位置可能是像素位置的一个函数。

查询像素的实际采样位置是不可能的。

3.3 点

点的大小是从着色器的内建变量gl_PointSize获取,受到依赖实现的点的大小范围的钳制。如果gl_PointSize小于等于0,将产生未定义的结果。点大小的范围由ALIASED_POINT_SIZE_RANGE决定,可以通过第六章的描述中那样查询到。支持的点的最大大小至少是1.

点的栅格化为每个帧缓冲的像素产生一个碎片,像素中心在中心点为(x_w,y_w)
的方形中,方形的边长和点的大小相同。

栅格化点产生的所有碎片分配给相同的关联数据,即对应于该点的顶点的数据。同时,gl_PointCoord片段着色器的输入定义了一个机遇每个碎片单元的坐标系空间(s,t),s变量是水平的从左到右0到1的点,t是垂直的从上到下为0到1的点。
下面的公式用来评估(s,t)的值:
s = \frac {1} {2} + \frac {x_f + \frac {1} {2} - x_w} {size}
t = \frac {1} {2} - \frac {y_f + \frac {1} {2} - y_w} {size}

size是点的大小,x_f,y_f是碎片的窗口坐标的整数值,x_w,y_w是该点顶点的精确的,无边界的窗口坐标。

3.3.1 点的多重栅格化

如果SAMPLE_BUFFERS是1,点的栅格化将使用下面的方式。点的栅格化对每个帧缓冲的像素生成一个碎片,这些像素有一个或者多个采样点,这些采样点交汇在一个以点(x_w,y_w)为中心的区域中。这个区域是一个正方形的,边长等于像素大小。和这个区域相交的对应的采样点的所有的覆盖位为1,其他的覆盖位为0。碎片的每个样本相关联的数据就是和点的栅格化相关联的数据。点大小的支持范围和没有多重采样的点是一样的。

3.4 线段

线段有线条,线回路,或者一系列的分开的线段。线的宽度可以通过调用函数

void LineWidth(float width);

来设置,一个合适的正数来控制栅格化线段的宽度。默认宽度是1.0。小于或者等于0.0的值会产生INVALID_VALUE错误。

3.4.1基本的线段栅格化

线段光栅化首先将线段描述为x-major或y-major。x-major线段在闭合区间[-1,1]内有斜率;所有其他线段都是y-major线段(斜率由线段的端点确定)。我们将只为x-major部分指定光栅化,除非y-major部分的改变不明显。

理想情况下,GL使用“菱形退出”规则来确定通过对线段进行栅格化而产生的那些碎片。对于中心位于窗口坐标x_fy_f的每个片段f,定义四个半平面相交的菱形区域:
R_f = \{(x,y) | |x-x_f| + |y-y_f| < 1/2.\}
本质上,一条线段从P_a点到P_b点结束产生了一些和R_f相交的碎片f,除非R_f包含了P_b,见图3.2。

图3.2

为了避免结束点在R_f的边界上的困难,我们在原则上会对结束点进行少量的扰动。P_a点和P_b点各自的窗口坐标为(x_a,y_a)(x_b,y_b)。扰动端点P_a'P_b'的坐标为(x_a,y_a) - (ε, ε^2)(x_b, y_b) − (ε, ε^2)。从P_aP_b的线段产生了这些片段f,就是线段从P_a'P_b'的线段和R_f相交后产生的,除非P_b'点是在R_f中, ε是一个在栅格化线段时,为了使线段产生相同的碎片而选择的非常小的量,而δ就是对任何一个ε都有0 < δ ≤ ε的替换量。

P_aP_b在碎片的中心是,碎片的特征化处理简化到使用Bresenham算法做一处修改即可:这种情形下的产生的线段叫做“半开放”,这意味着没有绘制最后的片段(对应于P_b)。这意味着当栅格化一系列的相互连接的,共享端点的线段时,将只会作用一次而不是两次(就像Bresenham算法那样)。

由于菱形退出规则的初始条件和最终条件可能难以实现,因此允许其他线段光栅化算法,遵循以下规则:

  1. 由这种该算法产生的碎片的坐标,不论是在x或者y窗口坐标系的由菱形退出规则产生的对应的碎片的坐标,两者的偏离不会超过多余一个单元。
  2. 算法产生的碎片的总数可能和菱形退出规则产生的碎片的数目可能不同但不会超过1.
  3. 对于x-major的线,在同一窗口坐标列中不能产生两个碎片(对于y-major的线,在同一行也不会有两个碎片出现)
  4. 如果两个线段共享同一个端点,两个线段不是x-major(全部是从左到右或者从右到左)就是y-major(全部是从下到上或者从上到下),线段栅格化不会产生重复的碎片,也不会有任何碎片被遗漏因为这样会打断连接着的线段的连续性。

下一步,我们必须说明每个栅格化的碎片相关联的数据如何获取。一个产生的碎片的中心的窗口坐标值为P_r = (x_d,y_d)以及P_a = (x_a, y_a),P_b = (x_b,y_b),那么就有
t = \frac {(P_r-P_a) \cdot (P_b-P_a)} {||P_b - P_a||^2} \qquad(3.1)
(注意在P_a点的t = 0,在P_b点的t = 1)。碎片的相关数据f的值,不论它是裁剪w坐标还是一个顶点着色器varying变量输出的元素,都可以被表示为
f = \frac {(1-t)f_a/w_a + tf_b/w_b} {(1-t)/w_a + t/w_b} \qquad(3.2)
f_a,f_b是线段的起始点和终点的相关的数据。w_a,w_b是线段的裁剪坐标系w中的起始点和终点。窗口z是深度值,必须使用线性插值:
f = (1-t)f_a + tf_b \qquad(3.3)

3.4.2 其他的线段的特征

我们仅仅描述了非抗锯齿的线段的栅格化。现在我们介绍线段栅格化化操作时一些通用的参数。

  • 宽线

实际的非抗锯齿线的宽度是把需要的宽度四舍五入到整数的值来决定的,然后把这个数牵拉到实现相关的最大的非抗锯齿线的宽度。如果指定的宽度取整的结果是0,那么这个值被认为是1。

除了非抗锯齿的线段的宽度为1的线段之外,都是通过在次要方向(对于x-主要线,次要方向是y,对于y-主要线,次要方向是x)做偏移或者在次要方向复制碎片来进行栅格化操作(如图3.3)。w表示取整的宽度(如果w=0时,w设为1)。如果线段在窗口坐标系中为(x_0,y_0),(x_1,y_1),栅格化后线段的端点是(x_0,y_0-(w-1)/2)x_1,y_1-(w-1)/2,这样产生的并不是一个单一的碎片,而是在每个x的位置(y-major是y)产生一个高为w(对于y-major的线段来说w是一行碎片的长度)的列。这列的位置最低的这个碎片,就是在修正过的坐标系中宽度为1的线段栅格化后产生的碎片。

图3.3

3.4.3 线的栅格化状态

GL状态要求线的栅格化由线宽为浮点数构成。线宽的初始值是1.0。

3.4.4 线的多重采样的栅格化

如果SAMPLE_BUFFERS的值是1,线的栅格化将会使用下面的算法。每个帧缓冲像素,有一个或者多个采样点,和中心在线段上的矩形相交,线的栅格化为每个这样的像素产生一个碎片(如图3.4)。两条边和指定的线段平行;每条边到线段的距离是线宽的一半:一条在线段的上方,一条在下方。另外两条边穿过线段的端点和指定线段的方向垂直。

图 3.4

和矩形相交的采样点的覆盖位为1,其他的覆盖位为0。顶点着色器varying变量的输出值和深度值通过置换等式3.1中对应的采样位置的值做插值计算得出,得出的结果再用来计算等式3.2的值。实现过程可能会在多于一个采样值上使用相同的varying变量的值。

并不是所有的宽度都需要支持线段的多重采样,但是宽度为1.0的线段必须要提供支持。正如点的宽度一样,GL实现时可能会查询多重采样线段宽度的等级的范围和数目。

推荐阅读更多精彩内容