接上文 计算机图形学 学习笔记(五):多边形裁剪,文字裁剪
当我们观察空间任何一个不透明的物体时,只能看到该物体朝向我们的那些表面,其余的表面由于被物体所遮挡我们看不到。
如果把可见的和不可见的线都画出来,对视觉会造成多义性。
要消除这种二义性,就必须在绘制时消除被遮挡的不可见的线或面,习惯上称作消除隐藏线和隐藏面,简称为消隐。
要绘制出意义明确地、富有真实感的立体图形,首先必须消去形体中不可见的部分,而只在图形中表现可见部分。
消隐包括消除“隐藏线”和“隐藏面”两个问题。
消隐不仅与消隐对象有关,还与观察者的位置有关。如下图所示,E1位置只能观察到AB,E2位置只能观察到AD。
一、按消隐对象分类
二、按消隐空间分类
物体空间的消隐算法:以物体中的物体为处理单元。假设场景中有 k 个物体,将其中一个物体与其余的 k-1 个物体逐一比价,仅显示它可见表面以达到消隐的目的。(此类算法通常用于线框图的消隐)
在物体空间里典型的消隐算法有两个:Roberts 算法和光线投射法。
Roberts 算法数学处理严谨,计算量大。算法要求所有被显示的物体都是凸的,对于凹体要先分割成多个凸体的组合。
Roberts 算法基本步骤:(1)逐个的独立考虑每个物体自身,找出为其自身所遮挡的边和面(自消隐);(2)将每一物体上留下的边再与其他物体逐个的进行比较,以确定是完全可见还是部分或全部遮挡(两两物体消隐);(3)确定由于物体之间的相互贯穿等原因,是否要形成新的显示边等,从而使被显示的各个物体更加接近现实
光线投射是求光线与场景的交点,该光线就是所谓的视线(如视点与像素连成的线)。一条视线与场景中的物体可能有许多交点,求出这些交点后需要排序,在前面的才能被看到。人的眼睛一目了然,但计算机需要做大量的运算。
图像空间的消隐算法:以屏幕窗口内的每个像素作为处理单元。确定在每一个像素处,场景中的 k 个物体哪一个距离观察到最近,从而用它的颜色来显示该像素。(这类算法是消隐算法的主流,因为物体空间的消隐算法计算量太大)
而在图像空间的消隐算法中,我们将介绍 Z-buffer(Z 缓冲区) 消隐算法, 区间扫描线算法, Warnock 消隐算法。
Z 缓冲区 算法也叫做深度缓冲器算法,属于图像空间消隐算法。
该算法有帧缓冲器和深度缓冲器。对应两个数组:
例子:
先将 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 算法需要开一个与图像大小相等的缓存数组 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,而是0.2或0.1,那么基本上可以断定这个点在外部,可以认为是有计算误差引起的,实际上是0。
虽然算弧长比算射线的交点效率高,但是这个算法本身的效率也不高,因为算弧长也并不容易。因此又派生出一个新的方法:以顶点符号为基础的弧长累加方法。
(3)以顶点符号为基础的弧长累加方法
同一个象限认为是 0,跨过一个象限是 π/2,跨过二个象限是 π。这样当要计算代数和的时候,就不用去投影了,只要根据点所在的象限一下子就能判断出是多少度,这样几乎没有什么计算量,只有一些简单的判断,效率非常高。
Z-Buffer 算法是非常经典和重要的,在图形加速卡和固件里都有。只用一个深度缓存变量改进的 Z-Buffer 算法虽然减少了空间,但仍然没考虑相关性和连贯性。
前面介绍了经典的 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 值最大的面,这就存在一个问题。如何知道在这个区间上有哪些多边形是和这个区间相关的?(多边形扫描转换,边表,活动边表)
Warnock 算法是图像空间中非常经典的一个算法,Warnock 算法的重要性不在于它的效率比别的算法高,而在于采用了分而治之的思想,利用了堆栈的数据结构。
思路:把物体投影到全屏幕窗口上,然后递归分割窗口,直到窗口内目标足够简单,可以显示为止。
一、什么样的情况下,画面足够简单可以立即显示?
如何判别一个多边形和窗口时分离的?
如何判别一个多边形在窗口内?
如何判断一个多边形和窗口相交?
二、窗口有多个多边形投影面,如何显示?
把物体投影到全屏幕窗口上,然后递归分割窗口,直到窗口内目标足够简单,可以显示为止。
算法步骤:
如果到某个时刻,窗口仅有像素那么大,而窗口内仍然有两个以上的面,如何处理?
这时不必再分割,只需要取窗口内最近的可见面的颜色或所有可见面的平均颜色作为该像素的值。
1. 直线段的扫描转换算法(这是一维图形的显示基础)
2. 多边形的扫描转换和区域填充(这是二维图形显示的基础)
如何把边界显示的多边形转换成由像素逐点描述的多边形。
3. 直线和多边形裁剪和文字裁剪
4. 反走样
反走样主要有三种方法:提高分辨率,非加权区域采样,加权区域采样
5.消隐
光栅图形学核心思想: