openGL一之直線DDA,正負法,Bresenham演算法,圓弧正負法,Bresenham演算法
阿新 • • 發佈:2019-01-25
實驗目的:
1. 掌握OpenGL環境的配置方法。
2. 熟悉OpenGL應用程式基礎架構。
3. 熟練掌握簡單OpenGL應用程式的建立、除錯與執行。
4. 培養良好的程式設計習慣和風格,並且學習撰寫實驗報告。
實驗步驟與內容:
1. 實現直線的DDA演算法、正負法、Bresenham演算法。
實驗核心程式碼如下:
//直線DDA法 void dda(float x1, float y1,float x2 ,float y2 ) { float k, i; float x, y, dx, dy; k = fabsf(x2 - x1);//這裡用fabsf,這裡可以取浮點數的絕對值 if (fabsf(y2 - y1) > k) k =fabsf(y2 - y1); dx = float(x2 - x1) / k; dy = float(y2 - y1) / k; x = float(x1); y = float(y1); for (i = 0.0; i < k; i++) { glVertex2f(x, y); x = x + dx;//----q1 y = y + dy;//----q2 } }
測試方法:
void Display1(void) {
glClear(GL_COLOR_BUFFER_BIT);//注意這幾個glClear等函式放的位置
glPointSize(2.0);
glColor3f(1.0, 0.0, 0.0);/*用紅色繪製物件*/
glBegin(GL_POINTS);
dda(-250, 300, 300, 200);
dda(-50, -100, 300, 250);
dda(-500,0,0,-400);
dda(-570, 0, 570, 0); //x軸
dda(0, -570, 0, 570); //y軸
glEnd();
glFlush();
}
實驗結果:
1.2 正負法:
//直線正負法 void MidpointLine(float xs, float ys, float xe, float ye) { float a, b, dt1, dt2, d, x, y; float absY = ye - ys; float absX = xe - xs; //採用對稱的方法來繪畫其他象限的圖案 if ((fabsf(absY) > fabsf(absX)) && absX>0 && absY>0) {//斜率大於1 float m = ys; ys = xs; xs = m; float n = ye; ye = xe; xe = n; } else if (absY<0 && absX>0 && (fabsf(absY) <= fabs(absX))) {//斜率-1到0 ys = -ys; ye = -ye; } else if (absY<0 && absX>0 && (fabsf(absY)>fabs(absX))) {//斜率小於-1 float m = ys; ys = xs; xs = -m; float n = ye; ye = xe; xe = -n; /*printf("xs=%f\n", xs); printf("ys=%f\n", ys); printf("xe=%f\n", xe); printf("ye=%f\n", ye);*/ } else {//斜率0到1 //do nothing } a = ys - ye; b = xe - xs; d = 2 * a + b; dt1 = 2 * a; dt2 = 2 * (a + b); x = xs; y = ys; ManyGlVertex2f(absX, absY, x, y); while (x < xe) { if (d < 0) { x++; y++; d = d + dt2; } else { x++; d = d + dt1; } ManyGlVertex2f(absX, absY, x, y); //呼叫ManyGlVertex2f()函式 } float maxY = getMax(ys, ye); float minY = getMin(ys, ye); //畫豎直的線 if (xs == xe) { for (float i = minY; i <=maxY; i++) { glVertex2f(xs, i); } } }
其中ManyGlVertex2f(absX,absY, x, y);函式程式碼如下:
void ManyGlVertex2f(float absX,float absY,float x,float y) { if ((fabsf(absY) > fabsf(absX)) && absX>0 && absY>0) {//斜率大於1 glVertex2f(y, x); } else if (absY<0 && absX>0 && (fabsf(absY) <= fabs(absX))) {//斜率-1到0 glVertex2f(x, -y); } else if (absY<0 && absX>0 && (fabsf(absY)>fabs(absX))) {//斜率小於-1 //printf("到這了\n"); glVertex2f(y, -x); } else {//斜率0到1 glVertex2f(x, y); } }
測試方法:
void Display1(void) {
glClear(GL_COLOR_BUFFER_BIT);//注意這幾個glClear等函式放的位置
glPointSize(2.0);
glColor3f(1.0, 0.0, 0.0);/*用紅色繪製物件*/
glBegin(GL_POINTS);
MidpointLine(-250, 250, 350, 200);
MidpointLine(50, -150, 350, 250);
MidpointLine(-450,50,0,400);
MidpointLine(-570, 0, 570, 0);//x軸
MidpointLine(0, -570, 0, 570);//y軸
glEnd();
glFlush();
}
實驗結果:
1.3 Bresenham演算法:
//直線Bresenham法
void Bresenham(float xs, float ys, float xe, float ye) {
float absY = ye - ys;
float absX = xe - xs;
//採用對稱的方法來繪畫其他象限的圖案
if ((fabsf(absY) > fabsf(absX)) && absX>0 && absY>0) {//斜率大於1
float m = ys;
ys = xs;
xs = m;
float n = ye;
ye = xe;
xe = n;
}
else if (absY<0 && absX>0 && (fabsf(absY) <= fabs(absX))) {//斜率-1到0
ys = -ys;
ye = -ye;
}
else if (absY<0 && absX>0 && (fabsf(absY)>fabs(absX))) {//斜率小於-1
float m = ys;
ys = xs;
xs = -m;
float n = ye;
ye = xe;
xe = -n;
/*printf("xs=%f\n", xs);
printf("ys=%f\n", ys);
printf("xe=%f\n", xe);
printf("ye=%f\n", ye);*/
}
else {//斜率0到1
//do nothing
}
float dx = xe - xs;
float dy = ye - ys;
float m = (double)dy / (double)dx;
float e = m - 0.5;
float x, y;
x = xs;
y = ys;
for (int i = 0; i < dx; i++) {
ManyGlVertex2f(absX, absY, x, y); //呼叫ManyGlVertex2f()函式
if (e >= 0) {
y = y + 1; e = e - 1;
}
x = x + 1; e = e + m;
}
//畫豎直的線
if (xs == xe) {
float maxY = getMax(ys, ye);
float minY = getMin(ys, ye);
for (float i = minY; i <= maxY; i++) {
glVertex2f(xs, i);
}
}
}
其中呼叫的ManyGlVertex2f()的程式碼和直線正負法中呼叫的ManyGlVertex2f()的方法一樣。
測試方法:void Display1(void) {
glClear(GL_COLOR_BUFFER_BIT);//注意這幾個glClear等函式放的位置
glPointSize(2.0);
glColor3f(1.0, 0.0, 0.0);/*用紅色繪製物件*/
glBegin(GL_POINTS);
Bresenham(250, 250, 350, 200);
Bresenham(50,150, 350, 250);
Bresenham(-450,50,0,-400);
Bresenham(-570, 0, 570, 0);//x軸
Bresenham(0, -570, 0, 570);//y軸
glEnd();
glFlush();
}
實驗結果:
2. 實現圓弧的正負法和Bresenham演算法。
2.1圓弧正負法
//正負法
void pnarc(float radius,float m,float n) {
float x, y, f;
x = 0; y = 0 + radius; f = 0;
while (y > 0) {
glVertex2f(x+m, y+n);
glVertex2f(-x+m, y+n);
glVertex2f(-x+m,- y+n);
glVertex2f(x+m, -y+n);
if (f > 0) {
f = f - 2 * y + 1; y = y - 1;
}
else {
f = f + 2*x + 1; x = x + 1;
}
}
if (y == 0) {
glVertex2f(x + m, y + n);
glVertex2f(-x + m, y + n);
glVertex2f(-x + m, -y + n);
glVertex2f(x + m, -y + n);
}
}
測試方法:
void Display1(void) {
glClear(GL_COLOR_BUFFER_BIT);//注意這幾個glClear等函式放的位置
glPointSize(2.0);
glColor3f(1.0, 0.0, 0.0);/*用紅色繪製物件*/
glBegin(GL_POINTS);
pnarc(150, -250, 250);
pnarc(150, 250, 250);
pnarc(150, 250, -250);
pnarc(150, -250, -250);
Bresenham(-570, 0, 570, 0);//x軸
Bresenham(0, -570, 0, 570);//y軸
glEnd();
glColor3f(0, 1, 0);
glBegin(GL_POINTS);
pnarc(150, 0, 0);
glEnd();
glFlush();
}
實驗結果:
2.2圓弧Bresenham演算法
//Bresenham法
void bresenham_arc(float R,float m,float n) {
float x, y, d;
x = 0; y = R; d = 3 - 2 * R;
while (x < y) {
glVertex2f(x+m, y+n);
glVertex2f(y+m, x+n);
glVertex2f(y + m, -x + n);
glVertex2f(x + m, -y + n);
glVertex2f(-x + m, -y + n);
glVertex2f(-y + m, -x + n);
glVertex2f(-y + m, x + n);
glVertex2f(-x + m, y + n);
if (d < 0)
d = d + 4 * x + 6;
else {
d = d + 4 * (x - y) + 10;
y = y - 1;
}
x = x + 1;
}
if (x == y) {
glVertex2f(x + m, y + n);
glVertex2f(y + m, x + n);
glVertex2f(y + m, -x + n);
glVertex2f(x + m, -y + n);
glVertex2f(-x + m, -y + n);
glVertex2f(-y + m, -x + n);
glVertex2f(-y + m, x + n);
glVertex2f(-x + m, y + n);
}
}
測試方法:
void Display1(void) {
glClear(GL_COLOR_BUFFER_BIT);//注意這幾個glClear等函式放的位置
glPointSize(2.0);
glColor3f(1.0, 0.0, 0.0);/*用紅色繪製物件*/
glBegin(GL_POINTS);
bresenham_arc(150, -150, 100);
bresenham_arc(150, 150, 100);
bresenham_arc(150, 150, -100);
bresenham_arc(150, -150, -100);
Bresenham(-570, 0, 570, 0);//x軸
Bresenham(0, -570, 0, 570);//y軸
glEnd();
glColor3f(0, 1, 0);
glBegin(GL_POINTS);
pnarc(150, 0, 0);
glEnd();
glFlush();
}
實驗結果:
3. 利用上述完成的演算法繪製中國象棋的棋盤和棋子。
//呼叫以上函式繪製中國象棋的棋盤和棋子
void Display(void) {
glClear(GL_COLOR_BUFFER_BIT);//注意這幾個glClear等函式放的位置
glPointSize(2.0);
glColor3f(0.0, 0.0, 0.0);/*用紅色繪製物件*/
glBegin(GL_POINTS);
for (float i = -360; i <= 360; i += 80) {
dda(-360, i, 360, i);
}
for (float i = -360; i <= 360; i += 90) {
dda(i, 40, i, 360);
}
for (float i = -360; i <= 360; i += 90) {
dda(i, -40, i, -360);
}
dda(-360, -40, -360, 40);
dda(360, -40, 360, 40);
float b = 90, c = 200, d = 360;
dda(-b, c, b, d);
dda(-b, d, b, c);
dda(-b, -d, b, -c);
dda(-b, -c, b, -d);
glEnd();
//畫象棋圖上的圓
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_POINTS);
float R = 28;
for (float i = -360; i <= 360; i += 90) {
pnarc(R, i, 360);
pnarc(R, i, -360);
}
for (float i = -360; i <= 360; i += 180) {
pnarc(R, i, 40);
pnarc(R, i, -40);
}
for (float i = -270; i <= 320; i += 540) {
pnarc(R, i, 200);
pnarc(R, i, -200);
}
glEnd();
//最外側直線
glPointSize(3.0);
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_POINTS);
float a = 380;
dda(-a, a, a, a);
dda(a, a, a, -a);
dda(a, -a, -a, -a);
dda(-a, -a, -a, a);
glEnd();
glFlush();
}
結果:
4.以下是屬性設定函式和主函式main()
//設定屬性
void myinit(void) {
/*設定屬性*/
glClearColor(1.0, 1.0, 1.0, 1.0);/*白色背景*/
/*建立檢視*/
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-570.0, 570.0, -570.0, 570.0);//二維檢視區域*
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char * argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(20, 40);//設定位置
glutInitWindowSize(680, 680);//設定高寬
glutCreateWindow("第一個OpenGL程式");
myinit();
glutDisplayFunc(&Display);
glutMainLoop();
return 0;
}
結論分析與體會:
此次實驗著重獨立完成直線和圓弧的各種生成演算法,這也是我們學習計算機圖形學的目的,我們不僅要會使用openGL,更要懂得其內部實現演算法,通過此次的實驗課程,我們可以很清晰明瞭的瞭解具體實現。其實最有難度的還是前面的演算法,只要完成了前面的演算法,後面的棋盤繪畫就比較容易了。整體來說,此次實驗難度並不大,當然期間也遇到許多的小問題,不過最後問題都已解決。