您的位置:首页 > 编程语言

widnows游戏编程大师技巧 Draw_Line 的学习

2014-09-29 20:22 591 查看
最近在学习windows游戏大师编程技巧这本书,看到画线条部分由于作者对具体算法并未详解,导致我看了半天硬是没看懂,后来在网上查了下,原来该算法是赫赫有名的Bresenham算法。好吧,只能怪自己孤陋寡闻了。没办法,只能花时间简单理解下该算法以便继续看我的书。

网上对该算法的解释很多,也很全,我就纯粹看过后练练手,写写自己的理解。

画线条无非就是在屏幕上点“点”, 连续的像素点就是线。我们将画线过程简化为画两个点,假设已知一个点,推出下一个点的位置,以此类推就能画出所有点,最后画成一条线。



先解释下上图,4个黑点为屏幕上的相邻像素点,2个蓝点为点的实际物理位置(由于像素是整数,我们只能点在黑点上,所以会产生误差),E为误差,m为斜率(因为一格为单位1,所以斜率
= m/1 = m )。左下黑点坐标为(x,y),左上为(x,y+1),右下为(x+1,y),右上为(x+1,y+1)。

开始理解该图:假设左边蓝点是已知的,那么,可以知道右边蓝点位置为(x+1,y+E+m)。 当蓝点位置高于单元格一半也就是0.5时,我们将点画在右上黑点,当小于0.5时,点在右下黑点:

情况1:

1、当 y+E+m > y+0.5 ==> E+m>0.5 ==> 2E+2m > 1 时 所画点为(x+1,y+1);

2、这个时候,我们再来看看该点误差是多少:E=y+E+m-(y+1) ==> E=E+m-1;

这样我们就又可以将该点作为已知点推算下一点了,因为都是一样的算法,所以可以作循环 。这里我们只需要得出循环算法即可。

代码初成:

for(int i=0;i<dx;i++)
{
x+=1;
if(2E+2m>1)
{
y += 1;
E=E+m-1;
}
else
{
...;
}
DrawPixel(x,y,color);        //画该像素
}


根据以上推算,我们已经初步得出了该函数 ,其中dx为两点之间横坐标的模

再分析:1、m = dy/dx;(斜率) ==> 2E+2m>1 ==> 2E+2dy/dx > 1 ==>2Edx + 2dy > dx;对于确定的两点作线,dx是定值,

设E' = 2Edx得到 E' >dx-2dy;

2、E=E+m-1 ==> E=E+dy/dx -1 ==> Edx=Edx+dy-dx ==> 2Edx=2Edx+2dy-2dx ==> E'=E'+2dy-2dx ;

继续优化代码

int dx = x1-x0;
int dy = y1-y0;
int e = 2*0*dx;   //(E'=2Edx) 因为第一个点是没有误差的,所以E=0;
for(int i=0;i<dx;i++)
{
x+=1;
if(e>(dx-2dy))
{
y += 1;
e += 2dy-2dx;
}
else
{
...;
}
DrawPixel(x,y,color);
}
这段代码离宝典上的代码还有点距离,我们继续分析其他情况

情况2:

1、当 y+E+m < y+0.5 ==>
E+m<0.5 ==> 2E+2m < 1 时 所画点为(x+1,y);

2、这个时候,我们再来看看该点误差是多少:E=y+E+m-y ==> E=E+m;

得出循环:

代码初成:
for(int i=0;i<dx;i++)
{
x+=1;
if(2E+2m<1)
{
//y=y;  //y不变
E=E+m;
}
else
{
...;
}
DrawPixel(x,y,color);        //画该像素
}


根据以上推算,我们已经初步得出了该函数 ,其中dx为两点之间横坐标的模

再分析:1、m = dy/dx;(斜率) ==> 2E+2m<1 ==> 2E+2dy/dx < 1 ==>2Edx + 2dy < dx;对于确定的两点作线,dx是定值,

设E' = 2Edx得到 E' < dx-2dy;

2、E=E+m==> E=E+dy/dx ==> Edx=Edx+dy==> 2Edx=2Edx+2dy ==> E'=E'+2dy ;

继续优化代码

int dx = x1-x0;
int dy = y1-y0;
int e = 2*0*dx;   //(E'=2Edx) 因为第一个点是没有误差的,所以E=0;
for(int i=0;i<dx;i++)
{
x+=1;
if(e<(dx-2dy))
{
e += 2dy;
}
else
{
...;
}
DrawPixel(x,y,color);        //画该像素
}
合并两种情况可得

int dx = x1-x0;
int dy = y1-y0;
int e = 2*0*dx;   //(E'=2Edx) 因为第一个点是没有误差的,所以E=0;
for(int i=0;i<dx;i++)
{
x+=1;
if(e>(dx-2dy))
{
y+=1;
e += 2dy-2dx;
}
else
{
e += 2dy;
}
DrawPixel(x,y,color);        //画该像素
}
让我们往书中的代码再靠近一步

int dx = x1-x0;
int dy = y1-y0;
int dx2 = dx<<1;//(2*dx)
int dy2 = dy<<1;//(2*dy)
int e = 2*0*dx;   //(E'=2Edx) 因为第一个点是没有误差的,所以E=0;
for(int i=0;i<dx;i++)
{
x+=1;
if(e>(dx-dy2))
{
y+=1;
e += dy2-dx2;                // 情况1
}
else
{
e += dy2;                    // 情况2
}
DrawPixel(x,y,color);        //画该像素
}
情况1和情况2都要加dy2,只不过情况1还要减dx2,所以继续靠近书中代码

int dx = x1-x0;
int dy = y1-y0;
int dx2 = dx<<1;//(2*dx)
int dy2 = dy<<1;//(2*dy)
int error = 2*0*dx;   //(E'=2Edx) 因为第一个点是没有误差的,所以E=0;
for(int i=0;i<dx;i++)
{
x+=1;
if(error>(dx-dy2))
{
y+=1;
error-= dx2;                // 情况1
}
error+= dy2;

DrawPixel(x,y,color);        //画该像素
}


这里仅仅只是作了dx>dy时的情况分析,dx<dy时,其实也是一样的,无非就是x,y换了下
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: