这个学期报了学校开设的计算机图形学课程,由于前一个月老师讲的都太抽象完全不知道在说啥……于是我的入门现在才刚刚开始。最近的一节课教授了基本图元的生成算法,留的作业是使用OpenGL或者DirectX实现DDA算法画直线、方程法画圆以及Bresenham画直线和圆。
ps:本次作业使用的是OpenGL中的<GL/glut.h>
库
文章目录
1. 对于窗口的配置
原本默认的窗口是左下角坐标是(-1, -1),右上角坐标为(1, 1),窗口的中心坐标为(0, 0)。在此绘制的是1000*600的窗口,左下角坐标为(0, 0),右上角坐标为(1000, 600)。且变换窗口大小图形的大小和位置不会改变。这一操作中起到关键作用的函数是gluOrtho2D
和glViewport
,具体的理解可以参考这里:gluOrtho2D、glViewport、glutInitWindowSize区别与关系
大致的窗口配置如下:
void Init()
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, 1000, 0, 600);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(500, 250);
glutInitWindowSize(1000, 600);
glutCreateWindow("MyDraw");
Init();
glutDisplayFunc(display);
glutMainloop();
}
2. DDA算法绘制直线
DDA算法的原理很简单,即求出用户输入直线两端的坐标 ( x 1 , y 1 ) 、 ( x 2 , y 2 ) (x1, y1)、(x2,y2) (x1,y1)、(x2,y2)后求出要绘制直线的斜率 k k k。然后先画出更靠左的点,接着依次绘制 ( x i + 1 , r o u n d ( y i + k ) ) (x_{i}+1,round(y_{i}+k)) (xi+1,round(yi+k)),直到最右边的点即可。
在算法的实现过程中容易注意不到的错误有三个:
-
在斜率绝对值大于1时,再以x为步长每次自增1,会导致直线变成由离散的点组成的虚线。此时应该交换x和y的地位再进行绘图。
-
忽略 x1=x2 的情况,计算斜率时分母为零(但是画出的结果依然是正确的……)。
-
由于画直线默认是从左向右画,但是假如先输入的坐标更靠右,即x1>x2,需要调换两点坐标,否则无法画出直线。
实现的display函数如下:
void MyDraw::DDA_Draw()
{
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, 1000, 600);
glColor3f(0, 0, 0);
if(x1 == x2) { // 对于横坐标相等的点的特殊处理
if(y1 > y2) swap(y1, y2);
glBegin(GL_POINTS);
for(double y = y1; y<=y2; y++)
glVertex2f(x1, y);
glEnd();
glFlush();
return;
}
else if(x1 > x2) { // 保证先绘制的是更靠左的点
swap(x1, x2);
swap(y1, y2);
}
double k = (y2-y1)/(x2-x1); // 计算出斜率k
glBegin(GL_POINTS);
if(fabs(k) <=1) {
for(double x=x1, y=y1; x<=x2; x++, y+=k)
glVertex2f(x, round(y));
}
else { // 当斜率绝对值大于1时,交换原本x和y的地位进行计算
double xStep = 1/k;
if(k > 0)
for(double x=x1, y=y1; y<=y2; y++, x+=xStep)
glVertex2f(round(x), y);
else
for(double x=x1, y=y1; y>=y2; y--, x-=xStep)
glVertex2f(round(x), y);
}
glEnd();
glFlush();
}
DDA算法容易实现,非常直观,但是缺点也很明显,因为涉及了太多次浮点运算,所以不利于硬件实现 ,并不实用。
3. Bresenham算法绘制直线
以下关于算法的叙述适用于斜率 k k k位于0和1之间的情况下。
在绘出点 ( x i , y i ) 后 (x_{i},y_{i})后 (xi,yi)后,要确定 ( x I + 1 , y i + 1 ) (x_{I+1}, y_{i+1}) (xI+1,yi+1)的位置,实际上就是确定 ( x i + 1 , y i + 1 ) (x_{i+1},y_{i+1}) (xi+1,yi+1)是在下一个网格线段中点的上方还是下方,如图:
对于判断直线点在直线上还是在直线下,利用直线方程可以很容易地做到,即对于直线 y = k x + b y=kx+b y=kx+b,可以令函数 F ( x , y ) = y − k x − b F(x,y)=y-kx-b F(x,y)=y−kx−b,对于点 ( x , y ) (x,y) (x,y):
- 对于直线上的点, F ( x , y ) = 0 F(x,y)=0 F(x,y)=0;
- 对于直线上方的点, F ( x , y ) > 0 F(x,y)>0 F(x,y)>0;
- 对于直线下方的点, F ( x , y ) < 0 F(x,y)<0 F(x,