Estoy siguiendo este tutorial para implementar Dual Contouring http://www.sandboxie.com/misc/isosurf/isosurfaces.html
Mi fuente de datos es una cuadrícula de 16x16x16; Atravieso esta cuadrícula de abajo hacia arriba, de izquierda a derecha, de cerca a lejos.
Para cada índice de mi cuadrícula, creo una estructura de cubo:
public Cube(int x, int y, int z, Func<int, int, int, IsoData> d, float isoLevel) {
this.pos = new Vector3(x,y,z);
//only create vertices need for edges
Vector3[] v = new Vector3[4];
v[0] = new Vector3 (x + 1, y + 1, z);
v[1] = new Vector3 (x + 1, y, z + 1);
v[2] = new Vector3 (x + 1, y + 1, z + 1);
v[3] = new Vector3 (x, y + 1, z + 1);
//create edges from vertices
this.edges = new Edge[3];
edges[0] = new Edge (v[1], v[2], d, isoLevel);
edges[1] = new Edge (v[2], v[3], d, isoLevel);
edges[2] = new Edge (v[0], v[2], d, isoLevel);
}
Debido a cómo atravesar la cuadrícula, solo necesito mirar 4 vértices y 3 bordes. En esta imagen, los vértices 2, 5, 6, 7 corresponden a mis vértices 0, 1, 2, 3, y los bordes 5, 6, 10 corresponden a mis bordes 0, 1, 2.
Un borde se ve así:
public Edge(Vector3 p0, Vector3 p1, Func<int, int, int, IsoData> d, float isoLevel) {
//get density values for edge vertices, save in vector , d = density function, data.z = isolevel
this.data = new Vector3(d ((int)p0.x, (int)p0.y, (int)p0.z).Value, d ((int)p1.x, (int)p1.y, (int)p1.z).Value, isoLevel);
//get intersection point
this.mid = LerpByDensity(p0,p1,data);
//calculate normals by gradient of surface
Vector3 n0 = new Vector3(d((int)(p0.x+1), (int)p0.y, (int)p0.z ).Value - data.x,
d((int)p0.x, (int)(p0.y+1), (int)p0.z ).Value - data.x,
d((int)p0.x, (int)p0.y, (int)(p0.z+1) ).Value - data.x);
Vector3 n1 = new Vector3(d((int)(p1.x+1), (int)p1.y, (int)p1.z ).Value - data.y,
d((int)p1.x, (int)(p1.y+1), (int)p1.z ).Value - data.y,
d((int)p1.x, (int)p1.y, (int)(p1.z+1) ).Value - data.y);
//calculate normal by averaging normal of edge vertices
this.normal = LerpByDensity(n0,n1,data);
}
Luego verifico todos los bordes para un cambio de signo, si hay uno, encuentro los cubos circundantes y obtengo el punto característico de esos cubos.
Ahora que funciona si configuro el punto de característica en el centro del cubo, entonces obtengo el aspecto de Minecraft en bloque. Pero eso no es lo que quiero.
Para encontrar el punto destacado, quería hacerlo como en esta publicación: https://gamedev.stackexchange.com/a/83757/49583
Básicamente, comienzas el vértice en el centro de la celda. Luego, promedia todos los vectores tomados del vértice a cada plano y mueve el vértice a lo largo de ese resultado, y repite este paso un número fijo de veces. Descubrí que moverlo ~ 70% a lo largo de la resultante se estabilizaría en la menor cantidad de iteraciones.
Entonces obtuve una clase de avión:
private class Plane {
public Vector3 normal;
public float distance;
public Plane(Vector3 point, Vector3 normal) {
this.normal = Vector3.Normalize(normal);
this.distance = -Vector3.Dot(normal,point);
}
public float Distance(Vector3 point) {
return Vector3.Dot(this.normal, point) + this.distance;
}
public Vector3 ShortestDistanceVector(Vector3 point) {
return this.normal * Distance(point);
}
}
y una función para obtener el punto de entidad, donde creo 3 planos, uno para cada borde y promedio la distancia al centro:
public Vector3 FeaturePoint {
get {
Vector3 c = Center;
// return c; //minecraft style
Plane p0 = new Plane(edges[0].mid,edges[0].normal);
Plane p1 = new Plane(edges[1].mid,edges[1].normal);
Plane p2 = new Plane(edges[2].mid,edges[2].normal);
int iterations = 5;
for(int i = 0; i < iterations; i++) {
Vector3 v0 = p0.ShortestDistanceVector(c);
Vector3 v1 = p1.ShortestDistanceVector(c);
Vector3 v2 = p2.ShortestDistanceVector(c);
Vector3 avg = (v0+v1+v2)/3;
c += avg * 0.7f;
}
return c;
}
}
Pero no funciona, los vértices están por todas partes. ¿Dónde está el error? ¿Puedo calcular el borde normal promediando el normal de los vértices del borde? No puedo obtener la densidad en el punto medio del borde, ya que solo tengo una cuadrícula entera como fuente de datos ...
Editar: También encontré aquí http://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html que puedo usar matrices para calcular la intersección de los 3 planos, al menos así lo entendí, así que Creé este método
public static Vector3 GetIntersection(Plane p0, Plane p1, Plane p2) {
Vector3 b = new Vector3(-p0.distance, -p1.distance, -p2.distance);
Matrix4x4 A = new Matrix4x4 ();
A.SetRow (0, new Vector4 (p0.normal.x, p0.normal.y, p0.normal.z, 0));
A.SetRow (1, new Vector4 (p1.normal.x, p1.normal.y, p1.normal.z, 0));
A.SetRow (2, new Vector4 (p2.normal.x, p2.normal.y, p2.normal.z, 0));
A.SetRow (3, new Vector4 (0, 0, 0, 1));
Matrix4x4 Ainv = Matrix4x4.Inverse(A);
Vector3 result = Ainv * b;
return result;
}
que con estos datos
Plane p0 = new Plane (new Vector3 (2, 0, 0), new Vector3 (1, 0, 0));
Plane p1 = new Plane (new Vector3 (0, 2, 0), new Vector3 (0, 1, 0));
Plane p2 = new Plane (new Vector3 (0, 0, 2), new Vector3 (0, 0, 1));
Vector3 cq = Plane.GetIntersection (p0, p1, p2);
calcula una intersección en (2.0, 2.0, 2.0), por lo que supongo que funciona correctamente. Aún así, no son los vértices correctos. Realmente creo que es mi normalidad.
Can I actually calculate the edge normal by averaging the normal of the edge vertices?
- Puedo estar equivocado, pero creo que he visto consejos en otros lugares que dicen que nunca se debe interpolar para obtener la normalidad, simplemente no interpolan bien. Calcular por cara, es más seguro. Realmente, primero debe construir un caso de prueba mínimo para asegurarse de que su cálculo normal sea correcto. Entonces sigue con esto.
Plane
estructura definida ( consulte aquí ), que tiene los métodos que proporcionó ya definidos (excepto el método vectorial más corto, que puede agregar a laPlane
estructura utilizando los métodos de extensión de C #). Puede usar elGetDistanceToPoint
método en lugar de suDistance
método.