希望大家喜歡,點(diǎn)贊哦
思維導(dǎo)圖的矢量圖 訪問密碼 e9ed
深入討論在輸出設(shè)備上顯示二維圖形的問題
[TOC]
1. 二維觀察流水線
- 觀察流水線:將該場(chǎng)景的世界坐標(biāo)描述經(jīng)各種處理變換到一個(gè)或多個(gè)輸出設(shè)備參照系來顯示的過程;
- 裁剪窗口(世界窗口、觀察窗口、viewing window):二維場(chǎng)景中要顯示的部分;
- 視口(viewport):控制在顯示窗口的定位;
- 窗口選擇要看什么,而視口指定在輸出設(shè)備的什么位置進(jìn)行觀察
- 二維觀察變換(two-dimensional viewing transformation)有時(shí)稱為窗口到視口的變換或窗口變換**:場(chǎng)景的描述從二維世界坐標(biāo)系到設(shè)備坐標(biāo)系的映射;
- 規(guī)范化設(shè)備坐標(biāo):為了使觀察處理獨(dú)立于輸出設(shè)備,圖形系統(tǒng)將對(duì)象描述轉(zhuǎn)化到規(guī)范設(shè)備坐標(biāo)系并提供裁剪程序;
世界坐標(biāo)系的位置首先轉(zhuǎn)到與我們要對(duì)場(chǎng)景進(jìn)行觀察所對(duì)應(yīng)的觀察坐標(biāo)系,然后,對(duì)象位置變換到該場(chǎng)景的一個(gè)二維投影,該投影對(duì)應(yīng)于我們?cè)谳敵銎聊簧峡吹降慕Y(jié)果。然后將該場(chǎng)景存入規(guī)范化坐標(biāo)系(規(guī)范化設(shè)備坐標(biāo)系),最后,圖形經(jīng)掃描轉(zhuǎn)換到光柵系統(tǒng)的刷新緩存中進(jìn)行顯示。顯示設(shè)備的坐標(biāo)系稱為設(shè)備坐標(biāo)系(屏幕坐標(biāo)系)
2. 裁剪窗口
應(yīng)用程序要得到特殊的裁剪效果,可通過選擇裁剪窗口的不同性質(zhì)、大小和方向來實(shí)現(xiàn)
2.1 觀察坐標(biāo)系裁剪窗口
二維觀察變換的一般方法是在世界坐標(biāo)系中制定一個(gè)觀察坐標(biāo)。以該標(biāo)系為參考通過選定方向和位置來制定矩形裁剪窗口。
二維觀察坐標(biāo)系的確定:
- 選擇世界坐標(biāo)系的P0=(x0,y0)作為二維觀察坐標(biāo)系的原點(diǎn);
- 使用世界坐標(biāo)系的向量V作為觀察坐標(biāo)系yview軸的方向;(或者根據(jù)旋轉(zhuǎn)角度獲得觀察向上向量)
變換的步驟: - 將觀察坐標(biāo)系原點(diǎn)移動(dòng)到與世界坐標(biāo)系原點(diǎn)重合;
- 旋轉(zhuǎn)觀察坐標(biāo)系使其與世界坐標(biāo)系重合;
結(jié)果:
Mwc,vc=R *T
2.2 世界坐標(biāo)系裁剪窗口
如果要旋轉(zhuǎn)一個(gè)二維場(chǎng)景,可以簡(jiǎn)單地在世界坐標(biāo)系中將對(duì)象旋轉(zhuǎn)(可能有平移)到所需位置并建立裁剪窗口;(步驟同上)
3. 規(guī)范化和視口變換
規(guī)范化和窗口-視口轉(zhuǎn)換合并成一步/規(guī)范化和裁剪在窗口-視口轉(zhuǎn)換之前進(jìn)行
3.1 裁剪窗口到規(guī)范化視口的映射
- 以點(diǎn)(xwmin,ywmin)為中心執(zhí)行縮放變換,將窗口變幻成視口的大小
- 將(xwmin,ywmin)移到(xvmin,yvmin)
- 窗口-視口變換保持對(duì)象描述的相對(duì)位置;
- 只有在裁剪窗口和視口有相同的縱橫比時(shí)才能保持對(duì)象的相對(duì)比例不變
- 裁剪函數(shù)可是有裁剪窗口邊界或視口邊界實(shí)現(xiàn)裁剪
3.2 裁剪窗口到規(guī)范化正方形的映射
二維觀察的另一種方法是先將裁剪窗口變換到規(guī)范正方形,在規(guī)范化坐標(biāo)系中進(jìn)行裁剪,然后將場(chǎng)景描述變換到屏幕坐標(biāo)系中指定的視口中
3.3 字符串的顯示
- 保持字符串的大小不變 ==》 點(diǎn)陣字體
- 對(duì)字形輪廓中線段的定義位置進(jìn)行變換 ==》 輪廓線字體和其他圖元
3.4 分畫面效果和多輸出設(shè)備
工作站變換:到所選輸出設(shè)備的映射
工作站函數(shù):
- 用于為選定輸出設(shè)備制定裁剪窗口,用工作站號(hào)來標(biāo)識(shí);
- 用來為該設(shè)計(jì)建立相應(yīng)的視口
4. OpenGL二維觀察函數(shù)
GLU庫函數(shù)提供了制定二維裁剪窗口的函數(shù);GLUT庫提供了處理顯示窗口的函數(shù)。
4.1 OpenGL投影模式
//下列定義裁剪窗口和視口的函數(shù)將應(yīng)用于投影矩陣
glMatrixMode(GL_PROJECTION);//制定投影矩陣作為當(dāng)前矩陣,它原來設(shè)定為單位矩陣
glLoadIdentity();//矩陣重新設(shè)定為單位矩陣
4.2 GLU裁剪窗口函數(shù)
gluOrtho2D(xwmin,xwmax,ywmin,ywmax);//定義一個(gè)二維裁剪窗口、該函數(shù)給出了將場(chǎng)景映射到屏幕的正交投影
4.3 OpenGL視口函數(shù)
glViewport(xvmin,yvmin,vpWidth,vpHeight);//指定視口參數(shù)
glGetIntegerv(GL_VIEWPORT,vpArray);//獲取當(dāng)前活動(dòng)視口參數(shù)的查詢函數(shù),在交互式應(yīng)用中,我們可以使用該函數(shù)獲得光標(biāo)所在視口的參數(shù)
4.4 建立GLUT顯示窗口
//初始化glut
glutInit(&argc,argv);
//定義顯示窗口并選擇其尺寸及位置
glutInitWindowPosition(xTopLeft,yTopLeft);//位置
glutInitWindowSize(dwWidth,dwHeight);//寬高
glutCreateWindow("Title of Display Window");//標(biāo)題
4.5 設(shè)定GLUT顯示窗口的模式和顏色
//顯示窗口的參數(shù)有下列函數(shù)選擇
glutInitDisplayMode(mode);/*選擇顏色模式(RGB或索引號(hào))和不同的緩存組合,所選參數(shù)以邏輯‘或操作方式組合’ 默認(rèn)模式是但緩存和RGB(或RGBA)顏色模式。eg:glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); */
glClearColor(red,greeb,blue,alpha);//顯示窗口的背景顏色(RGB模式)
glClearIndex(index);//顯示窗口的背景顏色(顏色索引模式)
4.6 GLUT顯示窗口標(biāo)識(shí)
windowID = glutCreatWindow("A Display Window");//記錄窗口的標(biāo)識(shí)
4.7 刪除GLUT顯示窗口
glutDestroyWindow(windowID)
4.8 當(dāng)前GLUT顯示窗口
glutSetWindow(windowID);//指定顯示窗口
currentWindowID = glutGetWindow();//詢問系統(tǒng)當(dāng)前顯示窗口
4.9 修改GLUT顯示窗口的位置和大小
glutPositionWindow(xNewTopLeft,yNewTopLeft);//改變當(dāng)前顯示窗口的位置
glutReshapeWindow(dwNewWidth);//設(shè)定當(dāng)前顯示窗口的尺寸
glutFullScreen();//將當(dāng)前顯示窗口擴(kuò)展到整個(gè)屏幕
glutReshapeFunc(winReshapeFun);//調(diào)整顯示窗口的變化 4.16
4.10 管理多個(gè)GLUT顯示窗口
glutIconifyWindow();//將當(dāng)前顯示窗口變?yōu)橐粋€(gè)圖符,該圖符將使用賦予該窗口的名字來標(biāo)記
glutSetIconTitle("Icon Name");//改變上面的圖符名字
glutSetWindowTitle("New window Name");//改變顯示窗口的名字
glutSetWindow(windowID);//制定某個(gè)顯示窗口為當(dāng)前窗口
glutPopWindow();//使當(dāng)前窗口成為所有其他窗口之前的窗口
glutPushWindow();//使當(dāng)前窗口成為所有其他窗口之后的窗口
glutHideWindow();//讓當(dāng)前窗口從屏幕消失
glutShowWindow();//將隱藏的或變?yōu)閳D符的顯示窗口指定為當(dāng)前顯示窗口并調(diào)研該函數(shù)讓它重新顯示
4.11 GLUT子窗口
glutCreateSubWindow(windowID,xBottomLeft,yBottomLeft,width,height);//創(chuàng)建子窗口
4.12 顯示窗口屏幕光標(biāo)形狀的選擇
glutSetCursor(shape);//為當(dāng)前窗口選擇平模光標(biāo)的形狀。shape為符號(hào)常量
4.13 在GLUT顯示窗口中觀察圖形對(duì)象
glutDisplayFunc(pictureDescrip);//指定當(dāng)前窗口的顯示內(nèi)容,pictureDescrip是一種回調(diào)函數(shù)
glutPostRedisplay();//指出當(dāng)前顯示窗口的內(nèi)容應(yīng)該更新
4.14 執(zhí)行應(yīng)用程序
glutMainLoop();//啟動(dòng)程序執(zhí)行
4.15 其他GLUT函數(shù)
glutIdleFunc(function);//沒有其他時(shí)間需要處理是可以指定其運(yùn)行,該函數(shù)的參數(shù)可以是一個(gè)背景函數(shù)或更新一個(gè)動(dòng)畫參數(shù)的過程
glutGet(stateParam);//查詢系統(tǒng)某些參數(shù)的當(dāng)前值
4.16 OpenGL的二維觀察程序例
#include <GL/glut.h>
class wcPt2D {
public:
GLfloat x, y;
};
void init(void)
{
/* Set color of display window to white. */
//設(shè)置背景為白
glClearColor(1.0, 1.0, 1.0, 0.0);
/* Set parameters for world-coordinate clipping window. */
glMatrixMode(GL_PROJECTION);//GL_PROJECTION,對(duì)投影矩陣應(yīng)用隨后的矩陣操作.
gluOrtho2D(-100.0, 100.0, -100.0, 100.0);
/* Set mode for constructing geometric transformation matrix. */
glMatrixMode(GL_MODELVIEW); //GL_MODELVIEW, 對(duì)模型視景矩陣堆棧應(yīng)用隨后的矩陣操作.
}
void triangle(wcPt2D *verts)
{
GLint k;
glBegin(GL_TRIANGLES);
for (k = 0; k < 3; k++)
glVertex2f(verts[k].x, verts[k].y);
glEnd();
}
void displayFcn(void)
{
/* Define initial position for triangle. */
wcPt2D verts[3] = { { -50.0, -25.0 },{ 50.0, -25.0 },{ 0.0, 50.0 } };
glClear(GL_COLOR_BUFFER_BIT); // Clear display window.
//把整個(gè)窗口清除為當(dāng)前的清除顏色
glColor3f(0.0, 0.0, 1.0); // Set fill color to blue.
glViewport(0, 0, 300, 300); // Set left viewport.
/*
glViewport(GLint x, GLint y, GLsizei width, GLsizei height)為其函數(shù)原型。
X,Y————以像素為單位,指定了視口的左下角(在第一象限內(nèi),以(0,0)為原點(diǎn)的)位置。
width,height————表示這個(gè)視口矩形的寬度和高度,根據(jù)窗口的實(shí)時(shí)變化重繪窗口。
*/
triangle(verts); // Display triangle.
/* Rotate triangle and display in right half of display window. */
glColor3f(1.0, 0.0, 0.0); // Set fill color to red.
glViewport(300, 0, 300, 300); // Set right viewport.
glRotatef(90.0, 0.0, 0.0, 1.0); // Rotate about z axis.
//當(dāng)前坐標(biāo)系統(tǒng)關(guān)于Z軸旋轉(zhuǎn)90度
triangle(verts); // Display red rotated triangle.
glFlush();//強(qiáng)制刷新緩沖,保證繪圖命令將被執(zhí)行,而不是存儲(chǔ)在緩沖區(qū)中等待其他的OpenGL命令。
}
void main(int argc, char ** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(50, 50);
glutInitWindowSize(600, 300);
glutCreateWindow("Split-Screen Example");
init();
glutDisplayFunc(displayFcn);
glutMainLoop();
}
5. 裁剪算法
裁剪算法(clipping algorithm):用來消除制定區(qū)域內(nèi)或區(qū)域外的圖形部分的過程;
裁剪最多應(yīng)用于觀察流水線,目的是為了從場(chǎng)景(二維或三維)中提取制定部分顯示在輸出設(shè)備上;
-
二維裁剪算法:
- 點(diǎn)的裁剪
- 線段的裁剪
- 區(qū)域的裁剪(直線段)
- 曲線的裁剪(多邊形)
- 文字的裁剪
假設(shè)裁剪區(qū)域是一個(gè)正則矩形
6. 二維點(diǎn)裁剪
xwmin =< x =< xwmax
ywmin =< y =< ywmax
若點(diǎn)不滿足這四個(gè)等式中任何一個(gè),即裁剪掉改點(diǎn)
7. 二維線段裁剪
線段裁剪算法通過一系列的測(cè)試和求交計(jì)算來判斷是否整條線段或其中的某部分可以保存下來
- 減少交點(diǎn)計(jì)算是每一種線段裁剪算法的主要目標(biāo)
7.1 Cohen-Sutherland線段裁剪算法
該算法通過初始測(cè)試來減少交點(diǎn)計(jì)算,從而減少線段裁剪算法所用的時(shí)間
通過一個(gè)矩形的裁剪區(qū)域?qū)⒄麄€(gè)屏幕分成9個(gè)部分,并為每一個(gè)部分賦予相應(yīng)的區(qū)域碼,然后根據(jù)端點(diǎn)的位置確定這個(gè)端點(diǎn)的區(qū)域碼。
先判斷能否完全接受或者完全排除一條線段,若以上2個(gè)判斷無法直接得出,則逐步裁剪,選取一個(gè)位于裁剪區(qū)外的端點(diǎn),把端點(diǎn)的區(qū)域碼和裁剪邊界的區(qū)域碼進(jìn)行邏輯與運(yùn)算,若結(jié)果為真,則端點(diǎn)在該裁剪邊界外部,這時(shí)將端點(diǎn)移向線段和該邊界的交點(diǎn)處,如此循環(huán),直到裁剪結(jié)束。
#include <Windows.h>
#include <gl/glut.h>
//////////////////////////////////////////////////////////////////////////
//區(qū)域碼
const GLint leftBitCode=0x1;
const GLint rightBitCode=0x2;
const GLint buttonBitCode=0x4;
const GLint topBitCode=0x8;
GLint winWidth=640,winHeight=480;
class screenPT
{
public:
GLfloat x,y;
};
inline GLint inside(GLint code){return GLint(!code);} //判斷點(diǎn)是否在裁剪區(qū)內(nèi)
inline GLint reject(GLint code1,GLint code2){return GLint(code1&code2);} //判斷能否完全排除一條線段
inline GLint accept(GLint code1,GLint code2){return GLint(!(code1 | code2));} //判斷能否完全接受一條線段
inline void swapPT(screenPT& a,screenPT& b){screenPT t=a;a=b;b=t;} //交換兩個(gè)點(diǎn)
inline void swapCode(GLubyte& a,GLubyte& b){GLubyte t=a;a=b;b=t;} //交換兩個(gè)區(qū)域碼
//確定一個(gè)點(diǎn)所在位置的區(qū)域碼
GLubyte encode(const screenPT& p,const screenPT& winMin,const screenPT& winMax)
{
GLubyte code=0x00;
if(p.x<winMin.x)
code |= leftBitCode;
if(p.x>winMax.x)
code |= rightBitCode;
if(p.y<winMin.y)
code |= buttonBitCode;
if(p.y>winMax.y)
code |= topBitCode;
return code;
}
//在屏幕上畫一條未裁剪的線,由裁剪函數(shù)調(diào)用
void drawOneLine(const screenPT& a,const screenPT& b)
{
glBegin(GL_LINES);
glVertex2f(a.x,a.y);
glVertex2f(b.x,b.y);
glEnd();
}
//裁剪函數(shù)
void lineClip(screenPT winMin,screenPT winMax,screenPT lineBegin,screenPT lineEnd)
{
GLubyte code1,code2; //保存兩個(gè)端點(diǎn)的區(qū)域碼
GLboolean done=false,plotLine=false; //判斷裁剪是否結(jié)束和是否要繪制直線
GLfloat k; //斜率
while(!done)
{
code1 = encode(lineBegin,winMin,winMax);
code2 = encode(lineEnd,winMin,winMax);
if(accept(code1,code2)) //當(dāng)前直線能完全繪制
{
done=true;
plotLine=true;
}
else
{
if(reject(code1,code2)) //當(dāng)前直線能完全排除
done = true;
else
{
if(inside(code1)) //若lineBegin端點(diǎn)在裁剪區(qū)內(nèi)則交換兩個(gè)端點(diǎn)使它在裁剪區(qū)外
{
swapPT(lineBegin,lineEnd);
swapCode(code1,code2);
}
//計(jì)算斜率
if(lineBegin.x != lineEnd.x)
k = (lineEnd.y-lineBegin.y)/(lineEnd.x-lineBegin.x);
//開始裁剪,以下與運(yùn)算若結(jié)果為真,
//則lineBegin在邊界外,此時(shí)將lineBegin移向直線與該邊界的交點(diǎn)
if(code1 & leftBitCode)
{
lineBegin.y += (winMin.x-lineBegin.x)*k;
lineBegin.x = winMin.x;
}
else if(code1 & rightBitCode)
{
lineBegin.y += (winMax.x-lineBegin.x)*k;
lineBegin.x = winMax.x;
}
else if(code1 & buttonBitCode)
{
if(lineBegin.x != lineEnd.x)
lineBegin.x += (winMin.y-lineBegin.y)/k;
lineBegin.y = winMin.y;
}
else if(code1 & topBitCode)
{
if(lineBegin.x != lineEnd.x)
lineBegin.x += (winMax.y-lineBegin.y)/k;
lineBegin.y = winMax.y;
}
}
}
}
if(plotLine)
drawOneLine(lineBegin,lineEnd); //繪制裁剪好的直線
}
//////////////////////////////////////////////////////////////////////////
void rect(screenPT winMin,screenPT winMax)
{
glBegin(GL_LINE_LOOP);
glVertex2f(winMin.x,winMin.y);
glVertex2f(winMax.x,winMin.y);
glVertex2f(winMax.x,winMax.y);
glVertex2f(winMin.x,winMax.y);
glEnd();
}
void init()
{
glViewport(0,0,winWidth,winHeight);
glClearColor(1.0,1.0,1.0,0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,winWidth,0,winHeight);
glMatrixMode(GL_MODELVIEW);
}
void display()
{
screenPT winMin,winMax,lineBegin,lineEnd;
winMin.x=100.0; winMin.y=50.0;
winMax.x=400.0; winMax.y=300.0;
lineBegin.x=0.0; lineBegin.y=0.0;
lineEnd.x=winWidth; lineEnd.y=winHeight;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0,0.0,0.0);
rect(winMin,winMax); //為裁剪區(qū)域繪制一個(gè)邊框
lineClip(winMin,winMax,lineBegin,lineEnd);
lineBegin.y=240.0; lineEnd.y=240.0;
lineClip(winMin,winMax,lineBegin,lineEnd);
lineBegin.x=320.0; lineBegin.y=0.0;
lineEnd.x=320.0; lineEnd.y=winHeight;
lineClip(winMin,winMax,lineBegin,lineEnd);
glFlush();
}
int main(int argc,char** argv)
{
glutInit(&argc,argv);
glutInitWindowPosition(100,100);
glutInitWindowSize(winWidth,winHeight);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutCreateWindow("my app");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
7.2 梁友棟-Barsky 線段裁剪算法
Cyrus和Beck用參數(shù)化方法提出了比Cohen-Sutherland更有效的算法。后來梁友棟和Barsky獨(dú)立地提出了更快的參數(shù)化線段裁剪算法,也稱為L(zhǎng)iany-Barsky(LB)算法。
xmin≤x1+ u·Δx≤xmax
ymin≤y1+ u·Δy≤ymax
這四個(gè)不等式可以表示為:u·pk ≤qk , k=1,2,3,4
其中,p、q定義為:
p1=-Δx, q1=x1-xmin
p2= Δx, q2=xmax-x1
p3=-Δy, q3=y1-ymin
p4= Δy, q4=ymax-y1
可得:
任何平行于窗口某邊界的直線,其pk=0,k值對(duì)應(yīng)于相應(yīng)的邊界(k=1,2,3,4對(duì)應(yīng)于左、右、下、上邊界)。如果還滿足qk<0,則線段完全在邊界外,應(yīng)舍棄該線段。如果pk=0并且qk≥0,則線段平行于窗口某邊界并在窗口內(nèi).
1、當(dāng)pk<0時(shí),線段從裁剪邊界延長(zhǎng)線的外部延伸到內(nèi)部;
2、當(dāng)pk>0時(shí),線段從裁剪邊界延長(zhǎng)線的內(nèi)部延伸到外部;
梁友棟-Barsky 線段裁剪算法實(shí)現(xiàn)步驟:
1、 初始化線段交點(diǎn)的參數(shù):u1=0,u2=1;
2、 計(jì)算出各個(gè)裁剪邊界的p、q值;
3、 根據(jù)p、q來判斷:是舍棄線段還是改變交點(diǎn)的參數(shù)。
(1) 當(dāng)p<0時(shí),參數(shù)r用于更新u1; (u1=max{u1,…,rk})
(2) 當(dāng)p>0時(shí),參數(shù)r用于更新u2。 (u2=min{u2,…,rk})
(3) 如果更新了u1或u2后,使u1>u2,則舍棄該線段。
(4) 當(dāng)p=0且q<0時(shí),因?yàn)榫€段平行于邊界并且位于邊界之外,則舍棄該線段。見下圖所示。
4、 p、q的四個(gè)值經(jīng)判斷后,如果該線段未被舍棄,則裁剪線段的端點(diǎn)坐標(biāo)由參數(shù)u1和u2的值決定。
代碼:
class wcPt2D
{
private:
GLfloat x, y;
public:
/* Default Constructor: initialize position as (0.0, 0.0). */
wcPt3D ( ) {x = y = 0.0;}
setCoords (GLfloat xCoord, GLfloat yCoord) {
x = xCoord;
y = yCoord;
}
GLfloat getx ( ) const {
return x;
}
GLfloat gety ( ) const {
return y;
}
};
inline GLint round (const GLfloat a) { return GLint (a + 0.5); }
GLint clipTest (GLfloat p, GLfloat q, GLfloat * u1, GLfloat * u2)
{
GLfloat r;
GLint returnValue = true;
if (p < 0.0)
{
r = q / p;
if (r > *u2)
returnValue = false;
else if (r > *u1)
*u1 = r;
}
else if (p > 0.0)
{
r = q / p;
if (r < *u1)
returnValue = false;
else if (r < *u2)
*u2 = r;
}
else
/* Thus p = 0 and line is parallel to clipping boundary. */
if (q < 0.0)
/* Line is outside clipping boundary. */
returnValue = false;
return (returnValue);
}
void lineClipLiangBarsk (wcPt2D winMin, wcPt2D winMax, wcPt2D p1, wcPt2D p2)
{
GLfloat u1 = 0.0, u2 = 1.0, dx = p2.getx ( ) - p1.getx ( ), dy;
if (clipTest (-dx, p1.getx ( ) - winMin.getx ( ), &u1, &u2))
if (clipTest (dx, winMax.getx ( ) - p1.getx ( ), &u1, &u2))
{
dy = p2.gety ( ) - p1.gety ( );
if (clipTest (-dy, p1.gety ( ) - winMin.gety ( ), &u1, &u2))
if (clipTest (dy, winMax.gety ( ) - p1.gety ( ), &u1, &u2))
{
if (u2 < 1.0)
{
p2.setCoords (p1.getx ( ) + u2 * dx, p1.gety ( ) + u2 * dy);
}
if (u1 > 0.0)
{
p1.setCoords (p1.getx ( ) + u1 * dx, p1.gety ( ) + u1 * dy);
}
lineBres (round (p1.getx ( )), round (p1.gety ( )),round (p2.getx ( )), round (p2.gety ( )));
}
}
}
示例:
梁友棟-Barsky 線段裁剪算法比Cohen-Sutherland線段裁剪算法更有效率,因?yàn)闇p少了交點(diǎn)次數(shù)計(jì)算
7.3 Nicholl-Lee-Nicholl 線段裁剪算法
- 在Cohen-Sutherland線段裁剪算法中,在找到與裁剪矩形邊界的焦點(diǎn)之前或者完全舍棄改線段之前,必須對(duì)一條線段進(jìn)行多次求交
- NLN算法則在求交計(jì)算器按進(jìn)行跟多的區(qū)域測(cè)試,從而減少求交運(yùn)算
- 基本想法:對(duì)2D平面的更細(xì)的劃分
7.4 非矩形多邊形裁剪窗口的線段裁剪
- 基于參數(shù)化直線方程的算法
- 對(duì)于凹多邊形:
- 將其分解為一組凸多邊形再使用基于參數(shù)化直線方程的算法
- 添加一些便使凹裁剪區(qū)乘務(wù)凸裁剪區(qū)域,然后使用修改后的凸多邊形組對(duì)線段進(jìn)行一些列裁剪操作。
7.5 非線性裁剪窗口邊界的線段裁剪
也可以使用園或者其他期限邊界進(jìn)行裁剪,但使用這些區(qū)域的裁剪算法的速度較慢
8. 多邊形填充區(qū)裁剪
對(duì)于多邊形,線段裁剪進(jìn)行處理后的多邊形邊界顯示為不連接的線段
8.1 Sutherland-Hodgman 多邊形裁剪
基本思想:以多邊形頂點(diǎn)為初始集合, 首先用窗口左邊界剪裁多邊形,產(chǎn)生新的頂點(diǎn)序列。新的頂點(diǎn)集依次傳給下邊界、右邊界和上邊界進(jìn)行處理。 SH算法最終輸出定義剪裁后的多邊形邊界的頂點(diǎn)序列
沿著多邊形依次處理頂點(diǎn)的情況:
1、 若第一點(diǎn)在窗口邊界外、第二點(diǎn)在窗口邊界內(nèi),將交點(diǎn)I與窗口內(nèi)的點(diǎn)P輸入頂點(diǎn)表
2、 若兩頂點(diǎn)都在窗口邊界內(nèi),只將第二點(diǎn)輸入頂點(diǎn)表,如P
3、若第一點(diǎn)在窗口邊界內(nèi)、第二點(diǎn)在窗口邊界外,將交點(diǎn)I輸入頂點(diǎn)表
4、若兩點(diǎn)均在窗口邊界外,不輸入任何點(diǎn)
代碼:
typedef enum { Left, Right, Bottom, Top } Boundary;
const GLint nClip = 4;
//檢驗(yàn)?zāi)滁c(diǎn)是否在內(nèi)部
GLint inside (wcPt2D p, Boundary b, wcPt2D wMin, wcPt2D wMax)
{
switch (b)
{
case Left: if (p.x < wMin.x) return (false); break;
case Right: if (p.x > wMax.x) return (false); break;
case Bottom: if (p.y < wMin.y) return (false); break;
case Top: if (p.y > wMax.y) return (false); break;
}
return (true);
}
//檢驗(yàn)線段是否與邊界相交
GLint cross (wcPt2D p1, wcPt2D p2, Boundary winEdge, wcPt2D wMin, wcPt2D wMax)
{
if (inside (p1, winEdge, wMin, wMax) == inside (p2, winEdge, wMin, wMax))
return (false);
else
return (true);
}
//intersect(vt. 橫斷,橫切,橫穿;vt.& vi. (指線條、道路等)相交,交叉;)
wcPt2D intersect (wcPt2D p1, wcPt2D p2, Boundary winEdge, wcPt2D wMin, wcPt2D wMax)
{
wcPt2D iPt;
GLfloat m;
if (p1.x != p2.x)
m = (p1.y - p2.y) / (p1.x - p2.x);
switch (winEdge)
{
case Left:
iPt.x = wMin.x;
iPt.y = p2.y + (wMin.x - p2.x) * m;
break;
case Right:
iPt.x = wMax.x;
iPt.y = p2.y + (wMax.x - p2.x) * m;
break;
case Bottom:
iPt.y = wMin.y;
if (p1.x != p2.x) iPt.x = p2.x + (wMin.y - p2.y) / m;
else iPt.x = p2.x;
break;
case Top:
iPt.y = wMax.y;
if (p1.x != p2.x) iPt.x = p2.x + (wMax.y - p2.y) / m;
else iPt.x = p2.x;
break;
}
return (iPt);
}
void clipPoint (wcPt2D p, Boundary winEdge, wcPt2D wMin, wcPt2D wMax,wcPt2D * pOut, int * cnt, wcPt2D * first[], wcPt2D * s)
{
wcPt2D iPt;
/* If no previous point exists for this clipping boundary,
* save this point.
*/
if (!first[winEdge])
first[winEdge] = &p;
else
/* Previous point exists. If p and previous point cross
* this clipping boundary, find intersection. Clip against
* next boundary, if any. If no more clip boundaries, add
* intersection to output list.
*/
if (cross (p, s[winEdge], winEdge, wMin, wMax))
{
iPt = intersect (p, s[winEdge], winEdge, wMin, wMax);
if (winEdge < Top)
clipPoint (iPt, b+1, wMin, wMax, pOut, cnt, first, s);
else
{
pOut[*cnt] = iPt; (*cnt)++;
}
}
/* Save p as most recent point for this clip boundary. */
s[winEdge] = p;
/* For all, if point inside, proceed to next boundary, if any. */
if (inside (p, winEdge, wMin, wMax))
if (winEdge < Top)
clipPoint (p, winEdge + 1, wMin, wMax, pOut, cnt, first, s);
else
{
pOut[*cnt] = p; (*cnt)++;
}
}
void closeClip (wcPt2D wMin, wcPt2D wMax, wcPt2D * pOut,GLint * cnt, wcPt2D * first [ ], wcPt2D * s)
{
wcPt2D pt;
Boundary winEdge;
for (winEdge = Left; winEdge <= Top; winEdge++)
{
if (cross (s[winEdge], *first[winEdge], winEdge, wMin, wMax))
{
pt = intersect (s[winEdge], *first[winEdge], winEdge, wMin, wMax);
if (winEdge < Top)
clipPoint (pt, winEdge + 1, wMin, wMax, pOut, cnt, first, s);
else
{
pOut[*cnt] = pt; (*cnt)++;
}
}
}
}
GLint polygonClipSuthHodg (wcPt2D wMin, wcPt2D wMax, GLint n, wcPt2D * pIn, wcPt2D * pOut)
{
/* Parameter "first" holds pointer to first point processed for
* a boundary; "s" holds most recent point processed for boundary.
*/
wcPt2D * first[nClip] = { 0, 0, 0, 0 }, s[nClip];//指針數(shù)組
GLint k, cnt = 0;
for (k = 0; k < n; k++)
clipPoint (pIn[k], Left, wMin, wMax, pOut, &cnt, first, s);
closeClip (wMin, wMax, pOut, &cnt, first, s);
return (cnt);
}
缺點(diǎn):對(duì)凹多邊形裁剪會(huì)出現(xiàn)多余的線
8.2 Weiler-Atherton多邊形裁剪
基本思想:有時(shí)延多邊形某一條邊的方向來處理頂點(diǎn),有時(shí)沿窗口的邊界方向處理。一般按逆時(shí)針(順時(shí)針)以及當(dāng)前處理多邊形頂點(diǎn)對(duì)是由外到內(nèi)還是由內(nèi)到外
8.3 非矩形的多邊形窗口的多邊形裁剪
- 梁友棟-Barsky 線段裁剪算法
- Weiler-Atherton多邊形裁剪
8.4 非線性裁剪窗口邊界的多邊形裁剪
- 先用直線逼近邊界,然后使用一般的多邊形裁剪窗口算法對(duì)其進(jìn)行處理
- 使用線段裁剪中討論的通用算法
9. 曲線的裁剪
使用類似上一節(jié)的方法進(jìn)行裁剪
10. 文字的裁剪
-
全部保留或者全部舍棄字符串的裁剪策略,速度最快
裁剪前
- 全部保留或者舍棄字符
- 裁剪單個(gè)字符的組成部分
11. 小結(jié)
謝謝觀看
希望大家喜歡,點(diǎn)贊哦