图形学算法.docx
- 文档编号:9678523
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:21
- 大小:25.73KB
图形学算法.docx
《图形学算法.docx》由会员分享,可在线阅读,更多相关《图形学算法.docx(21页珍藏版)》请在冰豆网上搜索。
图形学算法
&5.简单多边形包含性检验的算法:
1.【准备】XnX0,yny0,m-1,i0
2.【排除必不相交情形】若下列条件有一个成立,则到4
1)xp 2)xp>=xi&&xp>=xi+1 3)xp 3.【计算交点】y=yi+(xp-xi)(yi+1-yi)/(xi+1-xi)分两种情形: 1)若y=yp,则P在多边形边界上,算法结束 2)若y 4.【结束判断】ii+1,若i *简述扫描线种子填充算法的工作步骤。 1,对种子所在的像素段进行填充 2,从右到左检查种子所在行的上一横行,将查到的像素段依次编号存入堆栈。 实际存入堆栈内的可以是每个像素段最右边像素的地址。 接着对种子所在行的下一横行同样处理。 3,若堆栈为空则算法结束,否则从堆栈顶部取出一个像素段。 因为按先进后出的顺序,所以将取出编号最大的像素段。 实际取出的是这个像素段最右边像素的地址。 就以这个像素为新的种子,返回到1. 写出Graham凸壳求解算法 1.(倾角排序)选出输入电集S中X坐标最小的点,若这样的点不唯一则再由其中选出y坐标最小的点,设为O.设想有一条从O向右的射线OX,对点集中其余每一点P,计算倾角POX,再按倾角排序,得到点序列Q1=O,Q2,Q3,…,Qn; 2.(准备扫描)vQ1; 3.(扫描)若NEXT(v)! =Q1,则循环执行下面的操作至NEXT(v)=Q1时止,此时点序列中剩下排成凸多边形的壳上顶点,算法结束。 若三个相继的点v,Next(v),Next(NEXT(v))形成一个左转,则vNEXT(v);否则先删除NEXT(v),再做vPRED(v); *写出Jarvis凸壳求解算法 1(准备)v0点集S中按x,y字典次序最小的点;d竖直向下的一个方向向量;点v0送入收集凸壳顶点的队列Q中;S1S–{u,v0};uv0 2(一步行进)v1Wrapping(u,d,S1);S1中各点相对于自u出发方向为d的射线,计算倾角,取倾角最小的点,若倾角相同,取与u距离最远的点,Wrapping(u,d,S1);返回下一个壳顶点。 3(准备下次)ifv1! =v0,v1送入Q中;S1=S–{u,v1};d从u到v1的一个方向向量;uv1;返回步骤2 4(结束)Q中存入所有的壳顶点,算法结束 *试写出判定一空间多边形与一凸多面体之间关系的算法 1.求出空间多边形和凸多面体的包围盒 分别做x,y,z三向测试,若包围盒检查(最大最小检验)有一个为真,则空间多边形和凸多面体分离。 算法输出分离并结束。 2.一次用凸多面体的每一表面多边形来求解与空间多边形各边的交点 若有交点,测试空间多边形的顶点是否均在凸多面体内,若均在体内,则为包含;若有交点,测试空间多边形的顶点有内有外,则相交;若无交点,测试空间多边形的顶点是否在凸多面体内,若均在体内,则为包含;若无交点,测试空间多边形的顶点均在凸多面体外,则为分离。 7.1线面比较法消除隐藏线 1)、消除隐藏线的线面比较法的最先一步就是利用外法线判断出所有可能的可见面。 2)、做xv方向和yv方向的范围检查。 3)、接着做zv方向的范围检查,即粗略的深度比较。 4)、进行精确的深度比较。 比较时,应计算线段两端点在可能遮挡它的平面上的投影点,比较相应的z坐标。 如果z1≤z1’并且z2≤z2’,则线段不会被遮挡;如果z1≥z1’并且z2≥z2’,则线段有可能被遮挡,还需要做进一步检查。 如果不是上述两种情况,必发生线段与表面相交。 这时,需要用交点,这些交点把线段的投影分成两部分。 判断得知线段确实被平面遮挡了哪些部分。 5)、做精确计算。 计算是求出线段的投影与遮挡平面上多边形表面边框投影的所有交点,这些交点把线段的投影分成可见和不可见的一些子线段。 对子线段的可见性,先取其上一点做点的包含性检验来进行判断。 7.3*深度排序(优先级)算法 1.把所有的多边形按顶点最大z坐标值进行排序。 2.解决当多边形在z坐标范围内发生交迭时出现的不明确问题 1)多边形的x坐标范围不相交迭,所以多边形不相交迭 2)多边形的y坐标范围不相交迭,所以多边形不相交迭 3)P完全在Q远离观察点的一侧 4)Q完全在P靠近观察点的一侧 5)多边形在z=0平面上的投影本身不相交迭。 实现这个检查可以检查一个多边形的每条边与另一个多边形的每条边是否相交。 若这步检查为真,则两个多边形也是互补遮挡的。 如果上述5步检查都为假,就假定P遮挡了Q,交换P和Q在排序表中的位置。 事实上,P不一定遮挡Q,所以交换后应该重新上述5步的检查。 3.按最大z坐标值逐次减小的次序,对每个多边形进行扫描转换。 || 7.5*深度缓存算法(Z-Buffer)的基本工作流程。 帧缓冲区置成背景色;Z-缓冲区置成最大z值; For(各个多边形) {扫描转换该多边形 {for(计算多边形所覆盖的每个像素(x,y)) {计算多边形在该象素的深度值Z(x,y); If(Z(x,y)小于Z缓冲区中的(x,y)处的值) {把Z(x,y)存入Z缓冲区中(x,y)处; 把多边形在(x,y)处的亮度值存入更新缓存区(x,y)处}}} *对Z缓冲算法进行改进,只用一个缓冲存储器实现消隐。 Z-Buffer() {帧缓存全置为背景色 For(屏幕上的每个像素(I,j)) {深度缓存变量zb置最大值MaxValue For(多面体上的每个多边形pk) {if(像素点(I,j)在pk的投影多边形之内) {计算pk在(I,j)处的深度值depth; If(depth小于zb) {zb=depth;indexp=k;}}} If(zb! =MaxValue)计算多边形Pindexp在交点(I,j)处的光照颜色并显示。 7.7*区域分割算法(图像空间算法) 区域分割算法要将投影平面分割成区域,考察区域内的图像。 如果容易决定在这个区域内某些多边形是可见的,那么就可以显示那些可见的多边形,完成对这一区域的显示任务;否则,就将区域再分割成小的区域,对小的区域递归地进行判断。 由于区域逐渐变小,每个区域内的多边形逐渐变小,最终总可以判定哪些多边形是可见的。 在下面四种情况下,可以很容易做出判定,而不必再做进一步分割: 1.所有的多边形与区域分离,所以在区域内只显示背景值。 2.只有一个相交的多边形,或者只有一个被包含的多边形。 这时可以对区域首先填充背景值,然后对多边形进行扫描转换。 在某些显示设备上,将整个帧存储器都初始化为背景值,可能更为方便。 对于相交的多边形,只是被包含的部分被扫描转换 3.只有一个包围的多边形,没有其他的多边形。 这时整个区域可填充这个包围多边形的像素值。 4.有多于一个包围的、相交的或被包围的多边形,且至少有一个包围的多边形。 这时可以检查是否能有一个包围的多边形位于所有其他多边形的前面 *八叉树算法: 1.遍历形体原来的八叉树T1,对遇到的每个黑节点,做下述步2. 2.对遇到黑节点对应的正立方体做相应变换,得到一般来说斜置的新位置。 若这位置已超出定义八叉树的充分大正立方体C之外,报告出错;否则执行下述的步3. 3.从要计算求出的目标树T2的根开始,检查2.中确定的处于新位置立方体与T2中节点对应的直立的正立方体是否相交,分以下三种情况进行处理: (1)不相交,说明正考查直立正方体未被占据,可保持为白节点,不做处理。 (2)直立的正立方体整个被占据,即它在变换后“斜置”立方体内,置对应节点为黑节点。 (3)在上述两条都不成立时,生成当前节点的八个子节点,对八个子节点对应的八个直立子立方体,依次再递归执行步3.如果最终这八个节点被标上同样特性,比方为黑节点,则应再删掉这八个子节点而把它们的共同父节点置为黑。 *修改Cohen-Sutherland直线裁剪算法,使其成为一个直线“开窗”算法,即指定一个窗口后,窗口内舍弃,窗口外保留。 解答: 根据题意,可知只需要对原Cohen-Sutherland算法中的两处进行修改即可满足要求。 第一处是判断C1和C2的逻辑乘结果不为0时,此时如果该条件满足,表示线段完全在窗外,原算法此处需要将原线段完全舍弃,这里就需要将原线段完全绘制出来即可。 第二处是最后要将原线段中窗口中可见部分绘制出来,此时原算法已经完成对原线段的裁剪,得出来原线段在窗口内的部分,这里只需要改成将原线段去掉窗口以内部分后的线段绘制出来即可。 根据以上分析,修改Cohen-Sutherland直线裁剪算法为直线“开窗”算法如下: doublexl,xr,yt,yb;(这里事先给出窗口的位置,四个数值是已知的),修改的部分用蓝色表示 voidCohen_Sutherland(doublex0,y0,x2,y2) {intc,c1,c2; doublex,y; //需要将原线段的端点保存起来,以备后面需要确定原线段去除窗口内部分时使用 doublex00=x0,y00=y0,x22=x2,y22=y2; makecode(x0,y0,c1);makecode(x2,y2,c2); while(c1! =0||c2! =0) { if(c1&c2! =0){ showline(x00,y00,x22,y22);//显示原线段,能走到这说明原线段都在窗口外 return; } c=c1;if(c==0)c=c2; if(c&1==1){y=y0+(y2-y0)*(x1-x0)/(x2-x0);x=x1;} elseif(c&2==2){y=y0+(y2-y0)*(xr-x0)/(x2-x0);x=xr;} elseif(c&4==4){x=x0+(x2-x0)*(yb-y0)/(y2-y0);y=yb;} elseif(c&8==8){x=x0+(x2-x0)*(yt-y0)/(y2-y0);y=yt;} if(c==c1) {x0=x;y0=y;makecode(x,y,c1);} else {x2=x;y2=y;makecode(x,y,c2);} } //因为原算法的线段分割保证了端点的顺序性,所以采用如下的方法可确定原线段在窗口外的部分 if(x00! =x0||y00! =y0)showline(x00,y00,x0,y0); if(x2! =x22||y2! =y22)showline(x2,y2,x22,y22); } 此算法已经编码实现并测试通过。 另一种方法是在分割线段的同时绘制窗口外的线段,该方法无需记录初始点坐标。 doublexl,xr,yt,yb;(这里事先给出窗口的位置,四个数值是已知的),修改的部分用蓝色表示 voidCohen_Sutherland(doublex0,y0,x2,y2) { intc,c1,c2; doublex,y; makecode(x0,y0,c1);makecode(x2,y2,c2); while(c1! =0||c2! =0) { if(c1&c2! =0){ //显示线段,此时绘制的是分割完后,完全在窗口外同侧的线段 showline(x0,y0,x2,y2); return; } c=c1;if(c==0)c=c2; if(c&1==1){y=y0+(y2-y0)*(x1-x0)/(x2-x0);x=x1;} elseif(c&2==2){y=y0+(y2-y0)*(xr-x0)/(x2-x0);x=xr;} elseif(c&4==4){x=x0+(x2-x0)*(yb-y0)/(y2-y0);y=yb;} elseif(c&8==8){x=x0+(x2-x0)*(yt-y0)/(y2-y0);y=yt;} if(c==c1) {showline(x0,y0,x,y);//绘制被分割抛弃的线段 x0=x;y0=y;makecode(x,y,c1);} else {showline(x,y,x2,y2);//绘制被分割抛弃的线段 x2=x;y2=y;makecode(x,y,c2);} } } DDA直线扫描转换算法 voidDDALine(intx1,inty1,intx2,inty2) {doubledx,dy,e,x,y; dx=x2-x1; dy=y2-y1; e=(fabs(dx)>fabs(dy))? fabs(dx): fabs(dy); dx/=e; dy/=e; x=x1; y=y1; for(inti=1;i<=e;i++) {SetPixel((int)(x+0.5),(int)(y+0.5)); x+=dx; y+=dy; } } 中点画线算法 voidMidpointLine(intx0,inty0,intx1,inty1) { inta,b,delta1,delta2,d,x,y; a=y0-y1; b=x1-x0; d=2*a+b; delta1=2*a; delta2=2*(a+b); x=x0; y=y0; SetPixel(x,y); while(x { if(d<0) { x++; y++; d+=delta2; } else {x++; d+=delta1; } SetPixel(x,y); } } Bresenham画线算法 voidBresenhamLine(intx1,inty1,intx2,inty2) { intx,y,dx,dy,p; x=x1; y=y1; dx=x2-x1; dy=y2-y1; p=2*dy-dx; for(;x<=x2;x++) {SetPixel(x,y); if(p>=0) {y++; p+=2*(dy-dx); } else { p+=2*dy; } } } 四连通内定义区域的填充算法 voidFloodfill(intx,inty,COLORREFoldvalue,COLORREFnewvalue) {if(GetPixel(x,y)==oldvalue) {SetPixel(x,y,newvalue);//赋值为新值 Floodfill(x,y-1,oldvalue,newvalue);//四向扩散 Floodfill(x,y+1,oldvalue,newvalue); Floodfill(x-1,y,oldvalue,newvalue); Floodfill(x+1,y,oldvalue,newvalue); } } 扫描线种子填充算法 1.对种子所在像素段进行填充。 2.从右至左检查种子所在行的上一横行,将查得的像素段依次编号存入堆栈。 接着对种子所在行的下一横行同样处理。 3.若堆栈为空则算法结束,否则从堆栈顶部取出一个像素段。 就以这个像素为新的种子,返回到1。 voidScanlineSeedfill(intx,inty,COLORREFboundaryvalue,COLORREFnewvalue) {intx0,xl,xr,y0,xid; intflag; Stacks; Pointp; s.push(Point(x,y));//种子像素入栈 while(! s.isempty()) { p=s.pop();//栈顶像素出栈 x=p.x; y=p.y; SetPixel(x,y,newvalue); x0=x+1; while(GetPixel(x0,y)! =boundaryvalue)//填充右方像素 {SetPixel(x0,y,newvalue); x0++; } xr=x0-1;//最右像素 x0=x-1; while(GetPixel(x0,y)! =boundaryvalue)//填充左方像素 { SetPixel(x0,y,newvalue); x0--; } xl=x0+1;//最左像素 //检查上一条扫描线和下一条扫描线, //若存在非边界且未填充的像素, //则选取代表各连续区间的种子像素入栈。 y0=y; for(inti=1;i>=-1;i-=2) { x0=xr; y=y0+i; while(x0>=xl) { flag=0; while((GetPixel(x0,y)! =boundaryvalue) &&(GetPixel(x0,y)! =newvalue) &&(x0>xl)) { if(flag==0) { flag=1; xid=x0; } x0--; } if(flag==1) { s.push(Point(xid,y)); flag=0;} while((GetPixel(x0,y)==boundaryvalue) ||(GetPixel(x0,y)==newvalue))x0--; }//while(x0>=xl) }//for(inti=1;i>=-1;i-=2) }//while(! s.isempty()) } 多边形的扫描转换算法 1.找出扫描线与多边形边界线的所有交点; 2.按x坐标增加顺序对交点排序; 3.在交点对之间进行填充。 Cohen算法 voidCohen_Sutherland(doublex0,y0,x2,y2) { intc,c1,c2; doublex,y; makecode(x0,y0,c1);makecode(x2,y2,c2); while(c1! =0||c2! =0) { if(c1&c2! =0)return; c=c1;if(c==0)c=c2; if(c&1==1){y=y0+(y2-y0)*(x1-x0)/(x2-x0);x=x1;} elseif(c&2){y=y0+(y2-y0)*(xr-x0)/(x2-x0); x=xr;} elseif(c&4){x=x0+(x2-x0)*(yb-y0)/(y2-y0); y=yb;} elseif(c&8){x=x0+(x2-x0)*(yt-y0)/(y2-y0); y=yt;} if(c==c1) {x0=x;y0=y;makecode(x,y,c1);} else {x2=x;y2=y;makecode(x,y,c2);} } showline(x0,y0,x2,y2);//显示可见线段} voidmakecode(doublex,y;intc) { c=0; if(x elseif(x>xr)c=2; if(y elseif(y>yt)c=c+8; } 梁友东算法 voidL_Barsky(doublex0,y0,x2,y2) { doublet0,t1,deltax,deltay; t0=0.0;t1=1.0; deltax=x2-x0; if(! cansee(-deltax,x0-x1,t0,t1))return; if(! cansee(deltax,xr-x0,t0,t1))return; deltay=y2-y0; if(! cansee(-deltay,y0-yb,t0,t1))return; if(! cansee(deltay,yt-y0,t0,t1))return; x2=x0+t1*deltax; y2=y0+t1*deltay; x0=x0+t0*deltax; y0=y0+t0*deltay; showline(x0,y0,x2,y2);//显示可见线段 } boolcansee(doubleq,d,t0,t1) {doubler; if(q<0) {r=d/q; if(r>tl){cansee=false;return;} elseif(r>t0)t0=r; } elseif(q>0) {r=d/q; if(r elseif(r } elseif(d<0){cansee=false;return;} cansee=true; } 裁剪区域为任意凸多边形区域时的直线段的裁剪算法 第一步计算所要裁剪的直线段所在直线与凸多边形区域的边界直线段的交点。 第二步当交点的个数为0或1时,该直线段处于凸多边形区域外 第三步,当交点的个数为2时,通过判断这两个交点与被裁剪的直线段的端点在直线段所处的直线上的关系来进行裁剪。 1. 如果直线段的两个端点和两个交点刚好全部重合,则显而易见该直线段完全可见。 2. 如果直线段的两个端点和两个交点中有一个 重合,则将重合的点视为一点,则将重合点与另外的一个端点、一个交点按在直线上的顺序排列。 根据排列的顺序有如下三种情况: 如果重合点和交点不相邻,则直线段完全可见;如果重合点与交点相邻,且该交点与另一个端点相邻,则直线段部分可见,且可见部分为重合点与另一个交点确定的直线段,它的可见部分为直线段;如果重合点与交点相邻,且该交点与另一个端点不相邻,则直线段完全不可见。 如果直线段的两个端点和两个交点都不重合。 将这四个点按在直线上的顺序排列,根据排列的顺序有如下四种情况: 如果两个端点和两个交点分别相邻,则直线段完全不可见; 如果两个端点在两个交点之间,则直线段完全可见;如果两个交点在两个端点之间,则直线段部分可见,且可见部分为两个交点决定的直线段,它的可见部分为直线段;如果交点和端点的排列顺序是交错的,则直线段部分可见,且可见部分为中间的一个交点和一个端点决定的直线段,它的可见部分为直线段。 两线段AB和CD交点的算法 1.〔计算行列式〕A←(xb-xa)(yc-yd)-(xc-xd)(yb-ya)若A=0,则两线段重合或平行,可算做无交点,算法结束; 2.〔计算交点参数〕B←((xc-xa)(yc-yd)-(xc-xd)(yc-ya))/若B<0或B>1,则无交点,算法结束; C←((xb-xa)(yc-ya)-(xc-xa)(yb-ya))/ 若C<0或C>1,则无交点,算法结束; 3.〔计算交点〕x←xa+B(xb-xa),y←ya+B(yb-ya)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 图形学 算法