OpenGL中点画圆法绘制曲线y=ax²+bx+c
安装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如果是整型的话,这个图像会很苗条,再使用中点画圆算法之后恐怕会出现一条线。如果有大佬研究出整型的计算方案的话,请务必在评论区告诉我,感激不尽!!
上一篇: 2.QML的基本用法