【Computer Graphics】1.直线生成算法(DDA 算法)
2016-12-03 21:28
267 查看
前言
本人是计算机图形学的初学者,虽然说大学上了计算机图形学的课,但是只是半斤八两的程度,有的地方没懂也就算了,不会去思考,现在重新学一遍,有写的不好的地方,希望各路大神指教,本人一定改正。。好了,让我们先来了解一些概念
1.图形的扫描转换
图形的扫描转换:确定一个像素集合,用于显示一个图形的过程
(ps:个人理解是图形变成一个个的像素点)
2.图形显示前需要做的工作:扫描转换 +裁剪
裁剪也就是将不在显示范围的部分删除
目前有两种方法:
①先裁剪,再扫描转换:最常用的方法,节约计算时间
②先扫描转换,再裁剪:这样比较耗时间,毕竟把不是范围内的也进行了扫描转换计算。。但是算法简单
3.直线的扫描转换:确定最佳逼近于该直线的一组像素,并且按扫描线顺序,对这些像素进行写操作
常用的直线扫描转换有:
① 数值微分法(DDA)
② 中点画线算法
③ Bresenham算法
本篇着重讲 数值微分法(DDA)
众所周知,两点确定一条直线,我们假设这两点的坐标都为整数P0(x0,y0) P1(x1,y1)
那么过这两点的直线的表达式可以为: y =kx+b
斜率 k = (y1 - y0)/(x1 - x0)
令 x = x0; x → x1变化
x =x+stepx;
通过y = kx+b计算y的值;
∴ 逼近直线的点为(x,round(y)) round在这的作用是四舍五入
我们设当前已经确定的点的坐标为(Xi,Yi),下一个点的坐标为(Xi+1,Yi+1) ,i和i+1为下标
那么由 y = kx+b得 Yi+1 = kXi+1 +b;
我们当前的点(Xi,Yi)已经在直线上了,所以Yi = kXi+b,所以我们可以转化:
Yi+1 = kXi+1 +b = k(Xi+△x)+b = kXi +k△x+b = Yi+k△x
当△x = 1 时,Yi+1 = Yi+k,即x每递增1,y递增k,即直线的斜率
上述过程只用于 |k| ≤ 1 的过程,x每增加1,y最多增加1;
当 |k| >1时,x,y互相交换。。。也就是将x = x+steps 换成y =y+ steps ,即△y = 1, 那么 x的增量为 1/k
这个算法可以概括为:
输入线段两个端点的像素位置,端点位置间的水平和垂直差值赋值给dx和dy。绝对值大的参数决定参数steps的值,从像素位置(x0,y0)开始,确定沿线段生成下一个像素位置的每一步所需要的偏移量,并玄幻上述过程的steps次。假如dx的绝对值大于dy的绝对值,且x0小于x1,那么x和y方向的增量值为1和m。加入x方向的变化较大,但x0大于x1,那么就采用减量 -1 和-k来生成线段上的每个点。。。在其他情况下,y方向使用单位增量(或减量),x方向使用1/k的增量(或减量)
ps:个人觉得有点像微积分的思想,就是x0→x1(或者y0→y1)微分,然后画点,成就一条直线
算法的具体内容
void DDALine(int x0,int y0,int x1,int y1) { int dx = x1 - x0; int dy = y1 - y0; int steps, k; float xIcrement, yIcrement; float x = x0, y = y0; if (fabs(dx) > fabs(dy)) steps = dx; else steps = dy; xIcrement = float(dx) / steps; yIcrement = float(dy) / steps; SetPixel(int(x+0.5), int(y+0.5)); for (k = 0; k < steps; k++) { x += xIcrement; y += yIcrement; SetPixel(int(x + 0.5), int(y + 0.5)); } }
稍稍解释一下:首先我们传入两个点,起始点(x0,y0)和结束点(x1,y1),判断dx和dy的绝对值,哪个大决定steps的值,xIcrement, yIcrement就是在决定了steps的情况下,每移动一次,x和y的增量,k表示次数,steps表示总共要执行的次数,SetPixel()表示画点,int(x + 0.5)表示四舍五入。。。
由算法可以看出DDA画直线的缺点:y和x必须是float,而且每计算一次就要进行四舍五入,在硬件上不利于实现
实战效果:
这是我在OpenGL的环境下画的,下面贴出源代码:
#include <GL\glut.h> #include <cmath> void Init() { glClearColor(0.0,0.0,0.0,0.0); glMatrixMode(GL_PROJECTION); gluOrtho2D(-100, 100, -100, 100); } void DDALine() { GLint x0 = -50.0f, y0 = -50.0f, x1 = 50.0f, y1 = 50.0f; glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0f, 0.0f, 0.0f); GLint dx = x1 - x0; GLint dy = y1 - y0; GLint steps, k; GLfloat xIcrement, yIcrement; GLfloat x = x0, y = y0; if (fabs(dx) > fabs(dy)) steps = dx; else steps = dy; xIcrement = float(dx) / steps; yIcrement = float(dy) / steps; glBegin(GL_POINTS); glVertex2i(int(x+0.5), int(y+0.5)); glEnd(); for (k = 0; k < steps; k++) { x += xIcrement; y += yIcrement; glBegin(GL_POINTS); glVertex2i(int(x + 0.5), int(y + 0.5)); glEnd(); } glFlush(); } void main(int argc, char** argv) { GLint x0, y0, x1, y1; glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowPosition(50, 100); glutInitWindowSize(500, 500); glutCreateWindow("DDALine"); Init(); glutDisplayFunc(DDALine); glutMainLoop(); }
本篇内容到此为止,主要写了一些本人DDA算法的理解,请各路大神指教。。不喜勿喷