OpenCV进行顶点法向量计算
先介绍一下我接触此题的背景。我需要将一个.obj文件中的vn即顶点法向量计算出来。现在有的数据是f(三角面片索引),v(顶点坐标),但是网上找的代码不是很多,逻辑也不是很清楚,因此自己写了代码,并与大家分享。有错误敬请指出!
顶点法向量的计算方法
这里大概摘自
这篇文章
最简单也是最直接的求顶点的法向量的方法是遍历此顶点所关联的所有三角形,将这些三角形上的法向量单位化后叠加,然后求出法向量的均值即可。
伪代码:
vertexNormal = (0,0,0);
for each triangle connect to current vertex;
vertexNormal += triangleNormal;
vertexNormal /= connectedTriangleNums;
但这样求出的法向量往往效果不好,特别是当一些很小的多边形共面时。为了解决这个问题就需要使各个三角形并不是简单的等比例贡献法向量。直观上很容易发现,每个三角形与顶点相关联的那个角的大小是变化的,因此就可以使用此角度的大小来作为单个三角形对顶点法向量的贡献因子。
vertexNormal = 0,0,0;
totalAngle = 0;
for each triangle connect to current vertex;
angle = acos(dot(edge1 , edge2));
vertexNormal += trangleNormal * angle;
totalAngle += angle;
vertexNormal /= totalAngle;
在实现时要注意,使用的角为关联当前顶点的那个角。
本文采用的算法是第二种
但是,根据数据结构不同,具体按需要编写代码
实现代码及解析
为每一个顶点定义一个存储法向量和与总角度的结构体:
using namespace cv;
typedef struct vet_data
{
Vec3f vn;
double total_angle;
} vet_data;
新建点与面数据进行模拟:
Vec3f v[4] = {
Vec3f(33.7193, 24.7910, 50.5333),
Vec3f(34.6217, 23.1564, 51.2146),
Vec3f(42.6307, 22.9352, 49.5766),
Vec3f(41.6282, 24.5848, 48.7240)
};
Vec3f f[4] = {
Vec3f(0, 1, 2),
Vec3f(0, 2, 3),
Vec3f(1, 2, 3),
Vec3f(0, 1, 3)
};
初始化:
vet_data v_data[4];
for(int i = 0; i < 4; ++i) {
v_data[i].vn = Vec3f(0,0,0);
v_data[i].total_angle = 0;
}
运算过程:
for(int i = 0; i < 4; ++i) {
int indx[3];
for(int j = 0;j < 3; ++j) {
indx[j] = f[i][j];
}
Vec3f v0 = v[indx[0]],
v1 = v[indx[1]],
v2 = v[indx[2]];
Vec3f e1 = v1 - v0;
Vec3f e2 = v2 - v1;
Vec3f vn = normalize(e1.cross(e2));
double angle[3] = { 0.0, 0.0, 0.0 };
double t1, t2, t3;
t1 = (v1 - v0).dot(v2 - v0) / mod(v1 - v0) / mod(v2 - v0) * CV_PI / 180.0;
t2 = (v0 - v1).dot(v2 - v1) / mod(v0 - v1) / mod(v2 - v1) * CV_PI / 180.0;
t3 = (v0 - v2).dot(v1 - v2) / mod(v0 - v2) / mod(v1 - v2) * CV_PI / 180.0;
angle[0] = acos(t1);
angle[1] = acos(t2);
angle[2] = acos(t3);
cout << angle[0] << " " << angle[1] << " " << angle[2] << " # " << t1 << " " << t2 << " " << t3 << std::endl;
for(int i = 0; i < 3; ++i) {
v_data[indx[i]].vn += vn * angle[i];
v_data[indx[i]].total_angle += angle[i];
}
}
ofstream out("outputvn.txt");
out << "-----vn-------" << endl;
for(int i = 0; i < 4; ++i) {
Vec3f vn = v_data[i].vn / v_data[i].total_angle;
out << "vn" << " " << vn[0] << " " << vn[1] << " " << vn[2] << std::endl;
cout << "vn" << " " << vn[0] << " " << vn[1] << " " << vn[2] << std::endl;
}