summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-03-15 19:44:17 -0700
committeryum <yum.food.vr@gmail.com>2026-03-15 19:44:17 -0700
commit4bf004c7e9c6eb2edf497b075ee01375ae878ff1 (patch)
treed111a5639ee7e9c6e5c383f65a5eabefcf8cc2a7
parenta00d67b75318d61c56446ecd90f98ce4c914ebfe (diff)
Optimize noise baker
-rw-r--r--Scripts/Editor/GenerateNoise.cs163
1 files changed, 101 insertions, 62 deletions
diff --git a/Scripts/Editor/GenerateNoise.cs b/Scripts/Editor/GenerateNoise.cs
index 324b5b5..73a5e20 100644
--- a/Scripts/Editor/GenerateNoise.cs
+++ b/Scripts/Editor/GenerateNoise.cs
@@ -1,5 +1,6 @@
using UnityEngine;
using UnityEditor;
+using System.Threading.Tasks;
public class GenerateNoise : EditorWindow
{
@@ -54,7 +55,7 @@ public class GenerateNoise : EditorWindow
GenerateWorleyField(res, _texelsPerCell, _metric, out var f1, out _);
var pixels = new byte[res * res * res];
for (int i = 0; i < pixels.Length; i++)
- pixels[i] = (byte)Mathf.RoundToInt(f1[i] * 255f);
+ pixels[i] = (byte)Mathf.RoundToInt(Mathf.Clamp01(f1[i]) * 255f);
tex.SetPixelData(pixels, 0);
break;
}
@@ -63,7 +64,7 @@ public class GenerateNoise : EditorWindow
tex = new Texture3D(res, res, res, TextureFormat.R8, false);
GenerateWorleyField(res, _texelsPerCell, _metric, out var f1, out var f2);
var pixels = new byte[res * res * res];
- GenerateEdgeSDF(pixels, f1, f2, _sdfRadius);
+ GenerateEdgeSDF(pixels, f2, _sdfRadius);
tex.SetPixelData(pixels, 0);
break;
}
@@ -111,24 +112,12 @@ public class GenerateNoise : EditorWindow
}
}
- static float Distance(Vector3 a, Vector3 b, DistanceMetric metric)
- {
- float dx = Mathf.Abs(a.x - b.x);
- float dy = Mathf.Abs(a.y - b.y);
- float dz = Mathf.Abs(a.z - b.z);
- switch (metric)
- {
- case DistanceMetric.L1: return dx + dy + dz;
- case DistanceMetric.LInfinity: return Mathf.Max(dx, Mathf.Max(dy, dz));
- default: return Mathf.Sqrt(dx * dx + dy * dy + dz * dz);
- }
- }
-
static void GenerateWorleyField(int res, int texelsPerCell, DistanceMetric metric,
- out float[] f1, out float[] f2)
+ out float[] f1, out float[] edgeDist)
{
int cellCount = Mathf.Max(1, res / texelsPerCell);
float cellSize = (float)res / cellCount;
+ float rcpCellSize = 1f / cellSize;
var points = new Vector3[cellCount * cellCount * cellCount];
for (int cz = 0; cz < cellCount; cz++)
@@ -142,68 +131,118 @@ public class GenerateNoise : EditorWindow
(cz + Random.value) * cellSize);
}
- float maxDist = cellSize;
+ float rcpMaxDist = rcpCellSize;
int count = res * res * res;
- f1 = new float[count];
- f2 = new float[count];
+ var f1Arr = new float[count];
+ var edgeArr = new float[count];
+ bool isL2 = metric == DistanceMetric.L2;
+ bool isL1 = metric == DistanceMetric.L1;
- for (int z = 0; z < res; z++)
- for (int y = 0; y < res; y++)
- for (int x = 0; x < res; x++)
+ Parallel.For(0, res, z =>
{
- var pos = new Vector3(x + 0.5f, y + 0.5f, z + 0.5f);
-
- int cx0 = Mathf.FloorToInt(pos.x / cellSize);
- int cy0 = Mathf.FloorToInt(pos.y / cellSize);
- int cz0 = Mathf.FloorToInt(pos.z / cellSize);
-
- float d1 = float.MaxValue;
- float d2 = float.MaxValue;
-
- for (int dz = -1; dz <= 1; dz++)
- for (int dy = -1; dy <= 1; dy++)
- for (int dx = -1; dx <= 1; dx++)
+ for (int y = 0; y < res; y++)
+ for (int x = 0; x < res; x++)
{
- int ncx = cx0 + dx;
- int ncy = cy0 + dy;
- int ncz = cz0 + dz;
+ float px = x + 0.5f, py = y + 0.5f, pz = z + 0.5f;
+ int cx0 = (int)(px * rcpCellSize);
+ int cy0 = (int)(py * rcpCellSize);
+ int cz0 = (int)(pz * rcpCellSize);
+
+ // Pass 1: Find closest point (metric determines cell ownership)
+ float bestDist = float.MaxValue;
+ float p1x = 0, p1y = 0, p1z = 0;
+ int p1nx = 0, p1ny = 0, p1nz = 0;
+
+ for (int dz = -1; dz <= 1; dz++)
+ for (int dy = -1; dy <= 1; dy++)
+ for (int dx = -1; dx <= 1; dx++)
+ {
+ int ncx = cx0 + dx, ncy = cy0 + dy, ncz = cz0 + dz;
+ int wcx = ((ncx % cellCount) + cellCount) % cellCount;
+ int wcy = ((ncy % cellCount) + cellCount) % cellCount;
+ int wcz = ((ncz % cellCount) + cellCount) % cellCount;
+ var pt = points[wcx + cellCount * (wcy + cellCount * wcz)];
+ float qx = pt.x + (ncx - wcx) * cellSize;
+ float qy = pt.y + (ncy - wcy) * cellSize;
+ float qz = pt.z + (ncz - wcz) * cellSize;
+
+ float ex = qx - px, ey = qy - py, ez = qz - pz;
+ float dist;
+ if (isL1)
+ {
+ float ax = ex < 0 ? -ex : ex, ay = ey < 0 ? -ey : ey, az = ez < 0 ? -ez : ez;
+ dist = ax + ay + az;
+ }
+ else if (isL2)
+ {
+ dist = ex * ex + ey * ey + ez * ez; // compare squared, defer sqrt
+ }
+ else
+ {
+ float ax = ex < 0 ? -ex : ex, ay = ey < 0 ? -ey : ey, az = ez < 0 ? -ez : ez;
+ dist = ax > ay ? (ax > az ? ax : az) : (ay > az ? ay : az);
+ }
+
+ if (dist < bestDist)
+ {
+ bestDist = dist;
+ p1x = qx; p1y = qy; p1z = qz;
+ p1nx = ncx; p1ny = ncy; p1nz = ncz;
+ }
+ }
- int wcx = ((ncx % cellCount) + cellCount) % cellCount;
- int wcy = ((ncy % cellCount) + cellCount) % cellCount;
- int wcz = ((ncz % cellCount) + cellCount) % cellCount;
+ float d1 = isL2 ? (float)System.Math.Sqrt(bestDist) : bestDist;
- Vector3 pt = points[wcx + cellCount * (wcy + cellCount * wcz)];
- pt.x += (ncx - wcx) * cellSize;
- pt.y += (ncy - wcy) * cellSize;
- pt.z += (ncz - wcz) * cellSize;
+ // Pass 2: Squared distance to nearest bisecting plane (Euclidean regardless
+ // of metric — linear under trilinear filtering). One sqrt at the end.
+ float r1x = p1x - px, r1y = p1y - py, r1z = p1z - pz;
+ float minEdgeSq = float.MaxValue;
- float dist = Distance(pos, pt, metric);
- if (dist < d1)
+ for (int dz = -2; dz <= 2; dz++)
+ for (int dy = -2; dy <= 2; dy++)
+ for (int dx = -2; dx <= 2; dx++)
{
- d2 = d1;
- d1 = dist;
- }
- else if (dist < d2)
- {
- d2 = dist;
+ int ncx = cx0 + dx, ncy = cy0 + dy, ncz = cz0 + dz;
+ if (ncx == p1nx && ncy == p1ny && ncz == p1nz) continue;
+
+ int wcx = ((ncx % cellCount) + cellCount) % cellCount;
+ int wcy = ((ncy % cellCount) + cellCount) % cellCount;
+ int wcz = ((ncz % cellCount) + cellCount) % cellCount;
+ var pt = points[wcx + cellCount * (wcy + cellCount * wcz)];
+ float qx = pt.x + (ncx - wcx) * cellSize;
+ float qy = pt.y + (ncy - wcy) * cellSize;
+ float qz = pt.z + (ncz - wcz) * cellSize;
+
+ float r2x = qx - px, r2y = qy - py, r2z = qz - pz;
+ float ax = r2x - r1x, ay = r2y - r1y, az = r2z - r1z;
+ // num = dot(r1 + r2, axis), sign determines which side of the plane
+ float num = (r1x + r2x) * ax + (r1y + r2y) * ay + (r1z + r2z) * az;
+ if (num > 0f)
+ {
+ // d = num / (2 * |axis|), so d² = num² / (4 * |axis|²)
+ float denSq = ax * ax + ay * ay + az * az;
+ float dSq = num * num / (4f * denSq);
+ if (dSq < minEdgeSq) minEdgeSq = dSq;
+ }
}
+
+ int i = x + res * (y + res * z);
+ f1Arr[i] = d1 * rcpMaxDist;
+ edgeArr[i] = (float)System.Math.Sqrt(minEdgeSq) * rcpMaxDist;
}
+ });
- int i = x + res * (y + res * z);
- f1[i] = Mathf.Clamp01(d1 / maxDist);
- f2[i] = Mathf.Clamp01(d2 / maxDist);
- }
+ f1 = f1Arr;
+ edgeDist = edgeArr;
}
- // Convert F2-F1 into a signed distance field relative to the cell boundary (F2-F1 = 0).
- // Output: 0.5 = on the boundary, 0 = clamped near-side, 1 = clamped far-side.
- // sdfRadius controls how much F2-F1 range maps to [0,1] — smaller = more precision near edges.
- static void GenerateEdgeSDF(byte[] pixels, float[] f1, float[] f2, float sdfRadius)
+ // Convert bisecting-plane distance into [0,1]: 0.5 = on boundary, 1 = deep inside cell.
+ // sdfRadius controls how much distance maps to [0,1] — smaller = more precision near edges.
+ static void GenerateEdgeSDF(byte[] pixels, float[] edgeDist, float sdfRadius)
{
for (int i = 0; i < pixels.Length; i++)
{
- float sd = f2[i] - f1[i];
- float normalized = sd / sdfRadius * 0.5f + 0.5f;
+ float normalized = edgeDist[i] / sdfRadius * 0.5f + 0.5f;
pixels[i] = (byte)Mathf.RoundToInt(Mathf.Clamp01(normalized) * 255f);
}
}