这一节进入了数学阶段,画线,主要介绍了BRESENHAM算法,开始于(x0,y0)点,但是不采用斜率,它先在X轴方向移动1个像素,然后决定如何移动Y方向的像素,以使所描绘的直线尽量接近于实际的线,这是通过使用一个衡量光栅化得出的直线与实际直线之间的接近程度的误差项来实现的,该算法对误差项不断调整,以使得数字化的光栅线能够尽量直接接近直接直线,
可以做一些改进,将初始点的位置考虑进去,然后把误差值设置在最小误差和最大误差之间,可以将误差设为0.5,但是由于现在使用的是整数量,所以必须将它乘以2,并将dx和dy的影响考虑进来。误差项调整如下:
X轴方向
Error = 2 * dy – dx
Y轴方向
Error = 2 * dx – dy
代码如下所示
int Draw_Line( int x0, int y0, int x1, int y1, UCHAR color, UCHAR * vb_start, int lpitch )
{
int dx,dy, dx2, dy2, x_inc, y_inc, error,index;
vb_start = vb_start + x0 + y0 * lpitch;
dx = x1- x0;
dy = y1 - y0;
if( dx >= 0 )
{
x_inc = 1;
}
else
{
x_inc = -1;
dx = -dx;
}
if( dy >= 0 )
{
y_inc = lpitch;
}
else
{
y_inc = -lpitch;
dy = -dy;
}
dx2 = dx << 1;
dy2 = dy << 1;
if( dx > dy )
{
error = dy2 - dx;
for( index = 0; index <= dx; index ++ )
{
* vb_start = color;
if( error >= 0 )
{
error -= dx2;
vb_start+= y_inc;
}
error += dy2;
vb_start += x_inc;
}
}
else
{
error = dx2 - dy;
for( index = 0; index <= dy; index++ )
{
*vb_start = color;
if( error >= 0 )
{
error -= dy2;
vb_start+= x_inc;
}
error += dx2;
vb_start += y_inc;
}
}
return ( 1 );
}
上述函数,可以分为三部分,第一部分是处理正负号和端点交换,并计算X,Y,轴的增量,然后,根据线的主要走向,即根据dx>dy还是dy>dx,两个主循环其中的一个把线绘制出来。
结果如下图所示,
OK,现在看看如何封装起来。
首先加一个成员函数
int DDRAW_Interface::Draw_Line( int x0, int y0, int x1, int y1, UCHAR color, UCHAR * vb_start, int lpitch )
{
int dx,dy, dx2, dy2, x_inc, y_inc, error,index;
vb_start = vb_start + x0 + y0 * lpitch;
dx = x1- x0;
dy = y1 - y0;
if( dx >= 0 )
{
x_inc = 1;
}
else
{
x_inc = -1;
dx = -dx;
}
if( dy >= 0 )
{
y_inc = lpitch;
}
else
{
y_inc = -lpitch;
dy = -dy;
}
dx2 = dx << 1;
dy2 = dy << 1;
if( dx > dy )
{
error = dy2 - dx;
for( index = 0; index <= dx; index ++ )
{
* vb_start = color;
if( error >= 0 )
{
error -= dx2;
vb_start+= y_inc;
}
error += dy2;
vb_start += x_inc;
}
}
else
{
error = dx2 - dy;
for( index = 0; index <= dy; index++ )
{
*vb_start = color;
if( error >= 0 )
{
error -= dy2;
vb_start+= x_inc;
}
error += dx2;
vb_start += y_inc;
}
}
return ( 1 );
}
在game_main()中
int Game_Main(void *parms = NULL, int num_parms = 0)
{
// this is the main loop of the game, do all your processing
// here
// make sure this isn't executed again
if (window_closed)
return(0);
// for now test if user is hitting ESC and send WM_CLOSE
if (KEYDOWN(VK_ESCAPE))
{
PostMessage(main_window_handle,WM_CLOSE,0,0);
window_closed = 1;
} // end if
ddraw->DDraw_Lock_Primary_Surface();
for( int index = 0; index < 1000; index ++ )
{
ddraw->Draw_Line( rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT,
rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT,
rand() % 256,
ddraw->getPrimarybuffer(),ddraw->getPrimarylpitch() );
}
ddraw->DDraw_Unlock_Primary_Surface();
Sleep( 33 );
// do nothing -- look at pretty picture
// return success or failure or your own return code here
return(1);
} // end Game_Main
运行OK
下一步看看T3DLIB,发现还有个16位的函数
int DDRAW_Interface::Draw_Line16( int x0, int y0, int x1, int y1, int color, UCHAR * vb_start, int lpitch )
{
int dx,dy, dx2, dy2, x_inc, y_inc, error,index;
int lpitch_2 = lpitch >> 1;
USHORT * vb_start2 = ( USHORT * )vb_start + x0 + y0 * lpitch_2;
dx = x1- x0;
dy = y1 - y0;
if( dx >= 0 )
{
x_inc = 1;
}
else
{
x_inc = -1;
dx = -dx;
}
if( dy >= 0 )
{
y_inc = lpitch_2;
}
else
{
y_inc = -lpitch_2;
dy = -dy;
}
dx2 = dx << 1;
dy2 = dy << 1;
if( dx > dy )
{
error = dy2 - dx;
for( index = 0; index <= dx; index ++ )
{
* vb_start2 = ( USHORT )color;
if( error >= 0 )
{
error -= dx2;
vb_start2+= y_inc;
}
error += dy2;
vb_start2 += x_inc;
}
}
else
{
error = dx2 - dy;
for( index = 0; index <= dy; index++ )
{
*vb_start2 =(USHORT) color;
if( error >= 0 )
{
error -= dy2;
vb_start2+= x_inc;
}
error += dx2;
vb_start2 += y_inc;
}
}
return ( 1 );
}
在GAME_MAIN()里,是 ddraw->Draw_Line16( rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT,
rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT,
RGB( rand() % 256, rand() % 256, rand() % 256 ),
ddraw->getPrimarybuffer(),ddraw->getPrimarylpitch() );
即RGB(r,g,b)代替以前的rand()%256
Ok