From 92529b562eaf4ad4bbb9faa4ef83b4f0a0f87b2f Mon Sep 17 00:00:00 2001 From: mm00 Date: Fri, 28 Mar 2025 18:51:40 +0100 Subject: [PATCH] Fixed sdf usage in gradient and added test cases --- SDFMapCreator.sln.DotSettings.user | 2 ++ SDFMapCreator/Program.cs | 56 +++++++++++++++++++++++------ SDFMapCreator/SDFMapCreator.csproj | 6 ++++ SDFMapCreator/spherecut.png | Bin 0 -> 8588 bytes SDFMapCreator/spherefull.png | Bin 0 -> 11590 bytes 5 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 SDFMapCreator.sln.DotSettings.user create mode 100644 SDFMapCreator/spherecut.png create mode 100644 SDFMapCreator/spherefull.png diff --git a/SDFMapCreator.sln.DotSettings.user b/SDFMapCreator.sln.DotSettings.user new file mode 100644 index 0000000..4b0a35a --- /dev/null +++ b/SDFMapCreator.sln.DotSettings.user @@ -0,0 +1,2 @@ + + ForceIncluded \ No newline at end of file diff --git a/SDFMapCreator/Program.cs b/SDFMapCreator/Program.cs index 416a2c9..e7f4b72 100644 --- a/SDFMapCreator/Program.cs +++ b/SDFMapCreator/Program.cs @@ -88,8 +88,13 @@ public class Program { var imagesPath = "images"; + /* for (int i = 0; i < 8; i++) LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i+1:00}.png"); + */ + + LoadImage("spherecut.png"); + LoadImage("spherefull.png"); //LoadImage("1.png"); //LoadImage("2.png"); @@ -132,14 +137,16 @@ public class Program { Console.WriteLine("Creating gradients..."); for (var i = 0; i < Masks.Count - 1; i++) { - var gradientData = Gradient(Masks[i], SDFs[i], SDFs[i + 1]); + var gradientData = Gradient(Masks[i+1], SDFs[i], SDFs[i + 1]); Gradients.Add(gradientData); if(!outputGradients) continue; - var gradient = new MagickImage(MagickColors.Black, (uint)Masks[i].Mask.GetLength(0), (uint)Masks[i].Mask.GetLength(1)); + var gradient = new MagickImage(MagickColors.Black, (uint)Masks[i+1].Mask.GetLength(0), (uint)Masks[i+1].Mask.GetLength(1)); gradient.GetPixels().SetPixels(gradientData.ToFloatArray()); gradient.Write($"gradient{i}.png", MagickFormat.Png24); } + return; + Console.WriteLine("Preparing transitions..."); //for each gradient read the corresponding mask //for each pixel in the mask, lerp the pixel value with the gradient value @@ -150,14 +157,35 @@ public class Program { var mask = Masks[i + 1]; var gradient = Gradients[i]; var transition = new float3[width, height]; + var absMax = MIN; + var absMin = MAX; for (var x = 0; x < mask.Mask.GetLength(0); x++) { for (var y = 0; y < mask.Mask.GetLength(1); y++) { - if (mask.Mask[x, y].X == 0) continue; - transition[x, y] = new(Lerp(Images[i].Pixels[x,y].X, MAX, gradient[x,y].X)); + if (mask.Mask[x, y].X == 0 || mask.Mask[x, y].Y > 0) continue; + transition[x, y] = new(Lerp(Images[i].Pixels[x, y].X, MAX, gradient[x, y].X)); + if (transition[x, y].X > absMax) absMax = transition[x, y].X; + if (transition[x, y].X < absMin) absMin = transition[x, y].X; } } + + absMax = MathF.Min(absMax, MAX); + absMin = MathF.Max(absMin, MIN); + + //normalize + Parallel.For(0, width * height, parallelOptions, (j) => { + var x = (int)(j % width); + var y = (int)(j / width); + //Console.WriteLine($"Transition {i} | Min: {absMin} | Max: {absMax}, {transition[x, y].X}"); + transition[x, y] = new(Remap(transition[x, y].X, absMin, absMax, MIN, MAX)); + }); + + Console.WriteLine($"Transition {i} | Min: {absMin} | Max: {absMax}"); + Console.WriteLine($"Transition {i} | Min: {transition.Cast().Min(x => x.X)} | Max: {transition.Cast().Max(x => x.X)}"); + var transitionImage = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1)); transitionImage.GetPixels().SetPixels(transition.ToFloatArray()); + + transitionImage.Write($"transition{i}.png", MagickFormat.Png24); } @@ -196,21 +224,28 @@ public class Program { var sw = new Stopwatch(); sw.Start(); float3[,] temp = new float3[width, height]; - Console.WriteLine("Running edge detection..."); + + var min = MAX; + var max = MIN; + + Console.WriteLine("Running gradient generation..."); Parallel.For(0, width * height, parallelOptions, (i) => { int x = (int)(i % width); int y = (int)(i / width); - var a = (sdfA.SDF[x,y].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3; - var b = (sdfB.SDF[x,y].X + sdfB.SDF[x,y].Y + sdfB.SDF[x,y].Z )/ 3; + if(mask.Mask[x,y].X == 0 || mask.Mask[x,y].Y > 0) return; + + var a = sdfA.SDF[x, y].X; + var b = sdfB.SDF[x, y].X; var gradient = a / (a + b); temp[x, y] = new(Remap(gradient, 0, 1, MIN, MAX)); + + if (gradient < min) min = gradient; + if (gradient > max) max = gradient; }); Console.WriteLine($"Gradient Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/(float)sw.Elapsed.TotalSeconds:N0} pixels/s)"); - var min = temp.Cast().Min(x => x.X); - var max = temp.Cast().Max(x => x.X); Console.WriteLine($"Min: {min} | Max: {max}"); return temp; } @@ -253,6 +288,8 @@ public class Program { var y = (int)(i / width); temp[x, y] = new(Remap(temp[x, y].X, 0, AbsMax, MIN, MAX)); }); + sw.Stop(); + Console.WriteLine($"SDF Normalization Time: {sw.Elapsed.TotalSeconds:N4}s ({iterCount/sw.Elapsed.TotalSeconds:N0} pixels/s)"); Console.WriteLine("AbsMax: " + AbsMax); return new(temp); @@ -262,7 +299,6 @@ public class Program { private static float EuclideanDistance(float2 a, float2 b) => MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2)); - private static void ImageData(MagickImage image1, IPixelCollection pixels1) { Console.WriteLine( $""" diff --git a/SDFMapCreator/SDFMapCreator.csproj b/SDFMapCreator/SDFMapCreator.csproj index 2d047f2..63e0f22 100644 --- a/SDFMapCreator/SDFMapCreator.csproj +++ b/SDFMapCreator/SDFMapCreator.csproj @@ -46,6 +46,12 @@ Always + + Always + + + Always + diff --git a/SDFMapCreator/spherecut.png b/SDFMapCreator/spherecut.png new file mode 100644 index 0000000000000000000000000000000000000000..cad0b783c6c96eb8b11bfe01d2d7c6232b6b6061 GIT binary patch literal 8588 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 literal 0 HcmV?d00001 diff --git a/SDFMapCreator/spherefull.png b/SDFMapCreator/spherefull.png new file mode 100644 index 0000000000000000000000000000000000000000..6cb948eb54d4eabd94de7171803a54c0d6446542 GIT binary patch literal 11590 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 literal 0 HcmV?d00001