升级obi

This commit is contained in:
2026-01-22 22:08:21 +08:00
parent 120b8cda26
commit 20f14322bc
1067 changed files with 149894 additions and 29583 deletions

View File

@@ -1,4 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
namespace Obi
@@ -9,7 +10,7 @@ namespace Obi
*/
public class VoxelDistanceField
{
public Vector3Int[,,] distanceField; // for each coordinate, stores coordinates of closest surface voxel.
public Vector3[,,] distanceField; // for each coordinate, stores coordinates of closest surface voxel.
private MeshVoxelizer voxelizer;
@@ -18,115 +19,159 @@ namespace Obi
this.voxelizer = voxelizer;
}
public float SampleUnfiltered(int x, int y, int z)
public Vector4 SampleUnfiltered(int x, int y, int z)
{
if (!voxelizer.VoxelExists(x, y, z)) return float.PositiveInfinity;
x = Mathf.Clamp(x, 0, voxelizer.resolution.x - 1);
y = Mathf.Clamp(y, 0, voxelizer.resolution.y - 1);
z = Mathf.Clamp(z, 0, voxelizer.resolution.z - 1);
float dist = Vector3.Distance(voxelizer.GetVoxelCenter(distanceField[x, y, z]),
voxelizer.GetVoxelCenter(new Vector3Int(x, y, z)));
var grad = distanceField[x, y, z];
float dist = grad.magnitude;
grad.Normalize();
if (voxelizer[x, y, z] == MeshVoxelizer.Voxel.Inside)
return -dist;
return dist;
return new Vector4(grad.x, grad.y, grad.z, -dist);
}
public Vector4 SampleFiltered(float x, float y, float z)
public Vector4 SampleFiltered(float x, float y, float z, Vector3Int axisMask)
{
var pos = new Vector3(x, y, z);
// clamp position inside the distance field:
var min = voxelizer.GetVoxelCenter(new Vector3Int(0, 0, 0));
var min = voxelizer.GetVoxelCenter(new Vector3Int(0, 0, 0));
var max = voxelizer.GetVoxelCenter(new Vector3Int(voxelizer.resolution.x - 1, voxelizer.resolution.y - 1, voxelizer.resolution.z - 1));
pos.x = Mathf.Clamp(pos.x, min.x, max.x - voxelizer.voxelSize * 0.05f);
pos.y = Mathf.Clamp(pos.y, min.y, max.y - voxelizer.voxelSize * 0.05f);
pos.z = Mathf.Clamp(pos.z, min.z, max.z - voxelizer.voxelSize * 0.05f);
var voxel = voxelizer.GetPointVoxel(pos - Vector3.one * voxelizer.voxelSize * 0.5f) - voxelizer.Origin;
pos.x = Mathf.Clamp(pos.x, min.x, max.x);
pos.y = Mathf.Clamp(pos.y, min.y, max.y);
pos.z = Mathf.Clamp(pos.z, min.z, max.z);
var voxel = voxelizer.GetPointVoxel(pos - (Vector3)axisMask * voxelizer.voxelSize * 0.5f) - voxelizer.Origin;
var voxelCenter = voxelizer.GetVoxelCenter(voxel);
var norm = (pos - voxelCenter) / voxelizer.voxelSize;
var norm = Vector3.Scale((pos - voxelCenter) / voxelizer.voxelSize, axisMask);
float xz00 = SampleUnfiltered(voxel.x, voxel.y, voxel.z);
float xz01 = SampleUnfiltered(voxel.x, voxel.y, voxel.z + 1);
float xz10 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z);
float xz11 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z + 1);
var xz00 = SampleUnfiltered(voxel.x, voxel.y, voxel.z);
var xz01 = SampleUnfiltered(voxel.x, voxel.y, voxel.z + 1);
var xz10 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z);
var xz11 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z + 1);
float yz00 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z);
float yz01 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z + 1);
float yz10 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z);
float yz11 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z + 1);
var yz00 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z);
var yz01 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z + 1);
var yz10 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z);
var yz11 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z + 1);
float X1 = Mathf.Lerp(xz00, xz10, norm.x);
float X2 = Mathf.Lerp(xz01, xz11, norm.x);
float X3 = Mathf.Lerp(yz00, yz10, norm.x);
float X4 = Mathf.Lerp(yz01, yz11, norm.x);
var X1 = Vector4.Lerp(xz00, xz10, norm.x);
var X2 = Vector4.Lerp(xz01, xz11, norm.x);
var X3 = Vector4.Lerp(yz00, yz10, norm.x);
var X4 = Vector4.Lerp(yz01, yz11, norm.x);
float Y1 = Mathf.Lerp(X1, X2, norm.z);
float Y2 = Mathf.Lerp(X3, X4, norm.z);
var Y1 = Vector4.Lerp(X1, X2, norm.z);
var Y2 = Vector4.Lerp(X3, X4, norm.z);
float R = Mathf.Lerp(Mathf.Lerp(xz10, xz11, norm.z), Mathf.Lerp(yz10, yz11, norm.z), norm.y);
float L = Mathf.Lerp(Mathf.Lerp(xz00, xz01, norm.z), Mathf.Lerp(yz00, yz01, norm.z), norm.y);
float F = Mathf.Lerp(X2, X4, norm.y);
float B = Mathf.Lerp(X1, X3, norm.y);
return new Vector4((R - L) / voxelizer.voxelSize,
(Y2 - Y1) / voxelizer.voxelSize,
(F - B) / voxelizer.voxelSize,
Mathf.Lerp(Y1, Y2, norm.y));
return Vector4.Lerp(Y1, Y2, norm.y);
}
public IEnumerator JumpFlood()
public void Smooth()
{
// create output buffer for ping-pong.
Vector3[,,] smoothed = new Vector3[voxelizer.resolution.x,
voxelizer.resolution.y,
voxelizer.resolution.z];
// create and initialize distance field:
distanceField = new Vector3Int[voxelizer.resolution.x,
voxelizer.resolution.y,
voxelizer.resolution.z];
// create auxiliar buffer for ping-pong.
Vector3Int[,,] auxBuffer = new Vector3Int[voxelizer.resolution.x,
voxelizer.resolution.y,
voxelizer.resolution.z];
// initialize distance field:
for (int x = 0; x < distanceField.GetLength(0); ++x)
for (int y = 0; y < distanceField.GetLength(1); ++y)
for (int z = 0; z < distanceField.GetLength(2); ++z)
{
if (voxelizer[x, y, z] == MeshVoxelizer.Voxel.Boundary)
distanceField[x, y, z] = new Vector3Int(x, y, z);
if (voxelizer[x, y, z] != MeshVoxelizer.Voxel.Outside)
{
var p = new Vector3Int(x, y, z);
Vector3 df = distanceField[x, y, z];
int count = 1;
foreach (var o in MeshVoxelizer.faceNeighborhood)
{
// offset voxel to get neighbor:
var n = p + o;
if (voxelizer.VoxelExists(n.x, n.y, n.z) && voxelizer[n.x, n.y, n.z] != MeshVoxelizer.Voxel.Outside)
{
df += distanceField[n.x, n.y, n.z];
count++;
}
}
df /= count;
smoothed[x, y, z] = df;
}
}
distanceField = smoothed;
}
private void CalculateGradientsAndDistances(Vector3Int[,,] buffer1)
{
distanceField = new Vector3[voxelizer.resolution.x,
voxelizer.resolution.y,
voxelizer.resolution.z];
for (int x = 0; x < buffer1.GetLength(0); ++x)
for (int y = 0; y < buffer1.GetLength(1); ++y)
for (int z = 0; z < buffer1.GetLength(2); ++z)
{
if (voxelizer[x, y, z] != MeshVoxelizer.Voxel.Outside)
{
distanceField[x, y, z] = voxelizer.GetVoxelCenter(buffer1[x, y, z]) -
voxelizer.GetVoxelCenter(new Vector3Int(x, y, z));
}
else
distanceField[x, y, z] = new Vector3Int(-1, -1, -1);
distanceField[x, y, z] = Vector3.zero;
}
}
public IEnumerator JumpFlood()
{
// create two buffers for ping-ponging:
Vector3Int[,,] buffer1 = new Vector3Int[voxelizer.resolution.x,
voxelizer.resolution.y,
voxelizer.resolution.z];
Vector3Int[,,] buffer2 = new Vector3Int[voxelizer.resolution.x,
voxelizer.resolution.y,
voxelizer.resolution.z];
// initialize distance field:
for (int x = 0; x < buffer1.GetLength(0); ++x)
for (int y = 0; y < buffer1.GetLength(1); ++y)
for (int z = 0; z < buffer1.GetLength(2); ++z)
{
if (voxelizer[x, y, z] == MeshVoxelizer.Voxel.Outside)
buffer1[x, y, z] = new Vector3Int(x, y, z);
else
buffer1[x, y, z] = new Vector3Int(-1, -1, -1);
}
// calculate the maximum size of the buffer:
int size = Mathf.Max(distanceField.GetLength(0),
distanceField.GetLength(1),
distanceField.GetLength(2));
int size = Mathf.Max(buffer1.GetLength(0),
buffer1.GetLength(1),
buffer1.GetLength(2));
int step = (int)(size / 2.0f);
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...",0);
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...", 0);
float numPasses = (int) Mathf.Log(size, 2);
float numPasses = (int)Mathf.Log(size, 2);
int i = 0;
// jump flood passes:
while (step >= 1)
{
JumpFloodPass(step, distanceField, auxBuffer);
JumpFloodPass(step, buffer1, buffer2);
// halve step:
step /= 2;
// swap buffers:
Vector3Int[,,] temp = distanceField;
distanceField = auxBuffer;
auxBuffer = temp;
Vector3Int[,,] temp = buffer1;
buffer1 = buffer2;
buffer2 = temp;
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...", ++i / numPasses);
}
CalculateGradientsAndDistances(buffer1);
}
private void JumpFloodPass(int stride, Vector3Int[,,] input, Vector3Int[,,] output)
@@ -155,34 +200,30 @@ namespace Obi
dist = (s - p).sqrMagnitude;
// for each neighbor voxel:
for (int nx = -1; nx <= 1; ++nx)
for (int ny = -1; ny <= 1; ++ny)
for (int nz = -1; nz <= 1; ++nz)
foreach (var o in MeshVoxelizer.fullNeighborhood)
{
// offset voxel to get neighbor:
var n = p + o * stride;
if (voxelizer.VoxelExists(n.x, n.y, n.z))
{
// neighbors' closest seed.
Vector3Int nc = input[n.x, n.y, n.z];
if (nc.x >= 0)
{
// neighbor's position:
int px = x + nx * stride;
int py = y + ny * stride;
int pz = z + nz * stride;
// distance to neighbor's closest seed:
float newDist = (nc - p).sqrMagnitude;
if (voxelizer.VoxelExists(px,py,pz))
// if the distance to the neighbor's closest seed is smaller than the distance to ours:
if (newDist < dist)
{
// neighbors' closest seed.
Vector3Int n = input[px,py,pz];
if (n.x >= 0)
{
// distance to neighbor's closest seed:
float newDist = (n - p).sqrMagnitude;
// if the distance to the neighbor's closest seed is smaller than the distance to ours:
if (newDist < dist)
{
output[x, y, z] = n;
dist = newDist;
}
}
output[x, y, z] = nc;
dist = newDist;
}
}
}
}
}
}