绘制一个Obj模型,效果如下图所示
成都创新互联主营武城网站建设的网络公司,主营网站建设方案,app软件开发公司,武城h5重庆小程序开发公司搭建,武城网站营销推广欢迎武城等地区企业咨询这里给模型加载顶点和纹理的信息,加上环境光、漫反射和镜面反射,这里我用的是一个方向光。
并且让模型每一帧旋转一个角度达到动态旋转的效果。
1、Obj模型基本内容及加载
v 表示顶点数据
vt 表示纹理数据
vn 表示法线数据
f 表示一个面的顶点数据的Index
这里我是直接解析的模型,代码如下所示:
objmode.h
#ifndef OBJMODE_H #define OBJMODE_H #include#include #include #include #include #include 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&nVertextData, \ QVector &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 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;另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
网页名称:基于Qt的OpenGL可编程管线学习(2)-obj模型绘制-创新互联
链接地址:http://cdxtjz.com/article/hoeip.html