1. 程式人生 > >實驗2 基本圖元光柵化

實驗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,用來處理特殊按鍵,如FiF_i,方向鍵,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.實驗思考

示範程式碼有個小錯誤,能否指出並改正?請將結果寫入實驗報告。