Qt下学习OpenGL之OBJ模型

4 篇文章 0 订阅
   我这里的OBJ格式不是c++代码产生的中间文件,而是那个g什么wave公司的OBJ格式,格式很简单,作用就是拿来存储3D模型的一些基本信息。以前在VS2005下能很轻松读取,这次换QT了,幸好QT公司对客户很用心和负责,在其QtLab下发现了不错的类和代码。加以运用,成功导入OBJ~

   切入正题。
   首先这个类会需要引用该文件point3d.h,其内容如下:

#ifndef POINT3D_H
#define POINT3D_H

#include “math.h”

#include <qglobal.h>

struct Point3d
{
    float x, y, z;

    Point3d()
        : x(0)
        , y(0)
        , z(0)
    {
    }

    Point3d(float x_, float y_, float z_)
        : x(x_)
        , y(y_)
        , z(z_)
    {
    }

    Point3d operator+(const Point3d &p) const
    {
        return Point3d(*this) += p;
    }

    Point3d operator-(const Point3d &p) const
    {
        return Point3d(*this) -= p;
    }

    Point3d operator*(float f) const
    {
        return Point3d(*this) *= f;
    }
    Point3d &operator+=(const Point3d &p)
    {
        x += p.x;
        y += p.y;
        z += p.z;
        return *this;
    }

    Point3d &operator-=(const Point3d &p)
    {
        x -= p.x;
        y -= p.y;
        z -= p.z;
        return *this;
    }

    bool operator==(const Point3d &p)
    {
        if((x==p.x)&&(y==p.y)&&(z==p.z))
            return true;
        return false;
    }

    Point3d &operator*=(float f)
    {
        x *= f;
        y *= f;
        z *= f;
        return *this;
    }

    Point3d normalize() const
    {
        float r = 1. / sqrt(x * x + y * y + z * z);
        return Point3d(x * r, y * r, z * r);
    }
    float &operator[](unsigned int index) {
        Q_ASSERT(index < 3);
        return (&x)[index];
    }

    const float &operator[](unsigned int index) const {
        Q_ASSERT(index < 3);
        return (&x)[index];
    }
};

inline float dot(const Point3d &a, const Point3d &b)
{
    return a.x * b.x + a.y * b.y + a.z * b.z;
}

inline Point3d cross(const Point3d &a, const Point3d &b)
{
    return Point3d(a.y * b.z – a.z * b.y,
                   a.z * b.x – a.x * b.z,
                   a.x * b.y – a.y * b.x);
}

#endif

然后是Model.h的内容:

#ifndef MODEL_H
#define MODEL_H

#include <QString>
#include <QVector>

#include <math.h>

#include “point3d.h”

class Model
{
public:
    Model() {}
    Model(const QString &filePath);

    //画出模型
    void render(bool wireframe = false, bool normals = false) const;

    QString fileName() const { return m_fileName; }
    int faces() const { return m_pointIndices.size() / 3; }
    int edges() const { return m_edgeIndices.size() / 2; }
    int points() const { return m_points.size(); }

private:
    QString m_fileName;//文件名
    QVector<Point3d> m_points;//顶点信息
    QVector<Point3d> m_normals;//向量信息
    QVector<int> m_edgeIndices;//边索引
    QVector<int> m_pointIndices;//点索引
};

#endif

最后是Model.cpp的内容:

#include “model.h”

#include <QFile>
#include <QTextStream>
#include <QVarLengthArray>

#include <QtOpenGL>

Model::Model(const QString &filePath)
    : m_fileName(QFileInfo(filePath).fileName())
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly))
        return;

    Point3d boundsMin( 1e9, 1e9, 1e9);
    Point3d boundsMax(-1e9,-1e9,-1e9);

    QTextStream in(&file);
    while (!in.atEnd()) {
        QString input = in.readLine();
        if (input.isEmpty() || input[0] == ‘#’)
            continue;

        QTextStream ts(&input);
        QString id;
        ts >> id;
        if (id == “v”) {
            Point3d p;
            for (int i = 0; i < 3; ++i) {
                ts >> p[i];
                boundsMin[i] = qMin(boundsMin[i], p[i]);
                boundsMax[i] = qMax(boundsMax[i], p[i]);
            }
            m_points << p;
        } else if (id == “f” || id == “fo”) {
            QVarLengthArray<int, 4> p;

            while (!ts.atEnd()) {
                QString vertex;
                ts >> vertex;
                const int vertexIndex = vertex.split(‘/’).value(0).toInt();
                if (vertexIndex)
                    p.append(vertexIndex > 0 ? vertexIndex – 1 : m_points.size() + vertexIndex);
            }

            for (int i = 0; i < p.size(); ++i) {
                const int edgeA = p[i];
                const int edgeB = p[(i + 1) % p.size()];

                if (edgeA < edgeB)
                    m_edgeIndices << edgeA << edgeB;
            }

            for (int i = 0; i < 3; ++i)
                m_pointIndices << p[i];

            if (p.size() == 4)
                for (int i = 0; i < 3; ++i)
                    m_pointIndices << p[(i + 2) % 4];
        }
    }

    const Point3d bounds = boundsMax – boundsMin;
    const qreal scale = 1 / qMax(bounds.x, qMax(bounds.y, bounds.z));
    for (int i = 0; i < m_points.size(); ++i)
        m_points[i] = (m_points[i] – (boundsMin + bounds * 0.5)) * scale;

    m_normals.resize(m_points.size());
    for (int i = 0; i < m_pointIndices.size(); i += 3) {
        const Point3d a = m_points.at(m_pointIndices.at(i));
        const Point3d b = m_points.at(m_pointIndices.at(i+1));
        const Point3d c = m_points.at(m_pointIndices.at(i+2));

        const Point3d normal = cross(b – a, c – a).normalize();

        for (int j = 0; j < 3; ++j)
            m_normals[m_pointIndices.at(i + j)] += normal;
    }

    for (int i = 0; i < m_normals.size(); ++i)
        m_normals[i] = m_normals[i].normalize();
}

void Model::render(bool wireframe, bool normals) const
{
    glEnable(GL_DEPTH_TEST);
    glEnableClientState(GL_VERTEX_ARRAY);
    if (wireframe) {
        glVertexPointer(3, GL_FLOAT, 0, (float *)m_points.data());
        glDrawElements(GL_LINES, m_edgeIndices.size(), GL_UNSIGNED_INT, m_edgeIndices.data());
    } else {
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_COLOR_MATERIAL);
        glShadeModel(GL_SMOOTH);

        glEnableClientState(GL_NORMAL_ARRAY);
        glVertexPointer(3, GL_FLOAT, 0, (float *)m_points.data());
        glNormalPointer(GL_FLOAT, 0, (float *)m_normals.data());
        glDrawElements(GL_TRIANGLES, m_pointIndices.size(), GL_UNSIGNED_INT, m_pointIndices.data());

        glDisableClientState(GL_NORMAL_ARRAY);
        glDisable(GL_COLOR_MATERIAL);
        glDisable(GL_LIGHT0);
        glDisable(GL_LIGHTING);
    }

    if (normals) {
        QVector<Point3d> normals;
        for (int i = 0; i < m_normals.size(); ++i)
            normals << m_points.at(i) << (m_points.at(i) + m_normals.at(i) * 0.02f);
        glVertexPointer(3, GL_FLOAT, 0, (float *)normals.data());
        glDrawArrays(GL_LINES, 0, normals.size());
    }
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisable(GL_DEPTH_TEST);
}

 在需要调用外部OBJ模型的地方,声明一个该类的对象,在构造函数里输入模型的uri,然后调用render函数就ok了。呵呵,封装得很彻底很强大,祝你成功。

如果文章中有什么错误的地方,请指出也方便以后看到该日志的人能少走弯路。

如果文章中有内容帮助了你,也希望可以不吝啬点时间留下言,作为我的动力,呵呵~。

本文固定链接: http://liusir.name/obj-model-under-the-load-qt.html | 民警小刘

要在Qt OpenGL中读取OBJ模型文件,可以使用Qt自带的QOpenGLFunctions库。 首先,需要在项目文件中添加以下依赖: ``` QT += opengl ``` 然后,可以使用QOpenGLFunctions类来加载模型文件和绘制模型。 以下是一个简单的示例代码,可以读取和绘制一个OBJ模型文件: ```c++ #include <QOpenGLFunctions> #include <QOpenGLShaderProgram> #include <QOpenGLBuffer> #include <QVector3D> #include <QVector2D> #include <QFile> #include <QStringList> struct VertexData { QVector3D position; QVector2D texCoord; }; class ObjModel : protected QOpenGLFunctions { public: ObjModel(); virtual ~ObjModel(); void init(QString filename); void render(); private: QOpenGLShaderProgram m_program; QOpenGLBuffer m_vbo; int m_vertexCount; }; ObjModel::ObjModel() : m_vertexCount(0) { } ObjModel::~ObjModel() { m_vbo.destroy(); } void ObjModel::init(QString filename) { initializeOpenGLFunctions(); // Load OBJ file QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; QVector<QVector3D> positions; QVector<QVector2D> texCoords; while (!file.atEnd()) { QByteArray line = file.readLine().trimmed(); QList<QByteArray> tokens = line.split(' '); if (tokens.isEmpty()) continue; if (tokens[0] == "v") { positions.append(QVector3D(tokens[1].toFloat(), tokens[2].toFloat(), tokens[3].toFloat())); } else if (tokens[0] == "vt") { texCoords.append(QVector2D(tokens[1].toFloat(), tokens[2].toFloat())); } else if (tokens[0] == "f") { for (int i = 1; i < tokens.size(); ++i) { QList<QByteArray> face = tokens[i].split('/'); VertexData data; data.position = positions[face[0].toInt() - 1]; data.texCoord = texCoords[face[1].toInt() - 1]; m_vertices.append(data); } } } m_vertexCount = m_vertices.size(); // Create VBO m_vbo.create(); m_vbo.bind(); m_vbo.allocate(m_vertices.constData(), m_vertexCount * sizeof(VertexData)); // Load shader program m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/obj.vert"); m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/obj.frag"); m_program.link(); } void ObjModel::render() { m_program.bind(); m_vbo.bind(); m_program.enableAttributeArray("position"); m_program.enableAttributeArray("texCoord"); m_program.setAttributeBuffer("position", GL_FLOAT, offsetof(VertexData, position), 3, sizeof(VertexData)); m_program.setAttributeBuffer("texCoord", GL_FLOAT, offsetof(VertexData, texCoord), 2, sizeof(VertexData)); glDrawArrays(GL_TRIANGLES, 0, m_vertexCount); m_vbo.release(); m_program.release(); } ``` 在上面的示例代码中,我们使用QFile类来读取OBJ文件,然后使用QOpenGLBuffer类创建一个VBO,并将OBJ文件中的顶点数据存储到VBO中。最后,使用QOpenGLShaderProgram类加载并绑定着色器程序,并使用glDrawArrays函数绘制模型。 注意,上面的代码仅仅是一个简单的示例,不足以处理所有的OBJ文件。在实际开发中,还需要对OBJ文件中的各种情况进行判断和处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值