有没有更有效的方法来纹理圆? [英] Is there a more efficient way of texturing a circle?

查看:23
本文介绍了有没有更有效的方法来纹理圆?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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).

我怎样才能在计算成本大大降低的同时获得类似的结果?

How might I achieve a similar result whilst being much less computationally expensive?

提前致谢.

推荐答案

为什么不用:

(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 进行着色功能...

all on integers, no floating or slow operations, no gaps ... Do not forget to use randseed for the coloring function ...

[Edit1] 更多内容

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

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.

正如评论中提到的,您可以使用以前的值摆脱 x*xy*y 内部循环(作为 x,y 只是递增).有关它的更多信息,请参阅:

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). For more info about it see:

数学是这样的:

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

所以我们只使用 xx+=x+x+1 而不是 xx = x*x 来表示尚未递增的 xxx+=x+x-1 如果 x 已经递增.

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.

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

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

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

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++). Just change the direct pixel access to style/api you use ...

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

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):

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 时钟频率.要获得它,只需这样做:

It returns clocks your CPU has elapsed since power up. Beware you should account for overflows as on fast machines the 32bit counter is overflowing in seconds. Also each core has separate counter so set affinity to single CPU. On variable speed clock before measurement heat upi CPU by some computation and to convert to time just divide by CPU clock frequency. To obtain it just do this:

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

和测量:

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

如果 t1 你溢出了,你需要添加一个常数到结果或再次测量.此外,测量过程必须少于溢出周期.为了提高精度忽略操作系统粒度.欲了解更多信息,请参阅:

if t1<t0 you overflowed and you need to add the a constant to result or measure again. Also the measured process must take less than overflow period. To enhance precision ignore OS granularity. for more info see:

  • Measuring Cache Latencies
  • Cache size estimation on your system? setting affinity example
  • Negative clock cycle measurements with back-to-back rdtsc?

这篇关于有没有更有效的方法来纹理圆?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆