一、实验目的
- 利用openMesh导入应用软件制作的3D模型,有一定的交互功能。
- 使用键盘实现切换图片
- 使用键盘实现平移
- 使用键盘实现旋转
- WireFrame/Flatlines/Flat模式切换
二、实验环境
- Visual Studio 2019
- Windows 10
三、算法分析与设计
-
设置全局变量
// 旋转角度 float xRotate = 0.0f; float yRotate = 0.0f; // 位置坐标 float ty = 0.0f; float tx = 0.0f; // 缩放比例 float scale = 1; //文件读取有关的 MyMesh mesh; //mesh把文件读取了,封装在mesh对象中 const string file_2 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\Armadillo.off"; const string file_5 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\cow.obj"; const string file_6 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\cow.obj"; const string file_7 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\cow.obj"; int currentfile = 1; GLuint showFaceList, showWireList; int showstate = 1; bool showFace = true; bool showWire = false; bool showFlatlines = false;
-
光照
-
读文件
-
键盘交互
// 键盘交互 // f2:切换文件 // f5:正常显示 // f6:缩放图片 // f7:旋转图片 // INSERT:切换模式 // key:平移 void mySpecial(int key, int x, int y) { switch (key) { case GLUT_KEY_F2: cout << "读取文件:" << file_2 << " 中......" << endl; readfile(file_2); scale = 0.003; currentfile = 2; initGL(); break; case GLUT_KEY_F5: cout << "读取文件:" << file_5 << " 中......" << endl; readfile(file_5); scale = 1; currentfile = 5; initGL(); break; case GLUT_KEY_F6: cout << "读取文件:" << file_6 << " 中......" << endl; readfile(file_6); scale = 0.5; currentfile = 3; initGL(); break; case GLUT_KEY_F7: cout << "读取文件:" << file_7 << " 中......" << endl; readfile(file_7); scale = 1; xRotate += 10.0f; currentfile = 3; initGL(); break; // 实现不同模式 case GLUT_KEY_INSERT: if (showFace == true) { showFace = false; showWire = true; cout << "切换显示模式为:WireFrame" << endl; } else if (showWire == true) { showWire = false; showFlatlines = true; cout << "切换显示模式为:Flatlines" << endl; } else if (showFlatlines == true) { showFlatlines = false; showFace = true; //DisplaySphere(0.5, "G:\\source\\OpenGL\\Cow\\checkerboard3.bmp"); cout << "切换显示模式为:Flat" << endl; } break; // 实现平移 case GLUT_KEY_LEFT: tx -= 0.01; break; case GLUT_KEY_RIGHT: tx += 0.01; break; case GLUT_KEY_UP: ty += 0.01; break; case GLUT_KEY_DOWN: ty -= 0.01; break; default: break; } glutPostRedisplay(); }
四、实验结果
f2
切换文件
f5
正常显示
f6
缩放图片
f7
旋转图片
key
平移
INSERT
切换模式
五、附录
#include <bits/stdc++.h>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include<GL/glut.h>
using namespace std;
typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;
// 旋转角度
float xRotate = 0.0f;
float yRotate = 0.0f;
// 位置坐标
float ty = 0.0f;
float tx = 0.0f;
// 缩放比例
float scale = 1;
//文件读取有关的
MyMesh mesh; //mesh把文件读取了,封装在mesh对象中
const string file_2 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\Armadillo.off";
const string file_5 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\cow.obj";
const string file_6 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\cow.obj";
const string file_7 = "D:\\Sophomore\\计算机图形学\\assignment\\lab07\\lab07\\openmesh模型\\cow.obj";
int currentfile = 1;
GLuint showFaceList, showWireList;
int showstate = 1;
bool showFace = true;
bool showWire = false;
bool showFlatlines = false;
void setLightRes() {
GLfloat lightPosition[] = { 0.0f, 1.0f, 0.0f, 0.0f }; // 平行光源, GL_POSITION属性的最后一个参数为0
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
glEnable(GL_LIGHTING); //启用光源
glEnable(GL_LIGHT0); //使用指定灯光
}
void SetupRC()
{
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
// 启用光照计算
glEnable(GL_LIGHTING);
// 指定环境光强度(RGBA)
GLfloat ambientLight[] = { 1.0f, 0.0f, 0.0f, 0.0f };
// 设置光照模型,将ambientLight所指定的RGBA强度值应用到环境光
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
// 启用颜色追踪
glEnable(GL_COLOR_MATERIAL);
// 设置多边形正面的环境光和散射光材料属性,追踪glColor
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
}
//初始化顶点和面
void initGL()
{
//实用显示列表
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClearDepth(2.0);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST); //用来开启深度缓冲区的功能,启动后OPengl就可以跟踪Z轴上的像素,那么它只有在前面没有东西的情况下才会绘制这个像素,在绘制3d时,最好启用,视觉效果会比较真实
glEnable(GL_TEXTURE_2D);
// Lighting
glEnable(GL_LIGHTING); // 如果enbale那么就使用当前的光照参数去推导顶点的颜色
glEnable(GL_LIGHT0); //第一个光源,而GL_LIGHT1表示第二个光源
// Display List
setLightRes();
SetupRC();//设置环境光
showFaceList = glGenLists(1);
showWireList = glGenLists(1);
int temp = mesh.n_edges();
// 绘制 wire
glNewList(showWireList, GL_COMPILE);
glDisable(GL_LIGHTING);
glLineWidth(1.0f);
glColor3f(0.5f, 0.5f, 0.5f);//灰色
glBegin(GL_LINES);
for (MyMesh::HalfedgeIter he_it = mesh.halfedges_begin(); he_it != mesh.halfedges_end(); ++he_it) {
//链接这个有向边的起点和终点
glVertex3fv(mesh.point(mesh.from_vertex_handle(*he_it)).data());
glVertex3fv(mesh.point(mesh.to_vertex_handle(*he_it)).data());
}
glEnd();
glEnable(GL_LIGHTING);
glEndList();
// 绘制flat
glNewList(showFaceList, GL_COMPILE);
for (MyMesh::FaceIter f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) {
glBegin(GL_TRIANGLES);
for (MyMesh::FaceVertexIter fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) {
glNormal3fv(mesh.normal(*fv_it).data());
glVertex3fv(mesh.point(*fv_it).data());
}
glEnd();
}
glEndList();
}
// 当窗体改变大小的时候,改变窗口大小时保持图形比例
void myReshape(GLint w, GLint h)
{
glViewport(0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h));
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w > h)
glOrtho(-static_cast<GLdouble>(w) / h, static_cast<GLdouble>(w) / h, -1.0, 1.0, -100.0, 100.0);
else
glOrtho(-1.0, 1.0, -static_cast<GLdouble>(h) / w, static_cast<GLdouble>(h) / w, -100.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 读取文件的函数
void readfile(string file) {
// 请求顶点法线 vertex normals
mesh.request_vertex_normals();
//如果不存在顶点法线,则报错
if (!mesh.has_vertex_normals())
{
cout << "错误:标准定点属性 “法线”不存在" << endl;
return;
}
// 如果有顶点发现则读取文件,读入到mesh对象中
OpenMesh::IO::Options opt;
if (!OpenMesh::IO::read_mesh(mesh, file, opt))
{
cout << "无法读取文件:" << file << endl;
return;
}
else cout << "成功读取文件:" << file << endl;
cout << endl;
//如果不存在顶点法线,则计算出
if (!opt.check(OpenMesh::IO::Options::VertexNormal))
{
// 通过面法线计算顶点法线
mesh.request_face_normals();
// mesh计算出顶点法线
mesh.update_normals();
// 释放面法线
mesh.release_face_normals();
}
}
// 键盘交互
// f2:切换文件
// f5:正常显示
// f6:缩放图片
// f7:旋转图片
// INSERT:切换模式
// key:平移
void mySpecial(int key, int x, int y) {
switch (key) {
case GLUT_KEY_F2:
cout << "读取文件:" << file_2 << " 中......" << endl;
readfile(file_2);
scale = 0.003;
currentfile = 2;
initGL();
break;
case GLUT_KEY_F5:
cout << "读取文件:" << file_5 << " 中......" << endl;
readfile(file_5);
scale = 1;
currentfile = 5;
initGL();
break;
case GLUT_KEY_F6:
cout << "读取文件:" << file_6 << " 中......" << endl;
readfile(file_6);
scale = 0.5;
currentfile = 3;
initGL();
break;
case GLUT_KEY_F7:
cout << "读取文件:" << file_7 << " 中......" << endl;
readfile(file_7);
scale = 1;
xRotate += 10.0f;
currentfile = 3;
initGL();
break;
// 实现不同模式
case GLUT_KEY_INSERT:
if (showFace == true) {
showFace = false;
showWire = true;
cout << "切换显示模式为:WireFrame" << endl;
}
else if (showWire == true)
{
showWire = false;
showFlatlines = true;
cout << "切换显示模式为:Flatlines" << endl;
}
else if (showFlatlines == true) {
showFlatlines = false;
showFace = true;
//DisplaySphere(0.5, "G:\\source\\OpenGL\\Cow\\checkerboard3.bmp");
cout << "切换显示模式为:Flat" << endl;
}
break;
// 实现平移
case GLUT_KEY_LEFT:
tx -= 0.01;
break;
case GLUT_KEY_RIGHT:
tx += 0.01;
break;
case GLUT_KEY_UP: // 与屏幕显示一致
ty -= 0.01;
break;
case GLUT_KEY_DOWN:
ty += 0.01;
break;
default:
break;
}
glutPostRedisplay();
}
void myDisplay()
{
//要清除之前的深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//与显示相关的函数
glRotatef(xRotate, 1.0f, 0.0f, 0.0f); // 让物体旋转的函数 第一个参数是角度大小,后面的参数是旋转的法向量
glRotatef(yRotate, 0.0f, 1.0f, 0.0f);
glTranslatef(0.0f, 0.0f, ty);
glTranslatef(tx, ty, 0);
glScalef(scale, scale, scale); // 缩放
//每次display都要使用glcalllist回调函数显示想显示的顶点列表
if (showFace)
glCallList(showFaceList);
if (showFlatlines) {
glCallList(showFaceList);
glCallList(showWireList);
}
if (showWire)
glCallList(showWireList);
glutSwapBuffers(); //这是Opengl中用于实现双缓存技术的一个重要函数
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); // GLUT_Double 表示使用双缓存而非单缓存
glutInitWindowPosition(100, 100);
glutInitWindowSize(800, 500);
glutCreateWindow("Mesh Viewer");
glutSpecialFunc(&mySpecial);
glutReshapeFunc(&myReshape);
glutDisplayFunc(&myDisplay);
glutMainLoop();
return 0;
}