单页营销网站后台,省级示范校建设专题网站,js音乐网站模板,wordpress 调查目录 功能分析#xff08;需求分析#xff09;技术点分析OpenGL立即渲染模式可编程渲染管线模式 QOpenGLWidget派生类 glwidget逻辑glwidget.hglwidget.cpp 鼠标交互功能obj格式介绍 效果bunnyCayman_GT 功能分析#xff08;需求分析#xff09;
基于QT平台#xff0c;使… 目录 功能分析需求分析技术点分析OpenGL立即渲染模式可编程渲染管线模式 QOpenGLWidget派生类 glwidget逻辑glwidget.hglwidget.cpp 鼠标交互功能obj格式介绍 效果bunnyCayman_GT 功能分析需求分析
基于QT平台使用OpenGL进行obj文件加载显示使用鼠标对场景进行缩放、移动、旋转交互
技术点分析
OpenGL OpenGL是基于C的学习曲线比较抖但是总的来说就是下面一幅图 用语言简单的描述个人理解可能不太准确是把cpu里内存里的3D数据传输到显卡的内存里以及如何转换成2D平面上像素点显示也就是矩阵变换在好多开源的框架里都进行了进一步封装形成了渲染器、场景、相机等。这个数据是顶点坐标、颜色等传输跟送快递一样除了要有数据本身之外还要有其他信息也就是要有个约定到显卡拿到数据之后怎么解析。 有个网站比较有名learnopengl可以对着学一遍在网上找资料的时候需要注意是立即渲染模式还是可编程渲染管线模式现在官方推荐是使用可编程渲染管线模式开发。
立即渲染模式
glBegin(GL_TRIANGLES)
glTranslatef(1,2,3);
// 其他操作
// glVertex*() 设置顶点坐标
// glColor*() 设置当前颜色
// glIndex*() 设置当前颜色表
// glNormal*() 设置法向坐标
// glEvalCoord*() 产生坐标
// glTexCoord*() 设置纹理坐标
// glEdgeFlag*() 控制边界绘制
// glMaterial*() 设置材质
glEnd()可编程渲染管线模式
1、创建VBO顶点数据对象GLuint VBO;glGenBuffers(1,VBO);
2、VBO与显卡缓存绑定glBindBuffer(GL_ARRAY_BUFFER, VBO)
3、绑定数据缓存对象glBufferData(GL_ARRAY_BUFFER,sizeof(vertexs),vertex,)
4、数据格式glVertexAttribPoint(0,3,GL_FLOAT,)
5、启用/绘制glEnableVertexAttribArray(0)glDrawArrays(GL_TRIANGLES, 0, 10);
6、最后关闭glBindBuffer(GL_ARRAY_BUFFER,0)glBindVertexArray(0);以上两段代码只是展示两种模式区别很明显立即渲染模式容易理解但是性能有限制可编程渲染管线模式理解和使用门槛高但更能接触底层。
QOpenGLWidget QT对OpenGL进行了封装提供了QOpenGLWidget类只需要对其继承在initialzeGL()resizeGL()paintGL()逻辑下进行业务实现,包括VBO、VAO的创建与绑定着色器程序的编译与链接等。
派生类 glwidget逻辑 先简单介绍派生类的写法当然为了方便扩展和使用抽象出了相机类、渲染器类、物体类完整的工程代码资源可以下载参考具体的可以根据自己要实现的功能进行编写。
glwidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H#include QOpenGLWidget
#include QOpenGLFunctions_3_3_Core
#include QOpenGLShaderProgram
#include QOpenGLBuffer
#include QOpenGLVertexArrayObject
#include QMouseEvent
#include QTimer
#include QQuaternion#include vector
#include QKeyEvent#include utils/Common.h
#include genericRender.h
#include grid.h
#include coorsystem.h
#include camera.hclass glwidget :public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{Q_OBJECT
public:glwidget(QWidget * parent nullptr);//鼠标交互事件重写void mouseMoveEvent(QMouseEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void mouseReleaseEvent(QMouseEvent *event) override;void wheelEvent(QWheelEvent *event) override;//键盘交互void keyReleaseEvent(QKeyEvent *event) override; //按键释放事件void keyPressEvent(QKeyEvent *event) override; //按键按下事件void slotTimeOut();
protected:virtual void initializeGL();virtual void resizeGL(int w, int h);virtual void paintGL();
public:GenericRender m_render;//渲染器Camera m_camera;//相机grid m_grid;//网格平面xycoorSystem m_coordsys;//坐标系QTimer tm_;
public:QListint keys;QTimer* keyRespondTimer; //头文件中添加成员static int mouse_button;static int modifier_key;
public://鼠标变量static double mouse_pos_x_old;static double mouse_pos_y_old;bool changeview_ false; //改变视角的标志位float fov 2.0f; //视野范围
};
#endif // GLWIDGET_Hglwidget.cpp
#include glwidget.h
#include box.h
int glwidget::mouse_button -1;
int glwidget::modifier_key 0;
double glwidget::mouse_pos_x_old 0;
double glwidget::mouse_pos_y_old 0;
glwidget::glwidget(QWidget * parent):QOpenGLWidget(parent)
{keyRespondTimer new QTimer(this); //构造函数中创建定时器对象并连接信号槽connect(keyRespondTimer, QTimer::timeout, this, glwidget::slotTimeOut);setFocusPolicy(Qt::StrongFocus);//否则进入不了键盘事件监听
}void glwidget::initializeGL()
{initializeOpenGLFunctions();glEnable(GL_DEPTH_TEST);m_render.init();m_camera.setMatPro(fov);QStringList renderfiles{:/obj/data/Cayman_GT.obj, :/obj/data/bunny_10k.obj};for(QString file : renderfiles){Box* b1 new Box();b1-load(file);m_render.addBox(b1);}m_grid.initize();m_coordsys.initize();
}void glwidget::resizeGL(int w, int h)
{m_camera.setRatio((float)width() / (float)height());
}void glwidget::paintGL()
{glClearColor(1.0f, 1.0f, 1.0f, 0.1f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glPolygonMode(GL_FRONT, GL_LINE);QOpenGLExtraFunctions *f QOpenGLContext::currentContext()-extraFunctions();QMatrix4x4 mMatrix;m_camera.setMatPro(fov);m_grid.paint(f,m_camera.getMatPro(),m_camera.getMatView(),mMatrix);m_coordsys.paint(f,m_camera.getMatPro(),m_camera.getMatView(),mMatrix);m_render.render(f,m_camera,mMatrix);
}void glwidget::mousePressEvent(QMouseEvent *event){mouse_pos_x_old event-pos().x();mouse_pos_y_old event-pos().y();mouse_button event-button();
}void glwidget::mouseMoveEvent(QMouseEvent *event){int x event-pos().x();int y event-pos().y();double w width(), h height();double d_x (mouse_pos_x_old - x)/w;double d_y (mouse_pos_y_old - y)/h;if (mouse_button Qt::LeftButton){foreach (int key, keys) {switch (key) {case Qt::Key_Control:m_camera.rotateY(-d_x*180*5);m_camera.rotateX(-d_y*180*5);break;case Qt::Key_Shift:m_camera.move(5*d_x, 5*d_y, 0);break;default:break;}}}mouse_pos_x_old x;mouse_pos_y_old y;this-repaint();
}void glwidget::mouseReleaseEvent(QMouseEvent *event){ //鼠标左键松开禁止改变相机视角changeview_ false;
}void glwidget::wheelEvent(QWheelEvent *event){if (event-delta() 0)fov-2.0f;elsefov2.0f;if (fov0.50f)fov 0.5f;if (fov200.f)fov 200.0;this-repaint();
}void glwidget::keyReleaseEvent(QKeyEvent *event)
{if(!event-isAutoRepeat()) //判断如果不是长按时自动触发的释放,就将key值从容器中删除keys.removeAll(event-key());if(keys.isEmpty()) //容器空了关闭定时器keyRespondTimer-stop();
}void glwidget::keyPressEvent(QKeyEvent *event)
{if(!event-isAutoRepeat()) //判断如果不是长按时自动触发的按下,就将key值加入容器keys.append(event-key());if(!keyRespondTimer-isActive()) //如果定时器不在运行就启动一下keyRespondTimer-start(4);
}void glwidget::slotTimeOut(){foreach (int key, keys) {switch (key) {case Qt::Key_D:break;case Qt::Key_Shift:modifier_key Qt::Key_Shift;break;default:break;}}
}鼠标交互功能 交互的功能实现是重载QOpenGLWidget的鼠标事件函数修改相应的矩阵也就对应最上面的相机变化、灯光变化等
obj格式介绍 OBJ文件(.obj)包含有关3D对象的几何体的信息下面是一个长方体的obj格式文件
#
# Object file
#
mtllib Cube3x3x10.mtl
# Cube3x3x10\实体
v 0 0 0
v 3.00000002607703 0 0
v 3.00000002607703 0 9.99999977648258
v 0 0 9.99999977648258
v 3.00000002607703 3.00000002607703 9.99999977648258
v 0 3.00000002607703 9.99999977648258
v 0 0 9.99999977648258
v 3.00000002607703 0 9.99999977648258
v 0 3.00000002607703 9.99999977648258
v 0 3.00000002607703 0
v 0 0 0
v 0 0 9.99999977648258
v 0 3.00000002607703 0
v 3.00000002607703 3.00000002607703 0
v 3.00000002607703 0 0
v 0 0 0
v 3.00000002607703 3.00000002607703 0
v 3.00000002607703 3.00000002607703 9.99999977648258
v 3.00000002607703 0 9.99999977648258
v 3.00000002607703 0 0
v 3.00000002607703 3.00000002607703 9.99999977648258
v 3.00000002607703 3.00000002607703 0
v 0 3.00000002607703 0
v 0 3.00000002607703 9.99999977648258
vn 0 -1 0
vn 0 -1 0
vn 0 -1 0
vn 0 -1 0
vn 0 0 1
vn 0 0 1
vn 0 0 1
vn 0 0 1
vn -1 0 0
vn -1 0 0
vn -1 0 0
vn -1 0 0
vn 0 0 -1
vn 0 0 -1
vn 0 0 -1
vn 0 0 -1
vn 1 0 0
vn 1 0 0
vn 1 0 0
vn 1 0 0
vn 0 1 0
vn 0 1 0
vn 0 1 0
vn 0 1 0
vt 0 0
vt 0 0.00300000002607703
vt 0.00999999977648258 0.00300000002607703
vt 0.00999999977648258 0
vt 0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0
vt 0.00150000001303852 0
vt 0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0
vt 0.00499999988824129 0
vt 0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0
vt 0.00150000001303852 0
vt 0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0
vt 0.00499999988824129 0
vt 0.00999999977648258 0.00300000002607703
vt 0 0.00300000002607703
vt 0 0
vt 0.00999999977648258 0
o Cube3x3x10\实体
s off
# face 0
f 2/2/2 3/3/3 1/1/1
f 1/1/1 3/3/3 4/4/4
# face 1
f 5/5/5 6/6/6 8/8/8
f 8/8/8 6/6/6 7/7/7
# face 2
f 9/9/9 10/10/10 12/12/12
f 12/12/12 10/10/10 11/11/11
# face 3
f 13/13/13 14/14/14 16/16/16
f 16/16/16 14/14/14 15/15/15
# face 4
f 17/17/17 18/18/18 20/20/20
f 20/20/20 18/18/18 19/19/19
# face 5
f 21/21/21 22/22/22 24/24/24
f 24/24/24 22/22/22 23/23/23其中
mtllib Cube3x3x10.mtl 表示引用的材质文件的文件名v 0 0 0 表示一个点的xyz坐标使用空格隔开vn 0 -1 0 表示一个点的法向量使用空格隔开vt 0.09 0 表示uv纹理坐标使用空格隔开f 2/2/2 3/3/3 1/1/1 表示一个面 2/2/2依次为顶点索引纹理坐标索引法向索引因为是三角面片所以是三组也有超多三个点的自己造轮子的时候要注意o Cube 表示指定了模型名称为Cubes off 表示关闭光滑组 void ObjLoader::load(QString filename)
{// 打开文件QFile file(filename);if(!file.open(QIODevice::ReadOnly)){qDebug()[Error] fail to open file: filename;return;}// 读取文件QTextStream ts(file);// 临时存储QVectorQVector3D v;QVectorQVector3D vn;QVectorQVector3D vt;QVectorQStringList str_faces;QVectorFace faces;while(!ts.atEnd()){QStringList list ts.readLine().split(QRegExp((\\s)));list.removeAll( );if(list.size() 0 )break;if(list[0] v)v.push_back(QVector3D(list[1].toFloat(),list[2].toFloat(),list[3].toFloat()));if(list[0] vn)vn.push_back(QVector3D(list[1].toFloat(),list[2].toFloat(),list[3].toFloat()));if(list[0] vt)vt.push_back(QVector3D(list[1].toFloat(),list[2].toFloat(),0));if(list[0] f)str_faces.push_back(list);}for( int i 0; i str_faces.size(); i){Face face;for( int j 1; j3; j) //obj中顶点索引是从1开始{QStringList list str_faces[i][j].split(/);face.v[j-1] list[0].toInt() -1;face.t[j-1] list[1].toInt() -1;face.n[j-1] list[2].toInt() -1;}faces.push_back(face);}for( int i 0; ifaces.size();i){QVector3D a,b,c,na,nb,nc;a v[faces[i].v[0]];b v[faces[i].v[1]];c v[faces[i].v[2]];na vn[faces[i].n[0]];nb vn[faces[i].n[1]];nc vn[faces[i].n[2]];mv.push_back(a.x());mv.push_back(a.y());mv.push_back(a.z());mv.push_back(b.x());mv.push_back(b.y());mv.push_back(b.z());mv.push_back(c.x());mv.push_back(c.y());mv.push_back(c.z());mn.push_back(na.x());mn.push_back(na.y());mn.push_back(na.z());mn.push_back(nb.x());mn.push_back(nb.y());mn.push_back(nb.z());mn.push_back(nc.x());mn.push_back(nc.y());mn.push_back(nc.z());}file.close();std::cout filename.toStdString() id succeded!\t model_size:mv.size()/3std::endl;
}效果 暂时还不会弄视频或者动态图先看个静态效果吧
bunny Cayman_GT