OpenGl读取导入3D模型并且添加鼠标移动旋转显示
原文作者:aircraft
原文链接:https://www.cnblogs.com/DOMLX/p/11543828.html
最近实习要用到opengl库就是跟opencv 有点像的那个,然后下了一个3D模型的读取显示来研究 现在分享给大家吧 注释基本我都打好了,所以也懒得再写很多解析了,自己看注释吧!
参考博客是这篇:https://blog.csdn.net/ding_programmer/article/details/91049357
本文用到obj 文件的百度云下载是:
链接:https://pan.baidu.com/s/1KVtVopy_aqtPXmvP4X3raA
提取码:ovdr
控制一个3d模型不难,那么控制多个呢?看下面这篇博客:
OpenGl 实现鼠标分别移动多个物体图形 ----------移动一个物体另外一个物体不动--读取多个3d模型操作的前期踏脚石
OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转
下载之后,复制代码到自己的项目运行 改一个那个我定义的路径,然后还要配置一下opengl和openmesh来读取obj文件解析,,配置过程肯定会出现很多的问题的 没事 习惯就好 加油 自己百度去 不要来问我!!!
路径就是这个:改一下file变成你们自己的就行了,,最后说一下 配置有问题自己百度OK 百度无敌!!!
一、操作
鼠标控制物体旋转移动,滚轮缩放,上下左右键可以控制模型的移动
F1,F2,F3,F4,F5,F6,F7,F8可以更换显示文件
Insert键 更换显示模式 (wire,flat,flatlines)
二、实验演示
按F1 读入 一个 cow的 obj文件
切换为只显示线的模式:
切换为可以显示线和面一起的模式:
可以实现旋转:
旋转之后:
可以实现缩放:
缩小:
可以实现平移:
按完相应键盘的按键之后,就会进行入读obj文件,使用命令行显示当前状态
#include <iostream>
#include<stdlib.h>
#include<OpenMesh/Core/IO/MeshIO.hh>
#include<OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include<GL/glut.h>
#include <math.h>
#include <Windows.h>
#include <string>
#define GLUT_WHEEL_UP 3 //定义滚轮操作
#define GLUT_WHEEL_DOWN 4
using namespace std;
typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh; //鼠标交互有关的
int mousetate = ; //鼠标当前的状态
GLfloat Oldx = 0.0; // 点击之前的位置
GLfloat Oldy = 0.0;
GLuint texture;
//与实现角度大小相关的参数,只需要两个就可以完成
float xRotate = 0.0f; //旋转
float yRotate = 0.0f;
float ty = 0.0f;
float tx = 0.0f;
float scale = 0.004; //文件读取有关的
MyMesh mesh; //mesh把文件读取了,封装在mesh对象中
//"dinosaur.obj";
const string file = "D:\\参考项目代码\\objdata\\objdata\\";
const string file_1 = file + "cow.obj";
const string file_2 = file + "bunny.obj";
const string file_3 = file + "dinosaur.obj";
const string file_4 = file + "mba1.obj";
const string file_5 = file + "monkey.obj";
const string file_6 = file + "porsche.obj";
//const string file_7 = "teddy.obj";
const string file_7 = file + "huangfeng.obj";
const string file_8 = file + "file.obj";
const string file_9 = file + "face.sur";
int currentfile = ; GLuint showFaceList, showWireList;
int showstate = ;
bool showFace = true;
bool showWire = false;
bool showFlatlines = false; void setLightRes() {
//GLfloat lightPosition[] = { 0.0f, 0.0f, 1.0f, 0.0f };
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_CULL_FACE)就可以了,OPENGL状态机会自动按照默认值进行CULL_FACE,
//默认是glFrontFace(GL_CCW) GL_CCW逆时针为正,GL_CW顺时针
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);
// 启用颜色追踪
//GL_COLOR_MATERIAL使我们可以用颜色来贴物体。如果没有这行代码,纹理将始终保持原来的颜色,glColor3f(r,g,b)就没有用了
glEnable(GL_COLOR_MATERIAL);
// 设置多边形正面的环境光和散射光材料属性,追踪glColor
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
//glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
//GL_AMBIENT表示各种光线照射到该材质上,经过很多次反射后最终遗留在环境中的光线强度(颜色)。
//GL_DIFFUSE表示光线照射到该材质上,经过漫反射后形成的光线强度(颜色)。
//GL_SPECULAR表示光线照射到该材质上,经过镜面反射后形成的光线强度(颜色)。
//通常,GL_AMBIENT和GL_DIFFUSE都取相同的值,可以达到比较真实的效果。
//使用GL_AMBIENT_AND_DIFFUSE可以同时设置GL_AMBIENT和GL_DIFFUSE属性。
} //初始化顶点和面
void initGL()
{
//实用显示列表
glClearColor(0.0, 0.0, 0.0, 0.0); //glClearDepth, 它给深度缓冲指定了一个初始值,缓冲中的每个像素的深度值都是这个,
//比如1,这个时候你往里面画一个物体, 由于物体的每个像素的深度值都小于等于1,
//所以整个物体都被显示了出来。 如果初始值指定为0, 物体的每个像素的深度值都大于等于0,
//所以整个物体都不可见。 如果初始值指定为0.5, 那么物体就只有深度小于0.5的那部分才是可见的
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();//创建分配一个显示列表
showWireList = glGenLists();
//int temp = mesh.n_edges(); //下面都是绘制编译 等待 callList调用显示列表来进行绘制显示
// 绘制 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(, , static_cast<GLsizei>(w), static_cast<GLsizei>(h));//跨平台进行强制类型转换 在c++下无需转换
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; // 为了ui显示好看一些
//obj文件中有的有法线有的没有 没有的话我们计算面法线来替代
//如果不存在顶点法线,则计算出
if (!opt.check(OpenMesh::IO::Options::VertexNormal))
{
// 通过面法线计算顶点法线
mesh.request_face_normals();
// mesh计算出顶点法线
mesh.update_normals();
// 释放面法线
mesh.release_face_normals();
}
} // 键盘交互 1. 切换文件 2.切换显示
void mySpecial(int key, int x, int y) {
switch (key) {
case GLUT_KEY_F1:
cout << "读取文件:" << file_1 << " 中......" << endl;
readfile(file_1);
scale = 0.004;
currentfile = ;
initGL();
break;
case GLUT_KEY_F2:
cout << "读取文件:" << file_2 << " 中......" << endl;
readfile(file_2);
scale = 0.003;
currentfile = ;
initGL();
break;
case GLUT_KEY_F3:
cout << "读取文件:" << file_3 << " 中......" << endl;
readfile(file_3);
scale = 0.02;
currentfile = ;
initGL();
break;
case GLUT_KEY_F4:
cout << "读取文件:" << file_4 << " 中......" << endl;
readfile(file_4);
scale = 0.005;
currentfile = ;
initGL();
break;
case GLUT_KEY_F5:
cout << "读取文件:" << file_5 << " 中......" << endl;
readfile(file_5);
scale = 0.5;
currentfile = ;
initGL();
break;
case GLUT_KEY_F6:
cout << "读取文件:" << file_6 << " 中......" << endl;
readfile(file_6);
scale = 0.02;
currentfile = ;
initGL();
break;
case GLUT_KEY_F7:
cout << "读取文件:" << file_7 << " 中......" << endl;
readfile(file_7);
scale = 0.04;
currentfile = ;
initGL();
break;
case GLUT_KEY_F8:
cout << "读取文件:" << file_8 << " 中......" << endl;
readfile(file_8);
scale = 0.04;
currentfile = ;
initGL();
break;
case GLUT_KEY_F9:
cout << "读取文件:" << file_9 << " 中......" << endl;
readfile(file_9);
scale = 0.04;
currentfile = ;
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 myMouse(int button, int state, int x, int y)
{ //鼠标左键按下或者松开
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
mousetate = ;
Oldx = x;
Oldy = y;
}
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
mousetate = ;
//滚轮事件
//scale 增加就是放大 减小就是缩小
//currentfile 对不同的模型用不用的scale
if (state == GLUT_UP && button == GLUT_WHEEL_UP) {
//cout << "hello" << endl;
//滑轮向上滑,则scale减小
if (currentfile == )
scale += 0.0005;
if (currentfile == )
scale += 0.001;
if (currentfile == ) {
scale += 0.1;
}
else
scale += 0.001;
}
if (state == GLUT_UP && button == GLUT_WHEEL_DOWN) {
//cout << "good" << endl;
//滑轮向下滑,则scale增加
if (currentfile == )
scale -= 0.0005;
if (currentfile == )
scale -= 0.001;
if (currentfile == ) {
scale -= 0.1;
}
else
scale -= 0.001;
}
glutPostRedisplay();//促使主程序尽快的重绘窗口
} // 鼠标运动时
void onMouseMove(int x, int y) {
//当鼠标状态为按下时进入后续判断
if (mousetate) {
//x对应y是因为对应的是法向量
yRotate += x - Oldx;
glutPostRedisplay();//标记当前窗口需要重新绘制
Oldx = x;
xRotate += y - Oldy;
glutPostRedisplay();
Oldy = y;
}
} 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, ); //移动
glScalef(scale, scale, scale); // 缩放 x,y,z分别乘以scale //每次display都要使用glcalllist回调函数显示想显示的顶点列表
//用键盘的insert按键控制显示的模式 网格,面,网格面 分别显示
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(, );
glutInitWindowSize(, );
glutCreateWindow("Mesh Viewer");
//一开始默认读取文件1
//readfile(file_1);
//initGL();
glutMouseFunc(myMouse); //鼠标点击处理函数
glutMotionFunc(onMouseMove); // 鼠标移动的时候的函数
glutSpecialFunc(&mySpecial);//键盘上下左右响应
glutReshapeFunc(&myReshape);//自适应窗口大小的改变 使得模型不会太宽 或者太高 若要精确显示模型不建议此操作
glutDisplayFunc(&myDisplay); glutMainLoop();
return ;
}
若有兴趣交流分享技术,可关注本人公众号,里面会不定期的分享各种编程教程,和共享源码,诸如研究分享关于c/c++,python,前端,后端,opencv,halcon,opengl,机器学习深度学习之类有关于基础编程,图像处理和机器视觉开发的知识
OpenGl读取导入3D模型并且添加鼠标移动旋转显示的更多相关文章
- OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11627508.html 前言: 因为接下来的项目需求是要读取多个3D模型,并且移动拼接,那么我 ...
- bullet物理引擎与OpenGL结合 导入3D模型进行碰撞检测 以及画三角网格的坑
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11681069.html 一.初始化世界以及模型 /// 冲突配置包含内存的默认设置,冲突设置. ...
- WPF利用HelixToolKit后台导入3D模型
原文:WPF利用HelixToolKit后台导入3D模型 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/m0_37591671/article/de ...
- cesium导入3D模型(obj转gltf)
cesium中支持载入3D模型,不过只支持gltf格式.gltf是khronos组织(起草OpenGL标准的那家)定义的一种交换格式,用于互联网或移动设备上展现3d内容,充分支持opengl,webg ...
- 【Unity】3.0 第3章 创建和导入3D模型
分类:Unity.C#.VS2015 创建日期:2016-04-02 一.简介 利用Unity内置的基本模型和工具,不需要借助任何其他的三维建模软件,就可以直接创建出各种3D模型,这是这一章我们首先学 ...
- Unity导入3D模型的过程与方法
一.介绍 资源是游戏开发中的原材料,也就是组成游戏的模块. Unity只是一个游戏开发引擎,而并不是一个资源开发软件.这就意味着在游戏中需要的资源通常是由一些设计者使用其他软件开发出来的,然后设计者会 ...
- Cesium学习笔记(九):导入3D模型(obj转gltf)
在用cesium的过程中难免需要导入别人做好的3D模型,这时候就需要将这些模型转成gltf格式了 当然,官方也给了我们一个网页版的转换器,但是毕竟是网页版的,效率极其低下,文件还不能太大,所以我们就需 ...
- OpenGl 实现鼠标分别移动多个物体 ----------移动一个物体另外一个物体不动--读取多个3d模型操作的前期踏脚石
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11620088.html 前言: 因为接下来的项目需求是要读取多个3D模型,并且移动拼接,那么我 ...
- c# winform用sharpGL(OpenGl)解析读取3D模型obj
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11783026.html 自己写了个简单的类读取解析obj模型,使用导入类,然后new个对象,在 ...
随机推荐
- pip安装第三方库
不是所有的第三方Python包都能通过pip来安装,只能是发布在pypi.org上面的才能通过pip安装. pypi是什么? pypi是一个仓库,上面存放了大量的Python第三方软件包,是由Pyth ...
- ubuntu16.04安装Ros(kinetic版本)【亲测好用】
准备 1.ubuntu16.04 64位桌面版 ps:关于系统的下载和安装这里不做介绍,请自行百度,不是介绍重点 2.更改源 图上的几个勾默认是选上的,如果没有选上,选成上图这样(如果修改过勾,点击关 ...
- Mybatis框架(8)---Mybatis插件原理
Mybatis插件原理 在实际开发过程中,我们经常使用的Mybaits插件就是分页插件了,通过分页插件我们可以在不用写count语句和limit的情况下就可以获取分页后的数据,给我们开发带来很大 的便 ...
- Spring-boot:多模块打包
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot ...
- 用java自制简易线程池(不依赖concurrent包)
很久之前人们为了继续享用并行化带来的好处而不想使用进程,于是创造出了比进程更轻量级的线程.以linux为例,创建一个进程需要申请新的自己的内存空间,从父进程拷贝一些数据,所以开销是比较大的,线程(或称 ...
- Kafka的消息会丢失和重复吗?——如何实现Kafka精确传递一次语义
我们都知道Kafka的吞吐量很大,但是Kafka究竟会不会丢失消息呢?又会不会重复消费消息呢? 图 无人机实时监控 有很多公司因为业务要求必须保证消息不丢失.不重复的到达,比如无人机实时监控系统, ...
- CSS布局:元素水平垂直居中
CSS布局:元素水平垂直居中 本文将依次介绍在不同条件下实现水平垂直居中的多种方法 水平垂直居中是在写网页时经常会用到的需求,在上两篇博客中,分别介绍了水平居中和垂直居中的方法.本文的水平垂直居中就是 ...
- Liunx学习总结(八)--服务
什么是服务 服务是向外提供服务的进程,一般来说都会放在后台,既然要持续不断的提供外界随时发来的服务请求,服务进程就需要常驻在内存中,且不应该和终端有关,否则终端退出服务程序就退出了.另外,要能够接待外 ...
- 水果机抽奖(CocosCreator)
推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 一.前言 在前面给大家分享了大转盘的抽奖方式,这是现在游戏使用较多的一种抽奖方式,今天给大家介绍另一抽奖方式--水果 ...
- unity之初级工程师
一.值类型与引用类型 值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中.值类型变量声明后,不管是否已经赋值,编译器为其分配内存.值类型的实例通常是在线程栈上分配的(静态 ...