opengl+openmesh重绘rabbit

参考网址:https://blog.csdn.net/chaojiwudixiaofeixia/article/details/50498629

                  https://blog.csdn.net/qq_28057541/article/details/51362945

本次实验的对象是斯坦福大学著名的bunny模型(兔子模型)。本文主要介绍了如何如下两点:

  • 使用openmesh读取obj文件
  • 使用opengl重绘读取的文件

本文所用的3d模型示意图如下所示:

具体的实验步骤讲解如下所示: 

  1. 利用openMesh读取obj文件
    void readfile(string file) {
    	//请求顶点法线
    	mesh.request_vertex_normals();
    	//如果顶点法线不存在则报错
    	if (!mesh.has_vertex_normals())
    	{
    		cout << "Error:Standard Vertex Property 'Normals' Not Available!" << endl;
    		return;
    	}
    	//读取obj文件
    	OpenMesh::IO::Options ort;
    	if (!OpenMesh::IO::read_mesh(mesh, file, ort))
    	{
    		cout << "Error:Cannot Read File:" << file << endl;
    		return;
    	}
    	else
    	{
    		cout << "Success Read File:" << file << endl;
    	}
    	//如果不存在顶点法线,则计算顶点法线
    	if (!ort.check(OpenMesh::IO::Options::VertexNormal))
    	{
    		//请求面法线
    		mesh.request_face_normals();
    		//利用面法线计算顶点法线
    		mesh.update_normals();
    		//释放面法线
    		mesh.release_face_normals();
    	}
    }

     

  2. 初始化顶点与面的绘制
    //初始化顶点与面的绘制 
    void initGL()
    {
    	//设置背景颜色值为黑色
    	glClearColor(0.0, 0.0, 0.0, 0.0);
    	//设置清除深度缓存时使用的深度值为2
    	glClearDepth(2.0);
    	//设置两点间颜色变化为过渡模式
    	glShadeModel(GL_SMOOTH);
    	//开启深度缓冲区的功能,从而跟踪Z轴上的像素
    	glEnable(GL_DEPTH_TEST);
        //启用光源,利用当前的光照参数推导顶点颜色
    	glEnable(GL_LIGHTING);
    	//启用1号光源
    	glEnable(GL_LIGHT0);
    	//申请一个面的显示列表,从而实现加速显示
    	showFaceList = glGenLists(1);
    	//申请一个线的显示列表,从而实现加速显示
    	showWireList = glGenLists(1);
    	//获取网格中边的数量
    	int temp = mesh.n_edges();
    
    	/*绘制图像的线*/
    	//创建线的显示列表,并指定模式为编译模式
    	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());
    	}
    	//与glBegin()配合
    	glEnd();
    	//启用光源
    	glEnable(GL_LIGHTING);
    	//与glNewList配合
    	glEndList();
    
    	/*绘制图像的面*/
    	//创建面的显示列表,并指定模式为编译模式
    	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());
    		}
    		//与glBegin()配合
    		glEnd();
    	}
    	//与glNewList配合
    	glEndList();
    }
    

     

  3. 设置鼠标的交互操作
    //鼠标交互
    void myMouse(int button, int state, int x, int y)
    {
    	//鼠标左键按下
    	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
    		mousetate = 1;
    		Oldx = x;
    		Oldy = y;
    	}
    	//鼠标右键按下
    	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
    	{
    		mousetate = 0;
    	}
    	//滚轮事件:缩小操作
    	if (button == 3 && state == GLUT_UP) {
    			scale -= 0.1f;
    	}
    	//滚轮事件:放大操作
    	if (button == 4 && state == GLUT_UP) {
    			scale += 0.1f;
    	}
    	//标记重绘该界面
    	glutPostRedisplay();
    }
    
    // 鼠标运动时
    void onMouseMove(int x, int y) {
    	if (mousetate) {
    		//计算y轴旋转角度:x对应y是因为其对应的是法向量
    		yRotate += x - Oldx;
    		//标记当前窗口需要重绘
    		glutPostRedisplay();
    		//更新Oldx的数值
    		Oldx = x;
    		//计算x轴旋转角度:y对应x是因为其对应的是法向量
    		xRotate += y - Oldy;
    		//标记当前窗口需要重绘
    		glutPostRedisplay();
    		//更新Oldy的数值
    		Oldy = y;
    	}
    }

     

  4. 设置键盘的交互操作
    //键盘操作显示模式
    void myKeyboard(unsigned char key, int x, int y)
    {
    	switch (key) {
    	case '1':
    		showFaces = true;
    		showWires = false;
    		showFaceWires = false;
    		break;
    	case '2':
    		showFaces = false;
    		showWires = true;
    		showFaceWires = false;
    		break;
    	case '3':
    		showFaces = false;
    		showWires = false;
    		showFaceWires = true;
    		break;
    	default:
    		break;
    	}
    	glutPostRedisplay();
    }

     

  5. 编写图像的绘制函数
    //图像显示函数
    void myDisplay()
    {
    	//要清除之前的深度缓存
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	//将矩阵单位化
    	glLoadIdentity();
    	//让物体在三维空间中进行缩放
    	glScalef(scale, scale, scale);
    	//让物体绕x轴旋转xRotate度
    	glRotatef(xRotate, 1.0f, 0.0f, 0.0f);
    	//让物体绕y轴旋转yRotate度
    	glRotatef(yRotate, 0.0f, 1.0f, 0.0f);
    	//让物体沿着z轴平移
    	glTranslatef(0.0f, 0.0f, 0.0f);
    
    	/*传递想要显示的列表*/
    	//显示图像的面
    	if (showFaces)
    	{
    		glCallList(showFaceList);
    	}
    	//显示图像的线
    	if (showWires)
    	{
    		glCallList(showWireList);
    	}
    	//显示图像的面和线
    	if (showFaceWires) {
    		glCallList(showFaceList);
    		glCallList(showWireList);
    	}
    	//将前后缓冲区域进行交换
    	glutSwapBuffers(); 
    }
    

     

  6. 完整的代码如下所示
    #include <OpenMesh/Core/IO/MeshIO.hh>                 // 读取文件
    #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh> // 操作文件
    #include <iostream>
    #include <math.h>
    #include <string.h>
    #include <sstream>
    #include <fstream>
    #include <vector>
    #include <windows.h>
    #include "GL\freeglut.h"
    
    using namespace std;
    typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;
    
    //文件读取有关的定义
    MyMesh mesh;
    const string modelfile = "bunny.obj";
    
    //文件的旋转与缩放
    float scale = 1;
    float xRotate = 0.0f;
    float yRotate = 0.0f;
    
    //定义点、线、面的显示列表
    GLuint showFaceList, showWireList;
    
    //设置显示的模式
    bool showFaces = false;
    bool showWires = false;
    bool showFaceWires = true;
    
    //鼠标交互有关的定义
    int mousetate = 0;  //鼠标当前的状态
    GLfloat Oldx = 0.0; //点击之前的x坐标
    GLfloat Oldy = 0.0; //点击之前的y坐标
    
    //利用OpenMesh读取obj文件
    void readfile(string file) {
    	//请求顶点法线
    	mesh.request_vertex_normals();
    	//如果顶点法线不存在则报错
    	if (!mesh.has_vertex_normals())
    	{
    		cout << "Error:Standard Vertex Property 'Normals' Not Available!" << endl;
    		return;
    	}
    	//读取obj文件
    	OpenMesh::IO::Options ort;
    	if (!OpenMesh::IO::read_mesh(mesh, file, ort))
    	{
    		cout << "Error:Cannot Read File:" << file << endl;
    		return;
    	}
    	else
    	{
    		cout << "Success Read File:" << file << endl;
    	}
    	//如果不存在顶点法线,则计算顶点法线
    	if (!ort.check(OpenMesh::IO::Options::VertexNormal))
    	{
    		//请求面法线
    		mesh.request_face_normals();
    		//利用面法线计算顶点法线
    		mesh.update_normals();
    		//释放面法线
    		mesh.release_face_normals();
    	}
    }
    
    //改变窗口大小时重绘函数
    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 myIdle()
    {
    	//设置绕x轴的旋转角度
    	xRotate += 0.5f;
    	//设置绕y轴的旋转角度
    	yRotate += 0.5f;
    	//调整绕x轴超限后的旋转角度
    	if (xRotate >= 360.0f)
    	{
    		xRotate -= 360.0f;
    	}
    	//调整绕y轴超限后的旋转角度
    	if (yRotate >= 360.0f)
    	{
    		yRotate -= 360.0f;
    	}
    	//标记重绘此界面
    	glutPostRedisplay();
    }
    
    //初始化顶点与面的绘制 
    void initGL()
    {
    	//设置背景颜色值为黑色
    	glClearColor(0.0, 0.0, 0.0, 0.0);
    	//设置清除深度缓存时使用的深度值为2
    	glClearDepth(2.0);
    	//设置两点间颜色变化为过渡模式
    	glShadeModel(GL_SMOOTH);
    	//开启深度缓冲区的功能,从而跟踪Z轴上的像素
    	glEnable(GL_DEPTH_TEST);
        //启用光源,利用当前的光照参数推导顶点颜色
    	glEnable(GL_LIGHTING);
    	//启用1号光源
    	glEnable(GL_LIGHT0);
    	//申请一个面的显示列表,从而实现加速显示
    	showFaceList = glGenLists(1);
    	//申请一个线的显示列表,从而实现加速显示
    	showWireList = glGenLists(1);
    	//获取网格中边的数量
    	int temp = mesh.n_edges();
    
    	/*绘制图像的线*/
    	//创建线的显示列表,并指定模式为编译模式
    	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());
    	}
    	//与glBegin()配合
    	glEnd();
    	//启用光源
    	glEnable(GL_LIGHTING);
    	//与glNewList配合
    	glEndList();
    
    	/*绘制图像的面*/
    	//创建面的显示列表,并指定模式为编译模式
    	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());
    		}
    		//与glBegin()配合
    		glEnd();
    	}
    	//与glNewList配合
    	glEndList();
    }
    
    //鼠标交互
    void myMouse(int button, int state, int x, int y)
    {
    	//鼠标左键按下
    	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
    		mousetate = 1;
    		Oldx = x;
    		Oldy = y;
    	}
    	//鼠标右键按下
    	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
    	{
    		mousetate = 0;
    	}
    	//滚轮事件:缩小操作
    	if (button == 3 && state == GLUT_UP) {
    			scale -= 0.1f;
    	}
    	//滚轮事件:放大操作
    	if (button == 4 && state == GLUT_UP) {
    			scale += 0.1f;
    	}
    	//标记重绘该界面
    	glutPostRedisplay();
    }
    
    // 鼠标运动时
    void onMouseMove(int x, int y) {
    	if (mousetate) {
    		//计算y轴旋转角度:x对应y是因为其对应的是法向量
    		yRotate += x - Oldx;
    		//标记当前窗口需要重绘
    		glutPostRedisplay();
    		//更新Oldx的数值
    		Oldx = x;
    		//计算x轴旋转角度:y对应x是因为其对应的是法向量
    		xRotate += y - Oldy;
    		//标记当前窗口需要重绘
    		glutPostRedisplay();
    		//更新Oldy的数值
    		Oldy = y;
    	}
    }
    
    //键盘操作显示模式
    void myKeyboard(unsigned char key, int x, int y)
    {
    	switch (key) {
    	case '1':
    		showFaces = true;
    		showWires = false;
    		showFaceWires = false;
    		break;
    	case '2':
    		showFaces = false;
    		showWires = true;
    		showFaceWires = false;
    		break;
    	case '3':
    		showFaces = false;
    		showWires = false;
    		showFaceWires = true;
    		break;
    	default:
    		break;
    	}
    	glutPostRedisplay();
    }
    
    //图像显示函数
    void myDisplay()
    {
    	//要清除之前的深度缓存
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	//将矩阵单位化
    	glLoadIdentity();
    	//让物体在三维空间中进行缩放
    	glScalef(scale, scale, scale);
    	//让物体绕x轴旋转xRotate度
    	glRotatef(xRotate, 1.0f, 0.0f, 0.0f);
    	//让物体绕y轴旋转yRotate度
    	glRotatef(yRotate, 0.0f, 1.0f, 0.0f);
    	//让物体沿着z轴平移
    	glTranslatef(0.0f, 0.0f, 0.0f);
    
    	/*传递想要显示的列表*/
    	//显示图像的面
    	if (showFaces)
    	{
    		glCallList(showFaceList);
    	}
    	//显示图像的线
    	if (showWires)
    	{
    		glCallList(showWireList);
    	}
    	//显示图像的面和线
    	if (showFaceWires) {
    		glCallList(showFaceList);
    		glCallList(showWireList);
    	}
    	//将前后缓冲区域进行交换
    	glutSwapBuffers(); 
    }
    
    int main(int argc, char** argv)
    {
    	//初始化glut库
    	glutInit(&argc, argv);
    	//设置glut的模式
    	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    	//设置显示窗口的位置
    	glutInitWindowPosition(100, 100);
    	//设置显示窗口的大小
    	glutInitWindowSize(800, 500);
    	//设置显示窗口的名称
    	glutCreateWindow("Mesh Viewer");
    	//读取模型文件
    	readfile(modelfile);
    	//初始化顶点与面的绘制
    	initGL();
    	//鼠标交互功能
    	glutMouseFunc(myMouse);
    	//鼠标移动功能
    	glutMotionFunc(onMouseMove);
    	//按键操作显示模式
    	glutKeyboardFunc(&myKeyboard);
    	//调整界面大小后的重绘操作
    	glutReshapeFunc(&myReshape);
    	//图像的绘制
    	glutDisplayFunc(&myDisplay);
    	//空闲时调用的图像绘制函数
    	glutIdleFunc(&myIdle);
    	//glut循环更新界面操作
    	glutMainLoop();
    	return 0;
    }

     

 常用的模型库:https://blog.csdn.net/u013467442/article/details/46673331

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值