QT+OPenGL十一之漫反射和镜面反射贴图

在说反射贴图之前,不得不说到材质

前面我们做了冯着色模型,但是他还有些缺点,就是现实中,金属和陶器会有明显的镜面高光,但是木制品几乎没有镜面高光,而我们那样做会将所有的物品通通附有镜面高光,很显然这不符合现实。
为了更加精准的控制,就引入了材质在learnOpenGL中我们能见到

#version 330 core
struct Material
{
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};
uniform Material material;

这样的结构体,它讲的在不同的反射下,物体反射的颜色。我觉得这样说不太恰当。因为:

void main()
{
    // 环境光
    vec3 ambient = lightColor * material.ambient;

    // 漫反射光
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = lightColor * (diff * material.diffuse);

    // 镜面高光
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = lightColor * (spec * material.specular);  

    vec3 result = ambient + diffuse + specular;
    color = vec4(result, 1.0f);
}

从他给出的代码中我们更能看出,材质本来就是物体固有属性,是我们给他定义的。也就是我们给他每种反射的颜色,从我们赋予它开始,物体本来就是那种颜色了,只是与光的颜色相结合,显现出不同的颜色效果(变暗或者变亮,甚至和有色光结合变了颜色)。对于模型而言,我们其实没有必要给材质定义颜色,因为模型本就有纹理(这是通常情况,特殊情况不需要纹理的话就得换类了,作为学习还是可以玩一玩,可以来做一些色彩效果)。我们上一章用的纹理做的光照模型,其实隐隐约约用来材质。物体的颜色别我们换成了纹理的颜色。

使用材质的好处:

shininess指的是光照散射半径,也就是前面说的pow函数中的次方,原理已经讲过。
为了给模型比较正确的效果,比如learnOpenGL中带有金属镶边的木箱子,金属部分会有镜面反射而木头部分只有漫反射。因此出现了漫反射和镜面反射贴图来充当材质。

struct Material
{
    sampler2D diffuse;//漫反射贴图
    sampler2D specular;//镜面反射贴图
    float shininess;//散射半径,越大光斑越小
};

这是我们只需给这个属性通过缓冲区发送过来。因为环境光其实和漫反射的材质颜色往往相同,因此直接使用漫反射就好了,不需要定义。

纹理解码:

因为QOpenGLTexture这个类不太支持一些特殊的图片格式,仅仅支持像png、jpg、bmp这样的因此我们需要下载解码库freeimage,这个库很强大能支持比如.tga这样的格式。
https://freeimage.sourceforge.io/download.html

image.png

vsc++目录链接后直接把.dll文件丢到.exe所在的目录就好了

我们写一个自己的纹理类。
Image.h

#ifndef IMAGE_H
#define IMAGE_H

#include 
class Image:protected QOpenGLFunctions
{
public:
    Image(QOpenGLContext *pContext=nullptr);
    ~Image();
    void loadImage(const QString &filePath);
    unsigned int getTextureID();
private:
    void crateTexture(int width, int height, byte* pixels);
private:
    unsigned int m_textureID;
};

#endif // IMAGE_H

Image.cpp

#include "Image.h"
#include "FreeImage/FreeImage.h"
Image::Image(QOpenGLContext *pContext):QOpenGLFunctions(pContext)
{

}

Image::~Image()
{

}

void Image::loadImage(const QString &filePath)
{
    QByteArray byteFilePath = filePath.toLatin1();
    FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType(byteFilePath.data());   //Get file type
    if (fifmt == FIF_UNKNOWN)
    {
        fifmt = FreeImage_GetFIFFromFilename(byteFilePath.data());
        if (fifmt == FIF_UNKNOWN)
        return;
    }
    FIBITMAP *pDib = FreeImage_Load(fifmt, byteFilePath.data());
    pDib = FreeImage_ConvertTo24Bits(pDib);   //convert to 24 bits
    int width = FreeImage_GetWidth(pDib);    //get the width of texture
    int height = FreeImage_GetHeight(pDib);  //height
    BYTE *pPixels = (BYTE*)FreeImage_GetBits(pDib); //pixels
    if(!pPixels)
    return;
    crateTexture(width,height,pPixels);
    FreeImage_Unload(pDib);   //release the memory
}

unsigned int Image::getTextureID()
{
    return m_textureID;
}

void Image::crateTexture(int width, int height, byte *pixels)
{
    GLuint textureId;
    glGenTextures(1,&textureId);
    glBindTexture(GL_TEXTURE_2D,textureId);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,height,0,GL_BGR_EXT,GL_UNSIGNED_BYTE,pixels);
    glGenerateMipmap(GL_TEXTURE_2D);//多行渐远纹理
    glBindTexture(GL_TEXTURE_2D,0);
    m_textureID=textureId;
}

Model.h

#pragma once
#include "assimp/Importer.hpp"
#include "assimp/scene.h"
#include "assimp/postprocess.h"
#include "MyMesh.h"
#include"iostream"
#include "Image.h"
using namespace std;
class Model
{
public:
    /*  成员函数   */
    Model();
    ~Model();
    void draw(Camera camera);
    void init(string path, QOpenGLShaderProgram* shaderProgram);
    void setModelLocation(QVector3D location);
private:
    ///*  模型数据  */
    vector meshes;
    string directory;
    QOpenGLShaderProgram* shaderProgram;
    ///*  私有成员函数   */
    void loadModel(string path);
    void processNode(aiNode* node, const aiScene* scene);
    MyMesh* processMesh(aiMesh* mesh, const aiScene* scene);
   vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName);
};


Model.cpp

#include "stdafx.h"
#include "Model.h"
Model::Model(){
   
}
Model::~Model() {
    meshes.clear();
}
void Model::init(string path, QOpenGLShaderProgram* shaderProgram) {
    this->shaderProgram = shaderProgram;
    loadModel(path);
}
void Model::setModelLocation(QVector3D location) {
    for (GLuint i = 0; i < this->meshes.size(); i++)
    {
        this->meshes[i]->setLocation(location.x(), location.y(), location.z());
    }
}
void Model::draw(Camera camera)
{
    for (GLuint i = 0; i < this->meshes.size(); i++)
    {
        this->meshes[i]->draw(camera);
    }
}
void Model::loadModel(string path)
{
    Assimp::Importer import;
    const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);

    if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
    {
        qDebug() << "ERROR::ASSIMP::" << import.GetErrorString() << endl;
        return;
    }
    this->directory =path.substr(0, path.find_last_of('/'));
    this->processNode(scene->mRootNode, scene);
}
void Model::processNode(aiNode* node, const aiScene* scene)
{
    // 添加当前节点中的所有Mesh
    //qDebug() << "mNumMeshes:"<mNumMeshes;
    for (GLuint i = 0; i < node->mNumMeshes; i++)
    {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        this->meshes.push_back(this->processMesh(mesh, scene));
    }
    // 递归处理该节点的子孙节点
    //qDebug() << "mNumChildren:" << node->mNumChildren;
    for (GLuint i = 0; i < node->mNumChildren; i++)
    {
        this->processNode(node->mChildren[i], scene);
    }
}
MyMesh* Model::processMesh(aiMesh* mesh, const aiScene* scene)
{
    vector vertices;
    vector indices;
    vector textures;
    //qDebug() << "mNumVertices:" << mesh->mNumVertices;
    for (GLuint i = 0; i < mesh->mNumVertices; i++)
    {
        Vertex vertex;
        // 处理顶点坐标、法线和纹理坐标
        vertex.Position.setX(mesh->mVertices[i].x);
        vertex.Position.setY(mesh->mVertices[i].y);
        vertex.Position.setZ(mesh->mVertices[i].z);
        vertex.Normal.setX(mesh->mNormals[i].x);
        vertex.Normal.setY(mesh->mNormals[i].y);
        vertex.Normal.setZ(mesh->mNormals[i].z);
        if (mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates?
        {
            vertex.TexCoords.setX(mesh->mTextureCoords[0][i].x);
            vertex.TexCoords.setY(mesh->mTextureCoords[0][i].y);
        }
        else { vertex.TexCoords.setX(0.0);
        vertex.TexCoords.setY(0.0);}
        vertices.push_back(vertex);
    }

    // 处理顶点索引
    //qDebug() << "mNumFaces:" << mesh->mNumFaces;
    for (GLuint i = 0; i < mesh->mNumFaces; i++)
    {
        aiFace face = mesh->mFaces[i];
        
        for (GLuint j = 0; j < face.mNumIndices; j++)
        {
            indices.push_back(face.mIndices[j]);
        }
    }

     //处理材质
    if(mesh->mMaterialIndex >= 0)
    {
        aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
        vector diffuseMats = this->loadMaterialTextures(material,
            aiTextureType_DIFFUSE, "texture_diffuse");
        textures.insert(textures.end(), diffuseMats.begin(), diffuseMats.end());//把区间[start,end]插入到迭代器的指定位置
        vector specularMats = this->loadMaterialTextures(material,
            aiTextureType_SPECULAR, "texture_specular");
        textures.insert(textures.end(), specularMats.begin(), specularMats.end());
    }

    MyMesh* myMesh = new  MyMesh( vertices, indices, textures);
    myMesh->init(shaderProgram);
    return myMesh;
}

vector Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
{
    vector textures;
    if (mat->GetTextureCount(type) == 0) {//没有纹理我们也创建一个空的,避免纹理不更新,被上次的覆盖。
        cout<< typeName <<":no find texture"<GetTextureCount(type); i++)
    {
        aiString folderPath;
         mat->GetTexture(type, i, &folderPath);
        
        GLboolean skip = false;
        for (GLuint j = 0; j < textures.size(); j++)
        {
            if (textures[j].fileName == folderPath.C_Str())
            {
                textures.push_back(textures[j]);
                skip = true;
                break;
            }
        }
        if (!skip)
        {   // 如果纹理没有被加载过,加载之
            Texture texture;
            string filePath = this->directory;
            filePath += folderPath.C_Str();
            Image image;
            image.loadImage(filePath);
            texture.id = image.getTextureID();
            texture.type = typeName;
            texture.fileName = folderPath.C_Str();
            textures.push_back(texture);
        }
    }
    return textures;
}

几乎没有修改就是把qstring改成string了因为OpenGL更多是支持string好一些。还有就是QOpenglTexture改成了我们的图像解码类Image。
MyMesh.h

#pragma once
#include"vector"
#include "MyShader.h"
#include"Camera.h"
#include"iostream"
using namespace std;
struct Vertex
{
    QVector3D Position;
    QVector3D Normal;
    QVector2D TexCoords;
};
struct Texture {
    GLint id;
    string type;
    string fileName;
};
class MyMesh : protected QOpenGLFunctions_4_3_Core
{
public:
    vector vertices;//顶点属性向量
    vectorindices;//索引向量
    vectortextures;//纹理属性向量
    MyMesh(vector vertices, vector indices, vector texture);
    void init(QOpenGLShaderProgram* shaderProgram);
    void draw(Camera camera);
    void setLocation(float x,float y,float z);
private:
    float locationX=0.0f, locationY=0.0f, locationZ=-7.0f;
    QOpenGLShaderProgram* shaderProgram;
    QOpenGLVertexArrayObject vao;
    QOpenGLBuffer vbo,ebo;
    GLuint vPosition,normal,uv, model_loc,view_loc, cameraPos_loc;
};


MyMesh.cpp

#include "stdafx.h"
#include "MyMesh.h"
MyMesh::MyMesh(vector vertices, vector indices, vector textures): ebo(QOpenGLBuffer::IndexBuffer)
{
    this->vertices = vertices;
    this->indices = indices;
    this->textures = textures;
}
void MyMesh::init(QOpenGLShaderProgram* shaderProgram)
{
    initializeOpenGLFunctions();
    this->shaderProgram = shaderProgram;
    shaderProgram->bind();
    vao.create();
    vbo.create();
    ebo.create();
    vao.bind();
    vbo.bind();
    vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    vbo.allocate(&vertices[0], this->vertices.size() * sizeof(Vertex));
    // 设置顶点坐标指针
    vPosition = shaderProgram->attributeLocation("vPosition");
    shaderProgram->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, sizeof(Vertex));
    glEnableVertexAttribArray(vPosition);
    // 设置法线指针
    normal= shaderProgram->attributeLocation("normal");
    shaderProgram->setAttributeBuffer("normal", GL_FLOAT, offsetof(Vertex, Normal), 3, sizeof(Vertex));//shader变量索引,参数类型,偏移量,元素大小,步长
    glEnableVertexAttribArray(normal);
    // 设置顶点的纹理坐标
    uv = shaderProgram->attributeLocation("uv");
    shaderProgram->setAttributeBuffer(uv, GL_FLOAT, offsetof(Vertex, TexCoords), 2, sizeof(Vertex));
    glEnableVertexAttribArray(uv);
    ebo.bind();
    ebo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    ebo.allocate(&this->indices[0], this->indices.size() * sizeof(GLuint));
    vao.release();
    ebo.release();
    vbo.release();
    model_loc = shaderProgram->uniformLocation("model");
    view_loc = shaderProgram->uniformLocation("view");
    cameraPos_loc = shaderProgram->uniformLocation("cameraPos");
    shaderProgram->setUniformValue("material.shininess", 32.0f);
    shaderProgram->release();
    vertices.clear();
}
void MyMesh::draw(Camera camera) {
    shaderProgram->bind();
    for (GLuint i = 0; i < this->textures.size(); i++)
    {
        string name = this->textures[i].type;
        if (name == "texture_diffuse")
            name = "diffuse";
        else if (name == "texture_specular")
            name = "specular";
        string s = "material." + name;
        const char* uniformName = s.c_str();
        glActiveTexture(GL_TEXTURE0 + this->textures[i].id); // 在绑定纹理前需要激活适当的纹理单元
        glBindTexture(GL_TEXTURE_2D, this->textures[i].id);
        shaderProgram->setUniformValue(uniformName, this->textures[i].id);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    //构建视图矩阵
    QMatrix4x4 rotat;
    rotat.rotate(90.0,QVector3D(-1.0,0.0,0.0));
    QMatrix4x4 m;
    m.translate(locationX, locationY, locationZ);
    m = m * rotat;
    shaderProgram->setUniformValue(model_loc, m);
    QMatrix4x4 v;
    v.lookAt(QVector3D(camera.location.x, camera.location.y, camera.location.z),
        QVector3D(camera.viewPoint.x, camera.viewPoint.y, camera.viewPoint.z),
        QVector3D(camera.worldY.x, camera.worldY.y, camera.worldY.z));
    shaderProgram->setUniformValue(view_loc, v);
    shaderProgram->setUniformValue(cameraPos_loc, QVector3D(camera.location.x, camera.location.y, camera.location.z));
    vao.bind();
    glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT,this->indices[0]);
    vao.release();
    shaderProgram->release();
}
void MyMesh::setLocation(float x, float y, float z) {
    locationX = x;
    locationY = y;
    locationZ = z;
}

主要就是增加了for循环去把这些贴图发送到结构体中

最后就是shader

shaderModel.fs

#version 430 
struct Material
{
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};
uniform Material material;
out vec4 color;
uniform vec3 cameraPos;
in vec2 texcoord;
in vec3 Normal;
in vec3 worldPos;
void main(void) 
{
float ambientFactor=0.2;
float diffuseFactor = 1.0f;
float specularFactor = 0.5f;
vec3 lightColor=vec3(1.0,1.0,1.0);
vec3 lightPos=vec3(0.0,0.0,5.0);
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 ambient=ambientFactor*lightColor*diffuseColor;
vec3 normol=normalize(Normal);
vec3 lightDir = normalize(lightPos - worldPos);
float diffuseStringth = max(dot(normol, lightDir), 0.0);
vec3 diffuse =diffuseFactor* diffuseStringth *lightColor*diffuseColor;
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 viewDir = normalize(cameraPos - worldPos);
vec3 reflectDir = reflect(-lightDir, normol);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = specularFactor * specularStringth*lightColor*specularColor;
color= vec4(ambient + diffuse+specular,1.0);
}

效果图:


image.png

这里网上下的模型法线没有镜面反射材质,因此结合光照效果不太好。如果材质比较全效果会好很多。再加上我们还没学光源,我们前面一直用的是点光源。

目录

VSC++2019+QT+OpenGL
QT+OpenGL一之绘制立方体(三角形图元)
QT+OpenGL二之纹理贴图
QT+OpenGL三之矩阵简解
QT+OpenGL四之相机的移动和旋转
QT+OpenGL五之绘制不同的模型(vao,vbo机制)
QT+OpenGL六之天空盒
QT+OpenGL七之使用EBO
QT+OPenGL八之模型准备
QT+OPenGL九之模型解码
QT+OPenGL十之光照模型
QT+OPenGL十一之漫反射和镜面反射贴图
QT+OPenGL十二之定向光
QT+OPenGL十三之真正的点光源和聚光灯
QT+OPenGL十四之多光源混合的问题
QT+OPenGL十五之深度缓冲区
QT+OPenGL十六之模板缓冲区
QT+OPenGL十七帧缓冲区(离屏渲染)
QT+OPenGL十八抗锯齿
QT+OPenGL十九镜面反射效率调整
QT+OPenGL二十Gamma校正

你可能感兴趣的:(QT+OPenGL十一之漫反射和镜面反射贴图)