2

我是 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

4 回答 4

6

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

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

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

对于真实世界的图形,不只是绘制自己的像素。那是没有效率的。或者至少不是在你不能直接使用硬件的设备上......

但出于您的目的,我认为 SDL 绝对是您的最佳选择!祝你好运。

于 2012-10-29T12:03:18.427 回答
2

您不会通过手动将像素绘制到屏幕上来绘制图形,那是疯狂的所在。

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

一旦你下载了这些库,就会有很多示例项目可供查看,它们会让你入门。

此时有两种方法:有一种计算向量的数学方法,该向量描述了具有大量边的形状(即,它看起来像一个圆)。或者有一种“作弊”方法,即使用 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;

}

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

我希望这是可以理解的。

于 2012-10-29T11:41:08.180 回答
1

这是绘制实心圆的一种方法。如您所见,它将执行得非常缓慢。

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

尽管您使用 C++ 编写代码,但除非您使用图形驱动程序,否则您不会在最接近核心的位置操作数据。甚至在您的 setPixelColour 级别方法和通过网络传递的实际二进制值之间也存在多层子例程调用;几乎在每一层都有检查和额外的计算和例程运行。那么,更快的图形的秘诀就是减少您进行的这些调用的数量。如果您可以将命令 drawCircle 一直获取到图形芯片,请执行此操作。当它像绘制规则形状一样平凡时,不要在单个像素上浪费调用。

在现代操作系统中,有多个图形处理层可以接受像您这样的单个应用程序的请求,并将它们与窗口化、合成和任何其他效果相结合。因此,您“绘制到屏幕”的命令已经由多个图层进行中间处理。您想要提供给 CPU 的是将计算卸载到图形子系统所需的最少信息。

我想说,如果你想学习在屏幕上画东西,玩画布和 js,因为开发周期很容易而且相对轻松。如果您想学习 C++,请尝试 Euler 项目,或使用现有的图形库绘制东西。如果您想编写 2d 图形库,请学习底层图形技术,如 DirectX 和 OpenGL,因为它们是现实中完成图形的方式。但它们看起来很复杂,你说?那么你需要先学习更多的C++。出于某些很好的原因,它们就是这样,无论结果多么复杂。

于 2012-10-29T11:59:46.787 回答
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。

于 2012-10-29T11:59:57.250 回答