このチュートリアルに従って、デュアルコンタリングを実装してい ますhttp://www.sandboxie.com/misc/isosurf/isosurfaces.html
私のデータソースはグリッド16x16x16です。私はこのグリッドを下から上、左から右、近くから遠くまで行き来しています。
グリッドのインデックスごとに、キューブ構造を作成します。
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);
}
グリッドをトラバースする方法により、4つの頂点と3つのエッジのみを確認する必要があります。この図では、頂点2、5、6、7が私の頂点0、1、2、3に対応し、エッジ5、6、10が私のエッジ0、1、2に対応しています。
エッジは次のようになります。
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);
}
次に、すべてのエッジの符号の変化をチェックします。エッジが存在する場合は、周囲のキューブを見つけて、それらのキューブの特徴点を取得します。
これで機能ポイントを立方体の中心に設定すると機能し、ブロック状のマインクラフトの外観になります。しかし、それは私が望んでいることではありません。
特徴点を見つけるために、私はこの投稿のようにそれをしたかった:https : //gamedev.stackexchange.com/a/83757/49583
基本的に、頂点はセルの中心から開始します。次に、頂点から各平面に取られたすべてのベクトルを平均し、その結果に沿って頂点を移動し、この手順を固定回数繰り返します。結果に沿って約70%移動すると、最小限の反復で安定します。
だから私は飛行機のクラスを得ました:
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);
}
}
そして、特徴点を取得する関数。ここでは、各エッジに1つずつ、3つの平面を作成し、中心までの距離を平均化します。
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;
}
}
しかし、機能していません。頂点はあちこちにあります。エラーはどこにありますか?エッジ頂点の法線を平均化することで、実際にエッジ法線を計算できますか?データソースとして整数グリッドしかないので、エッジの中点で密度を取得できません...
編集:私はここでもhttp://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html を見つけました。これは、行列を使用して3つの平面の交差を計算できることです 。少なくともそれが私が理解した方法です。このメソッドを作成しました
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;
}
このデータで
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)で交差を計算するので、正しく動作すると思います。それでも、正しい頂点ではありません。僕の常態だと思う。
Can I actually calculate the edge normal by averaging the normal of the edge vertices?
-私は間違っているかもしれませんが、法線を取得するために補間しないでくださいというアドバイスを他の場所で見たと思います-それらはうまく補間しないだけです。顔ごとに計算し、それはより安全です。実際には、最初に最小のテストケースを作成して、法線の計算が正しいことを確認する必要があります。次に、これを続行します。
Plane
構造が定義されています(ここを参照)。これには、指定したメソッドが既に定義されています(Plane
C#拡張メソッドを使用して構造に追加できる最短のベクトルメソッドを除く)。GetDistanceToPoint
メソッドの代わりにメソッドを使用できますDistance
。