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

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

问题描述

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

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,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 ,而不用增加 x xx + = 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 ...

[Edit2]

要在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< t0 溢出,则需要添加一个常数以得出结果或再次进行测量.同样,所测量的过程必须少于溢出时间.为了提高精度,请忽略OS粒度.有关更多信息,请参见:

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天全站免登陆