简体   繁体   English

有没有更有效的圆形纹理方法?

[英]Is there a more efficient way of texturing a circle?

I'm trying to create a randomly generated "planet" (circle), and I want the areas of water, land and foliage to be decided by perlin noise, or something similar.我正在尝试创建一个随机生成的“行星”(圆圈),并且我希望水、土地和树叶的区域由柏林噪声或类似的东西决定。 Currently I have this (psudo)code:目前我有这个(伪)代码:

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);
    }
}

Not only does this not work (there are "gaps" in the circle because of precision issues), it's incredibly slow.这不仅不起作用(由于精度问题,圆圈中有“间隙”),而且速度非常慢。 Even if I increase the resolution by changing the increment to 0.01, it still has missing pixels and is even slower (I get 10fps on my mediocre computer using Java (I know not the best) and an increment of 0.01. This is certainly not acceptable for a game).即使我通过将增量更改为 0.01 来提高分辨率,它仍然缺少像素并且速度更慢(我在使用 Java(我知道不是最好的)的普通计算机上获得 10fps 和 0.01 的增量。这当然是不可接受的用于游戏)。

How might I achieve a similar result whilst being much less computationally expensive?我如何才能获得类似的结果,同时计算成本要低得多?

Thanks in advance.提前致谢。

Why not use:为什么不使用:

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

so simply:很简单:

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);   
   }

all on integers, no floating or slow operations, no gaps ... Do not forget to use randseed for the coloring function ...都是整数,没有浮动或慢速操作,没有间隙......不要忘记使用 randseed 作为着色函数......

[Edit1] some more stuff [Edit1] 还有一些东西

Now if you want speed than you need direct pixel access (in most platforms Pixels, SetPixel, PutPixels etc are slooow. because they perform a lot of stuff like range checking, color conversions etc ... ) In case you got direct pixel access or render into your own array/image whatever you need to add clipping with screen (so you do not need to check if pixel is inside screen on each pixel) to avoid access violations if your circle is overlapping screen.现在,如果您想要速度而不是直接像素访问(在大多数平台中,Pixels、SetPixel、PutPixels 等都很慢。因为它们执行很多事情,例如范围检查、颜色转换等......)如果您有直接像素访问或渲染到您自己的数组/图像中,无论您需要添加屏幕剪辑(因此您无需检查每个像素上的像素是否在屏幕内)以避免访问冲突,如果您的圆圈与屏幕重叠。

As mentioned in the comments you can get rid of the x*x and y*y inside loop using previous value (as both x,y are only incrementing).如评论中所述,您可以使用先前的值(因为x,y都只是递增)摆脱x*xy*y内部循环。 For more info about it see:有关它的更多信息,请参阅:

the math is like this:数学是这样的:

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

so instead of xx = x*x we just do xx+=x+x+1 for not incremented yet x or xx+=x+x-1 if x is already incremented.所以代替xx = x*x我们只是做xx+=x+x+1对于尚未增加的xxx+=x+x-1如果x已经增加。

When put all together I got this:放在一起后,我得到了这个:

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;
    }

This renders a circle with radius 512 px in ~35ms so 23.5 Mpx/s filling on mine setup (AMD A8-5500 3.2GHz Win7 64bit single thread VCL/GDI 32bit app coded by BDS2006 C++).这会在~35ms渲染一个半径为512 px的圆,因此在我的设置中以23.5 Mpx/s填充(AMD A8-5500 3.2GHz Win7 64 位单线程 VCL/GDI 32 位应用程序,由 BDS2006 C++ 编码)。 Just change the direct pixel access to style/api you use ...只需更改对您使用的样式/ api 的直接像素访问...

[Edit2] [编辑2]

to measure speed on x86/x64 you can use RDTSC asm instruction here some ancient C++ code I used ages ago (on 32bit environment without native 64bit stuff):要测量 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);
    }

It returns clocks your CPU has elapsed since power up.它返回 CPU 上电后经过的时钟。 Beware you should account for overflows as on fast machines the 32bit counter is overflowing in seconds.请注意,您应该考虑溢出,因为在快速机器上,32 位计数器会在几秒钟内溢出。 Also each core has separate counter so set affinity to single CPU.此外,每个内核都有单独的计数器,因此将关联设置为单个 CPU。 On variable speed clock before measurement heat upi CPU by some computation and to convert to time just divide by CPU clock frequency.在测量前的变速时钟上,通过一些计算加热 CPU 并将其转换为时间,只需除以 CPU 时钟频率即可。 To obtain it just do this:要获得它,只需执行以下操作:

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

and measurement:和测量:

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

if t1<t0 you overflowed and you need to add the a constant to result or measure again.如果t1<t0你溢出了,你需要添加一个常量来计算结果或再次测量。 Also the measured process must take less than overflow period.此外,测量过程必须少于溢出周期。 To enhance precision ignore OS granularity.为了提高精度忽略操作系统粒度。 for more info see:有关更多信息,请参阅:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM