實驗2 基本圖元光柵化
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碼,其餘兩個提供了當鍵按下時當前的滑鼠位置。滑鼠位置是相對於當前客戶視窗的左上角而言的。
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.實驗思考
示範程式碼有個小錯誤,能否指出並改正?請將結果寫入實驗報告。