功能分析(需求分析)
- 基于QT平臺(tái),使用OpenGL進(jìn)行obj文件加載顯示;
- 使用鼠標(biāo)對(duì)場(chǎng)景進(jìn)行縮放、移動(dòng)、旋轉(zhuǎn)交互;
技術(shù)點(diǎn)分析
OpenGL
??OpenGL是基于C的,學(xué)習(xí)曲線比較抖,但是總的來(lái)說(shuō)就是下面一幅圖,
??用語(yǔ)言簡(jiǎn)單的描述(個(gè)人理解,可能不太準(zhǔn)確)是把cpu里內(nèi)存里的3D數(shù)據(jù),傳輸?shù)斤@卡的內(nèi)存里,以及如何轉(zhuǎn)換成2D平面上像素點(diǎn)顯示(也就是矩陣變換,在好多開源的框架里都進(jìn)行了進(jìn)一步封裝,形成了渲染器、場(chǎng)景、相機(jī)等)。這個(gè)數(shù)據(jù)是頂點(diǎn)坐標(biāo)、顏色等,傳輸跟送快遞一樣,除了要有數(shù)據(jù)本身之外,還要有其他信息,也就是要有個(gè)約定,到顯卡拿到數(shù)據(jù)之后怎么解析。
??有個(gè)網(wǎng)站比較有名learnopengl,可以對(duì)著學(xué)一遍,在網(wǎng)上找資料的時(shí)候,需要注意是立即渲染模式,還是可編程渲染管線模式,現(xiàn)在官方推薦是使用可編程渲染管線模式開發(fā)。
立即渲染模式
glBegin(GL_TRIANGLES)
glTranslatef(1,2,3);
// 其他操作
// glVertex*() 設(shè)置頂點(diǎn)坐標(biāo)
// glColor*() 設(shè)置當(dāng)前顏色
// glIndex*() 設(shè)置當(dāng)前顏色表
// glNormal*() 設(shè)置法向坐標(biāo)
// glEvalCoord*() 產(chǎn)生坐標(biāo)
// glTexCoord*() 設(shè)置紋理坐標(biāo)
// glEdgeFlag*() 控制邊界繪制
// glMaterial*() 設(shè)置材質(zhì)
glEnd()
可編程渲染管線模式
1、創(chuàng)建VBO頂點(diǎn)數(shù)據(jù)對(duì)象
GLuint VBO;
glGenBuffers(1,&VBO);
2、VBO與顯卡緩存綁定
glBindBuffer(GL_ARRAY_BUFFER, VBO)
3、綁定數(shù)據(jù)緩存對(duì)象
glBufferData(GL_ARRAY_BUFFER,sizeof(vertexs),vertex,)
4、數(shù)據(jù)格式
glVertexAttribPoint(0,3,GL_FLOAT,)
5、啟用/繪制
glEnableVertexAttribArray(0)
glDrawArrays(GL_TRIANGLES, 0, 10);
6、最后關(guān)閉
glBindBuffer(GL_ARRAY_BUFFER,0)
glBindVertexArray(0);
??以上兩段代碼只是展示兩種模式區(qū)別,很明顯立即渲染模式容易理解,但是性能有限制,可編程渲染管線模式理解和使用門檻高,但更能接觸底層。
QOpenGLWidget
??QT對(duì)OpenGL進(jìn)行了封裝,提供了QOpenGLWidget類,只需要對(duì)其繼承,在initialzeGL(),resizeGL(),paintGL()邏輯下進(jìn)行業(yè)務(wù)實(shí)現(xiàn),包括VBO、VAO的創(chuàng)建與綁定,著色器程序的編譯與鏈接等。
派生類 glwidget邏輯
??先簡(jiǎn)單介紹派生類的寫法,當(dāng)然為了方便擴(kuò)展和使用,抽象出了相機(jī)類、渲染器類、物體類,完整的工程代碼資源可以下載參考,具體的可以根據(jù)自己要實(shí)現(xiàn)的功能進(jìn)行編寫。
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.h"
class glwidget :public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
glwidget(QWidget * parent = nullptr);
//鼠標(biāo)交互事件重寫
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;//相機(jī)
grid m_grid;//網(wǎng)格平面xy
coorSystem m_coordsys;//坐標(biāo)系
QTimer tm_;
public:
QList<int> keys;
QTimer* keyRespondTimer; //頭文件中添加成員
static int mouse_button;
static int modifier_key;
public:
//鼠標(biāo)變量
static double mouse_pos_x_old;
static double mouse_pos_y_old;
bool changeview_ = false; //改變視角的標(biāo)志位
float fov = 2.0f; //視野范圍
};
#endif // GLWIDGET_H
glwidget.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); //構(gòu)造函數(shù)中創(chuàng)建定時(shí)器對(duì)象,并連接信號(hào)槽
connect(keyRespondTimer, &QTimer::timeout, this, &glwidget::slotTimeOut);
setFocusPolicy(Qt::StrongFocus);//否則進(jìn)入不了鍵盤事件監(jiān)聽
}
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){ //鼠標(biāo)左鍵松開禁止改變相機(jī)視角
changeview_ = false;
}
void glwidget::wheelEvent(QWheelEvent *event){
if (event->delta() > 0)
fov-=2.0f;
else
fov+=2.0f;
if (fov<0.50f)
fov = 0.5f;
if (fov>=200.f)
fov = 200.0;
this->repaint();
}
void glwidget::keyReleaseEvent(QKeyEvent *event)
{
if(!event->isAutoRepeat()) //判斷如果不是長(zhǎng)按時(shí)自動(dòng)觸發(fā)的釋放,就將key值從容器中刪除
keys.removeAll(event->key());
if(keys.isEmpty()) //容器空了,關(guān)閉定時(shí)器
keyRespondTimer->stop();
}
void glwidget::keyPressEvent(QKeyEvent *event)
{
if(!event->isAutoRepeat()) //判斷如果不是長(zhǎng)按時(shí)自動(dòng)觸發(fā)的按下,就將key值加入容器
keys.append(event->key());
if(!keyRespondTimer->isActive()) //如果定時(shí)器不在運(yùn)行,就啟動(dòng)一下
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;
}
}
}
鼠標(biāo)交互功能
??交互的功能實(shí)現(xiàn)是重載QOpenGLWidget的鼠標(biāo)事件函數(shù),修改相應(yīng)的矩陣,也就對(duì)應(yīng)最上面的相機(jī)變化、燈光變化等;
obj格式介紹
??OBJ文件(.obj)包含有關(guān)3D對(duì)象的幾何體的信息,下面是一個(gè)長(zhǎng)方體的obj格式文件;
#
# Object file
#
mtllib Cube3x3x10.mtl
# Cube3x3x10\實(shí)體
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\實(shí)體
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 ??表示引用的材質(zhì)文件的文件名
- v 0 0 0 ?????????表示一個(gè)點(diǎn)的xyz坐標(biāo),使用空格隔開
- vn 0 -1 0????????表示一個(gè)點(diǎn)的法向量,使用空格隔開
- vt 0.09 0????????表示uv紋理坐標(biāo),使用空格隔開
- f 2/2/2 3/3/3 1/1/1????表示一個(gè)面, 2/2/2依次為頂點(diǎn)索引,紋理坐標(biāo)索引,法向索引,因?yàn)槭侨敲嫫?,所以是三組,也有超多三個(gè)點(diǎn)的,自己造輪子的時(shí)候要注意
- o Cube??????表示指定了模型名稱為Cube
- s off ??????表示關(guān)閉光滑組
void ObjLoader::load(QString filename)
{
// 打開文件
QFile file(filename);
if(!file.open(QIODevice::ReadOnly)){
qDebug()<<"[Error] fail to open file: "<< filename;
return;
}
// 讀取文件
QTextStream ts(&file);
// 臨時(shí)存儲(chǔ)
QVector<QVector3D> v;
QVector<QVector3D> vn;
QVector<QVector3D> vt;
QVector<QStringList> str_faces;
QVector<Face> 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; j<=3; j++) //obj中頂點(diǎn)索引是從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; i<faces.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()/3<<std::endl;
}
效果
?暫時(shí)還不會(huì)弄視頻或者動(dòng)態(tài)圖,先看個(gè)靜態(tài)效果吧
bunny
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-761676.html
Cayman_GT
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-761676.html
到了這里,關(guān)于基于QT使用OpenGL,加載obj模型,進(jìn)行鼠標(biāo)交互的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!