计算机图形学finalal_project:MeshViewer

代码解读

a.首先从meshviewer.cpp看起,里面包含main函数入口,打印使用说明,设置窗口信息,初始化灯光材质等信息,创建菜单,绘制图像,窗口大小变化回调,鼠标点击和移动回调,默认回调,键盘回调,网格模型处理,主循环

b.然后要读懂的是MeshInterface.h文件,包含网格模型处理的操作,了解glNewListglCallList

c.接着看内核头文件,Kernal.hExKernal.h里包含了所有后面写代码要用到的函数声明,Kernal.h中要注意的是何种半边句柄的转换函数,ExKernal.h中的高级功能函数需要了解

提示

a.其中1. 2. 4. 5的函数声明在 mesh/extension/ExKernelT.h (截图如下)
函数实现请在mesh/extension/ExkernelT.cpp 中完成 (务必)

mark

b.其中3的函数声明在read_write/read_write.h
函数实现请在read_write/read_write.cpp 中完成 (务必)

mark

c.请把整个代码看明白,里面实现了各种基本操作,例如求三角网格中每个三角形的重心、求每个三角面片的法向等等。

d.程序运行截图如下:

mark

e.鼠标点击右键,会出现一个操作界面,里面有一些灯光和其他绘制效果。

mark

task1

计算三角网格中每个三角形(face)的面积;10分

此函数声明为: Scalar calc_facet_area(const FacetHandle& _fh); //给定一个三角面片,计算它的面积
Tips: 可以根据程序输出结果判定是否计算正确,例如,输入模型cow.off,它的所有三角面片中,面积最大的三角面片的面积为:The maximal area of the mesh is: 0.0494426

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
template<class ExItems>
typename ExKernelT<ExItems>::Scalar
ExKernelT<ExItems>::calc_facet_area(const FacetHandle& _fh){/////计算三角形的面积

///得到半边句柄
HalfedgeHandle& hh1 = halfedge_handle(_fh);
HalfedgeHandle& hh2 = next_halfedge_handle(hh1);
HalfedgeHandle& hh3 = next_halfedge_handle(hh2);

////由半边句柄得到边句柄
EdgeHandle& eh1 = edge_handle(hh1);
EdgeHandle& eh2 = edge_handle(hh2);
EdgeHandle& eh3 = edge_handle(hh3);

////由边句柄得到各边长
float a = calc_edge_length(eh1);
float b = calc_edge_length(eh2);
float c = calc_edge_length(eh3);

////利用海伦公式求面积s=sqrt(p(p-a)(p-b)(p-c))
Scalar area = 0.0;

float p = (a + b + c) / 2;
area = sqrt(p*(p - a)*(p - b)*(p - c));

facet_ref(_fh).area_ = area;
return area;
}

效果如下:

mark

task2

计算三角网格中每个顶点(vertex) 的法向;20分

此函数声明为:inline Normal calc_normal(const VertexHandle& _vh);///计算顶点的法向值
Tips: 注意归一化。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <class ExItems> 
typename ExKernelT<ExItems>::Normal
ExKernelT<ExItems>::calc_normal(const VertexHandle& _vh) {////更新一个顶点的法向
assert( _vh.is_valid());
assert( _vh.idx() < vertex_size() );

//Normal norm(1,1,1);///////用norm存储求得的法向值
Normal norm(0, 0, 0);

//////////////////////////在此实现////////////////////////////////////
HalfedgeHandle firstHH = halfedge_handle(_vh);
FacetHandle fh = facet_handle(firstHH);
norm = norm + calc_normal(fh);
HalfedgeHandle hh = opposite_halfedge_handle(next_halfedge_handle(firstHH));
while (hh != firstHH) {
norm = norm + calc_normal(fh);
hh = opposite_halfedge_handle(next_halfedge_handle(hh));
}

/////////////////////////////////////////////////////////////////////

return norm.normalize();
}

task3

用OpenGL把三角网格中每个三角面片(face)的法向画出来(提示:在每个三角面片的重心处画);20分

此函数声明为:bool ogl_writer2(bool _orient = true, bool _smooth = false);
Tips: 可以参照函数bool ogl_writer(bool _orient = true, bool _smooth = false)的来实现,这时只需要在每个三角面片中画出法向即可(法向用Line表示。按键盘上”m”可以查看画出面片法向后的结果。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
template <class Mesh>
bool ReaderWriterT<Mesh>::ogl_writer2(bool _orient, bool _smooth){////在里面把三角面片法向画出
HalfedgeHandle cshh;
Mesh::FacetIterator fit(mesh_->facet_begin());

//glShadeModel(GL_FLAT);
glShadeModel(GL_SMOOTH);
int orient = true;// (_orient) ? 1 : -1;
mesh_->update_normals(); //计算面法向和点法向

for (; fit != mesh_->facet_end(); ++fit) {

if ((*fit).status_.is_deleted()) continue;

cshh = fit->halfedge_handle_;
FacetHandle fh = mesh_->facet_handle(cshh);
const VertexHandle& vh0 = mesh_->vertex_handle(cshh);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINES);
do {
const VertexHandle& vh = mesh_->vertex_handle(cshh);

//glNormal3fv(mesh_->normal(fh)*orient);
//glVertex3fv(mesh_->coord(vh));
cshh = mesh_->next_halfedge_handle(cshh);

glVertex3fv(mesh_->calc_centroid(fh));
glVertex3fv(mesh_->calc_centroid(fh) + mesh_->normal(fh));
} while (cshh != fit->halfedge_handle_);
glEnd();
}

return true;
}

效果如下:

mark

task4

实现一种简单的三角网格去噪算法(例如拉普拉斯光顺);30分

此函数声明为:void Laplacian_Smoothing();//////////////////实现一种三角网格去噪算法
Tips: 可以参照已经实现的两个去噪算法;按键盘上”b”可以查看去噪结果。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
template<class ExItems>
void ExKernelT<ExItems>::Laplacian_Smoothing(){

/////请实现自己的去噪算法////////

int vertex_num = vertex_size();
int iterations;
std::cout << "Input laplacian vertex update iterations(5-30次): ";
std::cin >> iterations;
int i = 0;
std::cout << "由于迭代,比较耗时!" << std::endl;
std::cout << "Please wait..... " << std::endl;
clock_t t1 = clock();
do {
std::vector<Coord> updateVertexPosition;
updateVertexPosition.resize(vertex_num);
VertexIterator vi(vertex_begin());
for (i=0; vi != vertex_end(); vi++) {
HalfedgeHandle& hh = vi->halfedge_handle_;
HalfedgeHandle css(opposite_halfedge_handle(hh));
int j = 0;
//Coord cd(0, 0, 0);
do {
updateVertexPosition[i] += coord(vertex_handle(css));
//cd += coord(vertex_handle(css));
j++;
css = opposite_halfedge_handle(prev_halfedge_handle(css));
} while (opposite_halfedge_handle(css) != hh);
updateVertexPosition[i] /= j;
i++;
//updateVertexPosition.push_back(cd/j);
}
i = 0;
//std::vector<Coord>::iterator vpi = updateVertexPosition.begin();
for (vi = vertex_begin(); vi != vertex_end(); vi++) {
vi->coord_ = updateVertexPosition[i];
i++;
//vpi++;
}
} while (--iterations);

clock_t t2 = clock();
//t2 = (t2-t1)/CLOCKS_PER_SEC;
std::cout << "The time of laplacian vertex updating: " << (t2 - t1)*1.0 / CLOCKS_PER_SEC << "s" << std::endl;
}

效果如下:
去噪前mark去噪后mark

task5

(可选/可做可不做)实现一种基于三角网格的操作(例如,特征提取,三角网格分割,三角网格变形等等);20分

此函数声明为: void mesh_process();

思路:先求出一个点的一环邻接顶点,将每个点到该顶点的向量相加,将算得的向量加到该点原坐标上得到新坐标。步骤是先计算每个点的新坐标,存储到容器,然后统一修改程序中mesh的顶点数据。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
template<class ExItems>
void ExKernelT<ExItems>::mesh_process(){///////(可选,在三角网格上实现一种操作,例如特征提取,网格分割,三角网格变形等等)

int vertex_num = vertex_size();
int iterations;
std::cout << "Input sharpening vertex update iterations(1-30次): ";
std::cin >> iterations;
int i = 0;
std::cout << "由于迭代,比较耗时!" << std::endl;
std::cout << "Please wait..... " << std::endl;
clock_t t1 = clock();
do {
std::vector<Coord> updateVertexPosition;
updateVertexPosition.resize(vertex_num);
VertexIterator vi(vertex_begin());
for (i = 0; vi != vertex_end(); vi++) {
HalfedgeHandle& hh = vi->halfedge_handle_;
HalfedgeHandle css(opposite_halfedge_handle(hh));
int j = 0;
//Coord cd(0, 0, 0);
do {
updateVertexPosition[i] += coord(vertex_handle(css));
//cd += coord(vertex_handle(css));
j++;
css = opposite_halfedge_handle(prev_halfedge_handle(css));
} while (opposite_halfedge_handle(css) != hh);
updateVertexPosition[i] -= coord(vertex_handle(hh))*j;
updateVertexPosition[i] *= -1;
i++;
//updateVertexPosition.push_back(cd/j);
}
i = 0;
//std::vector<Coord>::iterator vpi = updateVertexPosition.begin();
for (vi = vertex_begin(); vi != vertex_end(); vi++) {
vi->coord_ += updateVertexPosition[i]*0.1;
i++;
//vpi++;
}
} while (--iterations);

clock_t t2 = clock();
//t2 = (t2-t1)/CLOCKS_PER_SEC;
std::cout << "The time of sharpening vertex updating: " << (t2 - t1)*1.0 / CLOCKS_PER_SEC << "s" << std::endl;
}

效果如下:

原始图形markmark
去噪后markmark
变形后markmark

思路总结&遇到的问题

task1中海伦公式注意p表示半周长

task2中的计算顶点法向的思想是将相邻的所有面片法向相加

task3使用面片迭代器,调用函数算出三角形中心和法向,使用 glBegin(LINES)画线

task4要首先算出所有点在一环邻接顶点作用下的正确位置(即邻接顶点相加除以邻接顶点个数),然后统一修改mesh中顶点数据。这里存储所有顶点新位置用到了容器,使用时要注意先给容器分配空间,而且容器下标从0开始,不然会报错容器下标越界

task5在task4的基础上以另一种方法修改了顶点位置实现一种新的网格变换操作必要时可以自己向内核代码添加一些函数

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 代码解读
  2. 2. 提示
  3. 3. task1
  4. 4. task2
  5. 5. task3
  6. 6. task4
  7. 7. task5
  8. 8. 思路总结&遇到的问题