1前面我们已经熟悉了opengl自定义顶点生成一个立方体,并且我们实现了立方体的旋转,光照等功能。下面我们来用opengl来加载一个obj文件。准备我们首先准备一个简单的obj文件(head.obj)。资源在本页下载
2 在obj文件里面,我们关注下 v 字段和f字段
v 字段就是顶点 f字段代表了面,意思是每个面的顶点编号。 结构如下
v -0.99179999 -2.98999995 4.05410025 表示顶点值
f 12791 127 126 表示这个面的三个点在 v 里面的index值是12791 也是就是v[12791]的值的顶点是这个面的顶点值。
3 下面我们来解析obj文件
bool load(QString fileName, QVector<float>& vPoints)
{
if (fileName.mid(fileName.lastIndexOf('.')) != ".obj" && fileName.mid(fileName.lastIndexOf('.')) != ".OBJ")
{
qDebug() << "file is not a obj file.";
return false;
}
QFile objFile(fileName);
if (!objFile.open(QIODevice::ReadOnly))
{
qDebug() << "open" << fileName << "failed";
return false;
}
else
{
qDebug() << "open" << fileName << "success!";
}
QVector<float> vertextPoints, texturePoints, normalPoints;
QVector<Face> facesIndexs;
while (!objFile.atEnd())
{
QByteArray lineData = objFile.readLine();
QList<QByteArray> strValues = lineData.trimmed().split(' ');
QString dataType = strValues.takeFirst();
for(int i=0;i<strValues.size();i++)
{
double nData = strValues.at(i).toDouble();
QString strTemp = QString::number(nData,'f',4);
if (dataType == "v")
{
vertextPoints.append(strTemp.toFloat());
}
else if (dataType == "vt")
{
texturePoints.append(strValues.at(i).toFloat());
}
else if (dataType == "vn")
{
normalPoints.append(strValues.at(i).toFloat());
}
else if (dataType == "f")
{
Face ondInfo;
if(strValues.at(i).contains("/"))
{
QList<QByteArray> strTemp = strValues.at(i).split('/');
if(strTemp.size()==2)
{
ondInfo.vertices = strTemp.at(0).toInt();
ondInfo.texCoords = strTemp.at(1).toInt();
}
else if(strTemp.size()==3)
{
ondInfo.vertices = strTemp.at(0).toInt();
ondInfo.texCoords = strTemp.at(1).toInt();
ondInfo.normals = strTemp.at(2).toInt();
}
}
else
{
ondInfo.vertices = strValues.at(i).toInt();
//qDebug()<<"Face ondInfo"<<ondInfo.vertices;
}
facesIndexs.append(ondInfo);
}
}
}
objFile.close();
int count =0;
for (int i=0;i<facesIndexs.size();i++)
{
int vIndex = facesIndexs[i].vertices - 1;
if(vIndex * 3 <=vertextPoints.size() ||vIndex * 3 + 1 <=vertextPoints.size()||vIndex * 3 + 2<=vertextPoints.size() )
{
vPoints << vertextPoints.at(vIndex * 3);
vPoints << vertextPoints.at(vIndex * 3 + 1);
vPoints << vertextPoints.at(vIndex * 3 + 2);
// qDebug()<<"vIndex"<<i<<vertextPoints.size()<<vIndex * 3;
}
else
{
// qDebug()<<"vIndex error"<<i<<vertextPoints.size()<<vIndex * 3;
}
}
vertextPoints.clear();
texturePoints.clear();
normalPoints.clear();
facesIndexs.clear();
return true;
}
接着我们来写加载代码
和以前的一样,就是写glsl语句,
#ifndef TESTOBJOPENGL_H
#define TESTOBJOPENGL_H
#include <QObject>
#include <QWidget>
#include <QOpenGLWidget>
#include <QOpenGLExtraFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QTimer>
#include <QOpenGLTexture>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QTimer>
#include <QMouseEvent>
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram);
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
class testobjOpengl : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
testobjOpengl( QWidget *parent=nullptr);
~testobjOpengl();
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
void rotateBy(int xAngle, int yAngle, int zAngle);
bool load(QString fileName, QVector<float>& vPoints);
struct Face{
int vertices;
int texCoords;
int normals;
Face()
{
vertices = -1;
texCoords = -1;
normals = -1;
}
};
private:
QVector<float> m_vPoints;
int m_xRot;
int m_yRot;
int m_zRot;
QOpenGLShaderProgram *m_program= nullptr;
QOpenGLVertexArrayObject vao;
QOpenGLBuffer vbo;
QVector3D cameraPos;
QVector3D cameraTarget;
QVector3D cameraDirection;
QOpenGLTexture *texture;
int m_projMatrixLoc;
int m_mvMatrixLoc;
int m_normalMatrixLoc;
int m_lightPosLoc;
QMatrix4x4 m_proj;
QMatrix4x4 m_camera;
QMatrix4x4 m_world;
QVector3D m_camera_pos;
QTimer* timer;
int m_yPos=0;
int m_zPos=0;
};
#endif // TESTOBJOPENGL_H
#include "testobjopengl.h"
static const char *vertexShaderSourceCore =
"#version 330\n"
"layout (location = 0) in vec4 vertex;\n"
"layout (location = 1) in vec3 normal;\n"
"out vec3 vert;\n"
"out vec3 vertNormal;\n"
"uniform mat4 matrix;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"uniform mat3 normalMatrix;\n"
"void main() {\n"
" vert = vertex.xyz;\n"
" vertNormal = normalMatrix * normal;\n"
" gl_Position = projection*view* matrix * vertex;\n"
"}\n";
static const char *fragmentShaderSourceCore =
"#version 150\n"
"in highp vec3 vert;\n"
"in highp vec3 vertNormal;\n"
"out highp vec4 fragColor;\n"
"uniform highp vec3 lightPos;\n"
"void main() {\n"
" highp vec3 L = normalize(lightPos - vert);\n"
" highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
" highp vec3 color = vec3(1.0, 1.0, 0.0);\n"
" highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
" fragColor = vec4(col, 1.0);\n"
"}\n";
testobjOpengl::testobjOpengl(QWidget *parent)
: QOpenGLWidget(parent),
m_xRot(0),
m_yRot(0),
m_zRot(0)
{
m_vPoints.clear();
cameraPos = QVector3D(0, 0, 3);
QString strTemp = "F:/Tree2/head.obj";
bool ret = load(strTemp,m_vPoints);
if(ret)
{
timer = new QTimer;
timer->setInterval(100);
connect(timer,&QTimer::timeout,this,[=]{
qDebug()<<"timeout";
rotateBy(10 * 16, +10 * 16, -1 * 16);
});
timer->start();
}
else
{
qDebug()<<"1111111111111111";
}
}
void testobjOpengl::rotateBy(int xAngle, int yAngle, int zAngle)
{
float cameraSpeed = 0.2;
m_camera_pos -= cameraSpeed * QVector3D(0, 0, -1);//由远到近
if(m_yPos>=100)
{
m_yPos = 0;
}
m_yPos+=10;
if(m_zPos>=100)
{
m_zPos = 0;
}
m_zPos+=10;
// m_camera_pos = QVector3D(0, m_yPos, m_zPos);//由远到近
// m_camera_pos.setY(m_yPos);
m_xRot += xAngle;
m_yRot += yAngle;
m_zRot += zAngle;
update();
//timer->stop();
}
testobjOpengl::~testobjOpengl()
{
}
void testobjOpengl::initializeGL()
{
initializeOpenGLFunctions();
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSourceCore);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSourceCore);
if (m_program->link())
{
qDebug() << "link success!";
}
else
{
qDebug() << "link failed";
}
m_program->bindAttributeLocation("vertex", 0);
m_program->bindAttributeLocation("normal", 1);
m_program->link();
m_program->bind();
// m_projMatrixLoc = m_program->uniformLocation("projection");
// m_mvMatrixLoc = m_program->uniformLocation("matrix");
// m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
// m_lightPosLoc = m_program->uniformLocation("lightPos");
vbo.create();
vbo.bind();
vbo.allocate(m_vPoints.data(), m_vPoints.size() * sizeof(float));
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
vbo.release();
m_program->setUniformValue(m_lightPosLoc, QVector3D(10, 10, 10));
}
void testobjOpengl::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
QMatrix4x4 m;
//m.ortho(-0.5f, +0.5f, +0.5f, -0.5f, 4.0f, 15.0f);
m.setToIdentity();
m.translate(0.0f, 0.0f, 0.0f);
m.rotate(m_xRot / 16.0f, 1.0f, 0.0f, 0.0f);
m.rotate(90, 0.0f, 1.0f, 0.0f);
m.rotate(0, 0.0f, 0.0f, 1.0f);
m.scale(0.5);
QMatrix4x4 view;
view.setToIdentity();
view.lookAt(QVector3D(0, 20, 20), QVector3D(0,0,0), QVector3D(1,0,0));
m_program->bind();
m_program->setUniformValue("projection", m_proj);
m_program->setUniformValue("matrix", m);
m_program->setUniformValue("view", view);
m_program->setUniformValue("lightPos", QVector3D(10, 10, 0));
QMatrix3x3 normalMatrix = m.normalMatrix();
m_program->setUniformValue("normalMatrix", normalMatrix);
glDrawArrays(GL_TRIANGLES, 0, m_vPoints.size()/3);
m_program->release();
}
void testobjOpengl::resizeGL(int width, int height)
{
m_proj.setToIdentity();
m_proj.perspective(45.0f, GLfloat(width) / height, 0.01f, 100.0f);
}
bool testobjOpengl::load(QString fileName, QVector<float>& vPoints)
{
if (fileName.mid(fileName.lastIndexOf('.')) != ".obj" && fileName.mid(fileName.lastIndexOf('.')) != ".OBJ")
{
qDebug() << "file is not a obj file.";
return false;
}
QFile objFile(fileName);
if (!objFile.open(QIODevice::ReadOnly))
{
qDebug() << "open" << fileName << "failed";
return false;
}
else
{
qDebug() << "open" << fileName << "success!";
}
QVector<float> vertextPoints, texturePoints, normalPoints;
QVector<Face> facesIndexs;
while (!objFile.atEnd())
{
QByteArray lineData = objFile.readLine();
QList<QByteArray> strValues = lineData.trimmed().split(' ');
QString dataType = strValues.takeFirst();
for(int i=0;i<strValues.size();i++)
{
double nData = strValues.at(i).toDouble();
QString strTemp = QString::number(nData,'f',4);
if (dataType == "v")
{
vertextPoints.append(strTemp.toFloat());
}
else if (dataType == "vt")
{
texturePoints.append(strValues.at(i).toFloat());
}
else if (dataType == "vn")
{
normalPoints.append(strValues.at(i).toFloat());
}
else if (dataType == "f")
{
Face ondInfo;
if(strValues.at(i).contains("/"))
{
QList<QByteArray> strTemp = strValues.at(i).split('/');
if(strTemp.size()==2)
{
ondInfo.vertices = strTemp.at(0).toInt();
ondInfo.texCoords = strTemp.at(1).toInt();
}
else if(strTemp.size()==3)
{
ondInfo.vertices = strTemp.at(0).toInt();
ondInfo.texCoords = strTemp.at(1).toInt();
ondInfo.normals = strTemp.at(2).toInt();
}
}
else
{
ondInfo.vertices = strValues.at(i).toInt();
//qDebug()<<"Face ondInfo"<<ondInfo.vertices;
}
facesIndexs.append(ondInfo);
}
}
}
objFile.close();
int count =0;
for (int i=0;i<facesIndexs.size();i++)
{
int vIndex = facesIndexs[i].vertices - 1;
if(vIndex * 3 <=vertextPoints.size() ||vIndex * 3 + 1 <=vertextPoints.size()||vIndex * 3 + 2<=vertextPoints.size() )
{
vPoints << vertextPoints.at(vIndex * 3);
vPoints << vertextPoints.at(vIndex * 3 + 1);
vPoints << vertextPoints.at(vIndex * 3 + 2);
// qDebug()<<"vIndex"<<i<<vertextPoints.size()<<vIndex * 3;
}
else
{
// qDebug()<<"vIndex error"<<i<<vertextPoints.size()<<vIndex * 3;
}
}
vertextPoints.clear();
texturePoints.clear();
normalPoints.clear();
facesIndexs.clear();
return true;
}
我们开始调用
testobjOpengl w;
w.show();
运行结果: