C ++ / SDL2 - 渲染圆

时间:2017-01-07 17:44:42

标签: c++ graphics geometry sdl-2

我是否在正确的轨道上,用SDL2画出一个圆圈?我认为使用参数方程和趋于零的半径将起作用,但就处理器使用而言似乎效率非常低。非常感谢任何其他想法提前感谢。

    //Circle test
    int circle_x = WINDOW_WIDTH/2;
    int circle_y = WINDOW_HEIGHT/2;
    int circle_radius = 100;
    SDL_SetRenderDrawColor(window.renderer, 100, 100, 255, 255);

    int point_x;
    int point_y;

    while (circle_radius > 0)
    {
        for (int t = 0; t < 360; t++)
        {
            point_x = circle_x + circle_radius * cos(t);
            point_y = circle_y + circle_radius * sin(t);
            SDL_RenderDrawPoint(window.renderer, point_x, point_y);
        }

        circle_radius--;
    }

Output Img

2 个答案:

答案 0 :(得分:5)

首先,您的代码中存在一些错误,因为sin()cos()函数是弧度而不是基于度数的函数。这将使您的圆圈出现在伪随机选择的点上,因为每个步骤绘制一个与前一个点相差大约57度的点。这将没有影响,因为现代图形工作缓冲,你将看到最终结果,而不是工作。

一旦这样说,今天没有人通过使用您的暴露算法画一个圆圈。看一下Bresenham's middle point algorithm,它基本上试图用八分圆绘制一个圆,但要快几倍。

这些算法背后的想法是考虑R^2 = x^2 + y^2公式并在其中一个轴上逐个像素地绘制,并考虑何时必须遵循另一个轴(您通过八度绘制,这样你不能应付不止一个衍生品而你只需决定你是否向上移动)。例程还考虑了圆对称性,只计算一个八分圆,然后在每次通过时绘制八个点。

当我年轻的时候从头开始开发这个算法(之前没见过Bresenham),我可能对解决方案有所帮助。

第一次尝试是考虑到小圆圈的分辨率(粒度不依赖于角度),你必须绘制比大光圈更少的像素,你必须重新设计你所遵循的一度方法以适应更精细或更粗糙的分辨率。我的想法是逐个像素地,而不是逐个程度地去,直到你绘制完整的东西。我们将仅绘制第一个八分圆,并将通过图的对称属性绘制其余部分。我们从(0, -R)点开始,逐个像素地走,直到我们到达(sqrt(2)*R, R - sqrt(2)*R)点。

我们要做的第一件事就是尽量保存我们必须做的所有操作。我们可以保存操作的第一个地方是计算方块...我们将使用R^2 = x^2 + y^2方程式,R一直只用作R^2,所以,假设我们想要绘制一个十个像素的半径圆,我们会将事物平方到100(这是10像素的半径平方)。

接下来,我们将看到正方形的一个属性,即它们从一个正方形变为奇数(0 -> 1(delta is 1) -> 4(delta is 3) -> 9(delta is 5) -> 16(delta is 7) ...)所以如果我们可以安排在x中增长1,我们可以计算x ^ 2很简单,只需在odd变量中添加两个,然后将odd添加到最后一个方格号,这样我们就可以使用两个数字:x和{{ 1}}。我们将两者初始化为x2,第一个按0增长,而第二个按关系增长x += 1;(我们初始化x2 += dx2; dx2 += 2;)这使我们允许{{1 }}和dx2 = 1;仅通过总和增长,而根本没有增殖。

如果有人认为我们需要x然后被迫计算x2 差不多 ,那么诀窍这里可以向后管理y2 = 100 - x2y = sqrt(y2)序列,与x对应序列相同。好吧,对,yy2可以反向管理,与yy2相同,但这次我们必须向后,减少奇数(什么?)到x x2,最后到达1。为此,请检查两个连续正方形之间的差异是否恰好是添加的两个数字,例如,dy2 -= 2; y2 -= dy2;0之间的差异为13^2 = 169,这是如果我们从14^2 = 196中的13 + 14 = 27返回R = 14,则会以奇数开头。

使事情变得如此复杂的原因是,这种方式我们只用整数进行加法而不需要进行乘法运算(好的,乘法不是那么昂贵,但是有一段时间它们都是)嗯,我们做乘法最初将半径0平方,但我们在开头只进行一次计算y

现在的想法是在出发点R设置原点并逐个像素地向右移动,添加(和修改)R^2(0, -R)和{{ 1}}(我们减去所有时间的总和),直到我们到达x中的下一个方格,然后更新所有y轴值(我们确实递减y,我们必须在那一刻向上移动一个像素) )x2sumy,并绘制像素(或之前绘制它,就像我们在例程中一样),直到......什么? (好吧,重点是直到我们在八分圆完成的45度点相遇,yy2坐标相等)重要的是停在那里,因为从那时起,它有可能一步使y坐标增加多个像素(导数大于1),这应该使整个算法复杂化(我们无论如何绘制其他对称的八个点,所以我们正在绘制其他部分图形)

所以,假设我们有半径为100,并以:

开头
dy2

标有星号的点是x值与下一个y值相交的点,使得x=0, x2= 0, dx2= 1, y=10, y2=100, dy2=19, sum=100 * x=1, x2= 1, dx2= 3, y= 9, y2= 81, dy2=17, sum= 99 x=2, x2= 4, dx2= 5, y= 9, y2= 81, dy2=17, sum= 96 x=3, x2= 9, dx2= 7, y= 9, y2= 81, dy2=17, sum= 91 x=4, x2=16, dx2= 9, y= 9, y2= 81, dy2=17, sum= 84 * x=5, x2=25, dx2=11, y= 8, y2= 64, dy2=15, sum= 75 * x=6, x2=36, dx2=13, y= 7, y2= 49, dy2=13, sum= 64 * x=7, x2=49, dx2=15, y= 7, y2= 49, dy2=13, sum= 51 值被去除并且必须移动我们绘制的像素。最后的例程是:

sum

如果你愿意,我写了一个简单的例子,在屏幕上的ascii中绘制一个圆圈,给定半径作为命令参数。在绘制单个星号之前,它使用ANSI转义序列将光标定位在屏幕中。刻度在X方向加倍以补偿字符高度(&#34;像素&#34;在ascii中不是正方形)我已经包含了一个新的绘图函数指针参数来回调点绘图和{{1}例程从命令行获取参数:

y2

最终结果是:

y

最后,如果你想要更好的结果(不是那些由精确的半径值引起的峰值,使得它们只触及x或y为零时的点)你可以直接传递例程半径的平方值(即允许用小数半径进行整数计算

填充一个圆圈

如果要填充圆圈,只需在一对计算点之间绘制所有点,如:

int bh_onlyonepoint(int r, int cx, int cy)
{
    int r2 = r*r;
    int x = 0, x2 = 0, dx2 = 1;
    int y = r, y2 = y*y, dy2 = 2*y - 1;
    int sum = r2;

    while(x <= y) {
            draw(cx + x, cy + y); /* draw the point, see below */
            sum -= dx2;
            x2 += dx2;
            x++;
            dx2 += 2;
            if (sum <= y2) {
                    y--; y2 -= dy2; dy2 -= 2;
            }
    } /* while */
    return x; /* number of points drawn */
}

这些都是水平线,所以也许你可以通过以下方式获得改进:

main

github

中创建了一个新的git存储库,其中包含允许填充,绘制或跟踪算法运行的最终程序。

答案 1 :(得分:-1)

如tp1所示,使用圆圈的BMP。添加了一个Alpha通道,使背景透明,然后将BMP加载为曲面,从曲面创建纹理,并渲染纹理。