和一般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 &vPoints,QVector &tPoints,QVector &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 vertextPoints,texturePoints,normalPoints;
QVector> 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 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 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
#include
#include
#include
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 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 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
#include
#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();
}
到此结束。