一. 任務目標
利用OpenGL,實現Bresenham算法畫圓。
二. 任務要求
- 使用整數來確定點的位置。
- 標出圓心。(不太明白show的含義,於是我在圓心處畫了一個點來表示。)
- 使用至少16個點表示一個圓。
三. 使用平台
Windows 8.1
Visual Studio 2012
四. 實現簡述
與Bresenham直線算法類似,采用的是中點畫圓算法。
定義圓的一個函數
可根據f(x, y)符號判斷點(x, y)的位置:
於是假設點pi(xi, yi)為當前點,下一個點要么是點E(xi + 1, yi),要么是點SE(xi + 1, yi – 1)。取兩點中點M(xi + 1, yi – 1/2),帶入f(x,y),根據f(M)符號來選擇下一給點。
就像bresenham畫直線算法一樣,
設一個決策變量為
並迭代出下一個中點M 的決策變量di+1, 而di+1是根據di正負決定的:
而(要求點都是整數)
首先順時針生成八分之一圓,然后根據對稱性生成全部圓。
優化:將di+1=di+2xi+3中的乘2用位運算表示:di+1=di+(xi<<1)+3,將di+1=di+2(xi-yi)+5表示為:di+1=di+((xi-yi)<<1)+5
五. 心得體會
開始一直不明白bresenham算法跟中點midpoint算法的區別。最后總算找到答案:midpoint算法是bresenham算法的推廣。兩者的推理角度不同(雖然都有使用迭代),但最終得到的結果是一樣的。Bresenham算法一開始的想法是用於畫直線和圓,而midpoint算法更加通用。
為了便於比較,用最基本的畫直線(0<k<1)來看兩種算法的思路:
bresenham算法:根據當前點和直線y=f(x)(0<k<1)計算下一個點和f(x+1)的距離,有可能是 ,有可能是。迭代的決策變量是dupper-dlower,根據變量符號判斷與哪個點更近來進行選擇,並更新決策變量來迭代進行下一次選擇。
Midpoint算法:使用了一個隱式方程f(x,y)=c0*x+c1*y+c2=0。對當前點,它的下一個中點M(,將該中點帶入上方程,通過判斷f()符號決定直線靠哪個點更近,並同樣使用迭代的方法進行下一次判斷。中點算發除了可畫直線和圓,還能畫橢圓、雙曲線等。
還有一點體會比較深的是這種問題必須看國外論壇才能找到好的解釋。
代碼:
#include <gl/glut.h> #include <math.h> void myInit(void) { glClearColor(1.0f, 1.0f, 1.0f, 0.0f); glColor3f(1.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, 500.0, 0.0, 500.0); glMatrixMode(GL_MODELVIEW); } void plotCirclePixel(int cx, int cy, int x, int y) { glBegin(GL_POINTS); glVertex2i(cx + x, cy + y); glVertex2i(cx - x, cy + y); glVertex2i(cx + x, cy - y); glVertex2i(cx - x, cy - y); glVertex2i(cx + y, cy + x); glVertex2i(cx - y, cy + x); glVertex2i(cx + y, cy - x); glVertex2i(cx - y, cy - x); glEnd(); } // @param cx x coordinate of center // @param cy y coordinate of center // @param r radius of circle void bresenhamCircle(int cx, int cy, int r) { int x = 0, y = r; //initial position int d = 1 - r; //draw a point at the center of the circle glBegin(GL_POINTS); glVertex2i(cx, cy); glEnd(); while (x <= y) { plotCirclePixel(cx, cy, x, y); if (d <= 0) { d += (x << 1) + 3; } else { d += ((x - y) << 1) + 5; y--; } x++; } } void display(void) { glClear(GL_COLOR_BUFFER_BIT); bresenhamCircle(250, 250, 150); glFlush(); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(500, 500); glutCreateWindow("Bresenham Circle"); myInit(); glutDisplayFunc(display); glutMainLoop(); return 0; }