二維觀察

二維觀察

希望大家喜歡,點(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ī)范化視口的映射

  1. 以點(diǎn)(xwmin,ywmin)為中心執(zhí)行縮放變換,將窗口變幻成視口的大小
  2. 將(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ù)

  1. 用于為選定輸出設(shè)備制定裁剪窗口,用工作站號(hào)來標(biāo)識(shí);
  2. 用來為該設(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í)間

區(qū)域碼
窗口及區(qū)域編碼
裁剪

算法的步驟

通過一個(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 線段裁剪算法示例
線段的參數(shù)方程
結(jié)果

梁友棟-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 非矩形多邊形裁剪窗口的線段裁剪

  1. 基于參數(shù)化直線方程的算法
  2. 對(duì)于凹多邊形:
  • 將其分解為一組凸多邊形再使用基于參數(shù)化直線方程的算法
  • 添加一些便使凹裁剪區(qū)乘務(wù)凸裁剪區(qū)域,然后使用修改后的凸多邊形組對(duì)線段進(jìn)行一些列裁剪操作。

7.5 非線性裁剪窗口邊界的線段裁剪

也可以使用園或者其他期限邊界進(jìn)行裁剪,但使用這些區(qū)域的裁剪算法的速度較慢

8. 多邊形填充區(qū)裁剪

對(duì)于多邊形,線段裁剪進(jìn)行處理后的多邊形邊界顯示為不連接的線段

直接采用直線段裁剪的結(jié)果
正確的裁剪結(jié)果

8.1 Sutherland-Hodgman 多邊形裁剪

基本思想:以多邊形頂點(diǎn)為初始集合, 首先用窗口左邊界剪裁多邊形,產(chǎn)生新的頂點(diǎn)序列。新的頂點(diǎn)集依次傳給下邊界、右邊界和上邊界進(jìn)行處理。 SH算法最終輸出定義剪裁后的多邊形邊界的頂點(diǎn)序列

用左邊界裁剪 輸入:ABCDEFGH 輸出:A12DEFGH
用下邊界裁剪 輸入:A12DEFGH 輸出:A134D56FGH
用右邊界裁剪 輸入:A134D56FGH 輸出:A134D5678GH
用上邊界裁剪 輸入:A134D5678GH 輸出:K34D56789IHJ

沿著多邊形依次處理頂點(diǎn)的情況
1、 若第一點(diǎn)在窗口邊界外、第二點(diǎn)在窗口邊界內(nèi),將交點(diǎn)I與窗口內(nèi)的點(diǎn)P輸入頂點(diǎn)表

輸出I、P

2、 若兩頂點(diǎn)都在窗口邊界內(nèi),只將第二點(diǎn)輸入頂點(diǎn)表,如P

輸出P

3、若第一點(diǎn)在窗口邊界內(nèi)、第二點(diǎn)在窗口邊界外,將交點(diǎn)I輸入頂點(diǎn)表

輸出I

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 非矩形的多邊形窗口的多邊形裁剪

  1. 梁友棟-Barsky 線段裁剪算法
  2. Weiler-Atherton多邊形裁剪

8.4 非線性裁剪窗口邊界的多邊形裁剪

  1. 先用直線逼近邊界,然后使用一般的多邊形裁剪窗口算法對(duì)其進(jìn)行處理
  2. 使用線段裁剪中討論的通用算法

9. 曲線的裁剪

使用類似上一節(jié)的方法進(jìn)行裁剪

10. 文字的裁剪

  1. 全部保留或者全部舍棄字符串的裁剪策略,速度最快


    裁剪前
裁剪后
  1. 全部保留或者舍棄字符
裁剪前
裁剪后
  1. 裁剪單個(gè)字符的組成部分
裁剪前
裁剪后

11. 小結(jié)

謝謝觀看
希望大家喜歡,點(diǎn)贊哦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 因?yàn)橐鲆粋€(gè)地圖操作的項(xiàng)目,需要用到這個(gè)地圖庫,但是查詢官方API麻煩,而且這個(gè)地圖框架的API做的用起來確實(shí)太麻...
    虛幻的銹色閱讀 34,012評(píng)論 1 15
  • 開發(fā)基于 OpenGL 的應(yīng)用程序,必須先了解 OpenGL 的庫函數(shù)。它采用 C 語言風(fēng)格,提供大量的函數(shù)來進(jìn)行...
    sillen閱讀 3,130評(píng)論 0 4
  • 1 前言 一直想沿著圖像處理這條線建立一套完整的理論知識(shí)體系,同時(shí)積累實(shí)際應(yīng)用經(jīng)驗(yàn)。因此有了從使用AVFounda...
    RichardJieChen閱讀 5,798評(píng)論 5 12
  • 有讀者給我留言,我和我老公是初戀,剛開始我們感情很好,朋友們都羨慕我們。因?yàn)閼言辛藗}(cāng)促結(jié)婚,他是不想要孩子的,說過...
    白衣梧桐閱讀 3,730評(píng)論 3 6
  • 陽光再暖 不及你的笑容 明媚到耀眼
    代香閱讀 61評(píng)論 2 1