1. 首页 > 科技快讯 >

bresenham算法怎么读 bresenman算法

求一用C语言画直线的程序

不调用画图 API,用C 或 C++ 如何实现画一条线?

bresenham算法怎么读 bresenman算法bresenham算法怎么读 bresenman算法


将尺放在纸上用笔在上面轻轻一滑,完美的动作创造出完美的直线。简单,不做作的线条,虽不讨大多数人的喜欢,但总有自己的方向。

出生以来即为简单的我们,却很难在多变的环境中保持透明,总是莫名其妙地被改变着。手中的笔再不能画出直线,弯曲的弧度若隐若现,跌宕起伏着我们的心,再没有直线的痛快淋漓。

忽然之间,觉察出自己的陌生,我何时变得这样。面对着无可挽回的误会或者结局,我在心里歇斯底里地喊叫,我真的不是这样,亦不想。我努力地画着曾经相似的直线,不敢看他们异样的眼神,在众多圆滑的曲线显得莽撞、突兀。我没有轻舞飞扬的勇气,在讶异的`目光中翩翩起舞。我所做的只有在所谓自己的错误上或满心愧疚或保持沉默。他们逼着我画圆,可我只了解直线。

是不是现实中真的不允许直线的存在,它完美得难以看见。是不是我真的错了,总是将童年错当成现在。是不是人都需要掩饰,太过透明只会让自己变得愚笨可笑。或许我们都该永存于缄默,那样就不会让别人看到自己的懦弱。那么多的人在阻止我拿尺的手,拼命塞给我圆规,叫我画讨人喜欢的圆。我在他们面前成了一个犯了错的罪犯,只因为我不会画圆。见多了大大小小的圆,平滑的,有瑕疵的,熟练的,稚嫩的,连自己也觉得画直线的我可恶起来。我就这样莫名地恨起自己来,听到他们达到目的后满意的笑,我不屑。我说我失去了信仰,你们失去了自尊。相比之下谁更可怜。并且,恨一件事物不代表爱它的对立面,如果恨还可以隐藏在意,那么不屑真的是悲哀的轻视。

我不是一个有主见的人,时而画直线,时而画圆。因为我没有从一而终的勇气和聪明敏锐的洞察力,这样的结果只有一个:直线不是直线,圆不是圆。我们都在纷繁的嘈杂中茫然地活着,弄痛了别人,伤害了自己。

但我知道总有一天我们会心有不甘,不甘唤醒他们的自尊。只画自己所爱的,或者,用一张白纸蒙上自己的双眼。

梦的天空,不该是座迷宫。

直线画出彩虹,心向风般自由。

爱的天空,开不开心直说。

哭过后的笑容,难过丢向天空。

不能控制直线宽度;

坐标为整数;

只能对像素写入一个颜色,只用单色会有严重的锯齿效果。

在图形学中,除了以逐个图元(如直线)方式渲染,我们还可以通过对每个像素进行采样(sampling),换句话说,我们可对整个图像逐像素询问:「这个像素的颜色是什么?」

用采样方式画直线时,我们可以用一个具有面积的形状去表示「直线」,例如是长方形。但在本文中,我们使用胶囊体(capsule)去表示直线。这样就能解决上面前两个问题:(1) 可用胶囊体半径设置直线的宽度;(2) 坐标可以为浮点数。不过,用简单的采样方式,我们需要在每像素查询所有图元是否占有该像素,效率很低。

// 判断一点 (px, py) 是否在胶囊体(两端为(ax, ay)、(bx, by),半径 r)之中

int capsule(float px, float py, float ax, float ay, float bx, float by, float r) {

float pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay;

float h = fmaxf(fminf((pax bax + pay bay) / (bax bax + bay bay), 1.0f), 0.0f);

float dx = pax - bax h, dy = pay - bay h;

return dx dx + dy dy < r r;

}// 对坐标 (x, y) 进行采样

float sample(float x, float y) {

float s = 0.0f, cx = W 0.5f, cy = H 0.5f;

for (int j = 0; j < 5; j++) {

float r1 = fmaxf(w, h) (j + 0.5f) 0.085f;

float r2 = fmaxf(w, h) (j + 1.5f) 0.085f;

float t = j PI / 64.0f, r = (j + 1) 0.5f;

for (int i = 1; i <= 64; i++, t += 2.0f PI / 64.0f) {

float ct = cosf(t), st = sinf(t);

s = fmaxf(s, capsule(x, y, cx + r1 ct, cy - r1 st, cx + r2 ct, cy - r2 st, r) ? 1.0f : 0.0f);

}}

return s;

}int main() {

unsigned char p = img;

for (int y = 0; y < H; y++)

for (int x = 0; x < W; x++, p += 3)

p[0] = p[1] = p[2] = (unsigned char)((1.0f - sample(x, y)) 255);

sg(fopen("line_sampling.png", "wb"), W, H, img, 0);

}完整代码 line_sampling.c

渲染结果及中间局部放大:

注意从中间往外,直线的宽度为 1, 2, 3, 4, 5。我们甚至可以渲染宽度少于一个像素的直线。

3. 超采样

所谓锯齿,其实应称作信号混叠(aliasing)。抗混叠/抗锯齿(anti-aliasing)的简单方法是做更多采样,即超采样(supersampling)。我们可以沿用上一个例子中的采样函数 sample(),只是在每个像素内平均多个像素采样,例如以正方栅格方式做 次采样,但计算时间也变成 25 倍。

int main() {

unsigned char p = img;

int sw = 5, sh = 5;

for (int y = 0; y < H; y++)

for (int x = 0; x < W; x++, p += 3) {

float sum = 0;

for (int j = -sh / 2; j <= sh / 2; j++)

for (int i = -sw / 2; i <= sw / 2; i++)

sum += sample(x + (float)i / sw, y + (float)j / sh);

p[0] = p[1] = p[2] = (unsigned char)((1.0f - sum / (sw sh)) 255);

}sg(fopen("line_supersampling.png", "wb"), W, H, img, 0);

}完整代码 line_supersampling.c

渲染结果及中间局部放大:

这个结果可算是接近的了,但它的缺点就是性能极低。我们尝试另一个方法。

4. 带符号距离场

在上面的采样中,我们只用布尔值来表示一个坐标是否在形状之中。我们可以用带符号距离场(signed distance field, SDF)去表示坐标与形状的带符号距离(如正数代表坐标在形状以外,距离形状的短距离)。[2] 采用这种方式提升矢量图形的渲染品质。

在VC下运行的,bai

int dx,dy,incrE,incrNE,d,x,y;

if ((point[1].x-point[0].x)==0){ //垂直的直线

x=point[0].x;

for(y=point[0].y;y

pDC->SetPixel(x,y,50);

}else if(abs((point[1].y-point[0].y)/(point[1].x-point[0].x))<=1){ //斜率du -1到 1 之间

dx=point[1].x-point[0].x;

dy=point[0].y-point[1].y;

d=dx-2dy;

incrE=-2dy;

incrNE=2(dx-dy);

x=point[0].x,y=point[0].y;

pDC->SetPixel(x,y,50);

if(point[0].y>point[1].y){

while(x

{if(d>=0){

d+=incrE;

x++;

}else

{d+=incrNE;

x++;

y--;

}pDC->SetPixel(x,y,50);

}}

else if(point[0].y<=point[1].y){

dy=point[1].y-point[0].y;

incrE=-2dy;

incrNE=2(dx-dy);

x=point[0].x,y=point[0].y;

pDC->SetPixel(x,y,50);

while(x

{if(d>=0){

d+=incrE;

x++;

}else

{d+=incrNE;

x++;

y++;

}pDC->SetPixel(x,y,50);

}}

}else { //斜率 <-1 和 >1的直线

if(point[1].x>=point[0].x){

dx=point[1].x-point[0].x;

dy=point[1].y-point[0].y;

d=2dx-dy;

incrE=2dx;

incrNE=2(dx-dy);

x=point[0].x,y=point[0].y;

pDC->SetPixel(x,y,50);

while(x

{if(d<0){

d+=incrE;

y++;

}else

{d+=incrNE;

pDC->SetPixel(x,y,50);

x++;

y++;

}pDC->SetPixel(x,y,50);

}}

else if((point[1].y-point[0].y)/(point[1].x-point[0].x)<-1){

dx=point[1].x-point[0].x;

dy=point[0].y-point[1].y;

d=2dx-dy;

incrE=2dx;

incrNE=2(dx-dy);

x=point[0].x,y=point[0].y;

pDC->SetPixel(x,y,50);

while(y

{if(d>0){

d+=incrE;

y++;

}else

{d+=incrNE;

x--;

y++;

}pDC->SetPixel(x,y,50);

}}

}

这个算不算?^o^

#include

//

int main(void)

{printf("-------------------\n");

system("PAUSE");

return 0;

}

//欢迎在线讨论

#include

void main()

{int driver,mode;

driver=DETECT;

mode=0;

initgraph(&driver,&mode,"");

setcolor(15);

line(66,66,88,88);

lineto(100,100);

linerel(36,64);

getch();

restorecrtmode();

}

printf("%d,\n",line)

Bresenham直线演算法的演算方法

Bresenham直线算法描绘的直线。假设我们需要由 (x0, y0) 这一点,绘画一直线至右下角的另一点(x1, y1),x,y分别代表其水平及垂直坐标,并且 x1 - x0 > y1 - y0。在此我们使用电脑系统常用的坐标系,即x坐标值沿x轴向右增长,y坐标值沿y轴向下增长。

用C实现Bresenham算法生成直线和圆的程序(要求具体步骤有必要解述)

Bresenham算法生成直线

假定直线从(x1,y1)到(x2,y2),

令dx=x2-x1,dy=y2-y1

不妨设(dx,dy)在第一象限,并且直线的斜率不大于1

画线过程中有三个循环变量

x,y,d

初值

x=x1,y=y1,d=2dy-dx

循环,直到x==x2为止

{如果d>=0,y++,d+=2(dy-dx)

如果d<0 ,x++,d+=2dy

}如果(dx,dy)不在第一象限,要做变换,即先把第一象限的画出来

如果斜率大于1,x,y交换

非常简单的,很容易实现

圆的算法:

int Bres(int x0,int y0,double r,int color)

{int x,y,d;

x=0;

y=(int)r;

d=(int)(3-2r);

while(x

{cirpot(x0,y0,x,y,color);

if(d<0)

d+=4x+6;

else

{d+=4(x-y)+10;

y--;

}x++;

}if(x==y)

cirpot(x0,y0,x,y,color);

return(0);

}int cirpot(int x0,int y0,int x,int y,int color)

{setcolor(color);

putxicl((x0+x),(y0+y));

putxicl((x0+y),(y0+x));

putxicl((x0+y),(y0-x));

putxicl((x0+x),(y0-y));

putxicl((x0-x),(y0-y));

putxicl((x0-y),(y0-x));

putxicl((x0-y),(y0+x));

putxicl((x0-x),(y0+y));

setcolor(color);

return(0);

}这是圆的算法,你若要整个程序,把你的电邮给我,我给你发过去、

运行环境是Turboc 2.0

int Bresline(int x1,inty1,int x2,int y2,int color)

{int color,itag;

int dx,dy,tx,ty,inc1,inc2,d,curx,cury;

setcolor(color);

putxicl(x1,y1);

if(x1==x2&&y1==y2)

{setcolor(color);

return(1);

}itag=0;

dx=abs(x2-x1);

dy=abs(y2-y1);

if(dx

{itag=1;]

iswap(&x1,&y1);

iswap(&x2,&y2);

iswap(&dx,&dy);

}tx=(x2-x1)>0? 1:-1;

ty=(y2-y1)>0? 1:-1;

curx=x1;

cury=y1;

inc1=2dy;

inc2=2(dy-dx);

d=inc1-dx;

while(curx!x2)

{if(d<0)

{d+=inc1;

}else

{cury+=ty;

d+=inc2;

}if(itag)

setpixel(cury,curx);

else

setpixel(curx,cury);

curxd+=tx;

}setcolor(color);

return(0);

}iswap(inta,intb)

{int tmp;

tmp=a;

a=b;

b=tmp;

}这是直线的算法:和圆的不多,你可以参考一下:)

Bresenham画线算法

基本上Bresenham画线算法的思路如下:

//

假设该线段位于第一象限内且斜率大于0小于1,设起点为(x1,y1),终点为(x2,y2).

//

根据对称性,可推导至全象限内的线段.

1.画起点(x1,y1).

2.准备画下个点。x坐标增1,判断如果达到终点,则完成。否则,由图中可知,下个要画的点要么为当前点的右邻接点,要么是当前点的右上邻接点.

2.1.如果线段ax+by+c=0与x=x1+1的交点的y坐标大于M点的y坐标的话,下个点为U(x1+1,y1+1)

2.2.否则,下个点为B(x1+1,y1+1)

3.画点(U或者B).

4.跳回第2步.

5.结束.

这里需要细化的是怎么判断下个要画的点为当前点的右邻接点还是当前点的右上邻接点.

设线段方程:ax+by+c=0(x1

令dx=x2-x1,dy=y2-y1

则:斜率-a/b

=dy/dx.

从第一个点开始,我们有F(x,1,y1)

=ax1+by1+c=0

下面求线段ax+by+c=0与x=x1+1的交点:

由a(x1+1)+by+c

=0,

求出交点坐标y=(-c-a(x1+1))/b

所以交点与M的y坐标值Sub1

=(-c-a(x1+1))/b

-(y1+0.5)

=-a/b-0.5,即Sub1的处始值为-a/b-0.5。

则可得条件当

Sub1

=-a/b-0.5>0时候,即下个点为U.

反之,下个点为B.

代入a/b,则Sub1

=dy/dx-0.5.

因为是个循环中都要判断Sub,所以得求出循环下的Sub表达式,我们可以求出Sub的值的表达式.下面求x=x1+2时的Sub,即Sub2

1.如果下下个点是下个点的右上邻接点,则

Sub2

=(-c-a(x1+2))/b

-(y1+1.5)

=-2a/b

-1.5

故Sub值Dsub

=Sub2

-Sub1

=-2a/b

-1.5

-(-a/b-0.5)

=-a/b

-1.代入a/b得Dsub

=dy/dx

-1;

2.如果下下个点是下个点的右邻接点,

Sub2

=(-c-a(x1+2))/b

-(y1+0.5)

=-2a/b

-0.5

故Sub值Dsub

=Sub2

-Sub1

=-2a/b

-0.5

-(-a/b-0.5)

=-a/b.

代入a/b得Dsub

=dy/dx;

于是,我们有了Sub的处始值Sub1

=-a/b-0.5

=dy/dx-0.5,又有了Sub的值的表达式Dsub

=dy/dx

-1

(当Sub1

>0)或

dy/dx(当Sub1

<0).细化工作完成。

于是pcode可以细化如下:

//

Pcode

for

Bresenham

Line

//

By

SoRoMan

x=x1;

y=y1;

dx

=x2-x1;

dy

=y2-y1;

Sub

=dy/dx-0.5;

//

赋初值,下个要画的点与中点的值

DrawPixel(x,

y);

//

画起点

while(x

{x++;

if(Sub

>0)

//

下个要画的点为当前点的右上邻接点

{Sub

+=

dy/dx

-1;

//下下个要画的点与中点的值

y++;

//

右上邻接点y需增1

}else//

下个要画的点为当前点的右邻接点

{Sub

+=

dy/dx;

}//

画下个点

DrawPixel(x,y);

}PS:一般优化:

为避免小数转整数以及除法运算,由于Sub只是用来进行正负判断,所以可以令Sub

=2dxSub

=2dy-dx,则

相应的DSub

=2dy

-2dx或2dy.

思考1:如果Sub

=0时,会产生取两个点都可以的问题。这个问题还没深入。

基本上Bresenham画线算法的思路如下:

//

假设该线段位于第一象限内且斜率大于0小于1,设起点为(x1,y1),终点为(x2,y2).

//

根据对称性,可推导至全象限内的线段.

1.画起点(x1,y1).

2.准备画下个点。x坐标增1,判断如果达到终点,则完成。否则,由图中可知,下个要画的点要么为当前点的右邻接点,要么是当前点的右上邻接点.

2.1.如果线段ax+by+c=0与x=x1+1的交点的y坐标大于M点的y坐标的话,下个点为U(x1+1,y1+1)

2.2.否则,下个点为B(x1+1,y1+1)

3.画点(U或者B).

4.跳回第2步.

5.结束.

这里需要细化的是怎么判断下个要画的点为当前点的右邻接点还是当前点的右上邻接点.

设线段方程:ax+by+c=0(x1

令dx=x2-x1,dy=y2-y1

则:斜率-a/b

=dy/dx.

从第一个点开始,我们有F(x,1,y1)

=ax1+by1+c=0

下面求线段ax+by+c=0与x=x1+1的交点:

由a(x1+1)+by+c

=0,

求出交点坐标y=(-c-a(x1+1))/b

所以交点与M的y坐标值Sub1

=(-c-a(x1+1))/b

-(y1+0.5)

=-a/b-0.5,即Sub1的处始值为-a/b-0.5。

则可得条件当

Sub1

=-a/b-0.5>0时候,即下个点为U.

反之,下个点为B.

代入a/b,则Sub1

=dy/dx-0.5.

因为是个循环中都要判断Sub,所以得求出循环下的Sub表达式,我们可以求出Sub的值的表达式.下面求x=x1+2时的Sub,即Sub2

1.如果下下个点是下个点的右上邻接点,则

Sub2

=(-c-a(x1+2))/b

-(y1+1.5)

=-2a/b

-1.5

故Sub值Dsub

=Sub2

-Sub1

=-2a/b

-1.5

-(-a/b-0.5)

=-a/b

-1.代入a/b得Dsub

=dy/dx

-1;

2.如果下下个点是下个点的右邻接点,

Sub2

=(-c-a(x1+2))/b

-(y1+0.5)

=-2a/b

-0.5

故Sub值Dsub

=Sub2

-Sub1

=-2a/b

-0.5

-(-a/b-0.5)

=-a/b.

代入a/b得Dsub

=dy/dx;

于是,我们有了Sub的处始值Sub1

=-a/b-0.5

=dy/dx-0.5,又有了Sub的值的表达式Dsub

=dy/dx

-1

(当Sub1

>0)或

dy/dx(当Sub1

<0).细化工作完成。

于是pcode可以细化如下:

//

Pcode

for

Bresenham

Line

//

By

SoRoMan

x=x1;

y=y1;

dx

=x2-x1;

dy

=y2-y1;

Sub

=dy/dx-0.5;

//

赋初值,下个要画的点与中点的值

DrawPixel(x,

y);

//

画起点

while(x

{x++;

if(Sub

>0)

//

下个要画的点为当前点的右上邻接点

{Sub

+=

dy/dx

-1;

//下下个要画的点与中点的值

y++;

//

右上邻接点y需增1

}else//

下个要画的点为当前点的右邻接点

{Sub

+=

dy/dx;

}//

画下个点

DrawPixel(x,y);

}PS:一般优化:

为避免小数转整数以及除法运算,由于Sub只是用来进行正负判断,所以可以令Sub

=2dxSub

=2dy-dx,则

相应的DSub

=2dy

-2dx或2dy.

思考1:如果Sub

=0时,会产生取两个点都可以的问题。这个问题还没深入。

void Bresenhamline(int x1,int y1,int x2,int y2,int c,CDC pDC)//画直线

{int x,y,dx,dy,p,const1,const2,inc,tmp;

dx=x2-x1;

dy=y2-y1;

if(dxdy>=0)//准备x或y的单位递变值

inc=1;

else

inc=-1;

if(abs(dx)>=abs(dy))

{if(dx<0)

{tmp=x1;//将2a、3a象限方向的直线变换到1a、4a方向

x1=x2;

x2=tmp;

tmp=y1;

y1=y2;

y2=tmp;

dx=-dx;

dy=-dy;

}if(dy<0)

dy=-dy;

p=2dy-dx;

const1=2dy;

const2=2(dy-dx);

x=x1;

y=y1;

pDC->SetPixel(x,y,c);

while(x

{x++;

if(p<0)

p+=const1;

else

{y+=inc;

p+=const2;

}pDC->SetPixel(x,y,c);

}}

else

{if(dy<0)

{tmp=x1;//将3b、4b象限方向的直线变换到2b、1b方向

x1=x2;

x2=tmp;

tmp=y1;

y1=y2;

y2=tmp;

dx=-dx;

dy=-dy;

}if(dx<0)

dx=-dx;

p=2dx-dy;

const1=2dx;

const2=2(dx-dy);

x=x1;

y=y1;

pDC->SetPixel(x,y,c);

while(y

{y++;

if(p<0)

p+=const1;

else

{x+=inc;

p+=const2;

}pDC->SetPixel(x,y,c);

}}

}在View.h中找到下面内容并添加num和spt的定义

class CMyView : public CView

{protected: // create from serialization only

CMy072414View();

DECLARE_DYNCREATE(CMy072414View)

int num;//添加

CPoint spt[2];//添加

// Attributes

然后建立类向导,建立函数OnLButtonDown:

num=0;

void CMyView::OnLButtonDown(UINT nFlags, CPoint point)

{CDC pDC=GetDC();

CPen newpen(PS_SOLID,1,RGB(255,0,255));

CPen old=pDC->SelectObject(&newpen);

int c=RGB(255,0,255);

spt[num]=point;

if(num)

Bresenhamline(spt[num-1].x,spt[num-1].y,spt[num].x,spt[num].y,c,pDC);

else

num++;

CView::OnLButtonDown(nFlags, point);

}运行时只要单击两个点就会显示直线了~

请问中点bresenham算法画圆与bresenham算法画圆有区别吗?

Bresenham算法画圆:

Bresenham算法用来画直线非常方便,但上次也说了,Bresenham算法也可以用来显示圆和其他曲线,只需要把直线方程改成圆方程或者其他曲线的方程就行,具体的推理过程就不演示了,大体跟直线的不多!但由推算的结果可以看出,用Bresenham算法来画圆的确是不大明智的做法,要计算的步骤太多,计算速度比专门的画圆方法慢很多!并且在斜率越大的地方像素的间距就越大,当然我们可以在画某个像素之前先判断一下这一点跟前面一点的连线的斜率,然后在适当的时候交换x、y的坐标,但这样计算量必将增加!

直接给出Bresenham画圆的代码:

#include

#include

#include

void draw_pixel(int ix,int iy)

{glBegin(GL_POINTS);

glVertex2i(ix,iy);

glEnd();

}//int inline round(const float a){return int (a+0.5);}

void Bresenham(int x1,int y1,int r,double a,double b,double c)/圆心在(x1,y1),半径为r的圆/

{glColor3f(a,b,c);

int dx=r;//int dy=abs(yEnd-y1);

// int p=2dy-dx;

// int twoDy=2dy,twoDyMinusDx=2dy-2dx;

int x,y,d1,d2;

/ if (x1>xEnd)

{x=xEnd;y=yEnd;

xEnd=x1;

}else

{x=x1;

y=y1;

}/

x=x1;

y=y1+r;

draw_pixel(x1,y1);

draw_pixel(x,y);//起始点装入帧缓存,起始点是圆的上面一点,然后按顺时针来画

while(x<=x1+dx)

{d1=y1+sqrt(pow(r,2)-pow(x-x1,2));/ lower /

x++;

d2=2(y1+sqrt(pow(r,2)-pow(x-x1,2)))-2d1-1;/ lower-upper /

if(1)

{y=d1;

draw_pixel(x,y);

draw_pixel(x,2y1-y);

draw_pixel(2x1-x,y);

draw_pixel(2x1-x,2y1-y);

}else

{y++;

//p+=twoDyMinusDx;

draw_pixel(x,y);

}}

}void display()

{glClear(GL_COLOR_BUFFER_BIT);

Bresenham(250,250,200,0.0,0.0,1.0);

Bresenham(300,250,150,1.0,0.0,0.0);

Bresenham(200,250,150,0.0,1.0,0.0);

//Bresenham(250,300,150,0.8,0.4,0.3);

//Bresenham(250,200,150);

glFlush();

}void myinit()

{glClearColor(0.8,1.0,1.0,1.0);

//glColor3f(0.0,0.0,1.0);

glPointSize(1.0);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(0.0,500.0,0.0,500.0);

}void main(int argc,char argv )

{glutInit(&argc,argv);

glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);

glutInitWindowSize(500,500);

glutInitWindowPosition(200.0,200.0);

glutCreateWindow("CG_test_Bresenham_Circle example");

glutDisplayFunc(display);

myinit();

glutMainLoop();

}以下为程序运行效果:

中点画圆:

用光栅画圆的不足在上次已经用实例表示的很明白了,上次画的那个圆怎么都不能算满意,虽然可以通过修改算法来得到改善,但本来计算步骤就已经很多了,交换坐标重新计算将会大大增加计算机的就是负担,为此我们采用另一种更加常用的画圆算法——中点画圆算法,之所以叫做“中点”画圆算法是由于它不是像Bresenham算法那样所绘像素不是(xk+1,yk)就是(xk+1,yk+1),而是根据这两个点的中点来判断是(xk+1,yk)还是(xk+1,yk-1)更接近于圆!

对于给定的半径r和圆心(x0,y0),我们先计算圆心在原点(0,0)的点,然后将其平移到圆心(x0,y0)处即可,跟Bresenham算法一样,我们也可以借助圆的高度对称性来减少计算机的计算步骤,在这里我们可以先计算出八分之一圆的像素点,然后根据对称性绘出其他点。这样可以大大加快画圆的速度!

跟光栅化方法一样,我们还是采用步进的方法来逐点描绘,但这里的决策参数计算方式跟Bresenham不大一样,设决策参数为p,则:

P=x2+y2-r2

对于任一个点(x,y),可以根据p的符号来判断点是在圆内还是圆外还是在圆上,这里不多说,假设我们在(xk,yk)处绘制了一个像素,下一步需要确定的是(xk+1,yk)还是(xk+1,yk-1)更接近于圆,在此代入这两个点的中点来求出决策参数:

Pk=(xk+1)2+(yk -1/2)2-r2

如果Pk<0,则yk上的像素更接近于圆,否则就是yk-1更接近于圆

同理可以推出Pk+1= Pk +2(xk+1)+(yk +12-yk2)-(yk +1-yk)+1

给出一个示例,这个圆比用Bresenham画出来的好看多了:

#include

class screenPt

{private:

int x,y;

public:

screenPt(){ x=y=0;}

void setCoords(GLint xCoordValue,GLint yCoordValue)

{x=xCoordValue;

y=yCoordValue;

}GLint getx()const

{return x;

}GLint gety()const

{return y;

}void incrementx(){x++;}

void decrementy(){y--;}

};

void draw_pixel(int xCoord,int yCoord)

{glBegin(GL_POINTS);

glVertex2i(xCoord,yCoord);

glEnd();

}void circleMidpoint(GLint xc,GLint yc,GLint radius)

{screenPt circPt;

GLint p=1-radius;

circPt.setCoords(0,radius);

void circlePlotPoints(GLint ,GLint, screenPt);

circlePlotPoints(xc,yc,circPt);

while (circPt.getx()

{circPt.incrementx();

if(p<0)

p+=2circPt.getx()+1;

else

{circPt.decrementy();

p+=2(circPt.getx()-circPt.gety())+1;

}circlePlotPoints(xc,yc,circPt);

}}

void circlePlotPoints(GLint xc,GLint yc,screenPt circPt)//描绘八分圆各点

{draw_pixel(xc+circPt.getx(),yc+circPt.gety());

draw_pixel(xc-circPt.getx(),yc+circPt.gety());

draw_pixel(xc+circPt.getx(),yc-circPt.gety());

draw_pixel(xc-circPt.getx(),yc-circPt.gety());

draw_pixel(xc+circPt.gety(),yc+circPt.getx());

draw_pixel(xc-circPt.gety(),yc+circPt.getx());

draw_pixel(xc+circPt.gety(),yc-circPt.getx());

draw_pixel(xc-circPt.gety(),yc-circPt.getx());

}void display()

{//screenPt Pt;

glClear(GL_COLOR_BUFFER_BIT);

circleMidpoint(250,250,200);

glFlush();

}void myinit()

{glClearColor(0.8,1.0,1.0,1.0);

glColor3f(0.0,0.0,1.0);

glPointSize(1.0);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(0.0,500.0,0.0,500.0);

}void main(int argc,char argv )

{glutInit(&argc,argv);

glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);

glutInitWindowSize(500,500);

glutInitWindowPosition(200.0,200.0);

glutCreateWindow("CG_test_中点画圆 example");

glutDisplayFunc(display);

myinit();

glutMainLoop();

}运行效果:

关于Bresenham算法的求助

void Bresenham_Line(x0, y0, x1, y1, color)

int x0, y0, x1, y1, color;

{ int x, y, dx, dy;

float k, e;

dx=x1-x0; dy=y1-y0;

k=dy/dx; e=-0.5;

x=x0; y=y0;

for(i=0; i<=dx; i++)

{ putpixel(x, y, color);

x=x+1; e=e+k;

if(e>=0)

{ y=y+1; e=e-1;

} }}

算法原理:过各行、 各列像素中心构造一组虚拟网格线, 按直线从起点到终点的顺序计算直线与各垂直网格线的交点, 然后确定该列像素中与该交点近的像素

使用整数Bresenham算法光栅化直线起点P0(2,1)到终点P1(14,10)。1)主位移方向?

在使用整数Bresenham算法光栅化直线之前,我们需要确定主位移方向。这是指在每个步骤中哪个方向上的像素会被涂色或改变状态。

对于起点P0(2,1)到终点P1(14,10),我们可以通过将两个点的坐标相减并取来得出线段的斜率。即:$|m| = \frac{|y_1-y_0|}{|x_1 - x_0|} = \frac{|10-1|}{|14-2|} = \frac{9}{12} = \frac{3}{4}$

由于斜率小于1,则主要位移方向为水平方向(即x轴方向),而次要位移方向为竖直方向(即y轴方向)。因此,在每个步骤中,x值将增加1,而y值将根据Bresenham算法的规则增加或不变。

现在可以开始使用整数Bresenham算法光栅化直线从起点P0(2,1)到终点P1(14,10)。具体步骤如下:

1. 计算Δx和Δy

$\Delta x = x_1 - x_0 = 14 - 2 = 12$

$\Delta y = y_1 - y_0 = 10 - 1 = 9$

2. 计算初始决策参数d

$d_{ini} = 2\Delta y - \Delta x = 2(9) - 12 = 6$

3. 在起点P0(2,1)处绘制像素,即(x,y)=(2,1)

4. 接下来,根据Bresenham算法的规则,在主位移方向上逐步增加x值,同时在次要位移方向上根据决策参数d的值决定是否增加y值。

5. 对于每个新的像素位置,根据该像素是否涂色或改变状态来确定决策参数d的更新规则。

6. 重复步骤4和步骤5直到达到终点P1(14,10)。

下面是按照以上步骤为P0(2,1)到P1(14,10)计算出的结果(每个步骤中应该绘制的像素位置):

Step 1: (x,y)=(2,1)

Step 2: (x,y)=(3,1), d=12

Step 3: (x,y)=(4,2), d=18

Step 4: (x,y)=(5,3), d=10

Step 5: (x,y)=(6,4), d=14

Step 6: (x,y)=(7,5), d=6

Step 7: (x,y)=(8,6), d=-2

Step 8: (x,y)=(9,7), d=-8

Step 9: (x,y)=(10,8), d=-12

Step 10: (x,y)=(11,9), d=-14

Step 11: (x,y)=(12,10), d=-14

Step 12: (x,y)=(13,10), d=-18

Step 13: (x,y)=(14,10)

因此,从起点P0(2,1)到终点P1(14,10)的整数Bresenham算法光栅化直线的像素位置为:(2,1), (3,1), (4,2), (5,3), (6,4), (7,5), (8,6), (9,7), (10,8), (11,9), (12,10), (13,10)和(14,10)。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至836084111@qq.com 举报,一经查实,本站将立刻删除。

联系我们

工作日:9:30-18:30,节假日休息