有一项需求: 求出模型的任意方向的视图
本文写一个求顶视图和底视图的方式, 任意方向的视图只是投影平面方程不同而已
测试模型:
顶视图
底视图
顶部高度图(灰度, 未取材质颜色, 懒没写)
底部高度图(灰度)
本算法原理分以下几部:
1: 求模型外包围盒box, 根据顶视图输出大小计算输出分辨率resx,resy
2: 遍历图像宽高范围内的所有像素, 求每点与模型每个三角面片的交点 z值
3: 一个点可能与多个三角形在Z方向上交, 按照最大最小值记录, 最小值底视图, 最大值顶视图
4: 得到的高度数组根据高度范围拉伸到0-255, 保存为图片
初步估算算法复杂度为n的3次方, 优化空间不大, 除了求接点与三角形外包可用box加速, 目前就剩开omp硬件加速,或者simd加速等非软件方法了.
可以看出求底部的时候中间有一个高度不正常, 检查模型发现是模型有个洞, 所以是正常现象. 关键算法代码如下:
关键代码如下:
void Mesh2HeightMap(Mesh & mesh, int w, int h, ByteBuffer * bufferhm, double hmin, double hmax)
{
Help ctb;
auto box = mesh.pTriMesh->GetBox();
float Width = box[3] - box[0];
float Height = box[4] - box[1];
double resx = Width / w;
double resy = Height / h;
if (bufferhm)
bufferhm->AllocateT<float>(w*h);
float* Head = (float*)bufferhm->BufferHead();
#pragma omp parallel for
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++)
{
double x = box[0] + i * resx + 0.25 * resx;
double y = box[4] - j * resy - 0.25 * resy;
Point3D pOut(x, y, 0.);
double maxvalue = -DBL_MAX;
double minvalue = DBL_MAX;
for (unsigned int k = 0; k < mesh.pTriMesh->triangles.size(); k++)
{
int nindep1 = mesh.pTriMesh->triangles[k][0];
int nindep2 = mesh.pTriMesh->triangles[k][1];
int nindep3 = mesh.pTriMesh->triangles[k][2];
size_t pointssize = mesh.pTriMesh->points.size();
if (nindep1 >= pointssize || nindep1 < 0)
continue;
if (nindep2 >= pointssize || nindep2 < 0)
continue;
if (nindep3 >= pointssize || nindep3 < 0)
continue;
Vec3F p1 = mesh.pTriMesh->points[nindep1];
Vec3F p2 = mesh.pTriMesh->points[nindep2];
Vec3F p3 = mesh.pTriMesh->points[nindep3];
Point3D pt1(p1.X(), p1.Y(), p1.Z());
Point3D pt2(p2.X(), p2.Y(), p2.Z());
oint3D pt3(p3.X(), p3.Y(), p3.Z());
double xmin = std::min(std::min(pt1.X, pt2.X), std::min(pt3.X, pt2.X));
double ymin = std::min(std::min(pt1.Y, pt2.Y), std::min(pt3.Y, pt2.Y));
double xmax = std::max(std::max(pt1.X, pt2.X), std::max(pt3.X, pt2.X));
double ymax = std::max(std::max(pt1.Y, pt2.Y), std::max(pt3.Y, pt2.Y));
if (pOut.X < xmin || pOut.Y < ymin || pOut.X > xmax || pOut.Y >ymax)
continue;
//点在三角形上z值, 就是平面方程求z
if (ctb.Point2Ttiangle(pt1, pt2, pt3, pOut))
{
maxvalue = maxvalue < pOut.Z ? pOut.Z : maxvalue;
minvalue = minvalue > pOut.Z ? pOut.Z : minvalue;
}
}
//Head[w * i + j] = maxvalue;
Head[w * i + j] = minvalue;
}
}
//保存高度数组为图像
void SaveHeight( int w,int h, ByteBuffer*bufferhm)
{
ByteBuffer bufftttt;
bufftttt.AllocateT<unsigned int>(w*h);
unsigned int* buffhead = (unsigned int*)bufftttt.BufferHead();
float* f = (float*)bufferhm->BufferHead();
double min = DBL_MAX, max = -DBL_MIN;
for (int i = 0; i < bufferhm->BufferSizeT<float>(); i++)
{
if (std::isinf(f[i]))
continue;
min = min > f[i] ? f[i]: min ;
max = max < f[i] ? f[i]: max ;
}
BitmapPtr ptrIMG = new Bitmap(w, h);
for (int i = 0; i < bufferhm->BufferSizeT<float>(); i++)
{
unsigned char* g = (unsigned char*)&buffhead[i];
if (std::isinf(f[i]))
{
g[0] = 0;
g[1] = 0;
g[2] = 0;
g[3] = 0;
continue;
}
unsigned char RGB = 255 * (fabs(f[i] / (max - min)));
g[0] = RGB;
g[1] = RGB;
g[2] = RGB;
g[3] = 255;
}
ptrIMG->FillImageData(bufftttt.BufferHead(), bufftttt.BufferSize(), ptrIMG->RGBAType());
char file[512];
sprintf(&file[0], "D:\\testdata\\%d_%d_%d.png", 0, 0, 0);
ptrIMG->SavePNG(file);
}
以上代码vs2015 测过( 高度图赋值颜色就是求z再去三角形种内插颜色即可