参考网址:https://blog.csdn.net/chaojiwudixiaofeixia/article/details/50498629
https://blog.csdn.net/qq_28057541/article/details/51362945
本次实验的对象是斯坦福大学著名的bunny模型(兔子模型)。本文主要介绍了如何如下两点:
本文所用的3d模型示意图如下所示:
具体的实验步骤讲解如下所示:
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 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();
}
#include // 读取文件
#include // 操作文件
#include
#include
#include
#include
#include
#include
#include
#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(w), static_cast(h));
//将当前矩阵指定为投影矩阵
glMatrixMode(GL_PROJECTION);
//将矩阵设置为单位矩阵
glLoadIdentity();
/*设置修建空间的范围,进行正射投影,从而创建一个平行视景图*/
if (w > h)
glOrtho(-static_cast(w) / h, static_cast(w) / h, -1.0, 1.0, -100.0, 100.0);
else
glOrtho(-1.0, 1.0, -static_cast(h) / w, static_cast(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