A. 怎样用C语言画圆
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char arg[50]={0};
arg[0]= '\ " ';
strcpy(arg+1,argv[0]);
int len=int(strlen(arg));
arg[len]= '\ " ';
HWND hWnd=FindWindow(NULL,arg); //找到程序运行窗口的句柄
HDC hDC=GetDC(hWnd);//通过窗口句柄得到该窗口的设备场境句柄
HPEN hPen,hOldPen; //画笔
int i=0;
for(;i <500;++i)
SetPixel(hDC,10+i,10+i,0x0000ff);//用画点的办法画一根线,最后一个参数是颜色(32位)
hPen=CreatePen(PS_SOLID,2,0x00ff00);//生成绿色画笔
hOldPen=(HPEN)SelectObject(hDC,hPen);//把画笔引入设备场境
MoveToEx(hDC,20,50,NULL); //设置画线起点
LineTo(hDC,520,550); //画到终点
Arc(hDC,100,100,300,300,350,500,350,500);//画圆
SelectObject(hDC,hOldPen);
ReleaseDC(hWnd,hDC);
//下面是对比,表明它确实是控制台程序
printf( "hello console ");
system( "pause ");
return 0;
}
B. 关于Bresenham算法的求助
今天一下子遇到三个类似的问题,所以我这篇东西就连续复制粘贴了三遍:
(下面的坐标本来是有下标的,但复制过来就变没了,你可能看的有点晕)
Bresenham算法是Bresenham提出的一种光栅线生成算法!
DDA算法表面上看起来很有效,并且代码也比较容易实现,但是显示每个像素都需要进行一次浮点数加法运算,而Bresenham算法的最大优点是不需要进行浮点数运算!这是一种精确而有效的光栅线生成算法,该算法仅使用增量整数计算,计算速度比DDA要快,另外,Bresenham算法还可用于显示圆和其他曲线,这里暂时只显示直线!
与DDA一样,我们假设线段的两个端点坐标是整数值(x0,y0)(xEnd,yEnd),且斜率m满足0<=m>=1!坐标轴的垂直轴表示扫描线位置,水平轴标识像素列,假设以单位x间隔取样,需要确定下一个每次取样时两个可能的像素位置中的哪一个更接近于线路径!
从给定线段的左端点(x0,y0)开始,逐步处理每个后继列(x位置),并在其扫描线y值最接近线段的像素处描出一点,假如已经确定要显示的像素在(xk,yk),那么下一步就要确定在列xk+1=xk+1上绘制哪个像素,是在位置(xk+1,yk)还是在(xk+1,yk+1)
在取样位置xk+1,我们使用dlower和pper来标识两个像素与数学上线路径的垂直偏移(就是通过这两个值来比较哪个点离线上的点最近,以下推导过程你可能看得有点晕,但都是为了推出后续的点而已,你可以结合下面例子程序中的Bresenham函数来看),在像素列xk+1处的直线上的y坐标根据直线方程可计算得:
y=m(xk+1)+b
那么可求得:
dlower=y-yk=m(xk+1)+b-yk
pper=(yk+1)-y=yk+1-m(xk+1)-b
令斜率m=dy/dx,引入决策参数Pk,定义为:
Pk=dx(dlower-pper)
=2dx*xk-2dy*yk+c
C是一个常数,值为2dx+dx(2b-1)
由此可以计算得到
pk+1=Pk+2dy-2dx(yk+1-yk)
其中yk+1-yk取0还是取1取决于参数Pk的符号,Pk为负时取0,Pk非负时取1!
而Pk为负时,下一个要绘制的点就是(xk+1,yk)且pk+1=Pk+2dy
Pk为非负时则下一个要绘制的点就是(xk+1,yk+1)且pk+1=Pk+2dy-2dx
至此,Bresenham算法介绍完毕,以下为某个示例:
#include<gl/glut.h>
#include<math.h>
#include<stdio.h>
voiddraw_pixel(intix,intiy)
{
glBegin(GL_POINTS);
glVertex2i(ix,iy);
glEnd();
}
voidBresenham(intx1,inty1,intxEnd,intyEnd)
{
intdx=abs(xEnd-x1),dy=abs(yEnd-y1);
intp=2*dy-dx;
inttwoDy=2*dy,twoDyMinusDx=2*dy-2*dx;
intx,y;
if(x1>xEnd)
{
x=xEnd;y=yEnd;
xEnd=x1;
}
else
{
x=x1;
y=y1;
}
draw_pixel(x,y);
while(x<xEnd)
{
x++;
if(p<0)
p+=twoDy;
else
{
y++;
p+=twoDyMinusDx;
draw_pixel(x,y);
}
}
}
voiddisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
Bresenham(0,0,400,400);
glFlush();
}
voidmyinit()
{
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);
}
voidmain(intargc,char**argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutInitWindowPosition(200.0,200.0);
glutCreateWindow("CG_test_Bresenham_Lineexample");
glutDisplayFunc(display);
myinit();
glutMainLoop();
}
运行效果:
C. 【译】Bresenham 直线算法
原文链接: deepnight.net/tutorial/.../ 作者:Sébastien Bénard 译者:mnikn
1. 什么是 Bresenham 直线算法
Bresenham 直线算法是一种广泛使用的算法,它在绘制两点之间的直线时特别有效,尤其适合于像素网格。其优点是生成的直线像素精确,看起来非常酷。此外,该算法还应用于多种其他用途,包括 AI、寻路算法优化、路径平滑、视野检测、光照检测等。
2. 实现
基于 Haxe 的实现可以在我的 GitHub 仓库中找到:BRESENHAM.HXFrom deepnightLibs on GitHub
以下是一个基于 Haxe 的示例代码:
注意:fastAbs 和 fastFloor 是绝对值和向下取整的高效实现。
要使用 Bresenham 算法,您可以参考维基网络上关于常规实现的资料,并在必要时对算法进行特殊优化。下面是一些使用案例:
3. 使用案例
3.1. AI
在编写怪物敌人的 AI 时,可以使用 Bresenham 直线算法来检测视野中的障碍物。例如,通过在直线路径上检查每个点来确定是否有障碍物。
3.2. 寻路算法优化
在实现寻路算法(如 A-star 算法)时,可以使用 Bresenham 直线算法来减少不必要的寻路调用,从而提高性能。
3.3. 平滑寻路路径
通过应用 Bresenham 直线算法,可以优化寻路算法结果,使其路径看起来更加平滑。
3.4. 视野检测(圆锥)
可以使用 Bresenham 直线算法实现像 Metal Gear Solid 或 Commando 那样的圆锥视野检测,检测敌人是否能看到玩家。
3.5. 关于对角的注意点
应注意,即使游戏中有对角的墙,基本的 Bresenham 直线算法也能穿透它们。
3.6. 光照
使用 Bresenham 直线算法检测火炬是否能照亮特定点,可以实现动态光照效果。预计算光照值可以显着降低性能开销。
3.7. Pixel perfect 的圆
Bresenham 直线算法不仅可以绘制直线,还可以用于绘制圆。虽然它不如直线常用,但在某些情况下仍然有用,而且实现相对简单。
总结:Bresenham 直线算法是一个多功能的算法,广泛应用于游戏开发中的多种场景。虽然现代游戏引擎可能内置了类似功能,但了解 Bresenham 算法的基本原理和应用依然非常有价值。
D. 请问中点bresenham算法画圆与bresenham算法画圆有区别吗
Bresenham算法画圆:
Bresenham算法用来画直线非常方便,但上次也说了,Bresenham算法也可以用来显示圆和其他曲线,只需要把直线方程改成圆方程或者其他曲线的方程就行,具体的推理过程就不演示了,大体跟直线的差不多!但由推算的结果可以看出,用Bresenham算法来画圆的确是不大明智的做法,要计算的步骤太多,计算速度比专门的画圆方法慢很多!并且在斜率越大的地方像素的间距就越大,当然我们可以在画某个像素之前先判断一下这一点跟前面一点的连线的斜率,然后在适当的时候交换x、y的坐标,但这样计算量必将增加!
直接给出Bresenham画圆的代码:
#include<gl/glut.h>
#include<math.h>
#include<stdio.h>
voiddraw_pixel(intix,intiy)
{
glBegin(GL_POINTS);
glVertex2i(ix,iy);
glEnd();
}
//intinlineround(constfloata){returnint(a+0.5);}
voidBresenham(intx1,inty1,intr,doublea,doubleb,doublec)/*圆心在(x1,y1),半径为r的圆*/
{
glColor3f(a,b,c);
intdx=r;//intdy=abs(yEnd-y1);
//intp=2*dy-dx;
//inttwoDy=2*dy,twoDyMinusDx=2*dy-2*dx;
intx,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)))-2*d1-1;/*lower-upper*/
if(1)
{
y=d1;
draw_pixel(x,y);
draw_pixel(x,2*y1-y);
draw_pixel(2*x1-x,y);
draw_pixel(2*x1-x,2*y1-y);
}
else
{
y++;
//p+=twoDyMinusDx;
draw_pixel(x,y);
}
}
}
voiddisplay()
{
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();
}
voidmyinit()
{
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);
}
voidmain(intargc,char**argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutInitWindowPosition(200.0,200.0);
glutCreateWindow("CG_test_Bresenham_Circleexample");
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<glglut.h>
classscreenPt
{
private:
intx,y;
public:
screenPt(){x=y=0;}
voidsetCoords(GLintxCoordValue,GLintyCoordValue)
{
x=xCoordValue;
y=yCoordValue;
}
GLintgetx()const
{
returnx;
}
GLintgety()const
{
returny;
}
voidincrementx(){x++;}
voiddecrementy(){y--;}
};
voiddraw_pixel(intxCoord,intyCoord)
{
glBegin(GL_POINTS);
glVertex2i(xCoord,yCoord);
glEnd();
}
voidcircleMidpoint(GLintxc,GLintyc,GLintradius)
{
screenPtcircPt;
GLintp=1-radius;
circPt.setCoords(0,radius);
voidcirclePlotPoints(GLint,GLint,screenPt);
circlePlotPoints(xc,yc,circPt);
while(circPt.getx()<circPt.gety())
{
circPt.incrementx();
if(p<0)
p+=2*circPt.getx()+1;
else
{
circPt.decrementy();
p+=2*(circPt.getx()-circPt.gety())+1;
}
circlePlotPoints(xc,yc,circPt);
}
}
voidcirclePlotPoints(GLintxc,GLintyc,screenPtcircPt)//描绘八分圆各点
{
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());
}
voiddisplay()
{
//screenPtPt;
glClear(GL_COLOR_BUFFER_BIT);
circleMidpoint(250,250,200);
glFlush();
}
voidmyinit()
{
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);
}
voidmain(intargc,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();
}
运行效果:
E. C语言用Bresenham算法画圆,哪位高手教教,主要是算法里的内容,谢谢!
的确哈,关键在于对delta的理解
可以看到,都是delta=2*(1-radius)这样的,起作用应该是判断要画的点x、y坐标的变化趋势,先把我注释了的代码贴下,加了getch();可以看到画的过程
-----------------------------------------------------------------
#include<graphics.h>
#include<stdio.h>
void BresenhemCircle(int centerx, int centery, int radius, int color, int type);
void main()
{
int drive=DETECT,mode;
int i,j;
initgraph(&drive,&mode,"");
BresenhemCircle(300,200,100,15,0);
getch();
}
void BresenhemCircle(int centerx, int centery, int radius, int color, int type)
{
int x =type = 0;/*初始横坐标为原点*/
int y = radius; /*初始纵坐标远离原点*/
int delta = 2*(1-radius);
int direction;
while (y >= 0)
{
getch();
if (!type)/*执行*/
{
/*在上半圆画两点*/
putpixel(centerx+x, centery+y, color);
putpixel(centerx-x, centery+y, color);
/*在下半圆画两点*/
putpixel(centerx-x, centery-y, color);
putpixel(centerx+x, centery-y, color);
getch();
}
else/*不执行*/
{
line(centerx+x, centery+y, centerx+x, centery-y);
line(centerx-x, centery+y, centerx-x, centery-y);
getch();
}
/*以下代码设置下次四点的位置,圆是对称的,且此方法相当于同时画四个圆弧
观察右上方圆弧可知,前一半是x增的要快些,后一半是y减的快些*/
if (delta < 0)
{
if ((2*(delta+y)-1) < 0)
direction = 1; /*选择横向加*/
else
direction = 2;
}
else if(delta > 0)
{
if ((2*(delta-x)-1) > 0)
direction = 3; /*选择纵向减*/
else
direction = 2;
}
else
direction=2;
switch(direction)
{
case 1:
x++;/*只横坐标远离原点*/
delta += (2*x+1); /*小执行到这,所以加*/
break;
case 2:
x++;
y--;/*横向远离,同时纵向靠近*/
delta += 2*(x-y+1); /*即(2*x+1)+(-2*y+1)*/
break;
case 3:
y--;/*只纵坐标靠近原点*/
delta += (-2*y+1); /*大执行到这,所以减*/
break;
}
}
}