1.實驗目的:
- 理解基本圖形元素光柵化的基本原理;
- 掌握基本圖形元素光柵化方法,如中點方法,Bresenham方法;
- 利用OpenGL實現基本圖形元素的光柵化算法。
2.實驗內容:
(1) 閱讀學習所給的直線光柵化的DDA算法示範代碼,將其徹底弄懂,根據實驗思考題找出其中的錯誤;同時能在計算機上編譯運行,輸出正確結果,指出錯誤並截圖保存爲圖1至word實驗文檔(30分鐘);
(2) 在示範程序的基礎上,根據程序所留接口,增加中點線算法,並給出若干條測試直線實例,,截圖保存爲圖2至word實驗文檔(30分鐘);
(3) 爲示範程序增加中點圓繪製算法,同時增加鍵盤按鍵控制(數字按鍵3),並給出若干個測試圓的實例,截圖保存爲圖3至word實驗文檔(30分鐘);
(4) 整理圖1-3,並增加程序代碼合併到一個word文檔,將其命名爲“序號-姓名-Prj2.doc”,電子版提交至雨課堂,A4打印稿下一次課前或實驗課前提交。
3.實驗原理:
示範代碼原理參見教材直線光柵化一節中的DDA算法。下面介紹下OpenGL畫線的一些基礎知識和glutReshapeFunc()函數。
(1)數學上的直線沒有寬度,但OpenGL的直線則是有寬度的。同時,OpenGL的直線必須是有限長度,而不是像數學概念那樣是無限的。可以認爲,OpenGL的“直線”概念與數學上的“線段”接近,它可以由兩個端點來確定。這裏的線由一系列頂點順次連結而成,有閉合和不閉合兩種。
前面的實驗已經知道如何繪“點”,那麼OpenGL是如何知道拿這些頂點來做什麼呢?是一個一個的畫出來,還是連成線?或者構成一個多邊形?或是做其它事情呢?爲了解決這一問題,OpenGL要求:指定頂點的命令必須包含在glBegin函數之後,glEnd函數之前(否則指定的頂點將被忽略),並由glBegin來指明如何使用這些點。
例如:
glBegin(GL_POINTS);
glVertex2f(0.0f, 0.0f);
glVertex2f(0.5f, 0.0f);
glEnd();
則這兩個點將分別被畫出來。如果將GL_POINTS替換成GL_LINES,則兩個點將被認爲是直線的兩個端點,OpenGL將會畫出一條直線。還可以指定更多的頂點,然後畫出更復雜的圖形。另一方面,glBegin支持的方式除了GL_POINTS和GL_LINES,還有GL_LINE_STRIP,GL_LINE_LOOP,GL_TRIANGLES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN等,每種方式的大致效果如圖A.2所示:
圖A.2 OpenGL幾何圖元類型
(2)首次打開窗口、移動窗口和改變窗口大小時,窗口系統都將發送一個事件,以通知程序員。如果使用的是GLUT,通知將自動完成,並調用向glutReshapeFunc()註冊的函數。該函數必須完成下列工作:
-
重新建立用作新渲染畫布的矩形區域;
-
定義繪製物體時使用的座標系。
如:
void Reshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) w, 0.0, (GLdouble) h);
}
在GLUT內部,將給該函數傳遞兩個參數:窗口被移動或修改大小後的寬度和高度,單位爲像素。glViewport()調整像素矩形,用於繪製整個窗口。接下來三個函數調整繪圖座標系,使左下角位置爲(0, 0),右上角爲(w, h)。
(3) 鍵盤輸入
當你按下一個鍵後,GLUT提供了兩個函數爲這個鍵盤消息註冊回調。第一個是glutKeyboardFunc,用來處理普通按鍵,如字母,數字,和其他可以用ASCII代碼表示的鍵;另一個是glutSpecialFunc,用來處理特殊按鍵,如,方向鍵,Home,End鍵等。
glutKeyboardFunc函數原型如下:
void glutKeyboardFunc(void(*func)(unsigned char key,int x,int y));
參數:
func: 處理普通按鍵消息的函數的名稱。如果傳遞NULL,則表示GLUT忽略普通按鍵消息。
這個作爲glutKeyboardFunc函數參數的函數需要有三個形參:第一個表示按下的鍵的ASCII碼,其餘兩個提供了當鍵按下時當前的鼠標位置。鼠標位置是相對於當前客戶窗口的左上角而言的。
glutSpecialFunc函數請參考https://blog.csdn.net/xie_zi/article/details/1911891。
4.實驗代碼:
#include <GL/glut.h>
int flag = 0;
void LineDDA(int x0,int y0,int x1,int y1/*,int color*/)
{
int x, dy, dx, y;
float m;
dx=x1-x0;
dy=y1-y0;
m=dy/dx;
y=y0;
glColor3f (1.0f, 1.0f, 0.0f);
glPointSize(1);
for(x=x0;x<=x1; x++)
{
glBegin (GL_POINTS);
glVertex2i (x, (int)(y+0.5));
glEnd ();
y+=m;
}
}
void LineMidPoint(int x0, int y0, int x1, int y1)
{
//請在這裏填寫你的代碼
}
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f (1.0f, 0.0f, 0.0f);
glRectf(25.0, 25.0, 75.0, 75.0);
glPointSize(5);
glBegin (GL_POINTS);
glColor3f (0.0f, 1.0f, 0.0f); glVertex2f (0.0f, 0.0f);
glEnd ();
glBegin (GL_LINES);
glColor3f (1.0f, 0.0f, 0.0f); glVertex2f (100.0f, 0.0f);
glColor3f (0.0f, 1.0f, 0.0f); glVertex2f (180.0f, 240.0f);
glEnd ();
if(flag == 1)
LineDDA(0, 0, 200, 300);
//if (flag == 2)
//LineMidPoint(...);
glFlush();
}
void Init()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
}
void Reshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) w, 0.0, (GLdouble) h);
}
void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case '1'://DDA Line
flag = 1;
break;
case '2': // MidPoint Line
//請在這裏填寫你的代碼
break;
default:
break;
}
glutPostRedisplay();//重畫
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("Hello World!");
Init();
glutDisplayFunc(myDisplay);
glutReshapeFunc(Reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
注: glShadeModel選擇平坦或光滑漸變模式。GL_SMOOTH爲缺省值,爲光滑漸變模式,GL_FLAT爲平坦漸變模式。
5.實驗思考
示範代碼有個小錯誤,能否指出並改正?請將結果寫入實驗報告。