Skip to main content
 首页 » 编程设计

c#之具有单纯形噪声的陨石发生器

2024年08月12日28Renyi-Fan

我尝试制作像 this video 这样的 meteor , 但我只能这样:

这是我的单纯形噪声:

public class PerlinNoise{ 
int B = 256; 
int[] m_perm = new int[B+B]; 
Texture2D m_permTex; 
public int octava; 
public float frequencia, amplitud; 
 
public PerlinNoise(int seed, float frec, float amp, int oct) 
{ 
    octava = oct; 
    amplitud = amp; 
    frequencia = frec; 
    UnityEngine.Random.seed = seed; 
 
     int i, j, k; 
    for (i = 0 ; i < B ; i++)  
     { 
        m_perm[i] = i; 
    } 
 
     while (--i != 0)  
    { 
        k = m_perm[i]; 
        j = UnityEngine.Random.Range(0, B); 
        m_perm[i] = m_perm[j]; 
        m_perm[j] = k; 
     } 
 
    for (i = 0 ; i < B; i++)  
    { 
        m_perm[B + i] = m_perm[i]; 
    } 
 
 } 
 
float FADE(float t) { return t * t * t * ( t * ( t * 6.0f - 15.0f ) + 10.0f ); } 
 
float LERP(float t, float a, float b) { return (a) + (t)*((b)-(a)); } 
 
float GRAD1(int hash, float x )  
{ 
    int h = hash & 15; 
    float grad = 1.0f + (h & 7); 
    if ((h&8) != 0) grad = -grad; 
    return ( grad * x ); 
} 
 
float GRAD2(int hash, float x, float y) 
{        
    int h = hash & 7; 
    float u = h<4 ? x : y; 
    float v = h<4 ? y : x; 
    return (((h&1) != 0)? -u : u) + (((h&2) != 0) ? -2.0f*v : 2.0f*v); 
} 
 
 
float GRAD3(int hash, float x, float y , float z) 
{ 
 
 
    int h = hash & 15; 
    float u = h<8 ? x : y; 
    float v = (h<4) ? y : (h==12 || h==14) ? x : z; 
    return (((h&1) != 0)? -u : u) + (((h&2) != 0)? -v : v); 
} 
 
float Noise1D( float x ) 
{ 
    //returns a noise value between -0.5 and 0.5 
    int ix0, ix1; 
    float fx0, fx1; 
    float s, n0, n1; 
 
    ix0 = (int)Mathf.Floor(x);  // Integer part of x 
    fx0 = x - ix0;          // Fractional part of x 
    fx1 = fx0 - 1.0f; 
    ix1 = ( ix0+1 ) & 0xff; 
    ix0 = ix0 & 0xff;       // Wrap to 0..255 
 
    s = FADE(fx0); 
 
    n0 = GRAD1(m_perm[ix0], fx0); 
    n1 = GRAD1(m_perm[ix1], fx1); 
    return 0.188f * LERP( s, n0, n1); 
} 
 
public float Noise2D( float x, float y ) 
{ 
 
    int ix0, iy0, ix1, iy1; 
    float fx0, fy0, fx1, fy1, s, t, nx0, nx1, n0, n1; 
 
    ix0 = (int)Mathf.Floor(x);  
    iy0 = (int)Mathf.Floor(y);   
    fx0 = x - ix0;         
    fy0 = y - iy0;       
    fx1 = fx0 - 1.0f; 
    fy1 = fy0 - 1.0f; 
    ix1 = (ix0 + 1) & 0xff; // Wrap to 0..255 
    iy1 = (iy0 + 1) & 0xff; 
    ix0 = ix0 & 0xff; 
     iy0 = iy0 & 0xff; 
 
    t = FADE( fy0 ); 
    s = FADE( fx0 ); 
 
    nx0 = GRAD2(m_perm[ix0 + m_perm[iy0]], fx0, fy0); 
    nx1 = GRAD2(m_perm[ix0 + m_perm[iy1]], fx0, fy1); 
 
    n0 = LERP( t, nx0, nx1 ); 
 
    nx0 = GRAD2(m_perm[ix1 + m_perm[iy0]], fx1, fy0); 
    nx1 = GRAD2(m_perm[ix1 + m_perm[iy1]], fx1, fy1); 
 
    n1 = LERP(t, nx0, nx1); 
 
    return 0.507f * LERP( s, n0, n1 ); 
 } 
 
float Noise3D( float x, float y, float z ) 
{ 
    //returns a noise value between -1.5 and 1.5 
    int ix0, iy0, ix1, iy1, iz0, iz1; 
    float fx0, fy0, fz0, fx1, fy1, fz1; 
    float s, t, r; 
    float nxy0, nxy1, nx0, nx1, n0, n1; 
 
    ix0 = (int)Mathf.Floor( x ); // Integer part of x 
    iy0 = (int)Mathf.Floor( y ); // Integer part of y 
    iz0 = (int)Mathf.Floor( z ); // Integer part of z 
    fx0 = x - ix0;        // Fractional part of x 
    fy0 = y - iy0;        // Fractional part of y 
    fz0 = z - iz0;        // Fractional part of z 
    fx1 = fx0 - 1.0f; 
    fy1 = fy0 - 1.0f; 
    fz1 = fz0 - 1.0f; 
    ix1 = ( ix0 + 1 ) & 0xff; // Wrap to 0..255 
    iy1 = ( iy0 + 1 ) & 0xff; 
    iz1 = ( iz0 + 1 ) & 0xff; 
    ix0 = ix0 & 0xff; 
    iy0 = iy0 & 0xff; 
     iz0 = iz0 & 0xff; 
 
     r = FADE( fz0 ); 
     t = FADE( fy0 ); 
    s = FADE( fx0 ); 
 
    nxy0 = GRAD3(m_perm[ix0 + m_perm[iy0 + m_perm[iz0]]], fx0, fy0, fz0); 
     nxy1 = GRAD3(m_perm[ix0 + m_perm[iy0 + m_perm[iz1]]], fx0, fy0, fz1); 
    nx0 = LERP( r, nxy0, nxy1 ); 
 
    nxy0 = GRAD3(m_perm[ix0 + m_perm[iy1 + m_perm[iz0]]], fx0, fy1, fz0); 
     nxy1 = GRAD3(m_perm[ix0 + m_perm[iy1 + m_perm[iz1]]], fx0, fy1, fz1); 
    nx1 = LERP( r, nxy0, nxy1 ); 
 
    n0 = LERP( t, nx0, nx1 ); 
 
     nxy0 = GRAD3(m_perm[ix1 + m_perm[iy0 + m_perm[iz0]]], fx1, fy0, fz0); 
    nxy1 = GRAD3(m_perm[ix1 + m_perm[iy0 + m_perm[iz1]]], fx1, fy0, fz1); 
     nx0 = LERP( r, nxy0, nxy1 ); 
 
    nxy0 = GRAD3(m_perm[ix1 + m_perm[iy1 + m_perm[iz0]]], fx1, fy1, fz0); 
        nxy1 = GRAD3(m_perm[ix1 + m_perm[iy1 + m_perm[iz1]]], fx1, fy1, fz1); 
    nx1 = LERP( r, nxy0, nxy1 ); 
 
    n1 = LERP( t, nx0, nx1 ); 
 
    return 0.936f * LERP( s, n0, n1 ); 
} 
 
public float FractalNoise1D(float x, int octNum, float frq, float amp) 
{ 
    float gain = 1.0f; 
    float sum = 0.0f; 
 
    for(int i = 0; i < octNum; i++) 
    { 
        sum +=  Noise1D(x*gain/frq) * amp/gain; 
        gain *= 2.0f; 
    } 
    return sum; 
} 
 
public float FractalNoise2D(float x, float y, int octNum, float frq, float amp) 
{ 
    float gain = 1.0f; 
    float sum = 0.0f; 
 
    for(int i = 0; i < octNum; i++) 
    { 
        sum += Noise2D(x*gain/frq, y*gain/frq) * amp/gain; 
        gain *= 2.0f; 
    } 
     return sum; 
} 
public int Noise2D(Vector3Int v3) { 
    return Mathf.RoundToInt(FractalNoise2D(v3.x,v3.z,octava,frequencia,amplitud)); 
} 
public int Noise2D(int x, int z) 
{ 
    return Mathf.RoundToInt(FractalNoise2D(x, z, octava, frequencia, amplitud)); 
} 
public float FractalNoise3D(float x, float y, float z, int octNum, float frq, float amp) 
{ 
    float gain = 1.0f; 
    float sum = 0.0f; 
 
    for(int i = 0; i < octNum; i++) 
    { 
        sum +=  Noise3D(x*gain/frq, y*gain/frq, z*gain/frq) * amp/gain; 
        gain *= 2.0f; 
    } 
    return sum; 
} 
 
public void LoadPermTableIntoTexture() 
{ 
    m_permTex = new Texture2D(256, 1, TextureFormat.Alpha8, false); 
    m_permTex.filterMode = FilterMode.Point; 
    m_permTex.wrapMode = TextureWrapMode.Clamp; 
 
    for(int i = 0; i < 256; i++) 
    { 
        float v = (float)m_perm[i] / 255.0f; 
 
        m_permTex.SetPixel(i, 0, new Color(0,0,0,v)); 
    } 
 
    m_permTex.Apply(); 
} 
} 

这是我的实现:

public void Resimulate() { 
  PerlinNoise per = new PerlinNoise(seed, frec, amp, octa); 
 
    for (int x = 0; x < TamX; x++) 
    { 
        for (int y = 0; y < TamY; y++) 
        { 
            for (int z = 0; z < TamZ; z++) 
            { 
                if (per.FractalNoise3D(x, y, z, octa, frec, amp)>0) 
                { 
                 lista.Add(new Bloque(new Vector3Int(x, y, z))); 
                } 
 
            } 
        } 
     }      
 } 

我在各种页面、博客和此处找到了信息,但没有找到对我有用的信息。如果有人有信息,我将非常感谢您的帮助。

谢谢你帮助我。

请您参考如下方法:

首先,重要的是要注意您的代码不是单纯形噪声!它是较旧的“Perlin 噪声”算法,往往会表现出明显的视觉方向伪影。

我建议完全避免 Perlin 噪声和 Simplex 噪声。 Perlin 是因为定向伪影,而 Simplex 是因为您遇到了 3D 实现的专利问题。

我最近设计了一种算法来解决这两个问题,它开始被相当多的游戏开发者采用,称为 OpenSimplex 噪声。代码:https://gist.github.com/KdotJPG/b1270127455a94ac5d19

(编辑:这是一个 C# 端口:https://gist.github.com/omgwtfgames/601497972e4e30fd9c5f)

做这样的事情:

const double S = 24; //Sparsity of noise features 
public void Resimulate() { 
    //To get fractal noise, ideally you want multiple instances of noise so you don't get odd behavior near (0,0,0) 
    OpenSimplexNoise n1 = new OpenSimplexNoise(seed); 
    OpenSimplexNoise n2 = new OpenSimplexNoise(seed+1); 
    OpenSimplexNoise n3 = new OpenSimplexNoise(seed+2); 
 
    for (int x = 0; x < TamX; x++) { 
        for (int y = 0; y < Tamy; z++) { 
            for (int z = 0; z < TamZ; z++) { 
                double n = (n1.eval(x / S, y / S, z / S) 
                    + n2.eval(x / S / 2, y / S / 2, z / S / 2) * .5 
                    + n3.eval(x / S / 4, y / S / 4, z / S / 4) * .25) 
                    / (1 + .5 + .25); 
                double dx = (TamX / 2.0 - x); 
                double dy = (TamY / 2.0 - y); 
                double dz = (TamZ / 2.0 - z); 
                double d = dx*dx + dy*dy + dz*dz; 
 
                //d is now your squared distance from the center. 
                //n is your fractal noise value 
                //you want to combine d and n to form some threshold that 
                //determines whether or not there is a block. 
                //threshold = d / C1 + n + C2 > 0 where C1 and C2 are constants you choose 
            } 
        } 
    } 
} 

编辑:这是 Perlin 和 OpenSimplex 之间的视觉差异:

  • 左边是noise(x, y, 0)灰度
  • 接下来是 noise(x, y, 0) > 0 吗?白:黑
  • 接下来是 |noise(x, y, 0)| > 0.1?白:黑
  • 接下来是noise(x, y, 0.5)灰度