OpenGL實驗一:畫圖小程序

要求:開發(fā)一個畫圖程序,用戶可以用鼠標繪制線段、矩形、圓和三角形等。通過菜單讓用戶選擇需要繪制的圖元。

框架參考

/* globals */
GLsizei wh = 500, ww = 500; /* initial window size */
float xm, ym, xmm, ymm;
int first = 0;
void mouse(int btn, int state, int x, int y)
{
    if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
    {
        xm = x;
        ym = wh-y;
        glColor3f(0.0, 0.0, 1.0);
        glLogicOp(GL_XOR);
        first = 0;
    }

    if(btn==GLUT_LEFT_BUTTON && state==GLUT_UP)
    {
        if (first == 1)
        {
            glRectf(xm, ym, xmm, ymm);
            glFlush();
        }

        xmm = x;
        ymm = wh-y;
        glColor3f(0.0, 1.0, 0.0);
        glLogicOp(GL_COPY);
        glRectf(xm, ym, xmm, ymm);
        glFlush();
    }

    if(btn==GLUT_RIGHT_BUTTON && state==GLUT_DOWN)  
        exit(0);
}

void move(int x, int y)
{
    if(first == 1)
    {
        glRectf(xm, ym, xmm, ymm);
        glFlush();
    }

    xmm = x;
    ymm = wh-y;
    glRectf(xm, ym, xmm, ymm);
    glFlush();
    first = 1;
}

void reshape(GLsizei w, GLsizei h)
{
    
    /* adjust clipping box */
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity(); 
    glOrtho(0.0,(GLdouble)w, 0.0,(GLdouble)h, -1.0, 1.0);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity(); 
    
    /* adjust viewport and clear */
    
    glViewport(0,0,w,h);
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();
    
    /* set global size for use by drawing routine */
    
    ww = w;
    wh = h; 
}

void init(void)
{
    glViewport(0,0,ww,wh);
    
    /* Pick 2D clipping window to match size of screen window 
    This choice avoids having to scale object coordinates
    each time window is resized */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity(); 
    glOrtho(0.0,(GLdouble) ww , 0.0,(GLdouble) wh , -1.0, 1.0);
    
    /* set clear color to black and clear window */
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();  

    glEnable(GL_COLOR_LOGIC_OP);
}


/* display callback required by GLUT 3.0 */
void display(void)
{}

int main(int argc, char** argv)
{
    /***GLUT窗口管理,就當做是固定寫法吧***/
    glutInit(&argc,argv);                         //初始化GLUT,在調用其他GLUT函數(shù)前調用
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);  //設置窗口的顯示模式
    glutInitWindowSize(ww, wh);                   //設置窗口的初始寬度和高度,單位為像素,缺省300x300
    glutInitWindowPosition(500, 500)              //窗口左上角相對于屏幕左上角的位置,單位為像素,缺省(0,0) 
    glutCreateWindow("rubber banding");           //創(chuàng)建窗口,標題為title。調用glutMainLoop()之前,窗口不會被顯示
    
    init();  //設置OpenGL狀態(tài)

    /***GLUT事件處理***/
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMotionFunc(move);
    glutDisplayFunc(display);
    glutMainLoop();
}

OpenGL庫

首先你會發(fā)現(xiàn)這些函數(shù)有些不是以gl開頭就是以glut開頭,以什么開頭就代表是哪個庫中的函數(shù)。

  • OpenGL核心庫(OpenGL Core Library)
    gl是核心,這部分函數(shù)用于常規(guī)的、核心的圖形處理。
  • OpenGL實用庫(OpenGL Utility Library, GLU)
    glu是對gl的部分封裝,glut是OpenGL的跨平臺工具庫,gl中包含了最基本的3D函數(shù),而glu似乎對gl的輔助,如果算數(shù)好,不用glu的情況下,也是可以做出同樣的效果。
  • OpenGL實用工具庫(OpenGL Utility Toolkit Library, GLUT)
    glut是基本的窗口界面,是獨立于gl和glu的,如果不喜歡用glut可以用MFC和Win32窗口等代替,但是glut是跨平臺的,這就保證了我們編出的程序是跨平臺的。

OpenGL函數(shù)名稱格式

OpenGL函數(shù)名稱格式

GLUT事件處理

我們這里界面都是采用GLUT庫來寫。GLUT使用回調函數(shù)(callback)機制來進行事件處理。

  • 啥叫回調函數(shù)呢(callback)?
    回調就是你自己定義一個函數(shù)(比如mouse),然后把這個函數(shù)作為參數(shù)傳入別的(或系統(tǒng))函數(shù)(比如glutMouseFunc)中,由別人(或系統(tǒng))的函數(shù)在運行時來調用的函數(shù)。簡單來說,就是由別人的函數(shù)運行期間回過來調用你已經實現(xiàn)的那個函數(shù)。
  • void glutDisplayFunc(void(*func)(void))
    每個GLUT程序都必須有一個顯式回調函數(shù),啥作用呢?比如窗口首次被打開,或者窗口移動了,在窗口中切換繪圖選項時,系統(tǒng)都會自動調用這個函數(shù),繼而調用傳入其中的函數(shù)。
  • 函數(shù)指針
    上面那個函數(shù)的參數(shù)void(*func)(void)其實就是個函數(shù)指針。這個原理和int a是一樣的,int是參數(shù)a的類型, a是參數(shù)名;類比一下,這里函數(shù)指針類型是void(* )(void),這里參數(shù)是個函數(shù)指針類型的變量,參數(shù)名是func;其實每個函數(shù)的函數(shù)名都可以說是一個函數(shù)指針名,我們在用的時候才只要闖入相應的函數(shù)名就實現(xiàn)了所謂的回調。所以專業(yè)點說,回調函數(shù)就是一個通過函數(shù)指針調用的函數(shù)。
  • void glutMainLoop()
    進入GLUT事件處理循,環(huán)實際就是個死循環(huán)啦。當有事件發(fā)生時,調用相應的回調函數(shù);否則,處于等待狀態(tài)。main()函數(shù)是以程序進入事件循環(huán)做為結束,而不是我們通常所寫的return 0;
  • init()函數(shù)
    這個函數(shù)中我們定義了一些OpenGL的狀態(tài)量,個人認為和glutDisplayFunc里面將要傳入的函數(shù)(比如這里的display)有些雷同,所以上面的display()函數(shù)我們雖然寫了,但卻是個空函數(shù),(必須有這個display函數(shù)),因為一些狀態(tài)我們都在init函數(shù)中設置了。里面涉及到了視圖等的設置,這些之后會講到,現(xiàn)在只需要這樣跟著寫就好了。
  • GLUT使用回調函數(shù)機制來進行事件處理,我們的程序中主要用到了前4個:
    –窗口重繪glutDisplayFunc()
    –窗口改變大小glutReshapeFunc()
    –鼠標按鍵glutMouseFunc()
    –鼠標移動glutMotionFunc()
    –鍵盤輸入glutKeyboardFunc()
    –空閑函數(shù)glutIdleFunc()

窗口

窗口是顯示器上的一塊矩形區(qū)域。窗口內的位置用窗口坐標來指定,單位是像素。

注意原點的位置

  • 光柵顯示器由于是按照從上到下,從左到右的順序進行掃描,所以左上角是原點。窗口系統(tǒng)返回的信息(例如鼠標位置)假定原點在左上角。
  • 學科學和工程中,左下角是原點(0,0)。OpenGL命令假定原點在左下角

所以在畫圖的時候,我們要把鼠標位置的坐標轉換成OpenGL命令中對應的坐標。原點在左上角和左下角相比較一下,不就是x坐標不用變,y坐標是個互補的關系嘛,用窗口的高度(wh)減掉鼠標位置的y坐標就得到OpenGL命令中的坐標啦。

畫直線要求的橡皮條功能

這里用到一個函數(shù)glLogicOp

  • glLogicOp(GL_COPY) (默認值)
    缺省的寫入模式是復制模式(copy)或替換模式 (replacement),直接用源像素取代目標像素。用這種方法不能繪制一條臨時直線,因為我們不能用快速簡 單的方法恢復在臨時直線下面的內容。
  • glLogicOp(GL_XOR)
    XOR寫入模式。 異或操作(XOR):相同值為0,不同值為1
    因此如果應用XOR模式畫一條直線,那么只要在原地再畫 一遍這條直線就可以刪除這條直線。

畫圓

OpenGL沒有提供直接畫圓的函數(shù),實現(xiàn)方式用一個多邊形來逼近。大家都知道正多邊形的邊如越多越接近圓吧~
這里我用到了G_TRIANGLE_FAN這個參數(shù),書上都有講解。
截取一部分代碼:

if (btn == GLUT_LEFT_BUTTON && state == GLUT_UP)
    {
        xmm = x;
        ymm = wh - y;
        float r = sqrt(pow((xm - xmm), 2) + pow((ym - ymm), 2));
        glBegin(GL_TRIANGLE_FAN);
        for (int i = 0; i< 1000; ++i){
            glVertex2f(xm + r*cos(2 * PI / 1000 * i), ym + r*sin(2 * PI / 1000 * i));
        }
        glEnd();
        glFlush();
    }

畫三角形

簡單的一個實現(xiàn)的思路:用一個變量將三個點的坐標保存起來,每點擊一次鼠標記錄一個位置并計數(shù),當點數(shù)滿三個的時候,就把這個三角形畫出來。
截取一部分代碼:

void mouse_triangle(int btn, int state, int x, int y)
{
    if (btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
        triVertex[ver][0] = x;
        triVertex[ver][1] = wh - y;
        glColor3f(1.0, 0.5, 0.0);
        glBegin(GL_POINTS);
        glVertex2f(triVertex[ver][0], triVertex[ver][1]);
        glEnd();
        glFlush();
        ver++;
        if (ver == 3)
        {   
            //glLogicOp(GL_COPY);//直接用源像素取代目標像素 默認的
            glBegin(GL_TRIANGLES);
            glVertex2f(triVertex[0][0], triVertex[0][1]);
            glVertex2f(triVertex[1][0], triVertex[1][1]);
            glVertex2f(triVertex[2][0], triVertex[2][1]);
            glEnd();
            glFlush();
            ver = 0;
        }

    }
}

添加菜單

用到如下的三個函數(shù),比如:

    glutCreateMenu(menu); //創(chuàng)建菜單
    glutAddMenuEntry("line", 0);  //在菜單中添加選項
    glutAddMenuEntry("square", 1);
    glutAddMenuEntry("triangle", 2);
    glutAddMenuEntry("circle", 3);
    glutAddMenuEntry("clear", 4);
    glutAttachMenu(GLUT_RIGHT_BUTTON); //將菜單綁定鼠標操作
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容