温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

基于Qt的OpenGL可编程管线学习(2)- obj模型绘制

发布时间:2020-07-17 08:50:06 来源:网络 阅读:1124 作者:Douzhq 栏目:编程语言

绘制一个Obj模型,效果如下图所示

基于Qt的OpenGL可编程管线学习(2)- obj模型绘制

这里给模型加载顶点和纹理的信息,加上环境光、漫反射和镜面反射,这里我用的是一个方向光。

并且让模型每一帧旋转一个角度达到动态旋转的效果。


1、Obj模型基本内容及加载

v 表示顶点数据

vt 表示纹理数据

vn 表示法线数据

f 表示一个面的顶点数据的Index


这里我是直接解析的模型,代码如下所示:

objmode.h

#ifndef OBJMODE_H #define OBJMODE_H #include <QObject> #include <QString> #include <QStringList> #include <QVector> #include <QFile> #include <QTextStream> struct VertexData {     float postion[3];     float texcoord[2];     float normal[3]; }; struct VertexPos {     float postion[3]; }; struct VertexTex {     float coord[2]; }; struct VertexNor {     float normal[3]; }; struct VertexIndex {     int posIndex;     int coordIndex;     int normalIndex; }; class ObjMode : QObject { public:     ObjMode();     ~ObjMode();     bool loadObjModel(QString nFileStr, QVector&nVertextData, QVector&index); private:     QVectorm_VertexInfo;     QVectorm_TextureInfo;     QVectorm_NormalInfo;     QVectorm_VertexIndex;     QVectorm_FaceIndex; }; #endif // OBJMODE_H

其中loadObjModel()函数就是加载模型的函数,输入为文件路径,会返回顶点的数据内容和每个面的三角形对应的顶点的索引。

函数实现如下所示:

bool ObjMode::loadObjModel(QString nFileStr, QVector<VertexData> &nVertextData, \                             QVector<unsigned int> &index) {     QFile nObjFile(nFileStr);     if (!nObjFile.exists())         return false;     if (!nObjFile.open(QFile::ReadOnly))         return false;     nVertextData.clear();     index.clear();     m_TextureInfo.clear();     m_NormalInfo.clear();     m_VertexInfo.clear();     m_VertexIndex.clear();     m_FaceIndex.clear();     QTextStream nTextStream(&nObjFile);     for (;!nTextStream.atEnd();)     {         QString nLineString = nTextStream.readLine();         QByteArray nData = nLineString.toLocal8Bit();         if (nData.length() <= 2)             continue;         if (nData.at(0) == 'v')         {             QStringList nStrList = nLineString.split(" ");             if (nData[1] == 't')             {                 if (nStrList.count() <= 0 || nStrList.at(0) != "vt")                     continue;                 VertexTex nTexture;                 for (int i=1; i<nStrList.count(); ++i)                     nTexture.coord[i - 1] = nStrList.at(i).toFloat();                 m_TextureInfo << nTexture;             }             else if (nData[1] == 'n')             {                 if (nStrList.count() <= 0 || nStrList.at(0) != "vn")                     continue;                 VertexNor nNormal;                 for (int i=1; i<nStrList.count(); ++i)                     nNormal.normal[i - 1] = nStrList.at(i).toFloat();                 m_NormalInfo << nNormal;             }             else             {                 if (nStrList.count() <= 0 || nStrList.at(0) != "v")                     continue;                 VertexPos nPos;                 for (int i=1; i<nStrList.count(); ++i)                     nPos.postion[i - 1] = nStrList.at(i).toFloat();                 m_VertexInfo << nPos;             }         }         else if (nData[0] == 'f')         {             QStringList nStrList = nLineString.split(" ");             if (nStrList.count() <= 0 || nStrList.at(0) != "f")                 continue;             for (int i=1; i<nStrList.count(); ++i)             {                 VertexIndex nIndex;                 QString nFaceIndexStr = nStrList.at(i);                 QStringList nFaceList = nFaceIndexStr.split("/");                 nIndex.posIndex = nFaceList.at(0).toUInt() - 1;                 nIndex.coordIndex = nFaceList.at(1).toUInt() - 1;                 nIndex.normalIndex = nFaceList.at(2).toUInt() - 1;                 bool isFinded = false;                 for (int j=0; j<m_VertexIndex.count(); ++j)                 {                     if (nIndex.posIndex == m_VertexIndex.at(j).posIndex && \                         nIndex.coordIndex == m_VertexIndex.at(j).coordIndex && \                         nIndex.normalIndex == m_VertexIndex.at(j).normalIndex)                     {                         isFinded = true;                         m_FaceIndex << j;                         break;                     }                 }                 if (!isFinded)                 {                     m_VertexIndex << nIndex;                     m_FaceIndex << m_VertexIndex.count() - 1;                 }             }         }     }     nObjFile.close();     for (int i=0; i<m_VertexIndex.count(); ++i)     {         VertexData nPerVertextData;         int posIndex = m_VertexIndex.at(i).posIndex;         int textureIndex = m_VertexIndex.at(i).coordIndex;         int normalIndex = m_VertexIndex.at(i).normalIndex;         memcpy(nPerVertextData.postion, m_VertexInfo.at(posIndex).postion, sizeof(VertexPos));         memcpy(nPerVertextData.texcoord, m_TextureInfo.at(textureIndex).coord, sizeof(VertexTex));         memcpy(nPerVertextData.normal, m_NormalInfo.at(normalIndex).normal, sizeof(VertexNor));         nVertextData.push_back(nPerVertextData);     }     for (int i=0; i<m_FaceIndex.count(); ++i)     {         index.push_back(m_FaceIndex.at(i));     }     return true; }


2、创建纹理

GLuint OpenGLOperate::createTexture(QString nFile) {     GLuint textureId = 0;     QFile file(nFile);     if (!file.exists())         return 0;     QImage p_w_picpath(nFile);     QImage textureImage;     int width = p_w_picpath.width();     int height = p_w_picpath.height();     textureImage = p_w_picpath.convertToFormat(QImage::Format_RGBA8888);     textureImage = textureImage.mirrored();     m_OpenGLCore->glGenTextures(1, &textureId);     m_OpenGLCore->glBindTexture(GL_TEXTURE_2D, textureId);     m_OpenGLCore->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);     m_OpenGLCore->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);     m_OpenGLCore->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);     m_OpenGLCore->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);     m_OpenGLCore->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, \                                GL_RGBA, GL_UNSIGNED_BYTE, textureImage.bits());     m_OpenGLCore->glBindTexture(GL_TEXTURE_2D, 0);     return textureId; }


为Shader的纹理赋值,默认使用0号纹理单元

m_Texture = m_OpenGLOperate->createTexture(":/niutou.bmp"); OpenGLCore->glBindTexture(GL_TEXTURE_2D, m_Texture); OpenGLCore->glUniform1i(m_TextureLocation, 0);


3、关于环境光、漫反射及高光

环境光:在无光源下的环境的亮度;这里将环境光直接写到fragment shader中(也可以在用uniform在CPU上设置)

设置环境光的亮度及材质

// Ambient vec4 M_AmbientLightColor = vec4(0.2, 0.2, 0.2, 1.0); vec4 M_AmbientMaterial = vec4(0.2, 0.2, 0.2, 1.0); vec4 ambientColor = M_AmbientLightColor * M_AmbientMaterial;



漫反射:在光的照射下物体反射的颜色,也就是物体反射的颜色

光照强度的计算:物体表面的点指向光源的向量 点乘 法线,就是发光强度。

分析:指向光源的向量与法线的夹角为0度时,反射的光线越多,发光强度越大。夹角为90度时,发光强度就为0,如果大于90度则为背光面。

// Diffuse vec3 M_LightPos = vec3(10.0, 10.0, 0.0); vec3 LightNormal = normalize(M_LightPos);       // 指向光源的单位向量,方向光 vec3 NormalNormal = normalize(M_normal);      //  法线的单位向量 // 点乘获取光照强度 vec4 M_DiffuseLightColor = vec4(1.0, 1.0, 1.0, 1.0); vec4 M_DiffuseMaterial = vec4(0.9, 0.9, 0.9, 1.0); vec4 diffuseColor = M_DiffuseLightColor * M_DiffuseMaterial *                      max(0.0, dot(NormalNormal, LightNormal));



镜面发射:高光,在金属等物体表面的光

标准phong模型 光照强度的计算:反射光线 点乘 直线眼睛的向量作为基底,然后取一个幂

分析:当反射光线与指向眼睛的向量夹角为0度时,则为最亮的部分

// 镜面反射 vec4 specularLightColor = vec4(1.0, 1.0, 1.0, 1.0); vec4 specularMaterial = vec4(0.4, 0.4, 0.4, 1.0); vec3 reflerDir = normalize(reflect(-LightNormal, NormalNormal)); vec3 eyeDir = normalize(vec3(0.0) - M_WordPos); vec4 specularColor = specularLightColor * specularMaterial *                       pow(max(0.0, dot(reflerDir, eyeDir)), 180);


4、法线矩阵(Normal Matrix)

当模型矩阵变换时,法线也会变,需要重新计算法线,公式为:

法线矩阵 = 模型矩阵的逆的转置

变换后的法线 = 法线矩阵 * 法线

QMatrix4x4 nMormalMat;    // 法线矩阵 QMatrix4x4 nModeMat;      // 模型矩阵 nModeMat.translate(0, -50, -200); if (m_Rote > 360)     m_Rote = 0; else     m_Rote += 1.0f; nModeMat.rotate(m_Rote, 0.0f, 1.0f, 0.0f); QMatrix4x4 nNormalMatrix = nModeMat.inverted().transposed();


Vertex Shader:

attribute vec3 pos; attribute vec2 coord; attribute vec3 normal; uniform mat4 M; uniform mat4 V; uniform mat4 P; uniform mat4 NM; varying vec2 M_coord; varying vec3 M_normal; varying vec3 M_WordPos; void main() {         M_coord = coord;         M_WordPos = vec3(M * vec4(pos, 1.0));         M_normal = mat3(NM) * normal;// 计算法线         gl_Position = P * V * M * vec4(pos, 1.0); }



Fragment Shader

uniform sampler2D U_MainTexture; varying vec2 M_coord; varying vec3 M_normal; varying vec3 M_WordPos; //uniform vec3 M_LightPos;        // 平行光 //uniform vec4 M_AmbientLightColor; //uniform vec4 M_AmbientMaterial; //uniform vec4 M_DiffuseLightColor; //uniform vec4 M_DiffuseMaterial; void main() {         // Ambient         vec4 M_AmbientLightColor = vec4(0.2, 0.2, 0.2, 1.0);         vec4 M_AmbientMaterial = vec4(0.2, 0.2, 0.2, 1.0);         vec4 ambientColor = M_AmbientLightColor * M_AmbientMaterial;         // Diffuse         vec3 M_LightPos = vec3(10.0, 10.0, 0.0);         vec3 LightNormal = normalize(M_LightPos);       // 指向光源的单位向量         vec3 NormalNormal = normalize(M_normal);      //  法线的单位向量         // 点乘获取光照强度         vec4 M_DiffuseLightColor = vec4(1.0, 1.0, 1.0, 1.0);         vec4 M_DiffuseMaterial = vec4(0.9, 0.9, 0.9, 1.0);         vec4 diffuseColor = M_DiffuseLightColor * M_DiffuseMaterial *                              max(0.0, dot(NormalNormal, LightNormal));         // 镜面反射         vec4 specularLightColor = vec4(1.0, 1.0, 1.0, 1.0);         vec4 specularMaterial = vec4(0.4, 0.4, 0.4, 1.0);         vec3 reflerDir = normalize(reflect(-LightNormal, NormalNormal));         vec3 eyeDir = normalize(vec3(0.0) - M_WordPos);         vec4 specularColor = specularLightColor * specularMaterial *                               pow(max(0.0, dot(reflerDir, eyeDir)), 180);         gl_FragColor = ambientColor + texture2D(U_MainTexture, M_coord) * 1                         + specularColor; }


为了使模型看的更清楚,我没用漫反射。如果使用漫反射可以将最后一句改成:

gl_FragColor = ambientColor +           texture2D(U_MainTexture, M_coord) * diffuseColor +           specularColor;


向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI