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函數前調用
    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ā)現這些函數有些不是以gl開頭就是以glut開頭,以什么開頭就代表是哪個庫中的函數梅忌。

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

OpenGL函數名稱格式

OpenGL函數名稱格式

GLUT事件處理

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

  • 啥叫回調函數呢(callback)绪商?
    回調就是你自己定義一個函數(比如mouse)苛谷,然后把這個函數作為參數傳入別的(或系統(tǒng))函數(比如glutMouseFunc)中,由別人(或系統(tǒng))的函數在運行時來調用的函數格郁。簡單來說腹殿,就是由別人的函數運行期間回過來調用你已經實現的那個函數。
  • void glutDisplayFunc(void(*func)(void))
    每個GLUT程序都必須有一個顯式回調函數例书,啥作用呢锣尉?比如窗口首次被打開,或者窗口移動了雾叭,在窗口中切換繪圖選項時悟耘,系統(tǒng)都會自動調用這個函數,繼而調用傳入其中的函數织狐。
  • 函數指針
    上面那個函數的參數void(*func)(void)其實就是個函數指針。這個原理和int a是一樣的移迫,int是參數a的類型旺嬉, a是參數名;類比一下厨埋,這里函數指針類型是void(* )(void)邪媳,這里參數是個函數指針類型的變量,參數名是func;其實每個函數的函數名都可以說是一個函數指針名雨效,我們在用的時候才只要闖入相應的函數名就實現了所謂的回調迅涮。所以專業(yè)點說,回調函數就是一個通過函數指針調用的函數徽龟。
  • void glutMainLoop()
    進入GLUT事件處理循叮姑,環(huán)實際就是個死循環(huán)啦。當有事件發(fā)生時据悔,調用相應的回調函數传透;否則,處于等待狀態(tài)极颓。main()函數是以程序進入事件循環(huán)做為結束朱盐,而不是我們通常所寫的return 0;
  • init()函數
    這個函數中我們定義了一些OpenGL的狀態(tài)量菠隆,個人認為和glutDisplayFunc里面將要傳入的函數(比如這里的display)有些雷同兵琳,所以上面的display()函數我們雖然寫了,但卻是個空函數浸赫,(必須有這個display函數)闰围,因為一些狀態(tài)我們都在init函數中設置了。里面涉及到了視圖等的設置既峡,這些之后會講到,現在只需要這樣跟著寫就好了碧查。
  • GLUT使用回調函數機制來進行事件處理运敢,我們的程序中主要用到了前4個:
    –窗口重繪glutDisplayFunc()
    –窗口改變大小glutReshapeFunc()
    –鼠標按鍵glutMouseFunc()
    –鼠標移動glutMotionFunc()
    –鍵盤輸入glutKeyboardFunc()
    –空閑函數glutIdleFunc()

窗口

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

注意原點的位置

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

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

畫直線要求的橡皮條功能

這里用到一個函數glLogicOp

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

畫圓

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

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();
    }

畫三角形

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

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;
        }

    }
}

添加菜單

用到如下的三個函數,比如:

    glutCreateMenu(menu); //創(chuàng)建菜單
    glutAddMenuEntry("line", 0);  //在菜單中添加選項
    glutAddMenuEntry("square", 1);
    glutAddMenuEntry("triangle", 2);
    glutAddMenuEntry("circle", 3);
    glutAddMenuEntry("clear", 4);
    glutAttachMenu(GLUT_RIGHT_BUTTON); //將菜單綁定鼠標操作
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末旨涝,一起剝皮案震驚了整個濱河市蹬屹,隨后出現的幾起案子,更是在濱河造成了極大的恐慌白华,老刑警劉巖慨默,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異弧腥,居然都是意外死亡厦取,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咱旱,你說我怎么就攤上這事■浚” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵澡为,是天一觀的道長漂坏。 經常有香客問我,道長媒至,這世上最難降的妖魔是什么顶别? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮塘慕,結果婚禮上筋夏,老公的妹妹穿的比我還像新娘。我一直安慰自己图呢,他們只是感情好条篷,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布骗随。 她就那樣靜靜地躺著,像睡著了一般赴叹。 火紅的嫁衣襯著肌膚如雪鸿染。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天乞巧,我揣著相機與錄音涨椒,去河邊找鬼。 笑死绽媒,一個胖子當著我的面吹牛蚕冬,可吹牛的內容都是我干的。 我是一名探鬼主播是辕,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼囤热,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了获三?” 一聲冷哼從身側響起旁蔼,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疙教,沒想到半個月后棺聊,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡贞谓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年限佩,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片经宏。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡犀暑,死狀恐怖,靈堂內的尸體忽然破棺而出烁兰,到底是詐尸還是另有隱情,我是刑警寧澤徊都,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布沪斟,位于F島的核電站,受9級特大地震影響暇矫,放射性物質發(fā)生泄漏主之。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一李根、第九天 我趴在偏房一處隱蔽的房頂上張望槽奕。 院中可真熱鬧,春花似錦房轿、人聲如沸粤攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夯接。三九已至焕济,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盔几,已是汗流浹背晴弃。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逊拍,地道東北人上鞠。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像芯丧,于是被迫代替她去往敵國和親芍阎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內容