安裝glut庫
這篇博客的編譯器是VS2017,vs2019的安裝步驟也一樣:glut
中點畫圓法
這篇博客寫得挺詳細的,也介紹了速度更快的Bresenham算法:中點畫圓法
思路分析
首先,利用導數找出斜率爲1的點,從這點開始將曲線分開兩段處理,斜率小於1的曲線利用y作爲增量計算,斜率大於1的曲線利用x作爲增量計算。
其次,由於關於函數對稱軸對稱,所以只要畫出右邊的圖像,再進行取對稱點操作就能得到左邊圖像,從而拼出整個圖像。
注意,有3個參數,下面兩種參數變化的情況會對程序產生較大影響
情況分析
a=0
此時,曲線會退化成直線
a=0且b=0
此時,曲線會退化成一條平行於x軸的直線
判別式推導
原函數變爲
當時,利用y作爲增量
對任意一點,有
若則應取作爲下一像素,而且再下一像素的判別式爲:
此時,
若則應取作爲下一像素,而且再下一像素的判別式爲:
此時,
同理,當時,利用x作爲增量
對任意一點,有
若則應取作爲下一像素,而且再下一像素的判別式爲:
此時,
若則應取作爲下一像素,而且再下一像素的判別式爲:
此時,
重要函數實現
通用函數
畫點函數
void DrawPoint(int x, int y)
{
glBegin(GL_POINTS);
glVertex2i(x, y);
glEnd();
}
窗口初始化函數
由於OpenGL glut默認的窗口顯示範圍只有[-1,1],所以要擴大顯示區域,我這裏擴大到[-35,35]
void InitWindow(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);//RGB渲染通道、雙緩衝
glutInitWindowPosition(100, 100);//窗口位置
glutInitWindowSize(800, 800);//窗口大小
id = glutCreateWindow("繪製曲線");//窗口名字
glPointSize(5);//將像素點調整爲5倍大小,方便看清楚
gluOrtho2D(-35.0, 35.0, -35.0, 35.0);//更改顯示區域
glutKeyboardFunc(KeyBoardHandle);//接收鍵盤事件
}
鍵盤事件函數
程序運行後按下任意鍵即可結束程序,銷燬窗口需要用到窗口的id,我用一個全局變量來接收這個id
int id;//窗口id
void KeyBoardHandle(unsigned char key, int x, int y)
{
glutDestroyWindow(id);//按下任意鍵銷燬窗口
exit(0);//退出程序
}
繪製函數
這裏之所以用一個void函數封裝起來的原因是初始化窗口函數glutCreateWindow的參數只接受void(*func)(),即沒有參數的void函數指針。這也導致如果想將用戶的輸入數據傳給myBreasehamCurve方法只能使用全局變量接受用戶的輸入數據。
void myWork()
{
myBreasehamCurve(-0.25, 5, 0);
}
主函數
int main(int argc, char* argv[])
{
InitWindow(argc, argv);
glutDisplayFunc(&myWork);
glutMainLoop();
return 0;
}
繪製函數實現
a=0的情況
這也是Bresenham兩點一線算法
void BreasehamDrawLine(int x0, int y0, int x1, int y1)
{
glColor3f(1.0f, 1.0f, 1.0f);
int s1 = x1 > x0 ? 1 : -1;
int s2 = y1 > y0 ? 1 : -1;
int dx, dy, e, x, y;
bool bigOne = false;
dx = abs(x1 - x0);
dy = abs(y1 - y0);
if (dx < dy)
{
Swap(dx, dy);
bigOne = true;
}
e = 2 * dy - dx;
x = x0;
y = y0;
for (int i = 0; i <= dx; i++)
{
DrawPoint(x, y);
if (e >= 0)
{
if (!bigOne)
y += s2;
else
x += s1;
e -= 2 * dx;
}
if (!bigOne)
x += s1;
else
y += s2;
e += 2 * dy;
}
glFlush();
glutSwapBuffers();
}
a=0,b=0的情況
void myBreasehamCurve_c(double c)//a,b爲0時
{
for (int i = -35; i < 35; i++)
DrawPoint(i, c);
glFlush();
glutSwapBuffers();
}
一般情況
由於int參數的擬合度不高,效果太差,所以這裏使用double參數
只要用x減去2倍x到對稱軸的距離即可得到另一個x的座標
void myBreasehamCurve(double a, double b, double c)
{
bool isAZ = true;//a爲正數時
if (a == 0 && b == 0)
{
myBreasehamCurve3(c);
return;
}
else if (a < 0)
{
a = -a;
isAZ = false;
}
else if (a == 0)
{
BreasehamDrawLine(-30, -30 * b + c, 30, 30 * b + c);
return;
}
double x = 0, y = 0;
double d = a + b + c - 0.5;
double lastX = 0;
double sx;
double cutPoint = -b / 2 / a;
double symmetry = cutPoint;//對稱軸的位置
//斜率大於0而小於1時
while (x < cutPoint)
{
if (d < 0)
d += a * (2 * x + 3) + b;
else
{
d += a * (2 * x + 3) + b - 1;
y++;
}
x++;
sx = abs(x - symmetry);//x到對稱軸的絕對值
if (!isAZ)
{
DrawPoint(x, -y);
DrawPoint(x - 2 * sx, -y);
}
else
{
DrawPoint(x, y);
DrawPoint(x - 2 * sx, y);
}
}
//斜率大於1時
x = cutPoint;
y = c - b * b / a / 4;
d = a / 4 + b / 2 + c - 1;
while (y <= 30 && y > -30)
{
if (d >= 0)
d -= 1;
else
{
d += a * (2 * x + 2) + b - 1;
x++;
}
y++;
sx = abs(x - symmetry);
if (!isAZ)
{
DrawPoint(x, -y);
DrawPoint(x - 2 * sx, -y);
}
else
{
DrawPoint(x, y);
DrawPoint(x - 2 * sx, y);
}
}
glFlush();
glutSwapBuffers();
}
運行效果
後記
理論上如果能用整型計算是速度最快的,但我考慮到參數a如果是整型的話,這個圖像會很苗條,再使用中點畫圓算法之後恐怕會出現一條線。如果有大佬研究出整型的計算方案的話,請務必在評論區告訴我,感激不盡!!