要求:開發(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函數名稱格式
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); //將菜單綁定鼠標操作