双轮廓和二次误差函数
我已经在C#中实施了行军立方体,双行军立方体和自适应行进立方体,只是为了发现我需要双轮廓为我的目的。 我读过关于双轮廓的所有作品,除了双轮廓自身的核心之外,我还完成了所有作品:最小化二次误差函数(QEF)。
现在,我只是通过找到共享单个顶点(3到6个边)的所有边点之间的平均值来计算内部体素的顶点位置,并且它工作良好,但它显然不会在正确的位置创建内部顶点。
这是我正在尝试创建的一段代码。 任何帮助将非常感激
/// <summary>
/// ORIGINAL WORK: Dual Contouring of Hermite Data by Tao Ju (remember me of a MechCommander 2 character)
/// 2.3 Representing and minimizing QEFs
/// The function E[x] can be expressed as the inner
/// product (Ax-b)T (Ax-b) where A is a matrix whose rows are the
/// normals ni and b is a vector whose entries are ni*pi. <------------ (dot product?)>
/// Typically, the quadratic function E[x] is expanded into the form
/// E[x] = xT AT Ax - 2xT AT b + bT b (2)
/// where the matrix AT A is a symmetric 3x3 matrix, AT b is a column
/// vector of length three and bT b is a scalar. The advantage of this expansion
/// is that only the matrices AT A, AT b and bT b need be stored
/// (10 floats), as opposed to storing the matrices A and b. Furthermore,
/// a minimizing value ˆ x for E[x] can be computed by solving
/// the normal equations AT Aˆ x = AT b.
/// </summary>
public Vector3 GetMinimumError(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 n0, Vector3 n1, Vector3 n2)
{
//so, here we are. I'm creating a vector to store the final value.
Vector3 position = Vector3.Zero;
//Values of b are supposed to b (:P) three floats. The only way i know to find a float value
//by multiplying 2 vectors is to use dot product.
Vector3 b = new Vector3(
Vector3.Dot(p0, n0),
Vector3.Dot(p1, n1),
Vector3.Dot(p2, n2));
//What the transpose of a vector is supposed to be?
//I don't know, but i think should be the vector itself :)
float bTb = Vector3.Dot(b, b);
//i create a square matrix 3x3, so i can use c# matrix transformation libraries.
//i know i will probably have to build bigger matrix later on, but it should fit for now
Matrix A = new Matrix(
n0.X, n0.Y, n0.Z, 0,
n1.X, n1.Y, n1.Z, 0,
n2.X, n2.Y, n2.Z, 0,
0, 0, 0, 0);
//easy
Matrix AT = Matrix.Transpose(A);
//EASY
Matrix ATA = Matrix.Multiply(AT, A);
//Another intuition. Hope makes sense...
Vector3 ATb = Vector3.Transform(b, AT);
//...
// some cool stuff about solving
// the normal equations AT Aˆ x = AT b
//...
return position; //profit!
}
基金是很难理解的。 希望我能帮上忙。 双轮廓方法计算每个交叉点处的“Hermite”数据,或换句话说,在体素边缘上创建的每个点处,表面的法线已知。 用一个点和一个正常的人可以计算一个平面的方程。
QEF是从体素的内部点到与体素相关的每个平面的距离的平方和。 以下是计算QEF的一些伪代码。
double get_QEF(Point3d point, Voxel3d voxel)
{
double QEF = 0.0;
foreach(plane in voxel.planes)
{
double dist_to_plane = plane.distance(point);
QEF += dist_to_plane*dist_to_plane;
}
return(QEF);
}
目标是在体素内部选择一个最小化QEF的点。 文献建议使用Grahm-Schmidt过程来定位最佳点,但这可能很复杂,也可能导致位于体素之外的点。
另一个选择(hack-ish)是在体素内部创建点网格并计算每个点的QEF并选择最低点的点,网格越精细,越接近您将要到达的最佳点,但时间越长计算。
在我目前使用一种非常简单的方法来解决QEF的双轮廓实现中。 由于QEF实质上是最小二乘逼近,我发现计算QEF的最简单方法是通过计算伪逆。 这种伪逆可以用你的语言中的任何代数库来计算。
这是我正在使用的代码:
public static Vector<float> CalculateCubeQEF(Vector3[] normals, Vector3[] positions, Vector3 meanPoint)
{
var A = DenseMatrix.OfRowArrays(normals.Select(e => new[] { e.X, e.Y, e.Z }).ToArray());
var b = DenseVector.OfArray(normals.Zip(positions.Select(p => p - meanPoint), Vector3.Dot).ToArray());
var pseudo = PseudoInverse(A);
var leastsquares = pseudo.Multiply(b);
return leastsquares + DenseVector.OfArray(new[] { meanPoint.X, meanPoint.Y, meanPoint.Z });
}
函数的输入是交点和法线,meanPoint是给定交点的平均值。
总结数学运算:该函数计算位于由交点和法线定义的所有平面的交点上的点。 由于这没有一个确切的解决方案,计算最小二乘逼近,这找到了'最不错'的点。 另外,交点被“移动”,以便平均点成为原点。 这确保了当QEF有多个解决方案时,选择最接近平均点的解决方案。
链接地址: http://www.djcxy.com/p/37203.html上一篇: Dual Contouring and Quadratic Error Function
下一篇: Multiply a number by 3.5 without using *, /, % operators