diff --git a/SDFMapCreator.sln.DotSettings.user b/SDFMapCreator.sln.DotSettings.user
new file mode 100644
index 0000000..085f3a7
--- /dev/null
+++ b/SDFMapCreator.sln.DotSettings.user
@@ -0,0 +1,5 @@
+
+ ForceIncluded
+ <AssemblyExplorer>
+ <Assembly Path="/mnt/nvme2/Railgun/SDFMapCreator/SDFMapCreator/bin/Debug/net8.0/Magick.NET-Q16-HDRI-OpenMP-x64.dll" />
+</AssemblyExplorer>
\ No newline at end of file
diff --git a/SDFMapCreator/Program.cs b/SDFMapCreator/Program.cs
index 386d322..ef572c2 100644
--- a/SDFMapCreator/Program.cs
+++ b/SDFMapCreator/Program.cs
@@ -24,7 +24,8 @@ public struct float4(float r, float g, float b, float a) {
}*/
public record ImageData(MagickImage Image, float3[,] Pixels, List Edges);
-public record MaskData(float3[,] Mask, ImageData A, List Edges);
+public record MaskData(float3[,] Mask, ImageData Image, List Edges);
+public record TransitionMaskData(float3[,] Mask, ImageData ImageA, ImageData ImageB);
public record SDFData(float3[,] SDF);
@@ -61,7 +62,8 @@ public class Program {
private const bool outputGradients = true;
static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = MAX_THREADS };
static List Images = new();
- static List Masks = new();
+ static List TransitionMasks = new();
+ static List ImageMasks = new();
static List SDFs = new();
static List Gradients = new();
@@ -89,9 +91,14 @@ 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");
//check if all the images in Images are the same resolution
@@ -101,29 +108,36 @@ public class Program {
}
Console.WriteLine("Creating masks...");
- Masks.Add(new (SelfMask(Images[0].Pixels, Images[0].Image.Width, Images[0].Image.Height), Images[0], new()));
//for each image pair, create a mask
- for (int i = 1; i < Images.Count; i++) {
- var mask = GetABMask(Images[i-1].Pixels, Images[i].Pixels, Images[i].Image.Width, Images[i].Image.Height);
- Masks.Add(new(mask, Images[i], new()));
+ var width = Images[0].Image.Width;
+ var height = Images[0].Image.Height;
+ for (int i = 0; i < Images.Count; i++) {
+ ImageMasks.Add(new (SelfMask(Images[i].Pixels, width, height), Images[i], new()));
+
+ if (i < Images.Count - 1)
+ {
+ Console.WriteLine($"Creating mask {i}...");
+ var mask = GetABMask(Images[i].Pixels, Images[i + 1].Pixels, width, height);
+ TransitionMasks.Add(new(mask, Images[i], Images[i + 1]));
+ }
}
Console.WriteLine("Edge detecting masks...");
//EdgeDetect all masks
- foreach (var t in Masks) { EdgeDetect(t); }
+ foreach (var t in ImageMasks) { EdgeDetect(t); }
if(outputMasks) {
Console.WriteLine("Writing masks...");
- for (int i = 0; i < Masks.Count; i++) {
- var mask = new MagickImage(MagickColors.Black, (uint)Masks[i].Mask.GetLength(0), (uint)Masks[i].Mask.GetLength(1));
- mask.GetPixels().SetPixels(Masks[i].Mask.ToFloatArray());
+ for (int i = 0; i < TransitionMasks.Count; i++) {
+ var mask = new MagickImage(MagickColors.Black, (uint)TransitionMasks[i].Mask.GetLength(0), (uint)TransitionMasks[i].Mask.GetLength(1));
+ mask.GetPixels().SetPixels(TransitionMasks[i].Mask.ToFloatArray());
mask.Write($"mask{i}.png", MagickFormat.Png24);
}
}
Console.WriteLine("Creating SDFs...");
- for (var i = 0; i < Masks.Count; i++) {
- var mask = Masks[i];
+ for (var i = 0; i < ImageMasks.Count; i++) {
+ var mask = ImageMasks[i];
SDFs.Add(SDF(mask));
if(!outputSDFs) continue;
var sdf = new MagickImage(MagickColors.Black, (uint)mask.Mask.GetLength(0), (uint)mask.Mask.GetLength(1));
@@ -132,42 +146,42 @@ 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]);
+ for (var i = 0; i < TransitionMasks.Count; i++) {
+ Console.WriteLine($"Generating gradient {i}...");
+ var gradientData = Gradient(TransitionMasks[i], 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)TransitionMasks[i].Mask.GetLength(0), (uint)TransitionMasks[i].Mask.GetLength(1));
gradient.GetPixels().SetPixels(gradientData.ToFloatArray());
gradient.Write($"gradient{i}.png", MagickFormat.Png24);
}
-
- 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
- //write the result to a new image
- int width = Masks[0].Mask.GetLength(0);
- int height = Masks[0].Mask.GetLength(1);
+
+ // generate final image
+ var finalImage = new float3[width, height];
+ var currStep = 0f;
+ var stepIncrement = MAX / (Gradients.Count + 1);
for (var i = 0; i < Gradients.Count; i++) {
- var mask = Masks[i + 1];
+ var mask = ImageMasks[i + 1];
var gradient = Gradients[i];
- var transition = new float3[width, height];
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;
+ finalImage[x, y] = new(Remap(gradient[x,y].X, MAX, MIN, currStep, currStep + stepIncrement));
}
}
- 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);
+ currStep += stepIncrement;
}
+ var finalImageMagick = new MagickImage(MagickColors.Black, (uint)width, (uint)height);
+ finalImageMagick.GetPixels().SetPixels(finalImage.ToFloatArray());
+ finalImageMagick.Write("final.png", MagickFormat.Png24);
+
Console.WriteLine("Done!");
}
private static void EdgeDetect(MaskData maskData) {
- uint width = maskData.A.Image.Width;
- uint height = maskData.A.Image.Height;
+ uint width = maskData.Image.Image.Width;
+ uint height = maskData.Image.Image.Height;
int iterCount = 0;
var sw = new Stopwatch();
sw.Start();
@@ -183,35 +197,42 @@ public class Program {
lock(maskData.Edges) maskData.Edges.Add(new(x, y));
iterCount++;
if (iterCount % (width * height / 100) == 0) {
- ConsoleUpdateLine($"Progress: {iterCount/(width*height):P}% | {iterCount/(sw.Elapsed.TotalSeconds):N0} pixels/s");
+ ConsoleUpdateLine($"Progress: {iterCount/(float)(width*height):P} | {iterCount/(sw.Elapsed.TotalSeconds):N0} pixels/s");
}
});
sw.Stop();
Console.WriteLine($"Edge pixels: {maskData.Edges.Count} | {maskData.Edges.Count/sw.ElapsedMilliseconds} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s");
}
- static float3[,] Gradient(MaskData mask, SDFData sdfA, SDFData sdfB) {
- uint width = mask.A.Image.Width;
- uint height = mask.A.Image.Height;
+ static float3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
+ uint width = mask.ImageA.Image.Width;
+ uint height = mask.ImageA.Image.Height;
int iterCount = 0;
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;
}
@@ -235,10 +256,10 @@ public class Program {
//loop through all the pixels in the mask
foreach (var edge in mask.Edges) {
float dist = float2.DistanceSquared(p, edge);
- if (dist < minDist) minDist = dist;
+ if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist;
}
- temp[x, y] = new(float.Abs(minDist));
+ temp[x, y] = new(minDist);
if (minDist > AbsMax) AbsMax = minDist;
iterCount++;
if (iterCount % (width * height / 100) == 0) {
@@ -254,6 +275,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);
@@ -263,7 +286,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 0000000..cad0b78
Binary files /dev/null and b/SDFMapCreator/spherecut.png differ
diff --git a/SDFMapCreator/spherefull.png b/SDFMapCreator/spherefull.png
new file mode 100644
index 0000000..6cb948e
Binary files /dev/null and b/SDFMapCreator/spherefull.png differ