Bladeren bron

Noise optimize (#29)

* double -> float

* Optimize noise

* Fix
sunnycase 8 jaren geleden
bovenliggende
commit
5e78ef6e3d

+ 4 - 2
src/MineCase.Algorithm/Noise/INoise.cs

@@ -7,8 +7,10 @@ namespace MineCase.Algorithm.Noise
 {
     public interface INoise
     {
-        double Noise(double x, double y, double z);
+        float Noise(float x, float y, float z);
 
-        void Noise(double[,,] noise, Vector3 offset, Vector3 scale);
+        void Noise(float[,,] noise, Vector3 offset, Vector3 scale);
+
+        void AddNoise(float[,,] noise, Vector3 offset, Vector3 scale, float noiseScale);
     }
 }

+ 0 - 33
src/MineCase.Algorithm/Noise/NoiseBase.cs

@@ -1,33 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Numerics;
-using System.Text;
-
-namespace MineCase.Algorithm.Noise
-{
-    public abstract class NoiseBase : INoise
-    {
-        public abstract double Noise(double x, double y, double z);
-
-        public virtual void Noise(double[,,] noise, Vector3 offset, Vector3 scale)
-        {
-            var xExtent = noise.GetUpperBound(0) + 1;
-            var yExtent = noise.GetUpperBound(1) + 1;
-            var zExtent = noise.GetUpperBound(2) + 1;
-
-            for (int z = 0; z < zExtent; z++)
-            {
-                var zOffset = offset.Z + z * scale.Z;
-                for (int y = 0; y < yExtent; y++)
-                {
-                    var yOffset = offset.Y + y * scale.Y;
-                    for (int x = 0; x < xExtent; x++)
-                    {
-                        var xOffset = offset.X + x * scale.X;
-                        noise[x, y, z] = Noise(xOffset, yOffset, zOffset);
-                    }
-                }
-            }
-        }
-    }
-}

+ 35 - 6
src/MineCase.Algorithm/Noise/OctavedNoise.cs

@@ -1,27 +1,28 @@
 using System;
 using System.Collections.Generic;
+using System.Numerics;
 using System.Text;
 
 namespace MineCase.Algorithm.Noise
 {
-    public class OctavedNoise<TNoise> : NoiseBase, INoise
+    public class OctavedNoise<TNoise>
         where TNoise : INoise
     {
         private readonly TNoise _innerNoise;
         private readonly int _octaves;
-        private readonly double _persistence;
+        private readonly float _persistence;
 
-        public OctavedNoise(TNoise innerNoise, int octaves, double persistence)
+        public OctavedNoise(TNoise innerNoise, int octaves, float persistence)
         {
             _innerNoise = innerNoise;
             _octaves = octaves;
             _persistence = persistence;
         }
 
-        public override double Noise(double x, double y, double z)
+        public float Noise(float x, float y, float z)
         {
             double total = 0;
-            double frequency = 1;
+            int frequency = 1;
             double amplitude = 1;
             double maxValue = 0;
 
@@ -33,7 +34,35 @@ namespace MineCase.Algorithm.Noise
                 frequency *= 2;
             }
 
-            return total / maxValue;
+            return (float)(total / maxValue);
+        }
+
+        public void Noise(float[,,] noise, Vector3 offset, Vector3 scale)
+        {
+            Array.Clear(noise, 0, noise.Length);
+            int frequency = 1;
+            double amplitude = 1;
+            double maxValue = 0;
+
+            for (int i = 0; i < _octaves; i++)
+            {
+                _innerNoise.AddNoise(noise, offset * frequency, scale * frequency, (float)amplitude);
+                maxValue += amplitude;
+                amplitude *= _persistence;
+                frequency *= 2;
+            }
+
+            var xExtent = noise.GetUpperBound(0) + 1;
+            var yExtent = noise.GetUpperBound(1) + 1;
+            var zExtent = noise.GetUpperBound(2) + 1;
+            for (int x = 0; x < xExtent; x++)
+            {
+                for (int y = 0; y < yExtent; y++)
+                {
+                    for (int z = 0; z < zExtent; z++)
+                        noise[x, y, z] /= (float)maxValue;
+                }
+            }
         }
     }
 }

+ 154 - 34
src/MineCase.Algorithm/Noise/PerlinNoise.cs

@@ -8,7 +8,7 @@ namespace MineCase.Algorithm.Noise
     /// <summary>
     /// Implementation for Improved Perlin Noise (http://mrl.nyu.edu/~perlin/noise/)
     /// </summary>
-    public class PerlinNoise : NoiseBase, INoise
+    public class PerlinNoise : INoise
     {
         /// <summary>
         /// Permutation
@@ -26,7 +26,7 @@ namespace MineCase.Algorithm.Noise
                 _p[i + 256] = _p[i] = random.Next(0, 256);
         }
 
-        public override double Noise(double x, double y, double z)
+        public float Noise(float x, float y, float z)
         {
             var xcoord = Split(x);
             var ycoord = Split(y);
@@ -36,54 +36,174 @@ namespace MineCase.Algorithm.Noise
             var v = Fade(ycoord.remainder);
             var w = Fade(zcoord.remainder);
 
-            int aaa, aba, aab, abb, baa, bba, bab, bbb;
-            aaa = _p[_p[_p[xcoord.integer] + ycoord.integer] + zcoord.integer];
-            aba = _p[_p[_p[xcoord.integer] + ycoord.integer + 1] + zcoord.integer];
-            aab = _p[_p[_p[xcoord.integer] + ycoord.integer] + zcoord.integer + 1];
-            abb = _p[_p[_p[xcoord.integer] + ycoord.integer + 1] + zcoord.integer + 1];
-            baa = _p[_p[_p[xcoord.integer + 1] + ycoord.integer] + zcoord.integer];
-            bba = _p[_p[_p[xcoord.integer + 1] + ycoord.integer + 1] + zcoord.integer];
-            bab = _p[_p[_p[xcoord.integer + 1] + ycoord.integer] + zcoord.integer + 1];
-            bbb = _p[_p[_p[xcoord.integer + 1] + ycoord.integer + 1] + zcoord.integer + 1];
-
-            double x1, x2, y1, y2;
-            x1 = Lerp(
+            int a = _p[xcoord.integer];
+            int b = _p[xcoord.integer + 1];
+            int aa = _p[a + ycoord.integer];
+            int ab = _p[a + ycoord.integer + 1];
+            int ba = _p[b + ycoord.integer];
+            int bb = _p[b + ycoord.integer + 1];
+
+            int aaa = _p[aa + zcoord.integer];
+            int aba = _p[ab + zcoord.integer];
+            int aab = _p[aa + zcoord.integer + 1];
+            int abb = _p[ab + zcoord.integer + 1];
+            int baa = _p[ba + zcoord.integer];
+            int bba = _p[bb + zcoord.integer];
+            int bab = _p[ba + zcoord.integer + 1];
+            int bbb = _p[bb + zcoord.integer + 1];
+
+            var xa = new Vector4(
                 Grad(aaa, xcoord.remainder, ycoord.remainder, zcoord.remainder),
-                Grad(baa, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder),
-                u);
-            x2 = Lerp(
                 Grad(aba, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder),
-                Grad(bba, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder),
-                u);
-            y1 = Lerp(x1, x2, v);
-
-            x1 = Lerp(
                 Grad(aab, xcoord.remainder, ycoord.remainder, zcoord.remainder - 1),
+                Grad(abb, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder - 1));
+            var xb = new Vector4(
+                Grad(baa, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder),
+                Grad(bba, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder),
                 Grad(bab, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder - 1),
-                u);
-            x2 = Lerp(
-                Grad(abb, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder - 1),
-                Grad(bbb, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder - 1),
-                u);
-            y2 = Lerp(x1, x2, v);
-
-            return (Lerp(y1, y2, w) + 1) / 2;
+                Grad(bbb, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder - 1));
+            var xl = Vector4.Lerp(xa, xb, u);
+            var ya = new Vector2(xl.X, xl.Z);
+            var yb = new Vector2(xl.Y, xl.W);
+            var yl = Vector2.Lerp(ya, yb, v);
+
+            return (Lerp(yl.X, yl.Y, w) + 1) / 2;
+        }
+
+        public void Noise(float[,,] noise, Vector3 offset, Vector3 scale)
+        {
+            var xExtent = noise.GetUpperBound(0) + 1;
+            var yExtent = noise.GetUpperBound(1) + 1;
+            var zExtent = noise.GetUpperBound(2) + 1;
+
+            for (int x = 0; x < xExtent; x++)
+            {
+                var xOffset = offset.X + x * scale.X;
+                var xcoord = Split(xOffset);
+                var u = Fade(xcoord.remainder);
+
+                int a = _p[xcoord.integer];
+                int b = _p[xcoord.integer + 1];
+                for (int y = 0; y < yExtent; y++)
+                {
+                    var yOffset = offset.Y + y * scale.Y;
+                    var ycoord = Split(yOffset);
+                    var v = Fade(ycoord.remainder);
+
+                    int aa = _p[a + ycoord.integer];
+                    int ab = _p[a + ycoord.integer + 1];
+                    int ba = _p[b + ycoord.integer];
+                    int bb = _p[b + ycoord.integer + 1];
+                    for (int z = 0; z < zExtent; z++)
+                    {
+                        var zOffset = offset.Z + z * scale.Z;
+                        var zcoord = Split(zOffset);
+                        var w = Fade(zcoord.remainder);
+
+                        int aaa = _p[aa + zcoord.integer];
+                        int aba = _p[ab + zcoord.integer];
+                        int aab = _p[aa + zcoord.integer + 1];
+                        int abb = _p[ab + zcoord.integer + 1];
+                        int baa = _p[ba + zcoord.integer];
+                        int bba = _p[bb + zcoord.integer];
+                        int bab = _p[ba + zcoord.integer + 1];
+                        int bbb = _p[bb + zcoord.integer + 1];
+
+                        var xa = new Vector4(
+                            Grad(aaa, xcoord.remainder, ycoord.remainder, zcoord.remainder),
+                            Grad(aba, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder),
+                            Grad(aab, xcoord.remainder, ycoord.remainder, zcoord.remainder - 1),
+                            Grad(abb, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder - 1));
+                        var xb = new Vector4(
+                            Grad(baa, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder),
+                            Grad(bba, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder),
+                            Grad(bab, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder - 1),
+                            Grad(bbb, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder - 1));
+                        var xl = Vector4.Lerp(xa, xb, u);
+                        var ya = new Vector2(xl.X, xl.Z);
+                        var yb = new Vector2(xl.Y, xl.W);
+                        var yl = Vector2.Lerp(ya, yb, v);
+
+                        noise[x, y, z] = (Lerp(yl.X, yl.Y, w) + 1) / 2;
+                    }
+                }
+            }
+        }
+
+        public void AddNoise(float[,,] noise, Vector3 offset, Vector3 scale, float noiseScale)
+        {
+            var xExtent = noise.GetUpperBound(0) + 1;
+            var yExtent = noise.GetUpperBound(1) + 1;
+            var zExtent = noise.GetUpperBound(2) + 1;
+
+            for (int x = 0; x < xExtent; x++)
+            {
+                var xOffset = offset.X + x * scale.X;
+                var xcoord = Split(xOffset);
+                var u = Fade(xcoord.remainder);
+
+                int a = _p[xcoord.integer];
+                int b = _p[xcoord.integer + 1];
+                for (int y = 0; y < yExtent; y++)
+                {
+                    var yOffset = offset.Y + y * scale.Y;
+                    var ycoord = Split(yOffset);
+                    var v = Fade(ycoord.remainder);
+
+                    int aa = _p[a + ycoord.integer];
+                    int ab = _p[a + ycoord.integer + 1];
+                    int ba = _p[b + ycoord.integer];
+                    int bb = _p[b + ycoord.integer + 1];
+                    for (int z = 0; z < zExtent; z++)
+                    {
+                        var zOffset = offset.Z + z * scale.Z;
+                        var zcoord = Split(zOffset);
+                        var w = Fade(zcoord.remainder);
+
+                        int aaa = _p[aa + zcoord.integer];
+                        int aba = _p[ab + zcoord.integer];
+                        int aab = _p[aa + zcoord.integer + 1];
+                        int abb = _p[ab + zcoord.integer + 1];
+                        int baa = _p[ba + zcoord.integer];
+                        int bba = _p[bb + zcoord.integer];
+                        int bab = _p[ba + zcoord.integer + 1];
+                        int bbb = _p[bb + zcoord.integer + 1];
+
+                        var xa = new Vector4(
+                            Grad(aaa, xcoord.remainder, ycoord.remainder, zcoord.remainder),
+                            Grad(aba, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder),
+                            Grad(aab, xcoord.remainder, ycoord.remainder, zcoord.remainder - 1),
+                            Grad(abb, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder - 1));
+                        var xb = new Vector4(
+                            Grad(baa, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder),
+                            Grad(bba, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder),
+                            Grad(bab, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder - 1),
+                            Grad(bbb, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder - 1));
+                        var xl = Vector4.Lerp(xa, xb, u);
+                        var ya = new Vector2(xl.X, xl.Z);
+                        var yb = new Vector2(xl.Y, xl.W);
+                        var yl = Vector2.Lerp(ya, yb, v);
+
+                        noise[x, y, z] += (Lerp(yl.X, yl.Y, w) + 1) / 2 * noiseScale;
+                    }
+                }
+            }
         }
 
-        private static (int integer, double remainder) Split(double value)
+        private static (int integer, float remainder) Split(float value)
         {
             var integer = (int)value;
             return (integer % 256, value - integer);
         }
 
-        private static double Fade(double t)
+        private static float Fade(float t)
         {
             // 6t^5 - 15t^4 + 10t^3
             return t * t * t * (t * (t * 6 - 15) + 10);
         }
 
         // Source: http://riven8192.blogspot.com/2010/08/calculate-perlinnoise-twice-as-fast.html
-        public static double Grad(int hash, double x, double y, double z)
+        public static float Grad(int hash, float x, float y, float z)
         {
             switch (hash & 0xF)
             {
@@ -107,7 +227,7 @@ namespace MineCase.Algorithm.Noise
             }
         }
 
-        public static double Lerp(double a, double b, double x) =>
+        public static float Lerp(float a, float b, float x) =>
             a + x * (b - a);
     }
 }

+ 29 - 2
tests/UnitTest/NoiseTest.cs

@@ -33,7 +33,7 @@ namespace MineCase.UnitTest
             using (var image = new Image<ImageSharp.PixelFormats.Rgb24>(xExtent, yExtent))
             {
                 var noise = new PerlinNoise(100);
-                var noiseValue = new double[xExtent, yExtent, 1];
+                var noiseValue = new float[xExtent, yExtent, 1];
                 noise.Noise(noiseValue, Vector3.Zero, new Vector3(0.1f, 0.1f, 0));
                 for (int x = 0; x < xExtent; x++)
                 {
@@ -58,7 +58,7 @@ namespace MineCase.UnitTest
             using (var image = new Image<ImageSharp.PixelFormats.Rgb24>(xExtent, yExtent))
             {
                 var noise = new OctavedNoise<PerlinNoise>(new PerlinNoise(100), 8, 1);
-                var noiseValue = new double[xExtent, yExtent, 1];
+                var noiseValue = new float[xExtent, yExtent, 1];
                 noise.Noise(noiseValue, Vector3.Zero, new Vector3(0.1f, 0.1f, 0));
                 for (int x = 0; x < xExtent; x++)
                 {
@@ -72,5 +72,32 @@ namespace MineCase.UnitTest
                 image.SaveAsBmp(file);
             }
         }
+
+        [Fact]
+        public void TestPerlinNoise3DPerformance()
+        {
+            if (!Vector.IsHardwareAccelerated)
+                throw new NotSupportedException();
+
+            var noise = new PerlinNoise(100);
+            for (int i = 0; i < 100_0000; i++)
+            {
+                noise.Noise(i, i, i);
+            }
+        }
+
+        [Fact]
+        public void TestPerlinNoise3DPerformanceArray()
+        {
+            if (!Vector.IsHardwareAccelerated)
+                throw new NotSupportedException();
+
+            var noiseValue = new float[100, 100, 10];
+            var noise = new PerlinNoise(100);
+            for (int i = 0; i < 100; i++)
+            {
+                noise.Noise(noiseValue, new Vector3(i, i, i), new Vector3(0.1f, 0.1f, 0.1f));
+            }
+        }
     }
 }