从头开始绘制大圆圈

时间:2012-10-29 11:38:03

标签: c++ graphics drawing 2d geometry

我是C ++的新手,但这种语言对我来说似乎没问题。作为一个学习项目,我决定制作一个小型2D图形引擎。这似乎是一个艰难的项目,但我知道如何继续前进。

我还没有真正开始,但是当我遇到这个问题时,我正在脑子里形成一些东西: 在某些时候,我将不得不创建一个在屏幕上绘制圆圈的功能。我现在的方法是这样的:

in a square with sides from (x-r) to (x+r) loop through x and y,
if at each point, the current distance sqr(x^2+y^2) is less than or equal to r
, then draw a pixel at that point.

如果没有,这会有用,不要打扰告诉我,我会弄清楚的。如果x + r& y + r在屏幕上。

问题在于我有时需要画大圈。例如,如果我需要绘制半径为5000的圆,则(如果像素循环计算需要循环总共10000 ^ 2次)。因此,对于2Ghz的处理器,这个单圈只能渲染2Ghz /(10000 ^ 2),这是〜22次/秒,同时占据整个核心(相信它只需要每个像素一次计算,这是无处可去的真相)。

现在哪种方法是正确的?我想这与使用GFX进行这些简单的计算有关。如果是这样,我可以使用OpenGL for C ++吗?我也想了解一下:)

4 个答案:

答案 0 :(得分:6)

我的第一个C / C ++项目实际上也是图形库。我没有OpenGL或DirectX,当时正在使用DOS。我从中学到了很多东西,因为我不断发现新的,更好(更快)的方式来绘制到屏幕上。

现代操作系统的问题在于它们并不能真正让你做我当时所做的事情。您不能直接开始使用硬件。坦率地说,这些天你不再想要了。

你仍然可以自己画一切。如果你想放置自己的像素,请看看SDL。这是一个C库,您可以将其包装到您自己的C ++对象中。它适用于不同的平台(包括Linux,Windows,Mac,...),在内部它将使用DirectX或OpenGL之类的东西。

对于真实世界的图形,人们不仅仅是绘制一个自己的像素。那效率不高。或者至少不在不能直接使用硬件的设备上......

但为了您的目的,我认为SDL绝对是您要走的路!祝你好运。

答案 1 :(得分:2)

你不是通过手动将像素绘制到屏幕来做图形,这就是疯狂。

您要使用的是DirectX或OpenGL。我建议你破解谷歌并去阅读,那里有很多东西要读。

一旦你下载了libs,就会看到很多样本项目,它们会让你入门。

此时有两种方法:计算矢量的数学方法描述了具有大量边的形状(即它看起来像一个圆)。或者有一种“作弊”方法,只是将一个圆形的纹理(即图片)绘制到屏幕上,并使用alpha通道使纹理的其余部分透明。 (作弊方法更容易编码,执行速度更快,并且产生更好的结果,尽管灵活性较差)。

如果您想以数学方式进行,那么这两个库都允许您在屏幕上绘制线条,因此您需要从每条线的起点和终点视图开始接近,而不是单个像素。我,你想要矢量图形。

我现在不能做重数学,但矢量方法可能看起来有点像(sudo-code):

in-param: num_of_sides, length_of_side;
float angle = 360 / num_of_sides;
float start_x = 0;
float start_y = 0;

x = startX;
y = startX;
for(int i(0); i < num_of_sides; ++i)
{
  float endX, endY;
  rotateOffsetByAngle(x, y, x + lenth_of_side, y, angle * i, endX, endY);
  drawline(x, y, endX, endY);
  x = endX;
  y = endY;
}


drawline(float startX, startY, endX, endY)
{
  //does code that draws line between the start and end coordinates;
}

rotateOffsetByAngle(float startX, startY, endX, endY, angle, &outX, &outY)
{
  //the in-parameters startX, startY and endX, endY describe a line
  //we treat this line as the offset from the starting point

  //do code that rotates this line around the point startX, startY, by the angle.
  //after this rotation is done endX and endY are now at the same
  //distance from startX and startY that they were, but rotated.

  outX = endX;
  outY = endY; //pass these new coordinates back out by reference;

}

在上面的代码中,我们围绕圆圈的外部移动,逐个地围绕外部绘制每条单独的线。对于每条线,我们有一个起点和一个偏移,然后我们将偏移旋转一个角度(这个角度随着我们围绕圆圈移动而增加)。然后我们绘制从起点到偏移点的直线。在我们开始下一次迭代之前,我们将起点移动到偏移点,以便下一行从最后一行开始。

我希望这是可以理解的。

答案 2 :(得分:1)

这是绘制圆圈的一种方法。正如你所看到的,它的表现会非常缓慢。

现代图形基于抽象掉较低级别的东西,以便可以对其进行优化;开发人员编写drawCircle(x,y,r),图形库+驱动程序可以将其一直传递到芯片,这可以填充适当的像素。

虽然您使用C ++编写,但除非使用图形驱动程序,否则不会操作最接近核心的数据。即使你的setPixelColour级方法和通过线传递的实际二进制值之间也有多层子程序调用;在几乎每一层都有检查和额外的计算和例程运行。因此,更快的图形的秘诀是减少您进行的这些呼叫的数量。如果你可以将drawCircle命令一直带到图形芯片,那就去做吧。不要在单个像素上浪费一个调用,就像绘制常规形状一样平凡。

在现代操作系统中,有一些图形处理层可以处理像您这样的应用程序的请求,并将它们与窗口,合成和任何其他效果相结合。所以你的“绘制到屏幕”的命令已经被几个层中介了。您希望提供给CPU的是将计算卸载到图形子系统所需的最少信息。

我想说如果你想学习在屏幕上绘制内容,请使用canvas和js,因为开发周期很简单且相对无痛。如果你想学习C ++,试试项目Euler,或使用现有的图形库绘制东西。如果您想编写2D图形库,请学习DirectX和OpenGL等基础图形技术,因为它们是图形在现实中完成的方式。但你说它们似乎很复杂吗?然后你需要先学习更多的C ++。出于一些非常好的理由,它们就是它们的方式,无论结果多么复杂。

答案 3 :(得分:1)

正如第一个答案所说,你不应该为了认真的工作而自己这样做。但是如果你只是想以此为例,那么可以这样做:首先定义一个在屏幕上绘制线段的函数:

void draw_line(int x1, int y1, int x2, int y2);

这应该是相对简单的实现:只需找到变化最快的方向,并在使用整数逻辑时迭代该方向,以找出其他维度应该改变多少。即,如果x变化得更快,那么y = (x-x1)*(y2-y1)/(x2-x1)

然后使用此函数将圆形实现为分段线元素:

void draw_circle(int x, int y, int r)
{
    double dtheta = 2*M_PI/8/r;
    int x1 = x+r, x2, y1 = y, y2;
    int n = 2*M_PI/dtheta;
    for(int i = 1; i < n; i++)
    {
        double theta = i*dtheta;
        x2 = int(x+r*cos(theta)); y2 = int(y+r*sin(theta));
        draw_line(x1, y1, x2, y2);
        x1 = x2; y1 = y2;
    }
}

这使用浮点逻辑和三角函数来确定哪些线元素最接近圆。这是一个有点粗略的实现,但我认为任何想要对非常大的圈子有效的实现都必须做这样的事情。

如果只允许使用整数逻辑,一种方法可能是首先绘制一个低分辨率整数圆,然后将每个选定的像素细分为更小的像素,然后选择所需的子像素,依此类推。这将缩放为N log N,因此仍然比上面的方法慢。但是你可以避免罪恶和cos。