您的位置:首页 > 理论基础

计算机图形学 学习笔记(六):消隐算法:Z-buffer,区间扫描线,Warnock,光栅图形学小结

2017-07-12 14:05 2446 查看
接上文 计算机图形学 学习笔记(五):多边形裁剪,文字裁剪

光栅图形学算法

4.1 消隐算法简介和分类

消隐

当我们观察空间任何一个不透明的物体时,只能看到该物体朝向我们的那些表面,其余的表面由于被物体所遮挡我们看不到。

如果把可见的和不可见的线都画出来,对视觉会造成多义性。





要消除这种二义性,就必须在绘制时消除被遮挡的不可见的线或面,习惯上称作消除隐藏线和隐藏面,简称为消隐。

要绘制出意义明确地、富有真实感的立体图形,首先必须消去形体中不可见的部分,而只在图形中表现可见部分。



消隐包括消除“隐藏线”和“隐藏面”两个问题。

消隐不仅与消隐对象有关,还与观察者的位置有关。如下图所示,E1位置只能观察到AB,E2位置只能观察到AD。



消隐的分类

一、按消隐对象分类

线消隐:消隐对象是物体上的边,消除的是物体上不可见的边

面消隐:消隐对象是物体上的面,消除的是物体上不可见的面。(通常做真实感图形消隐时用面消隐)

二、按消隐空间分类

物体空间的消隐算法:以物体中的物体为处理单元。假设场景中有 k 个物体,将其中一个物体与其余的 k-1 个物体逐一比价,仅显示它可见表面以达到消隐的目的。(此类算法通常用于线框图的消隐)

在物体空间里典型的消隐算法有两个:Roberts 算法和光线投射法。

Roberts 算法数学处理严谨,计算量大。算法要求所有被显示的物体都是凸的,对于凹体要先分割成多个凸体的组合。

Roberts 算法基本步骤:(1)逐个的独立考虑每个物体自身,找出为其自身所遮挡的边和面(自消隐);(2)将每一物体上留下的边再与其他物体逐个的进行比较,以确定是完全可见还是部分或全部遮挡(两两物体消隐);(3)确定由于物体之间的相互贯穿等原因,是否要形成新的显示边等,从而使被显示的各个物体更加接近现实

光线投射是求光线与场景的交点,该光线就是所谓的视线(如视点与像素连成的线)。一条视线与场景中的物体可能有许多交点,求出这些交点后需要排序,在前面的才能被看到。人的眼睛一目了然,但计算机需要做大量的运算。

图像空间的消隐算法:以屏幕窗口内的每个像素作为处理单元。确定在每一个像素处,场景中的 k 个物体哪一个距离观察到最近,从而用它的颜色来显示该像素。(这类算法是消隐算法的主流,因为物体空间的消隐算法计算量太大)

而在图像空间的消隐算法中,我们将介绍 Z-buffer(Z 缓冲区) 消隐算法, 区间扫描线算法, Warnock 消隐算法。

4.2 Z-buffer(Z 缓冲区) 消隐算法

经典的Z-buffee算法定义

Z 缓冲区 算法也叫做深度缓冲器算法,属于图像空间消隐算法。

该算法有帧缓冲器和深度缓冲器。对应两个数组

intensity( x , y ) :属性数组(帧缓冲器);存储图像空间中每个可见像素的颜色值或亮度

depth( x , y ):深度数组(z-buffer):存放图像空间每个可见像素的 z 坐标(z坐标即像素的深度值)

例子:





经典的Z-buffer算法思想

先将 Z 缓冲器中各单元的初始置设置为最小值。当要改变某个像素的颜色值时,首先检查当前多边形的深度值是否大于该像素原来的深度值(保存在该像素所对应的Z 缓冲器的单元中)。如果大于原来的 Z 值,说明当前多边形更靠近观察点,用它的颜色替换像素原来的颜色。

伪代码描述:

Z-Buffer 算法()
{
帧缓存全部置为背景色
深度缓存全部置为最小的Z值
for(每一个多边形)
{
扫描转换该多边形
for(该多边形在该像素的深度值Z(x,y))
{
计算该多边形在该像素的深度值Z(x,y)
if( z(x,y) > z缓存在(x,y)的值 )
{
把z(x,y)存入 z缓存中(x,y)处
把多边形在(x,y)处的颜色值存入帧缓存的(x,y)处
}
}
}
}


经典的Z-buffer算法优点

算法比较简单,也很直观

在像素级上以近物取代远物。与物体在屏幕上出现的顺序是无关紧要的,有利于硬件实现。

经典的Z-buffer算法缺点

占用空间大(因为它开了一个和帧缓冲区一样大的空间,深度缓冲区)

没有利用图形的相关性与连续性,这是 z-buffer 算法的严重缺陷

我们可以从 占用空间大入手,改进 z-buffer 算法。

只用一个深度缓存变量 zb 的改进算法

一般认为, z-buffer 算法需要开一个与图像大小相等的缓存数组 ZB,实际上,可以改进算法,只用一个深度缓存变量 zb。

伪代码描述:

只用一个深度缓存变量改进的 Z-Buffer 算法()
{
帧缓存全部置为背景色
for( 屏幕上的每个像素(i,j) )
{
深度缓存变量zb 置为最小值MinValue
for(多面体上的每个多边形Pk)
{
if(像素点(i,j)在Pk的投影多边形之内 )
{
计算Pk在(i,j)处的深度值depth
if(depth > zb)
{
zb=depth;
indexp=k; (记录多边形的序号)
}
}
}
if(zb!=MinValue)
计算多边形P(indexp)在交点(i,j)处的光照颜色并显示
}
}


这个算法仍然还是有关键的问题:判断像素点(i,j) 在否在Pk的投影多边形之内,不是一件容易的事情,节省了空间但牺牲了时间。

点与多边形的包含性检测:

(1)射线法





用射线法判断一个点是否在多边形内有弊端:

计算量大(因为要求交点)

不稳定(计算机图形中的射线,不是完全的直线,是像素无限逼近产生的“直线”)

(改进这个算法,得到弧长法)

(2)弧长法



弧长的代数和为0,点在多边形外部

弧长的代数和为2π,点在多边形内部

弧长的代数和为2π,点在多边形边上

弧长法比射线法稳定的原因是:假如算出来后代数和不是0,而是0.2或0.1,那么基本上可以断定这个点在外部,可以认为是有计算误差引起的,实际上是0。

虽然算弧长比算射线的交点效率高,但是这个算法本身的效率也不高,因为算弧长也并不容易。因此又派生出一个新的方法:以顶点符号为基础的弧长累加方法。

(3)以顶点符号为基础的弧长累加方法





同一个象限认为是 0,跨过一个象限是 π/2,跨过二个象限是 π。这样当要计算代数和的时候,就不用去投影了,只要根据点所在的象限一下子就能判断出是多少度,这样几乎没有什么计算量,只有一些简单的判断,效率非常高。

Z-Buffer 算法是非常经典和重要的,在图形加速卡和固件里都有。只用一个深度缓存变量改进的 Z-Buffer 算法虽然减少了空间,但仍然没考虑相关性和连贯性。

4.3 区间扫描线算法

前面介绍了经典的 Z-buffer 算法,思想是开一个和帧缓存一样大小的存储空间,利用空间上的牺牲换取算法上的简介。

还介绍了只开一个缓存变量的 Z-buffer算法,是把问题转换成判别点是否在多边形内,通过把空间多边形投影到屏幕上,判别该像素是否在多边形内。

下面介绍区间扫描线算法。这个算法放弃了 z-buffer 的思想,是一个新的算法,这个算法被认为是消隐算法中效率最快之一。

因为不管是经典的 z-buffer 算法,或者是改进后的 z-buffer 算法,都是在像素级上处理问题,要进行消隐的时候,每个像素都要进行计算判别,甚至一个像素要进行多次(一个像素可能会被多个多边形覆盖)

算法思想



扫描线的交点把这条扫描线分成了若干个区间,每个区间上必然是同样一种颜色。对于有重合的区间,如a6a7这个区间,要么显示F2的绿色,要么显示F3的蓝色,不会出现颜色的跳跃。

如果把扫描线和多边形的这些交点都求出来,对每个区间,只要判断一个像素要画什么颜色,那么整个区间的颜色就解决了。这就是区间扫描线的主要思想。

算法的优点:将像素计算改为逐段计算,效率大大提高。

如何实现整个算法?



首先要有投影多边形,然后求交点,然后交点再进行排序,排序的结果就分成了一个个区间。

如何确定小区间的颜色?



(1)小区间上没有任何多边形,如 [ a4,a5 ],用背景色显示

(2)小区间只有一个多边形,如 [ a1,a2 ],显示该多边形的颜色(F1是红色)

(3)小区间上存在两个或两个以上的多边形,比如 [ a6,a7],必须通过深度测试判断哪个多边形可见

这个算法存在几个问题

1、真的去求扫描线和多边形的交点么? (不用,利用增量算法简化求交)

2、每段区间上要求 z 值最大的面,这就存在一个问题。如何知道在这个区间上有哪些多边形是和这个区间相关的?(多边形扫描转换,边表,活动边表)

4.4 Warnock(区域子分割)消隐算法

Warnock 算法是图像空间中非常经典的一个算法,Warnock 算法的重要性不在于它的效率比别的算法高,而在于采用了分而治之的思想,利用了堆栈的数据结构。

思路:把物体投影到全屏幕窗口上,然后递归分割窗口,直到窗口内目标足够简单,可以显示为止。

一、什么样的情况下,画面足够简单可以立即显示?

窗口中仅包含一个多边形(直接显示)

窗口与一个多边形相交,且窗口内无其他多边形(可以经过多边形裁剪,直接显示)

窗口为一个多边形所包围(直接显示多边形的颜色)

窗口与一个多边形相分离(窗口显示背景色)

如何判别一个多边形和窗口时分离的?



如何判别一个多边形在窗口内?



如何判断一个多边形和窗口相交?



二、窗口有多个多边形投影面,如何显示?

把物体投影到全屏幕窗口上,然后递归分割窗口,直到窗口内目标足够简单,可以显示为止。

算法步骤:

如果窗口内没有物体则按背景色显示

若窗口内只有一个面,则把该面显示出来

如果窗口含有两个以上的面,则把窗口等分成四个子窗口。对每个小窗口再做上述同样的处理。这样反复地进行下去,把四个子窗口压在堆栈中,后进先出。例子,如下所示:



如果到某个时刻,窗口仅有像素那么大,而窗口内仍然有两个以上的面,如何处理?

这时不必再分割,只需要取窗口内最近的可见面的颜色或所有可见面的平均颜色作为该像素的值。

4.5 光栅图形学算法小结

1. 直线段的扫描转换算法(这是一维图形的显示基础)

DDA 算法 主要利用了直线的斜截式方程(y=kx+b),在这个算法里引入了增量的思想,结果把一个乘法和加法变成了一个加法

中点法 是采用的直线的一般式方程,也采用了增量的思想,比DDA算法的优点是采用了整数加法

Bresenham 算法 也采用了增量和整数算法,优点是这个算法还能用于其他的二次曲线

2. 多边形的扫描转换和区域填充(这是二维图形显示的基础)

如何把边界显示的多边形转换成由像素逐点描述的多边形。

多边形的扫描转换:X 扫描线算法(求交,排序,配对,填色)

多边形的扫描转换:改进的X扫描线算法(为了避免求交的大量运算,引入新的思想:图形的连贯性。手段就是利用增量算法和特殊的数据结构:活性边表,边表。大大简化计算量)

区域填充算法(四连通种子填充算法):利用图形的连贯性进行填充

3. 直线和多边形裁剪和文字裁剪

直线裁剪:Cohen-Suther land:核心思想是编码。把屏幕分成9个部分,用4位编码来描述这9个区域,通过4位编码的“与”、“或”运算来判断直线段是否在窗口内或外

直线裁剪:中点分割法:和 Cohen-Suther land 算法思想类似,多采用了一个二分逼近的思想

直线裁剪:Liang-Barsky:直线参数用参数方程,把被裁剪的直线段看成是一条有方向的线段,把窗口的四条边分为两类:入边和出边

多边形裁剪:Suther land-Hodgeman:将多边形作为一个整体,每次用窗口的一条边对要裁剪的多边形和中间结果多边形进行裁剪,体现一种分而治之的思想。

文字裁剪:串精度裁剪:当字符串中的所有字符都在裁剪窗口内时,就全部保留它,否则舍弃整个字符串。

文字裁剪:字符精度裁剪:在进行裁剪时,任何与窗口有重叠或落在窗口边界以外的字符都被裁剪掉。

文字裁剪:笔划/像素精度裁剪:将笔划分解成直线段对窗口做裁剪。需要判断字符串中各字符的哪些像素、笔划的哪一部分在窗口内。保留窗口内的部分,裁剪掉窗口外的部分。

4. 反走样

反走样主要有三种方法:提高分辨率,非加权区域采样,加权区域采样

提高分辨率:有物理上的限制

非加权区域采样算法:是在关键的直线段、关键的区域绘制的时候,将关键部位变得模糊,使颜色由过度区域,这样会产生一种好的视觉效果。根据物体的覆盖率计算像素的颜色。(覆盖率:某个像素区域被物体覆盖的比例)

加权区域采样算法:不但要考虑区域采样,而且要考虑不同区域的权重。将直线段看做是具有一定宽度的狭长矩形。当直线段与像素有相交时,根据相交区域与像素中心的距离来决定其对像素亮度的贡献(比重)。

5.消隐

Z-buffer 算法:以像素点为单位,处理多边形。引入深度二维数组

单个变量的 z-buffer 算法:减少了数组开销,但是要不断判断一个点是否在多边形内(射线法,代数弧长累加法,以顶点符号的弧长累加法)

区间扫描线算法:发现扫描线和多边形的交点把扫描线分成若干区间,每个区间只有一个多边形可以显示。利用这个特点可以把逐点处理变成逐段处理,提高了算法效率。

Warnock消隐算法:采用了分而治之的思想,利用了堆栈的数据结构。把物体投影到全屏幕窗口上,然后递归分割窗口,直到窗口内目标足够简单,可以显示为止。

光栅图形学核心思想:

增量思想:通过增量算法可以减少计算量

编码思想

符号判别->整数算法:尽可能的提高底层算法的效率,底层上提高效率才是真正解决问题。

图形连贯性:利用连贯性可以大大减少计算量

分而治之:把一个复杂对象进行分块,分到足够简单再进行处理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: