qt opengl 加载3d模型(obj格式)

30 篇文章 10 订阅

     和一般c++程序加载3d模型一样,解读出数据内容,再用一个常规的着色程序就可以了。

我实现的效果如下,采用的免费模型

  实现思路和前面的略有不同,就是把自己生成顶点、纹理、法线的过程变成从文件读取了。

我的obj文件如下 

mtllib C:\Users\Animation\Desktop\XNALara\data\dummy\Vanquish - Augmented Reaction Suit\vanquish.mtl
g Model001_24_Vanquish_1.0_0.0_0.0
usemtl Model001_Material001
v 0.1132252 1.45228 0.0713402
vn 0.452102 0.483883 0.749307
vt 0.946289 0.365723

....

f -800/-800/-800 -810/-810/-810 -808/-808/-808
f -808/-808/-808 -799/-799/-799 -800/-800/-800
f -801/-801/-801 -800/-800/-800 -798/-798/-798
f -798/-798/-798 -802/-802/-802 -801/-801/-801
f -802/-802/-802 -798/-798/-798 -797/-797/-797
f -797/-797/-797 -803/-803/-803 -802/-802/-802
f -803/-803/-803 -797/-797/-797 -796/-796/-796

然后这里会有一个空行,下面的内容格式和上面保持一致,如此有几个循环

mtllib C:\Users\Animation\Desktop\XNALara\data\dummy\Vanquish - Augmented Reaction Suit\vanquish.mtl
g Model001_24_Vanquish_1.0_0.0_0.0
usemtl Model001_Material001
v 0.1132252 1.45228 0.0713402
vn 0.452102 0.483883 0.749307
vt 0.946289 0.365723

....

f -800/-800/-800 -810/-810/-810 -808/-808/-808
f -808/-808/-808 -799/-799/-799 -800/-800/-800
f -801/-801/-801 -800/-800/-800 -798/-798/-798
f -798/-798/-798 -802/-802/-802 -801/-801/-801
f -802/-802/-802 -798/-798/-798 -797/-797/-797
f -797/-797/-797 -803/-803/-803 -802/-802/-802
f -803/-803/-803 -797/-797/-797 -796/-796/-796 

关于obj文件的介绍我就不说了,百度吧。

解析如下

bool ObjLoader::load(QString fileName, QVector<float> &vPoints,QVector<float> &tPoints,QVector<float> &nPoints)
{
    if(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;
    }

    QVector<float> vertextPoints,texturePoints,normalPoints;
    QVector<std::tuple<int,int,int>> facesIndexs;
    while (!objFile.atEnd()) {
        QByteArray lineData = objFile.readLine();
        lineData = lineData.remove(lineData.count() - 2,2);
        if(lineData.isEmpty()){
            for(auto &verFaceInfo : facesIndexs){
                int vIndex = std::get<0>(verFaceInfo);
                int tIndex = std::get<1>(verFaceInfo);
                int nIndex = std::get<2>(verFaceInfo);
                int vPointSizes = vertextPoints.count() / 3;
                int tPointSizes = texturePoints.count() / 2;
                int nPointSizes = normalPoints.count() / 3;
                vPoints << vertextPoints.at((vPointSizes + vIndex) * 3);
                vPoints << vertextPoints.at((vPointSizes + vIndex) * 3 + 1);
                vPoints << vertextPoints.at((vPointSizes + vIndex) * 3 + 2);

                tPoints << texturePoints.at((tPointSizes + tIndex) * 2);
                tPoints << texturePoints.at((tPointSizes + tIndex) * 2 + 1);

                nPoints << normalPoints.at((nPointSizes + nIndex) * 3);
                nPoints << normalPoints.at((nPointSizes + nIndex) * 3 + 1);
                nPoints << normalPoints.at((nPointSizes + nIndex) * 3 + 2);
            }
            vertextPoints.clear();
            texturePoints.clear();
            normalPoints.clear();
            facesIndexs.clear();
            continue;
        }

        QList<QByteArray> strValues = lineData.split(' ');
        QString dataType = strValues.takeFirst();
        if(dataType == "v"){
            std::transform(strValues.begin(),strValues.end(),std::back_inserter(vertextPoints),[](QByteArray &str){
                return str.toFloat();
            });
        }else if(dataType == "vt"){
            std::transform(strValues.begin(),strValues.end(),std::back_inserter(texturePoints),[](QByteArray &str){
                return str.toFloat();
            });
        }else if(dataType == "vn"){
            std::transform(strValues.begin(),strValues.end(),std::back_inserter(normalPoints),[](QByteArray &str){
                return str.toFloat();
            });
        }else if(dataType == "f"){
            std::transform(strValues.begin(),strValues.end(),std::back_inserter(facesIndexs),[](QByteArray &str){
                QList<QByteArray> intStr = str.split('/');
                return std::make_tuple(intStr.first().toInt(),intStr.at(1).toInt(),intStr.last().toInt());
            });
        }
    }
    objFile.close();
}

 这样解析出来的数据就按顺序放入容器了,接下的步骤和前面一样的,写个常用渲染器,用就可以了

#ifndef GENERICRENDER_H
#define GENERICRENDER_H

#include <QOpenGLShaderProgram>
#include <QOpenGLExtraFunctions>
#include <QOpenGLTexture>
#include <QOpenGLBuffer>
class GenericRender
{
public:
    GenericRender() = default;
    void initsize(QString filename,QImage &textureImg);
    void render(QOpenGLExtraFunctions *f,QMatrix4x4 &pMatrix,QMatrix4x4 &vMatrix,QMatrix4x4 &mMatrix,QVector3D &cameraLocation,QVector3D &lightCation);

private:
    QOpenGLTexture *texture_{nullptr};
    QOpenGLBuffer vbo_;
    QOpenGLShaderProgram program_;
    QVector<float> vertPoints_,texturePoints_,normalPoints_;
};

#endif // GENERICRENDER_H
#include "genericrender.h"
#include "utils/objloader.h"
void GenericRender::initsize(QString filename, QImage &textureImg)
{
    program_.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,"vsrc.vert");
    program_.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,"fsrc.frag");
    program_.link();

    ObjLoader objModelLoader;
    objModelLoader.load(filename,vertPoints_,texturePoints_,normalPoints_);
    QVector<float> points;
    points << vertPoints_ << texturePoints_ << normalPoints_;
    vbo_.create();
    vbo_.bind();
    vbo_.allocate(points.data(),points.count() * sizeof(float));

    texture_ = new QOpenGLTexture(textureImg);
    texture_->setWrapMode(QOpenGLTexture::ClampToEdge);
    texture_->setMinMagFilters(QOpenGLTexture::Nearest,QOpenGLTexture::Linear);
}

void GenericRender::render(QOpenGLExtraFunctions *f, QMatrix4x4 &pMatrix, QMatrix4x4 &vMatrix, QMatrix4x4 &mMatrix, QVector3D &cameraLocation, QVector3D &lightCation)
{
    f->glEnable(GL_DEPTH_TEST);
    program_.bind();
    vbo_.bind();
    f->glActiveTexture(GL_TEXTURE0 + 0);
    program_.setUniformValue("uPMatrix",pMatrix);
    program_.setUniformValue("uVMatrix",vMatrix);
    program_.setUniformValue("uMMatrix",mMatrix);
    program_.setUniformValue("uLightLocation",lightCation);
    program_.setUniformValue("uCamera",cameraLocation);
    program_.setUniformValue("sTexture",0);

    program_.enableAttributeArray(0);
    program_.enableAttributeArray(1);
    program_.enableAttributeArray(2);
    program_.setAttributeBuffer(0,GL_FLOAT,0,3,3*sizeof(GLfloat));
    program_.setAttributeBuffer(1,GL_FLOAT,vertPoints_.count() * sizeof(GLfloat),2,2*sizeof(GLfloat));
    program_.setAttributeBuffer(2,GL_FLOAT,(vertPoints_.count() + texturePoints_.count()) * sizeof(GLfloat),3,3*sizeof(GLfloat));
    texture_->bind(0);
    f->glDrawArrays(GL_TRIANGLES,0,vertPoints_.count()/3);

    program_.disableAttributeArray(0);
    program_.disableAttributeArray(1);
    program_.disableAttributeArray(2);
    texture_->release();
    vbo_.release();
    program_.release();
    f->glDisable(GL_DEPTH_TEST);
}

 其shader如下

#version 330
uniform mat4 uPMatrix,uVMatrix,uMMatrix;
uniform vec3 uLightLocation,uCamera;
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec2 aTextureCoord;
layout (location = 2) in vec3 aNormal;
smooth out vec2 vTextureCood;
smooth out vec4 vAmbient;
smooth out vec4 vDiffuse;
smooth out vec4 vSpecular;

void pointLight(in vec3 normal,inout vec4 ambient,inout vec4 diffuse,inout vec4 specular,in vec4 lightAmbient,in vec4 lightDiffuse,in vec4 lightSpecular,in float shininess){
    ambient = lightAmbient;

    vec3 normalTarget = aPosition + normal;
    vec3 newNormal = normalize((uMMatrix * vec4(normalTarget,1)).xyz - (uMMatrix * vec4(aPosition,1)).xyz);
    vec3 eye = normalize(uCamera - (uMMatrix * vec4(aPosition,1)).xyz);
    vec3 vp = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);
    vec3 halfVector = normalize(eye + vp);

    float nDotViewPotision = max(0.0,dot(newNormal,vp));
    diffuse = lightDiffuse * nDotViewPotision;

    float nDotViewHalfVector = dot(newNormal,halfVector);
    float powerFactor = max(0.0,pow(nDotViewHalfVector,shininess));
    specular = lightSpecular * powerFactor;
}

void main(void)
{
    gl_Position = uPMatrix * uVMatrix * uMMatrix * vec4(aPosition,1);
    vec4 ambient = vec4(0.0,0.0,0.0,0.0),diffuse = vec4(0.0,0.0,0.0,0.0),specular = vec4(0.0,0.0,0.0,0.0);
    pointLight(aNormal,ambient,diffuse,specular,vec4(0.6,0.6,0.6,1.0),vec4(0.8,0.8,0.8,1.0),vec4(0.9,0.9,0.9,1),50);
    vAmbient = ambient;
    vDiffuse = diffuse;
    vSpecular = specular;
    vTextureCood = aTextureCoord;
}
#version 330
uniform sampler2D sTexture;
in vec2 vTextureCood;
in vec4 vAmbient;
in vec4 vDiffuse;
in vec4 vSpecular;
out vec4 fragColor;

void main(void)
{
    vec4 finalColor = texture2D(sTexture,vTextureCood);
    finalColor = finalColor * (vAmbient + vDiffuse + vSpecular);
    fragColor = finalColor;
}

在qwidget界面中调用如下

#ifndef WIDGET_H
#define WIDGET_H

#include <QOpenGLWidget>
#include <QTimer>
#include "genericrender.h"

class Widget : public QOpenGLWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

protected:
    void initializeGL() override;
    void resizeGL(int w,int h) override;
    void paintGL() override;

private:
    GenericRender render_;
    QTimer tm_;
    QVector3D cameraLocation_,lightLocation_;
    QMatrix4x4 pMatrix_;
    qreal angleX_,angleY,anglZ_;

private slots:
    void slotTimeout();
};

#endif // WIDGET_H
#include "widget.h"
#include "utils/objloader.h"

Widget::Widget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    connect(&tm_,SIGNAL(timeout()),this,SLOT(slotTimeout()));
    tm_.start(30);
}

Widget::~Widget()
{

}

void Widget::initializeGL()
{
    render_.initsize("vanquish.obj",QImage("vanquish.jpg"));
    cameraLocation_.setX(0);
    cameraLocation_.setY(0);
    cameraLocation_.setZ(3);
    lightLocation_.setX(10);
    lightLocation_.setY(10);
    lightLocation_.setZ(1);
}

void Widget::resizeGL(int w, int h)
{
    pMatrix_.setToIdentity();
    pMatrix_.perspective(45,float(w)/h,0.01f,100.0f);
}

void Widget::paintGL()
{
    QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
    f->glClearColor(0.0f,0.0f,0.0f,1.0f);
    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 vMatrix;
    vMatrix.lookAt(cameraLocation_,QVector3D(0,0,0),QVector3D(0,1,0));

    QMatrix4x4 mMatrix;
    mMatrix.translate(0,-0.8);
//    mMatrix.rotate(angleX_,1,0,0);
    mMatrix.rotate(angleY,0,1,0);
//    mMatrix.rotate(anglZ_,0,0,1);
    render_.render(f,pMatrix_,vMatrix,mMatrix,cameraLocation_,lightLocation_);
}

void Widget::slotTimeout()
{
    angleX_ += 5;
    angleY += 5;
    anglZ_ += 5;
    update();
}

 到此结束。

  • 21
    点赞
  • 148
    收藏
    觉得还不错? 一键收藏
  • 36
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值