首页 > 解决方案 > 有没有更有效的方法来纹理圆?

问题描述

我正在尝试创建一个随机生成的“行星”(圆圈),并且我希望水、土地和树叶的区域由柏林噪声或类似的东西决定。目前我有这个(psudo)代码:

for (int radius = 0; radius < circleRadius; radius++) {
    for (float theta = 0; theta < TWO_PI; theta += 0.1) {
        float x = radius * cosine(theta);
        float y = radius * sine(theta);
        int colour = whateverFunctionIMake(x, y);
        setPixel(x, y, colour);
    }
}

这不仅不起作用(由于精度问题,圆圈中有“间隙”),而且速度非常慢。即使我通过将增量更改为 0.01 来提高分辨率,它仍然缺少像素并且更慢(我在使用 Java 的平庸计算机上获得 10fps(我知道不是最好的)并且增量为 0.01。这当然是不可接受的游戏)。

我怎样才能在计算成本低得多的情况下获得类似的结果?

提前致谢。

标签: geometry

解决方案


为什么不使用:

(x-x0)^2 + (y-y0)^2 <= r^2

很简单:

int x0=?,y0=?,r=?; // your planet position and size
int x,y,xx,rr,col;
for (rr=r*r,x=-r;x<=r;x++)
 for (xx=x*x,y=-r;y<=r;y++)
  if (xx+(y*y)<=rr)
   {
   col = whateverFunctionIMake(x, y);
   setPixel(x0+x, y0+y, col);   
   }

全部在整数上,没有浮动或慢速操作,没有间隙......不要忘记使用 randseed 进行着色功能......

[Edit1] 还有一些东西

现在,如果您想要速度而不是需要直接像素访问(在大多数平台上,Pixels、SetPixel、PutPixels 等都很慢。因为它们执行很多东西,例如范围检查、颜色转换等......)如果您有直接像素访问或渲染到您自己的数组/图像中,无论您需要添加屏幕剪辑(因此您不需要检查每个像素上的像素是否在屏幕内)以避免访问冲突,如果您的圆圈与屏幕重叠。

如评论中所述,您可以使用先前的值摆脱x*xand y*yinside 循环(因为两者x,y都只是递增)。有关它的更多信息,请参见:

数学是这样的:

(x+1)^2 = (x+1)*(x+1) = x^2 + 2x + 1

因此,xx = x*x我们不只是xx+=x+x+1为尚未增加xxx+=x+x-1如果x已经增加。

当把所有东西放在一起时,我得到了这个:

void circle(int x,int y,int r,DWORD c)
    {
    // my Pixel access
    int **Pixels=Main->pyx;         // Pixels[y][x]
    int   xs=Main->xs;              // resolution
    int   ys=Main->ys;
    // circle
    int sx,sy,sx0,sx1,sy0,sy1;      // [screen]
    int cx,cy,cx0,    cy0    ;      // [circle]
    int rr=r*r,cxx,cyy,cxx0,cyy0;   // [circle^2]
    // BBOX + screen clip
    sx0=x-r; if (sx0>=xs) return; if (sx0<  0) sx0=0;
    sy0=y-r; if (sy0>=ys) return; if (sy0<  0) sy0=0;
    sx1=x+r; if (sx1<  0) return; if (sx1>=xs) sx1=xs-1;
    sy1=y+r; if (sy1<  0) return; if (sy1>=ys) sy1=ys-1;
    cx0=sx0-x; cxx0=cx0*cx0;
    cy0=sy0-y; cyy0=cy0*cy0;
    // render
    for (cxx=cxx0,cx=cx0,sx=sx0;sx<=sx1;sx++,cxx+=cx,cx++,cxx+=cx)
     for (cyy=cyy0,cy=cy0,sy=sy0;sy<=sy1;sy++,cyy+=cy,cy++,cyy+=cy)
      if (cxx+cyy<=rr)
       Pixels[sy][sx]=c;
    }

这会在我的设置中渲染一个带半径的圆512 px( AMD ~35msA8-5500 23.5 Mpx/s3.2GHz Win7 64 位单线程 VCL/GDI 32 位应用程序,由 BDS2006 C++ 编码)。只需更改对您使用的样式/api的直接像素访问...

[编辑2]

要测量 x86/x64 上的速度,您可以RDTSC在此处使用 asm 指令一些我很久以前使用的古老 C++ 代码(在没有本机 64 位内容的 32 位环境中):

double _rdtsc()
    {
    LARGE_INTEGER x; // unsigned 64bit integer variable from windows.h I think
    DWORD l,h;       // standard unsigned 32 bit variables
    asm {
        rdtsc
        mov l,eax
        mov h,edx
        }
    x.LowPart=l;
    x.HighPart=h;
    return double(x.QuadPart);
    }

它返回您的 CPU 自上电以来经过的时钟。请注意,您应该考虑溢出,因为在快速机器上,32 位计数器会在几秒钟内溢出。此外,每个内核都有单独的计数器,因此将关联设置为单个 CPU。在测量之前的变速时钟上,通过一些计算加热 CPU 并转换为时间,只需除以 CPU 时钟频率。要获得它,只需这样做:

t0=_rdtsc()
sleep(250);
t1=_rdtsc();
fcpu = (t1-t0)*4;

和测量:

t0=_rdtsc()
mesured stuff
t1=_rdtsc();
time = (t1-t0)/fcpu

如果t1<t0您溢出并且您需要将常数添加到结果或再次测量。此外,测量过程必须少于溢出周期。为了提高精度忽略操作系统粒度。有关更多信息,请参阅:


推荐阅读