Ben İkili Contouring uygulamak için bu öğretici takip ediyorum http://www.sandboxie.com/misc/isosurf/isosurfaces.html
Veri kaynağım bir ızgara 16x16x16; Bu ızgarayı aşağıdan yukarıya, soldan sağa, uzaklara doğru geçiyorum.
Izgaramın her dizini için bir küp yapısı oluşturuyorum:
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);
}
Izgarayı nasıl geçtiğime göre, sadece 4 köşeye ve 3 kenara bakmam gerekiyor. Bu resimde, 2, 5, 6, 7 köşeleri 0, 1, 2, 3 köşelerime karşılık gelir ve 5, 6, 10 kenarları 0, 1, 2 köşelerime karşılık gelir.
Bir kenar şöyle görünür:
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);
}
Daha sonra bir işaret değişikliği için tüm kenarları kontrol ederim, eğer bir tane varsa çevreleyen küpleri bulur ve bu küplerin özellik noktasını alırım.
Özellik noktasını küp merkezine ayarlarsam işe yarar, sonra bloklu minecraft görünümünü elde ederim. Ama istediğim bu değil.
Özellik noktasını bulmak için, bu yazıda olduğu gibi yapmak istedim: https://gamedev.stackexchange.com/a/83757/49583
Temel olarak, tepe noktasını hücrenin ortasından başlatırsınız. Sonra, tepe noktasından alınan her vektörü her bir düzlemde ortalamalandırır ve tepe noktasını bu sonuç boyunca hareket ettirirsiniz ve bu adımı sabit sayıda tekrarlayın. Sonuçta ~% 70 hareket ettirmenin en az yineleme miktarında stabilize olacağını buldum.
Bir Plane sınıfı aldım:
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);
}
}
ve her bir kenar için bir tane olmak ve merkeze olan uzaklığı ortalama olarak 3 düzlem oluşturduğum özellik noktasını almak için bir işlev:
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;
}
}
Ama işe yaramıyor, köşeler her yerde. Hata nerede? Aslında kenar köşelerinin normalinin ortalamasını alarak kenarı normal olarak hesaplayabilir miyim? Veri kaynağı olarak sadece bir tamsayı kılavuzum olduğu için, kenar orta noktasında yoğunluğu alamıyorum ...
Düzenleme: Burada da http://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html 3 düzlemin kesişimini hesaplamak için matrisler kullanabileceğimi buldum , en azından ben böyle anladım, yani Bu yöntemi yarattım
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;
}
hangi bu verilerle
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);
(2.0, 2.0, 2.0) 'da bir kavşak hesaplar, bu yüzden doğru çalıştığını varsayıyorum. Yine de, doğru köşeleri değil. Gerçekten benim normallerim olduğunu düşünüyorum.
Can I actually calculate the edge normal by averaging the normal of the edge vertices?
- Yanılıyor olabilirim, ama sanırım başka yerlerde normallere ulaşmak için asla enterpolasyon yapmamanızı söyleyen bir tavsiye gördüm - sadece iyi enterpolasyon yapmıyorlar. Yüz başına hesapla, daha güvenlidir. Gerçekten, normal hesaplamanızın doğru olduğundan emin olmak için önce minimum bir test senaryosu oluşturmalısınız. Sonra buna devam edin.
Plane
tanımlanmış yöntemlere sahip bir yapıya sahiptir ( buraya bakınız ) (Plane
C # uzatma yöntemlerini kullanarak yapıya ekleyebileceğiniz en kısa vektör yöntemi hariç ). Sen kullanabilirsinizGetDistanceToPoint
senin yerine yönteminiDistance
yöntemiyle.