美文网首页my-QT专栏
QT+OPenGL十二之定向光

QT+OPenGL十二之定向光

作者: c之气三段 | 来源:发表于2021-04-10 16:40 被阅读0次

    前面的章节我们使用了shader中的一个光源,我们给他定义了位置,因此可以计算出光线照射在每个点的方向:

    我使用了另外一个正常材质的模型:


    image.png

    我把光源设置在上方,可以发现飞龙上部分是光亮的而背面是黑暗的,这表面上没问题。但是和现实其实不相符,因为现实中这种点光源是会随着距离的增加而衰减。一个模型看不出问题,模型多了就会发现每个地方强度的是一样的。因此我们来讨论光源的问题。

    定向光:

    定向光顾名思义每一束光的方向应该是相同的,不会向点光源那样成放射状。


    image.png

    我们会认为他是太阳,因为太阳光在同一个城市(排除乌云影响)每个地方的强度几乎相同,且在天空中的强度和在地面的强度也几乎是相同的(如果排除大气层的影响),因为距离遥远,我们认为太阳在无穷远处,正因为距离遥远阳光照射在地球上,我们也认为每一束光大致是平行的。有了这个特点,我们把他抽象到定向光上。

    定向光的特点:

    因此定向光就有了这样的特点,它没有光源位置(无穷远,我们不关心他的位置),定向光处处方向相同,且不会衰减。
    于是我们以后就可以把光源向材质那样打包到结构体同,用统一(一致)变量来发送。

    struct Light
    {
        vec3 direction;//只需要方向就足够了
         vec3 ambientFactor;
        vec3 diffuseFactor;
        vec3 specularFactor;
        vec3 color;
    };
    ...
    void main()
    {
        vec3 lightDir = normalize(-light.direction);//我运算是光的方向的反方向,前面讲过原因了
        ...
    }
    

    这一次我会尽可能把代码弄详细一些,以后就是局部调整,我也不会在大面积粘贴了。
    shaderModel.fs

    #version 430 
    struct Material
    {
        sampler2D diffuse;
        sampler2D specular;
        float shininess;
    };
    struct Light
    {
        vec3 direct;
       float ambientFactor;
        float diffuseFactor;
        float specularFactor;
        vec3 color;
    };
    uniform Material material;
    uniform Light light;
    out vec4 color;
    uniform vec3 cameraPos;
    in vec2 texcoord;
    in vec3 Normal;
    in vec3 worldPos;
    void main(void) 
    {
    vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
    vec3 ambient=light.ambientFactor*light.color*diffuseColor;
    vec3 normol=normalize(Normal);
    vec3 lightDrict=normalize(light.direct);
    float diffuseStringth = max(dot(normol,-lightDrict), 0.0);//用光的反方向,依然与法线有关
    vec3 diffuse =light.diffuseFactor* diffuseStringth *light.color*diffuseColor;
    vec3 specularColor=vec3(texture(material.specular,texcoord));
    vec3 viewDir = normalize(cameraPos - worldPos);
    vec3 reflectDir = reflect(lightDrict, normol);
    float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specularFactor * specularStringth*light.color*specularColor;
    color= vec4(ambient + diffuse+specular,1.0);
    }
    

    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<Vertex> vertices;//顶点属性向量
        vector<GLuint>indices;//索引向量
        vector<Texture>textures;//纹理属性向量
        MyMesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> texture);
        void init(QOpenGLShaderProgram* shaderProgram);
        void draw(Camera camera);
        void setTranslate(float x,float y,float z);//我们让模型类中可以给每个mesh指定位置,缩放和旋转。
        void setScale(float x,float y,float z);
        void setRotate(float angle, 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;
        QMatrix4x4 scale, rotate, translate,model;
        QVector3D direct, color;//给光的统一变量发送的属性
        GLfloat  ambientFactor=0.1f, diffuseFactor=1.0f, specularFactor=0.5f, shininess=32.0f;
    };
    

    MyMesh.cpp这里修改的肯定多一些

    #include "stdafx.h"
    #include "MyMesh.h"
    MyMesh::MyMesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures): ebo(QOpenGLBuffer::IndexBuffer), direct(3.0,5.0,-2.0), color(1.0,1.0,1.0)
    {
        this->vertices = vertices;
        this->indices = indices;
        this->textures = textures;
        translate.translate(QVector3D(locationX, locationY, locationZ));
        rotate.rotate(0.0f, QVector3D(0.0,0.0,0.0));
        scale.scale(QVector3D(1.0,1.0,1.0));
    }
    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", shininess);
        shaderProgram->setUniformValue("light.direct", direct);
        shaderProgram->setUniformValue("light.color", color);
        shaderProgram->setUniformValue("light.ambientFactor", ambientFactor);
        shaderProgram->setUniformValue("light.diffuseFactor", diffuseFactor);
        shaderProgram->setUniformValue("light.specularFactor", specularFactor);
        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);
        }
        //构建视图矩阵
        model = translate * rotate* scale;
        shaderProgram->setUniformValue(model_loc, model);
        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, 0);
        vao.release();
        shaderProgram->release();
    }
    void MyMesh::setTranslate(float x, float y, float z) {
        translate.translate(QVector3D(x, y, z));
    }
    void MyMesh::setScale(float x, float y, float z) {
        scale.scale(QVector3D(x, y, z));
    }
    void MyMesh::setRotate(float angle, float x, float y, float z) {
        rotate.rotate(angle, QVector3D(x, y, z));
    }
    

    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) {//这连续3个函数暴露给外部以便于设置模型的位置,大小和旋转角度。
        for (GLuint i = 0; i < this->meshes.size(); i++)
        {
            this->meshes[i]->setTranslate(location.x(), location.y(), location.z());
        }
    }
    void Model::setModelRotate(float angle,QVector3D rotate) {
        for (GLuint i = 0; i < this->meshes.size(); i++)
        {
            this->meshes[i]->setRotate(angle,rotate.x(), rotate.y(), rotate.z());
        }
    }
    void Model::setModelScale(QVector3D scale) {
        for (GLuint i = 0; i < this->meshes.size(); i++)
        {
            this->meshes[i]->setScale(scale.x(), scale.y(), scale.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
        for (GLuint i = 0; i < node->mNumMeshes; i++)
        {
            aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
            this->meshes.push_back(this->processMesh(mesh, scene));
        }
        // 递归处理该节点的子孙节点
        for (GLuint i = 0; i < node->mNumChildren; i++)
        {
            this->processNode(node->mChildren[i], scene);
        }
    }
    MyMesh* Model::processMesh(aiMesh* mesh, const aiScene* scene)
    {
        vector<Vertex> vertices;
        vector<GLuint> indices;
        vector<Texture> 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);
        }
    
        // 处理顶点索引
        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<Texture> diffuseMats = this->loadMaterialTextures(material,
                aiTextureType_DIFFUSE, "texture_diffuse");
            textures.insert(textures.end(), diffuseMats.begin(), diffuseMats.end());//把区间[start,end]插入到迭代器的指定位置
            vector<Texture> 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<Texture> Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
    {
        vector<Texture> textures;
        if (mat->GetTextureCount(type) == 0) {//没有纹理我们也创建一个空的,避免纹理不更新,被上次的覆盖。
            cout<< typeName <<":no find texture"<<endl;
            Texture texture;
            texture.id = 0;
            texture.type = typeName;
            texture.fileName = "no";
            textures.push_back(texture);
            return textures;
        }
        for (GLuint i = 0; i < mat->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;
                string fileName = folderPath.C_Str();
                if (fileName[0] != '\\' || fileName[0] != '/') {//这里做了点改动,因为我法线下载的有的模型中存储的纹理是个路径,我们只需要他的名字就好了,然后和模型放在统一文件夹下。
                    if (fileName.find_last_of('/') != string::npos)
                        fileName = fileName.substr(fileName.find_last_of('/'));
                    if(fileName.find_last_of('\\') != string::npos)
                        fileName = fileName.substr(fileName.find_last_of('\\'));
                }
                //cout << typeName <<":"<< fileName <<endl;
                filePath += fileName;
                //cout << "texturePath:" << filePath << endl;
                Image image;
                image.loadImage(filePath);
                texture.id = image.getTextureID();
                texture.type = typeName;
                texture.fileName = fileName;
                textures.push_back(texture);
            }
        }
        return textures;
    }
    

    效果:


    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十二之定向光

        本文链接:https://www.haomeiwen.com/subject/hhrnkltx.html