用多边形近似球体表面(icosphere)的Mesh数据的生成并使用openGL绘制
2016-12-05 11:17
681 查看
这里用到的Mesh数据结构如果不清楚可以参照我的上一篇。
参考了一个牛人的文章, 他的代码是C#的,我用C++改写了一遍,为了便于理解,省去了他的cache优化。
左图为UVSphere,右图为Iconsphere。在一些情况下UVphere有很好的表现,但是,在另一些情境中,比如改变球体形状时,UVSphere两极高密度的顶点给变换带来了麻烦。这时Iconsphere这样顶点分布平均的方式就很受欢迎。
Iconsphere的生成基于一个由20个等边三角形组成的20面体,将三角形每个边2等分,它们的连线将一个三角形分为4个等边三角形,再对生成的小三角形重复以上步骤。
创建顶点列表很直接:
我按照每个顶点加入数组的顺序给图片标了号:
加入每个面中的顶点都要严格按照既定顺序,我使用从物体外部观察的逆时针方向,接下来只要小心地将points加入没个Face中:
接下来将三角形分成4个,但是不能仅仅用Xnew’ = (X1 + X2) / 2线性生成坐标,要确保新生成的点在球面上。线性生成的点记为Nnew’,length’=|Nnew’|。所求最终的点记为Nnew,(length=|Nnew|)==R,那么将Nnew’再做一次线性变换就可以得到Nnew。
完整代码如下:
参考了一个牛人的文章, 他的代码是C#的,我用C++改写了一遍,为了便于理解,省去了他的cache优化。
创建网格球面
一般3D球体的建模有两种Mesh可以选择,UVSphere和Iconsphere左图为UVSphere,右图为Iconsphere。在一些情况下UVphere有很好的表现,但是,在另一些情境中,比如改变球体形状时,UVSphere两极高密度的顶点给变换带来了麻烦。这时Iconsphere这样顶点分布平均的方式就很受欢迎。
Iconsphere的生成基于一个由20个等边三角形组成的20面体,将三角形每个边2等分,它们的连线将一个三角形分为4个等边三角形,再对生成的小三角形重复以上步骤。
创建一个20面体的Mesh结构
维基百科有一个很好的20面体的图片表示创建顶点列表很直接:
float t = (1.0 + sqrt(5.0)) / 2.0; mesh->points.push_back(new Point3D(-1, t, 0)); mesh->points.push_back(new Point3D(1, t, 0)); mesh->points.push_back(new Point3D(-1, -t, 0)); mesh->points.push_back(new Point3D(1, -t, 0)); mesh->points.push_back(new Point3D(0, -1, t)); mesh->points.push_back(new Point3D(0, 1, t)); mesh->points.push_back(new Point3D(0, -1, -t)); mesh->points.push_back(new Point3D(0, 1, -t)); mesh->points.push_back(new Point3D(t, 0, -1)); mesh->points.push_back(new Point3D(t, 0, 1)); mesh->points.push_back(new Point3D(-t, 0, -1)); mesh->points.push_back(new Point3D(-t, 0, 1));
我按照每个顶点加入数组的顺序给图片标了号:
加入每个面中的顶点都要严格按照既定顺序,我使用从物体外部观察的逆时针方向,接下来只要小心地将points加入没个Face中:
int ni = 0; Normal* temp = NULL; temp = new Normal(mesh->points[0], mesh->points[11], mesh->points[5]); mesh->faces.push_back(new Face(0, 11, 5, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[5], mesh->points[1]); mesh->faces.push_back(new Face(0, 5, 1, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[1], mesh->points[7]); mesh->faces.push_back(new Face(0, 1, 7, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[7], mesh->points[10]); mesh->faces.push_back(new Face(0, 7, 10, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[10], mesh->points[11]); mesh->faces.push_back(new Face(0, 10, 11, ni)); mesh->normals.push_back(temp); ++ni; // 5 adjacent faces temp = new Normal(mesh->points[1], mesh->points[5], mesh->points[9]); mesh->faces.push_back(new Face(1, 5, 9, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[5], mesh->points[11], mesh->points[4]); mesh->faces.push_back(new Face(5, 11, 4, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[11], mesh->points[10], mesh->points[2]); mesh->faces.push_back(new Face(11, 10, 2, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[10], mesh->points[7], mesh->points[6]); mesh->faces.push_back(new Face(10, 7, 6, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[7], mesh->points[1], mesh->points[8]); mesh->faces.push_back(new Face(7, 1, 8, ni)); mesh->normals.push_back(temp); ++ni; // 5 faces around point 3 temp = new Normal(mesh->points[3], mesh->points[9], mesh->points[4]); mesh->faces.push_back(new Face(3, 9, 4, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[4], mesh->points[2]); mesh->faces.push_back(new Face(3, 4, 2, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[2], mesh->points[6]); mesh->faces.push_back(new Face(3, 2, 6, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[6], mesh->points[8]); mesh->faces.push_back(new Face(3, 6, 8, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[8], mesh->points[9]); mesh->faces.push_back(new Face(3, 8, 9, ni)); mesh->normals.push_back(temp); ++ni; // 5 adjacent faces temp = new Normal(mesh->points[4], mesh->points[9], mesh->points[5]); mesh->faces.push_back(new Face(4, 9, 5, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[2], mesh->points[4], mesh->points[11]); mesh->faces.push_back(new Face(2, 4, 11, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[6], mesh->points[2], mesh->points[10]); mesh->faces.push_back(new Face(0, 1, 7, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[8], mesh->points[6], mesh->points[7]); mesh->faces.push_back(new Face(8, 6, 7, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[9], mesh->points[8], mesh->points[1]); mesh->faces.push_back(new Face(9, 8, 1, ni)); mesh->normals.push_back(temp); ++ni;
接下来将三角形分成4个,但是不能仅仅用Xnew’ = (X1 + X2) / 2线性生成坐标,要确保新生成的点在球面上。线性生成的点记为Nnew’,length’=|Nnew’|。所求最终的点记为Nnew,(length=|Nnew|)==R,那么将Nnew’再做一次线性变换就可以得到Nnew。
// refine triangles for (int i = 0; i < recursionLevel; i++) { vector<Face*> faces2; for(int j = 0; j < mesh->faces.size(); ++j) { // replace triangle by 4 triangles Face* f = mesh->faces[j]; int a = getMiddlePoint(mesh->faces[j]->vert[0]->vertIndex, mesh->faces[j]->vert[1]->vertIndex); int b = getMiddlePoint(mesh->faces[j]->vert[1]->vertIndex, mesh->faces[j]->vert[2]->vertIndex); int c = getMiddlePoint(mesh->faces[j]->vert[2]->vertIn 195d7 dex, mesh->faces[j]->vert[0]->vertIndex); temp = new Normal(mesh->points[f->vert[0]->vertIndex], mesh->points[a], mesh->points[c]); faces2.push_back(new Face(f->vert[0]->vertIndex, a, c, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[f->vert[1]->vertIndex], mesh->points[b], mesh->points[a]); faces2.push_back(new Face(f->vert[1]->vertIndex, b, a, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[f->vert[2]->vertIndex], mesh->points[c], mesh->points[b]); faces2.push_back(new Face(f->vert[2]->vertIndex, c, b, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[a], mesh->points[b], mesh->points[c]); faces2.push_back(new Face(a, b, c, ni)); mesh->normals.push_back(temp); ++ni; } mesh->faces = faces2; }
// add vertex to mesh, fix position to be on unit sphere, //return index int addVertex(Point3D* p) { double length = sqrt(p->X * p->X + p->Y * p->Y + p->Z * p->Z); mesh->points.push_back(new Point3D(p->X / length * 1.902, p->Y / length * 1.902, p->Z / length * 1.902)); return mesh->points.size() - 1; } // return index of point in the middle of p1 and p2 int getMiddlePoint(int p1, int p2) { // first check if we have it already int ret; // not in cache, calculate it Point3D *point1 = mesh->points[p1]; Point3D *point2 = mesh->points[p2]; Point3D *middle = new Point3D( (point1->X + point2->X) / 2.0, (point1->Y + point2->Y) / 2.0, (point1->Z + point2->Z) / 2.0); // add vertex makes sure point is on unit sphere int i = addVertex(middle); return i; }
完整代码如下:
#pragma once
#include "TriangleIndices.h"
#include "Point3D.h"
#include "Mesh.h"
#include <math.h>
using namespace std;
class IcoSphereCreator
{
public:
Mesh* mesh = new Mesh();
int index;
// return index of point in the middle of p1 and p2
/*int getMiddlePoint(int p1, int p2)
{
// first check if we have it already
bool firstIsSmaller = p1 < p2;
int smallerIndex = firstIsSmaller ? p1 : p2;
int greaterIndex = firstIsSmaller ? p2 : p1;
int key = (smallerIndex << 32) + greaterIndex;
int ret;
if (this.middlePointIndexCache.TryGetValue(key, out ret))
{
return ret;
}
// not in cache, calculate it
Point3D point1 = this.geometry.Positions[p1];
Point3D point2 = this.geometry.Positions[p2];
Point3D middle = new Point3D(
(point1.X + point2.X) / 2.0,
(point1.Y + point2.Y) / 2.0,
(point1.Z + point2.Z) / 2.0);
// add vertex makes sure point is on unit sphere
int i = addVertex(middle);
// store it, return index
this.middlePointIndexCache.Add(key, i);
return i;
}*/
Mesh* Create(int recursionLevel)
{
index = 0;
// create 12 vertices of a icosahedron
float t = (1.0 + sqrt(5.0)) / 2.0; mesh->points.push_back(new Point3D(-1, t, 0)); mesh->points.push_back(new Point3D(1, t, 0)); mesh->points.push_back(new Point3D(-1, -t, 0)); mesh->points.push_back(new Point3D(1, -t, 0)); mesh->points.push_back(new Point3D(0, -1, t)); mesh->points.push_back(new Point3D(0, 1, t)); mesh->points.push_back(new Point3D(0, -1, -t)); mesh->points.push_back(new Point3D(0, 1, -t)); mesh->points.push_back(new Point3D(t, 0, -1)); mesh->points.push_back(new Point3D(t, 0, 1)); mesh->points.push_back(new Point3D(-t, 0, -1)); mesh->points.push_back(new Point3D(-t, 0, 1));
// create 20 triangles of the icosahedron
//vector<Face> faces;
// 5 faces around point 0
int ni = 0; Normal* temp = NULL; temp = new Normal(mesh->points[0], mesh->points[11], mesh->points[5]); mesh->faces.push_back(new Face(0, 11, 5, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[5], mesh->points[1]); mesh->faces.push_back(new Face(0, 5, 1, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[1], mesh->points[7]); mesh->faces.push_back(new Face(0, 1, 7, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[7], mesh->points[10]); mesh->faces.push_back(new Face(0, 7, 10, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[0], mesh->points[10], mesh->points[11]); mesh->faces.push_back(new Face(0, 10, 11, ni)); mesh->normals.push_back(temp); ++ni; // 5 adjacent faces temp = new Normal(mesh->points[1], mesh->points[5], mesh->points[9]); mesh->faces.push_back(new Face(1, 5, 9, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[5], mesh->points[11], mesh->points[4]); mesh->faces.push_back(new Face(5, 11, 4, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[11], mesh->points[10], mesh->points[2]); mesh->faces.push_back(new Face(11, 10, 2, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[10], mesh->points[7], mesh->points[6]); mesh->faces.push_back(new Face(10, 7, 6, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[7], mesh->points[1], mesh->points[8]); mesh->faces.push_back(new Face(7, 1, 8, ni)); mesh->normals.push_back(temp); ++ni; // 5 faces around point 3 temp = new Normal(mesh->points[3], mesh->points[9], mesh->points[4]); mesh->faces.push_back(new Face(3, 9, 4, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[4], mesh->points[2]); mesh->faces.push_back(new Face(3, 4, 2, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[2], mesh->points[6]); mesh->faces.push_back(new Face(3, 2, 6, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[6], mesh->points[8]); mesh->faces.push_back(new Face(3, 6, 8, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[3], mesh->points[8], mesh->points[9]); mesh->faces.push_back(new Face(3, 8, 9, ni)); mesh->normals.push_back(temp); ++ni; // 5 adjacent faces temp = new Normal(mesh->points[4], mesh->points[9], mesh->points[5]); mesh->faces.push_back(new Face(4, 9, 5, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[2], mesh->points[4], mesh->points[11]); mesh->faces.push_back(new Face(2, 4, 11, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[6], mesh->points[2], mesh->points[10]); mesh->faces.push_back(new Face(0, 1, 7, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[8], mesh->points[6], mesh->points[7]); mesh->faces.push_back(new Face(8, 6, 7, ni)); mesh->normals.push_back(temp); ++ni; temp = new Normal(mesh->points[9], mesh->points[8], mesh->points[1]); mesh->faces.push_back(new Face(9, 8, 1, ni)); mesh->normals.push_back(temp); ++ni;
// refine triangles
for (int i = 0; i < recursionLevel; i++)
{
vector<Face*> faces2;
for(int j = 0; j < mesh->faces.size(); ++j)
{
// replace triangle by 4 triangles
Face* f = mesh->faces[j];
int a = getMiddlePoint(mesh->faces[j]->vert[0]->vertIndex, mesh->faces[j]->vert[1]->vertIndex);
int b = getMiddlePoint(mesh->faces[j]->vert[1]->vertIndex, mesh->faces[j]->vert[2]->vertIndex);
int c = getMiddlePoint(mesh->faces[j]->vert[2]->vertIndex, mesh->faces[j]->vert[0]->vertIndex);
temp = new Normal(mesh->points[f->vert[0]->vertIndex], mesh->points[a], mesh->points[c]);
faces2.push_back(new Face(f->vert[0]->vertIndex, a, c, ni));
mesh->normals.push_back(temp);
++ni;
temp = new Normal(mesh->points[f->vert[1]->vertIndex], mesh->points[b], mesh->points[a]);
faces2.push_back(new Face(f->vert[1]->vertIndex, b, a, ni));
mesh->normals.push_back(temp);
++ni;
temp = new Normal(mesh->points[f->vert[2]->vertIndex], mesh->points[c], mesh->points[b]);
faces2.push_back(new Face(f->vert[2]->vertIndex, c, b, ni));
mesh->normals.push_back(temp);
++ni;
temp = new Normal(mesh->points[a], mesh->points[b], mesh->points[c]);
faces2.push_back(new Face(a, b, c, ni));
mesh->normals.push_back(temp);
++ni;
}
mesh->faces = faces2;
}
return mesh;
}
// add vertex to mesh, fix position to be on unit sphere, return index
int addVertex(Point3D* p)
{
double length = sqrt(p->X * p->X + p->Y * p->Y + p->Z * p->Z);
mesh->points.push_back(new Point3D(p->X / length * 1.902, p->Y / length * 1.902, p->Z / length * 1.902));
return mesh->points.size() - 1;
}
// return index of point in the middle of p1 and p2
int getMiddlePoint(int p1, int p2)
{
// first check if we have it already
int ret;
// not in cache, calculate it
Point3D *point1 = mesh->points[p1];
Point3D *point2 = mesh->points[p2];
Point3D *middle = new Point3D(
(point1->X + point2->X) / 2.0,
(point1->Y + point2->Y) / 2.0,
(point1->Z + point2->Z) / 2.0);
// add vertex makes sure point is on unit sphere
int i = addVertex(middle);
return i;
}
};
相关文章推荐
- 【循序渐进学图形学之】OpenGL使用多边形近似模拟法构建表面
- OpenGL三维球体体数据生成与绘制
- IOS 中openGL使用教程2(openGL ES 入门篇 | 绘制一个多边形)
- OpenGL三维球体体数据生成与绘制
- 使用Jakarta POI EXCEL API自动生成ORACLE数据字典的源代码
- 使用该JavaBean可以将数据在JSP页面中以表格的形式显示出来,并具有动态排序、动态生成查询、自动分页功能
- 通过Spring.net来使用XCodeFactory生成的数据层!
- 使用Sql生成测试数据(转贴)
- 使用jdom操作xml数据,生成含Jtree的applet(转载 Jagie 原创 )
- WPF中如何使用代码操作数据模板生成的控件
- DWR使用体会2:获取数据库表数据,使用addRows生成列表
- 如何高效地使用XCodeFactory自动生成的数据层代码(一)
- 使用PD9.5将PDM生成数据字典文档
- 数据自动生成工具 DataFactory 的简单使用
- ASP.NET基础教程-Web 自定义控件的使用-根据属性值从数据库中提取数据并在页面上自动生成一个表格
- C#中使用byte[]数据,生成Bitmap(256色 灰度 BMP位图)源代码
- 使用OpenGL Midpoint Circle 算法来绘制一个八卦图
- 以实例说明如何使用C#从数据库中提取数据,按要求自动生成定制的Excel表格?[转]
- .NET下使用DataAdapter保存数据时,如何生成command语句及使用事务
- 使用iframe时Session重新生成的导致数据丢失的问题 ( by quqi99 )