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

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

创新互联建站网络公司拥有10多年的成都网站开发建设经验,上1000+客户的共同信赖。提供成都网站设计、成都网站建设、网站开发、网站定制、卖友情链接、建网站、网站搭建、响应式网站、网页设计师打造企业风格,提供周到的售前咨询和贴心的售后服务

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

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

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

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;

当前文章:基于Qt的OpenGL可编程管线学习(2)-obj模型绘制
链接分享:http://ybzwz.com/article/gcgejc.html