Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Matching noise on GPU and CPU

Discussion in 'Scripting' started by PanicEnsues, Jul 21, 2018.

  1. PanicEnsues

    PanicEnsues

    Joined:
    Jul 17, 2014
    Posts:
    185
    I'm looking for a noise solution with both a cg and c# solution, so I can generate matching values either on the GPU or the CPU.

    It can be Perlin, Simplex, Value, whatever, so long as it's fast (it will need to run on mobile).

    I've found plenty of cg shader noise solutions, but before I start porting them over to C# I wanted to see if anyone knows of such a beast that already exists...

    Thanks!
     
  2. Bouwie035

    Bouwie035

    Joined:
    Apr 19, 2017
    Posts:
    3
    Hey i’m having the same problem did you find a solution?
     
  3. PanicEnsues

    PanicEnsues

    Joined:
    Jul 17, 2014
    Posts:
    185
    I didn't find any existing solution, so I took this cg noise lib and ported it to c#.

    If you call SimplexNoise.snoise(Vector2 v) below with the same v value as the cg lib's snoise(float2 v) it will return an identical value.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //generate values that 100% match those from SimplexNoise.cginc.
    6. //CPU perf will never be close the GPU-optimized version, but good enough for spot-checking.
    7.  
    8. //Perf tests on Nexus 7 (2013):
    9. //    1000 calls/frame:    no impact on framerate
    10. //    10000 calls/frame:    fps drops from 30 to 10
    11. public static class SimplexNoise {
    12.  
    13.     const float NOISE_SIMPLEX_1_DIV_289 = 0.00346020761245674740484429065744f;
    14.     const float GLOBAL_SCALE_MULT = .01f; //scale all coordinates by this to make them more world-space friendly
    15.  
    16.     static Vector4 C = new Vector4(
    17.         0.211324865405187f, // (3.0-sqrt(3.0))/6.0
    18.         0.366025403784439f, // 0.5*(sqrt(3.0)-1.0)
    19.         -0.577350269189626f, // -1.0 + 2.0 * C.x
    20.         0.024390243902439f  // 1.0 / 41.0
    21.     );
    22.  
    23.     // 1 / 289
    24.     static float mod289(float x)
    25.     {
    26.         return x - Mathf.Floor(x * NOISE_SIMPLEX_1_DIV_289) * 289.0f ;
    27.     }
    28.  
    29.     static Vector2 mod289(Vector2 x)
    30.     {
    31.         return new Vector2(mod289(x.x), mod289(x.y));
    32.     }
    33.  
    34.     static Vector3 mod289(Vector3 x)
    35.     {
    36.         return new Vector3(mod289(x.x), mod289(x.y), mod289(x.z));
    37.     }
    38.  
    39.     static Vector4 mod289(Vector4 x)
    40.     {
    41.         return new Vector4(mod289(x.w), mod289(x.x), mod289(x.y), mod289(x.z));
    42.     }
    43.  
    44.  
    45.  
    46.     // ( x*34.0f f + 1.0f f )*x =
    47.     // x*x*34.0f f + x
    48.     static float permute(float x)
    49.     {
    50.         return mod289(
    51.             x * x * 34.0f + x
    52.         );
    53.     }
    54.  
    55.     static Vector3 permute(Vector3 x)
    56.     {
    57.         return new Vector3(permute(x.x), permute(x.y), permute(x.z));
    58.     }
    59.  
    60.     static Vector4 permute(Vector4 x)
    61.     {
    62.         return new Vector4(permute(x.w), permute(x.x), permute(x.y), permute(x.z));
    63.     }
    64.  
    65.     static float taylorInvSqrt(float r)
    66.     {
    67.         return 1.79284291400159f - 0.85373472095314f * r;
    68.     }
    69.     static Vector3 taylorInvSqrt(Vector3 r)
    70.     {
    71.         return new Vector4(taylorInvSqrt(r.x), taylorInvSqrt(r.y), taylorInvSqrt(r.z));
    72.     }
    73.     static Vector4 taylorInvSqrt(Vector4 r)
    74.     {
    75.         return new Vector4(taylorInvSqrt(r.w), taylorInvSqrt(r.x), taylorInvSqrt(r.y), taylorInvSqrt(r.z));
    76.     }
    77.  
    78.  
    79.  
    80.     static Vector4 grad4(float j, Vector4 ip)
    81.     {
    82.         //const Vector4 ones = Vector4(1.0f , 1.0f , 1.0f , -1.0f );
    83.         Vector3 ones = new Vector3(1f, 1f, -1f);
    84.         Vector4 p = new Vector4();
    85.      
    86.         //p.xyz = Mathf.Floor(Noise.frac(j * ip.xyz) * 7.0f) * ip.z - 1.0f;
    87.         p.x = Mathf.Floor(Noise.frac(j * ip.x) * 7.0f) * ip.z - 1.0f;
    88.         p.y = Mathf.Floor(Noise.frac(j * ip.y) * 7.0f) * ip.z - 1.0f;
    89.         p.z = Mathf.Floor(Noise.frac(j * ip.z) * 7.0f) * ip.z - 1.0f;
    90.         Vector3 PxyzAbs = new Vector3(Mathf.Abs(p.x), Mathf.Abs(p.y), Mathf.Abs(p.z));
    91.         p.w = 1.5f - Vector3.Dot(PxyzAbs, ones);
    92.  
    93.         // GLSL: lessThan(x, y) = x < y
    94.         // HLSL: 1 - step(y, x) = x < y
    95.         //Vector4 s = new Vector4(
    96.         //    1f - Noise.step(0f, p.w),
    97.         //    1f - Noise.step(0f, p.x),
    98.         //    1f - Noise.step(0f, p.y),
    99.         //    1f - Noise.step(0f, p.z)
    100.         //    );
    101.         // Optimization hint Dolkar
    102.         //p.xyz -= Mathf.Sign(p.xyz) * (p.w < 0);
    103.         float pw0 = (p.w < 0f) ? 1f : 0f;
    104.         p.x -= Mathf.Sign(p.x) * pw0;
    105.         p.y -= Mathf.Sign(p.y) * pw0;
    106.         p.z -= Mathf.Sign(p.z) * pw0;
    107.         return p;
    108.     }
    109.  
    110.  
    111.  
    112.     // ----------------------------------- 2D -------------------------------------
    113.  
    114.     public static float snoise(Vector2 v)
    115.     {
    116.         v *= GLOBAL_SCALE_MULT;
    117.         //// First corner
    118.         ////float2 i = floor(v + dot(v, C.yy));
    119.         float vDot = Vector2.Dot(v, new Vector2(C.y, C.y));
    120.         Vector2 i = Noise.floor(new Vector2(v.x + vDot, v.y + vDot));
    121.         ////float2 x0 = v - i + dot(i, C.xx);
    122.         float cDot = Vector2.Dot(i, new Vector2(C.x, C.x));
    123.         Vector2 x0 = new Vector2(v.x - i.x + cDot, v.y - i.y + cDot);
    124.  
    125.         ////float4 x12 = x0.xyxy + C.xxzz;
    126.         Vector4 x12 = new Vector4(x0.x + C.x, x0.y + C.x, x0.x + C.z, x0.y + C.z);
    127.         ////int2 i1 = (x0.x > x0.y) ? float2(1.0, 0.0) : float2(0.0, 1.0);
    128.         Vector2 i1 = (x0.x > x0.y) ? new Vector2(1.0f , 0.0f) : new Vector2(0.0f , 1.0f );
    129.         ////x12.xy -= i1;
    130.         x12.x -= i1.x;
    131.         x12.y -= i1.y;
    132.  
    133.         // Permutations
    134.         i = mod289(i); // Avoid truncation effects in permutation
    135.         ////float3 p = permute(
    136.         ////    permute(
    137.         ////        i.y + float3(0.0, i1.y, 1.0)
    138.         ////    ) + i.x + float3(0.0, i1.x, 1.0)
    139.         ////);
    140.         Vector3 p = permute(
    141.             permute(
    142.                 new Vector3(0.0f + i.y, i1.y + i.y, 1.0f + i.y)
    143.             ) + new Vector3(0.0f + i.x, i1.x + i.x, 1.0f + i.x)
    144.         );
    145.         ////float3 m = max(
    146.         ////    0.5 - float3(
    147.         ////        dot(x0, x0),
    148.         ////        dot(x12.xy, x12.xy),
    149.         ////        dot(x12.zw, x12.zw)
    150.         ////        ),
    151.         ////    0.0
    152.         ////);
    153.         Vector2 X12xy = new Vector2(x12.x, x12.y);
    154.         Vector2 X12zw = new Vector2(x12.z, x12.w);
    155.         Vector3 m = Vector3.Max(
    156.             new Vector3(
    157.                 0.5f - Vector2.Dot(x0, x0),
    158.                 0.5f - Vector2.Dot(X12xy, X12xy),
    159.                 0.5f - Vector2.Dot(X12zw, X12zw)
    160.                 ),
    161.             Vector3.zero
    162.         );
    163.         m.Scale(m);
    164.         m.Scale(m);
    165.  
    166.         // Gradients: 41 points uniformly over a line, mapped onto a diamond.
    167.         // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
    168.         ////float3 x = 2.0 * frac(p * C.www) - 1.0;
    169.         Vector3 x = 2.0f * Noise.frac(Vector3.Scale(p, new Vector3(C.w, C.w, C.w)));
    170.         x.x -= 1.0f;
    171.         x.y -= 1.0f;
    172.         x.z -= 1.0f;
    173.         ////float3 h = abs(x) - 0.5;
    174.         Vector3 h = new Vector3(Mathf.Abs(x.x) - 0.5f, Mathf.Abs(x.y) - 0.5f, Mathf.Abs(x.z) - 0.5f);
    175.         ////float3 ox = floor(x + 0.5);
    176.         Vector3 ox = new Vector3(Mathf.Floor(x.x + 0.5f), Mathf.Floor(x.y + 0.5f), Mathf.Floor(x.z + 0.5f));
    177.         Vector3 a0 = x - ox;
    178.  
    179.         // Normalise gradients implicitly by scaling m
    180.         //// Approximation of: m *= inversesqrt( a0*a0 + h*h );
    181.         ////m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
    182.         m = Vector3.Scale(m, taylorInvSqrt(Vector3.Scale(a0, a0) + Vector3.Scale(h, h)));
    183.  
    184.  
    185.         // Compute final noise value at P
    186.         Vector3 g;
    187.         g.x = a0.x * x0.x + h.x * x0.y;
    188.         //g.yz = a0.yz * x12.xz + h.yz * x12.yw;
    189.         g.y = a0.y * x12.x + h.y * x12.y;
    190.         g.z = a0.z * x12.z + h.z * x12.w;
    191.         //center and scale value to maximize 0 - 1 range
    192.         return .5f + 67.5f * Vector3.Dot(m, g);
    193.     }
    194.  
    195. }
    196.  
    Also see Noise.cs library below (https://forum.unity.com/threads/matching-noise-on-gpu-and-cpu.541635/#post-7702285)
     
    Last edited: Dec 2, 2021
  4. Bouwie035

    Bouwie035

    Joined:
    Apr 19, 2017
    Posts:
    3
    Thanks dude, appreciate you taking the time to help me out. only the Noise.frac and Noise.floor functions are missing is this the right implementation?

    Code (CSharp):
    1.  
    2.  
    3.     static float frac(float value)
    4.     {
    5.         return (float)((decimal)value - Decimal.Truncate((decimal)value));
    6.     }
    7.  
    8.     static Vector2 floor(Vector2 value)
    9.     {
    10.         value.x = Mathf.Floor(value.x);
    11.         value.y = Mathf.Floor(value.y);
    12.  
    13.         return value;
    14.     }
    15.  
    great work!
     
    Last edited: Nov 23, 2021
  5. ubbelito

    ubbelito

    Joined:
    Sep 24, 2018
    Posts:
    23
    Maybe a less fun solution, but how about using noise textures and sample with bilinear filtering? This would be very similar to value noise and is probably the lightest solution as most of the noise solutions use bilinear filtering.

    Obviously, works only for 2d noise, but you can simulate 3d noise with multiple lookups.

    https://docs.unity3d.com/ScriptReference/Texture2D.GetPixelBilinear.html
     
  6. Bouwie035

    Bouwie035

    Joined:
    Apr 19, 2017
    Posts:
    3
    Got it working using the script from OP and some manual offsets, noise textures also sounds like a good option tho
     
  7. PanicEnsues

    PanicEnsues

    Joined:
    Jul 17, 2014
    Posts:
    185
    Sorry, I apparently forgot to include the functions library script; looks like you got it working, but if anyone else needs it:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. //functions to emulate counterparts in the CG shader library
    5. public static class Noise {
    6.  
    7.     public static float frac(float value)
    8.     {
    9.         //return value % 1f;
    10.         //or
    11.         return value - Mathf.Floor(value);
    12.     }
    13.     public static Vector2 frac(Vector2 value)
    14.     {
    15.         return new Vector2(frac(value.x), frac(value.y));
    16.     }
    17.     public static Vector3 frac(Vector3 value)
    18.     {
    19.         return new Vector3(frac(value.x), frac(value.y), frac(value.z));
    20.     }
    21.     public static Vector4 frac(Vector4 value)
    22.     {
    23.         return new Vector4(frac(value.w), frac(value.x), frac(value.y), frac(value.z));
    24.     }
    25.  
    26.     public static Vector2 floor(Vector2 v)
    27.     {
    28.         return new Vector2(Mathf.Floor(v.x), Mathf.Floor(v.y));
    29.     }
    30.     public static Vector4 floor(Vector4 v)
    31.     {
    32.         return new Vector4(Mathf.Floor(v.w), Mathf.Floor(v.x), Mathf.Floor(v.y), Mathf.Floor(v.z));
    33.     }
    34.  
    35.     public static float rsqrt(float value)
    36.     {
    37.         return Mathf.Pow(value, -0.5f);
    38.     }
    39.  
    40.     public static Vector4 rsqrt(Vector4 value)
    41.     {
    42.         return new Vector4(rsqrt(value.w), rsqrt(value.x), rsqrt(value.y), rsqrt(value.z));
    43.     }
    44.  
    45.     //returns either 0 or 1 depending on whether the x parameter is greater than the y parameter.
    46.     public static float step(float x, float y)
    47.     {
    48.         return (x >= y) ? 1 : 0;
    49.     }
    50.  
    51.     public static Vector4 step(float x, Vector4 y)
    52.     {
    53.         return new Vector4(step(x, y.w), step(x, y.x), step(x, y.y), step(x, y.z));
    54.     }
    55.  
    56.     public static Vector3 abs(Vector3 v)
    57.     {
    58.         return new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z));
    59.     }
    60. }
    61.  
     
    Bouwie035 likes this.