OpenGL基础54:点光源阴影

前置: OpenGL基础53:阴影映射(下)

一、万象阴影贴图

之前成功实现了平行光阴影,生成阴影贴图时使用的矩阵是正交矩阵,若是想要实现点光源的阴影效果,那么理论上只需要修改投影矩阵为透视矩阵就好了,看上去并不是很难

但是要得到点光源阴影贴图要考虑到的是:如何渲染出周围所有的场景,像摄像机它是有一定视角的,而点光源确是360°全面照射,因此对于点光源的阴影贴图一张肯定是不够的,它只能展示出一个方向

下图是90°透视投影的视觉范围,朝向为x轴正方向,那么只需要再对剩下的5个方向各来一次投影理论上就可以得到全方位的深度贴图了, 而这6张贴图正一一对应着立方体贴图的6个面,所以可以直接渲染到立方体贴图一步到位

第一次使用立方体贴图是在《OpenGL基础36:天空盒》,这应该是第二次

把立方体贴图附加到帧缓冲上并不难,只需要修改下之前的getAttachmentTexture()方法:

//获取帧缓冲立方体贴图
GLuint getAttachmentCubeTexture()
{
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
    for (GLuint i = 0; i < 6; ++i)
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    return textureID;
}

然后对于投影矩阵,这次需要六个,表示各个方向:

  • 注意下投影矩阵的远平面距离,后面需要用到这个值以手动计算当前片段的深度
  • 六个lookat矩阵计算得到的摄像机六个上向量必然也要相互垂直
void DrawPointLightShadowMap(Shader shader, int width, int height, int index)
{
    if (pointLight.size() < index + 1)
    {
        cout << "错误,无法找到对应的点光源数据" << endl;
        return;
    }
    shader.Use();
    glm::vec3 lightPos = pointLight[index].position;
    float farPlane = pointLight[index].farPlane;
    glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), (GLfloat)width / (GLfloat)height, 0.1f, farPlane);
    std::vector<glm::mat4> shadowTransforms;
    shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(1.0, 0.0, 0.0), glm::vec3(0.0, -1.0, 0.0)));
    shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0, 0.0, 0.0), glm::vec3(0.0, -1.0, 0.0)));
    shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 1.0)));
    shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, -1.0, 0.0), glm::vec3(0.0, 0.0, -1.0)));
    shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 0.0, 1.0), glm::vec3(0.0, -1.0, 0.0)));
    shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 0.0, -1.0), glm::vec3(0.0, -1.0, 0.0)));
    for (GLuint i = 0; i < 6; i++)
        glUniformMatrix4fv(glGetUniformLocation(shader.Program, ("shadowMat[" + std::to_string(i) + "]").c_str()), 1, GL_FALSE, glm::value_ptr(shadowTransforms[i]));
    glUniform1f(glGetUniformLocation(shader.Program, "farPlane"), farPlane);
    glUniform3fv(glGetUniformLocation(shader.Program, "lightPos"), 1, &lightPos[0]);
}

顶点着色器和几何着色器如下:

这次用到了几何着色器,是为了一次渲染到整个立方体贴图:在顶点着色器中将顶点变换到世界空间,接着几何着色器再负责将所有世界空间的顶点变换到6个不同的光空间

相当于就是片段着色器负责变一个魔术:给它1个世界空间坐标,它返还给你6个光空间坐标

  • 很多时候可以优化,选择放弃其中的几个方向,如果那个方向上没有物体的话

几何着色器片段变量 gl_Layer:指定发散出的基本图形送到立方体贴图的哪个面

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 5) in mat4 model;
void main()
{
    gl_Position = model * vec4(position, 1.0f);
}

/

#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 18) out;
uniform mat4 shadowMat[6];
out vec4 FragPos;
void main()
{
    for(int face = 0; face < 6; face++)
    {
        gl_Layer = face;
        for(int i = 0; i < 3; i++)
        {
            FragPos = gl_in[i].gl_Position;
            gl_Position = shadowMat[face] * FragPos;
            EmitVertex();
        }    
        EndPrimitive();
    }
}

在片段着色器中,需要手动计算深度

深度的计算公式就是片段与光源的距离 / 平头截体远平面距离

#version 330 core
in vec4 FragPos;
uniform vec3 lightPos;
uniform float farPlane;
void main()
{
    float lightDistance = length(FragPos.xyz - lightPos);
    lightDistance = lightDistance / farPlane;
    gl_FragDepth = lightDistance;
}

一样,如果得到这样的效果,说明这部分对了,因为是立方体贴图,所以可以360°环绕看下

二、点光源阴影

相对于之前的平行光阴影,有了万象阴影贴图后实现点光源阴影会更加简单,因为不再需要在顶点着色器中将顶点转入光空间了,要修改的只有片段着色器

ShadowCalculation方法是唯一需要修改的部分,判断当前的片段是否被遮挡,如果被遮挡,在下面计算对应光照时就不考虑当前光源对片段颜色的贡献,而对于点光源阴影,ShadowCalculation方法中的逻辑:

  1. 现获取当前片段与光源的距离和方向,两个属性都要用
  2. 方向用于获取立方体贴图的深度,这个深度值在[0, 1]范围内,要将其乘回远平面得到实际深度距离
  3. 长度用于和②中获取的深度距离进行比较,看看哪一个更接近,以此决定当前的片段是否在阴影当中
  4. 一样考虑误差以解决阴影失真的问题
  5. 进行PCF采样,让阴影更平滑
float ShadowCalculation(vec3 fragPos, PointLight light)
{
    vec3 sampleOffsetDirections[20] = vec3[]
    (
       vec3(1, 1, 1), vec3(1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), 
       vec3(1, 1, -1), vec3(1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
       vec3(1, 1, 0), vec3(1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0),
       vec3(1, 0, 1), vec3(-1, 0, 1), vec3(1, 0, -1), vec3(-1, 0, -1),
       vec3(0, 1, 1), vec3(0, -1, 1), vec3(0, -1, -1), vec3(0, 1, -1)
    );

    vec3 fragToLight = fragPos - light.position;
    float fragDepth = length(fragToLight);
    float texelSize = texelSize = (1.0 + (fragDepth / light.farPlane)) / 25.0;
    float bias = 0.0004;
    float shadow = 0;
    for(int i = 0; i < 20; i++)
    {
        float closestDepth = texture(shadowMap, fragToLight + sampleOffsetDirections[i] * texelSize).r;
        closestDepth *= light.farPlane;
        if(fragDepth - bias > closestDepth)
            shadow += 0.93f;
    }
    shadow /= 20;
    return shadow;
}

参考自:https://learnopengl.com/#!Advanced-Lighting/Shadows/Point-Shadows

如果没有问题的话就可以得到下面的效果

三、代码参考

完整的主代码:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include"Camera.h"
#include"Light.h"
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Mesh.h"
#include"Model.h"
#include<opengl/freeglut.h>
#include<SOIL.h>

bool keys[1024];
Camera camera;
GLfloat lastX, lastY;
bool firstMouse = true;
bool openSpotLight = true;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
GLuint loadCubemap(vector<const GLchar*> faces);
GLuint* getAttachmentTexture(GLuint num, GLboolean isDepth = false);
GLuint getAttachmentCubeTexture();
GLuint getMultiSampleTexture(GLuint samples);           //MSAA几重采样
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void cameraMove();
void renderSkyBox();
void renderQuad();
Light InitLight(Shader shader);
const GLuint WIDTH = 800, HEIGHT = 600;
const GLuint SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;

int main(void)
{
    //------------------------------------------------------初始化---------------------------------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glewExperimental = GL_TRUE;
    glewInit();

    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);

    Shader shaderObj("ObjVShader.vert", "ObjFShader.frag", "ObjGShader.geom");
    Shader shaderObjToDebug("ObjNormalVShader.vert", "ObjNormalFShader.frag", "ObjNormalGShader.geom");
    Shader shaderObjWithoutTex("ObjVShaderWithoutTex.vert", "ObjFShaderWithoutTex.frag");
    Shader shaderLight("LightVShader.vert", "LightFShader.frag");
    Shader shaderSkyBox("SkyboxVShader.vert", "SkyboxFShader.frag");
    Shader shaderScreen("ScreenVShader.vert", "ScreenFShader.frag");
    Shader shaderBlur("GaussVShader.vert", "GaussFShader.frag");
    Shader shaderDepth("DepthVShader.vert", "DepthFShader.frag", "DepthGShader.geom");

    //------------------------------------------------------数据定义---------------------------------------------------------
    glm::vec3 positions[] = 
    {
        glm::vec3(-1.0f, 0.0f, 4.0f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 0.89f, 0.0f),
        glm::vec3(0.0f, 1.78f, 0.0f),
        glm::vec3(-2.0f, 0.0f, 0.0f),
        glm::vec3(-2.0f, 0.89f, 0.0f),
        glm::vec3(-3.0f, 0.0f, 0.0f),
        glm::vec3(-2.0f, 0.0f, 1.0f),
    };
    glm::vec3 wallPositions[] =
    {
        glm::vec3(-1, 0.3, -0.802),
        glm::vec3(-5.07, 0.3, -0.8),
        glm::vec3(3.07, 0.3, -0.8),
    };

    //------------------------------------------------------UBO---------------------------------------------------------
    GLuint UBO;
    glGenBuffers(1, &UBO);
    glBindBuffer(GL_UNIFORM_BUFFER, UBO);
    glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    glBindBufferRange(GL_UNIFORM_BUFFER, 0, UBO, 0, 2 * sizeof(glm::mat4));

    //------------------------------------------------------帧缓冲---------------------------------------------------------
    GLuint FBO, RBO;
    glGenFramebuffers(1, &FBO);
    glBindFramebuffer(GL_FRAMEBUFFER, FBO);

    GLuint texNum = 2;
    GLuint* textureColorBuffer = getAttachmentTexture(texNum);
    glGenRenderbuffers(1, &RBO);
    glBindRenderbuffer(GL_RENDERBUFFER, RBO);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, WIDTH, HEIGHT);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    for (GLuint i = 0; i < texNum; i++)
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textureColorBuffer[i], 0);
    GLuint attachments[5] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4 };
    glDrawBuffers(texNum, attachments);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RBO);
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    GLuint gaussFBO[2];
    GLuint* gaussColorbuffers = getAttachmentTexture(2);
    glGenFramebuffers(2, gaussFBO);
    for (GLuint i = 0; i < 2; i++)
    {
        glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[i]);
        glBindTexture(GL_TEXTURE_2D, gaussColorbuffers[i]);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gaussColorbuffers[i], 0);
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
            cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    GLuint depthFBO;
    glGenFramebuffers(1, &depthFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, depthFBO);
    GLuint depthColorBuffer = getAttachmentCubeTexture();
    glBindTexture(GL_TEXTURE_CUBE_MAP, depthColorBuffer);
    glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthColorBuffer, 0);
    glDrawBuffer(GL_NONE);
    glReadBuffer(GL_NONE);          //不需要考虑颜色
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    //------------------------------------------------------设置状态、加载模型和天空盒---------------------------------------------------------
    glEnable(GL_CULL_FACE);
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);             //模板测试和深度测试都成功时,将对应像素的模板值设置为用glStencilFunc函数设置的ref值
    glEnable(GL_BLEND);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    Model wood("Object/wood/file.fbx", "Object/wood/file.fbx");
    Model ground("Object/ground/ground.fbx", "Object/ground");
    Model wall("Object/wall/stonewall.fbx", "Object/wall");
    Model lightObj("Object/light/file.fbx", "Object/light");

    vector<const GLchar*> faces;
    faces.push_back("Texture/Skybox/StarryNight1024/Right.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Left.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Up.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Down.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Back.jpg");
    faces.push_back("Texture/Skybox/StarryNight1024/Front.jpg");
    GLuint cubemapTexture = loadCubemap(faces);

    //------------------------------------------------------实例化---------------------------------------------------------
    glm::mat4* modelMatrices;
    modelMatrices = new glm::mat4[1000];
    glm::mat4 model = glm::mat4(1.0f);
    for (GLuint i = 0; i <= 7; i++)
    {
        model = glm::translate(glm::mat4(1.0f), positions[i]);
        model = glm::scale(model, glm::vec3(0.01f));
        modelMatrices[i] = model;
    }
    wood.UpdateModelMatrix(modelMatrices, 8);

    GLint groundIndex = 0;
    for (int i = -1; i <= 2; i++)
    {
        for (int j = 0; j <= 2; j++)
        {
            model = glm::translate(glm::mat4(1.0f), glm::vec3(i * 3.52f, -0.05f, j * 3.72f));
            model = glm::scale(model, glm::vec3(0.1f));
            model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
            modelMatrices[groundIndex++] = model;
        }
    }
    ground.UpdateModelMatrix(modelMatrices, groundIndex);

    for (GLuint i = 0; i <= 2; i++)
    {
        model = glm::translate(glm::mat4(1.0f), wallPositions[i]);
        model = glm::scale(model, glm::vec3(0.15, 0.3, 0.15));
        model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
        modelMatrices[i] = model;
    }
    wall.UpdateModelMatrix(modelMatrices, 3);

    model = glm::translate(glm::mat4(1.0f), glm::vec3(-4.0f, 2.3f, -0.25f));
    model = glm::scale(model, glm::vec3(0.015, 0.015, 0.015));
    model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
    modelMatrices[0] = model;
    lightObj.UpdateModelMatrix(modelMatrices, 1);
    delete[] modelMatrices;

    //------------------------------------------------------渲染ing---------------------------------------------------------
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
        cameraMove();
        shaderObj.Use();
        Light light = InitLight(shaderObj);

        //--------------------------------------------------绘制阴影贴图--------------------------------------------------
        glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
        glBindFramebuffer(GL_FRAMEBUFFER, depthFBO);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

        shaderDepth.Use();
        light.DrawPointLightShadowMap(shaderDepth, SHADOW_WIDTH, SHADOW_HEIGHT, 0);
        wall.Draw(shaderObj, 3);
        wood.Draw(shaderObj, 8);
        ground.Draw(shaderObj, groundIndex + 1);

        //--------------------------------------------------绘制场景-----------------------------------------------------
        glViewport(0, 0, WIDTH, HEIGHT);
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

        glm::mat4 view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        glBindBuffer(GL_UNIFORM_BUFFER, UBO);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(view));
        glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(projection));
        glBindBuffer(GL_UNIFORM_BUFFER, 0);
        
        shaderObj.Use();
        light.AppAllLightToShader(shaderObj.Program);
        glUniform3f(glGetUniformLocation(shaderObj.Program, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z);
        glActiveTexture(GL_TEXTURE7);
        glUniform1i(glGetUniformLocation(shaderObj.Program, "shadowMap"), 7);
        glBindTexture(GL_TEXTURE_CUBE_MAP, depthColorBuffer);
        wall.Draw(shaderObj, 3);
        wood.Draw(shaderObj, 8);
        ground.Draw(shaderObj, groundIndex + 1);
        lightObj.Draw(shaderObj, 1);

        //--------------------------------------------------天空盒-----------------------------------------------------
        shaderSkyBox.Use();
        view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
        glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
        glActiveTexture(GL_TEXTURE1);
        glUniform1i(glGetUniformLocation(shaderSkyBox.Program, "skybox"), 1);
        glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
        renderSkyBox();
        
        //--------------------------------------------------高斯模糊-----------------------------------------------------
        GLuint amount = 10;
        bool horizontal = true;
        bool first_iteration = true;
        shaderBlur.Use();
        for (GLuint i = 0; i < amount; i++)
        {
            glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[horizontal]);
            glUniform1i(glGetUniformLocation(shaderBlur.Program, "horizontal"), horizontal);
            glActiveTexture(GL_TEXTURE1);
            glUniform1i(glGetUniformLocation(shaderBlur.Program, "gaussTexture"), 1);
            glBindTexture(GL_TEXTURE_2D, first_iteration ? textureColorBuffer[1] : gaussColorbuffers[!horizontal]);
            renderQuad();
            horizontal = !horizontal;
            if (first_iteration)
                first_iteration = false;
        }
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        //--------------------------------------------------默认帧缓冲--------------------------------------------------
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        shaderScreen.Use();
        glActiveTexture(GL_TEXTURE1);
        glUniform1i(glGetUniformLocation(shaderScreen.Program, "screenTexture"), 1);
        glBindTexture(GL_TEXTURE_2D, textureColorBuffer[0]);
        glActiveTexture(GL_TEXTURE2);
        glUniform1i(glGetUniformLocation(shaderScreen.Program, "bloomTexture"), 2);
        glBindTexture(GL_TEXTURE_2D, gaussColorbuffers[!horizontal]);
        renderQuad();

        glfwSwapBuffers(window);
    }

    //------------------------------------------------------解绑---------------------------------------------------------
    glDeleteFramebuffers(1, &FBO);
    glfwTerminate();
    return 0;
}

//初始化光照数据并传入着色器
Light InitLight(Shader shader)
{
    glm::vec3 pointLightPositions[] =
    {
       glm::vec3(-4.0f, 2.6f, -0.25f),
       glm::vec3(1000000.0f, 0.8f, 2.0f),
       glm::vec3(1000000.0f, 0.8f, 1.0f),
    };

    Light light;
    light.AddSunLight(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3(0.05f, 0.05f, 0.05f), glm::vec3(0.06f, 0.06f, 0.06f));
    for (int i = 0; i <= 2; i++)
        light.AddPointLight(glm::vec3(pointLightPositions[i].x, pointLightPositions[i].y, pointLightPositions[i].z), glm::vec3(0.6, 0.6, 0.6), glm::vec3(0.45, 0.45, 0.45));
    if(openSpotLight)
    {
        light.AddSpotLight(glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z), glm::vec3(camera.Front.x, camera.Front.y, camera.Front.z),
            glm::vec3(0.6f, 0.6f, 0.6f), glm::vec3(0.8f, 0.8f, 0.8f));
    }
    return light;
}

//渲染全屏贴图
GLuint quadVAO = 0;
GLuint quadVBO;
void renderQuad()
{
    if (quadVAO == 0)
    {
        GLfloat quadVertices[] =
        {
            -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
            -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
             1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
             1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        };
        glGenVertexArrays(1, &quadVAO);
        glGenBuffers(1, &quadVBO);
        glBindVertexArray(quadVAO);
        glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
        glEnableVertexAttribArray(1);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }
    glBindVertexArray(quadVAO);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glBindVertexArray(0);
}

//渲染天空盒
GLuint skyboxVAO = 0;
GLuint skyboxVBO;
void renderSkyBox()
{
    GLfloat skyboxVertices[] =
    {
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

        -1.0f,  1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f,  1.0f
    };
    if (skyboxVAO == 0)
    {
        glGenVertexArrays(1, &skyboxVAO);
        glGenBuffers(1, &skyboxVBO);
        glBindVertexArray(skyboxVAO);
        glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
        glEnableVertexAttribArray(0);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }
    glDepthFunc(GL_LEQUAL);
    glBindVertexArray(skyboxVAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);
    glDepthFunc(GL_LESS);
}

//获取多采样纹理(用于抗锯齿,参数为采样数量)
GLuint getMultiSampleTexture(GLuint samples)
{
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, WIDTH, HEIGHT, GL_TRUE);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
    return texture;
}

//加载立方体贴图(天空盒)
GLuint loadCubemap(vector<const GLchar*> faces)
{
    GLuint textureID;
    glGenTextures(1, &textureID);

    int width, height;
    unsigned char* image;
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
    for (GLuint i = 0; i < faces.size(); i++)
    {
        image = SOIL_load_image(faces[i], &width, &height, 0, SOIL_LOAD_RGB);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
        SOIL_free_image_data(image);
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    return textureID;
}

//获取帧缓冲贴图(支持MRT,第一个参数为贴图数量,第二个参数表示是否为仅深度贴图)
GLuint* getAttachmentTexture(GLuint num, GLboolean isDepth)
{
    GLuint *textureID;
    textureID = new GLuint[num];
    glGenTextures(num, textureID);
    for (GLuint i = 0; i < num; i++)
    {
        glBindTexture(GL_TEXTURE_2D, textureID[i]);
        if (isDepth)
        {
            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); 
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
            GLfloat borderColor[] = { 1.0, 1.0, 1.0, 1.0 };
            glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
        }
        else
        {
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        }
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    return textureID;
}

//获取帧缓冲立方体贴图
GLuint getAttachmentCubeTexture()
{
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
    for (GLuint i = 0; i < 6; ++i)
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    return textureID;
}

//摄像机移动
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void cameraMove()
{
    GLfloat currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;

    GLfloat cameraSpeed = 1.0f * deltaTime;
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(Camera_Movement(FORWARD), deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(Camera_Movement(BACKWARD), deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(Camera_Movement(LEFT), deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(Camera_Movement(RIGHT), deltaTime);
}

//按键输入
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key == GLFW_KEY_TAB && action == GLFW_PRESS)
        openSpotLight = !openSpotLight;
    if (action == GLFW_PRESS)           //如果当前是按下操作
        keys[key] = true;
    else if (action == GLFW_RELEASE)            //松开键盘
        keys[key] = false;
}

//鼠标滚轮输入
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

//鼠标输入
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }
    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;
    lastX = xpos;
    lastY = ypos;
    
    GLfloat sensitivity = 0.05;
    xoffset *= sensitivity;
    yoffset *= sensitivity;
    
    camera.ProcessMouseMovement(xoffset, yoffset);
}

完整的片段着色器代码:

考虑了几乎所有的东西:物体材质、3种不同光源、5种光照贴图、泛光与阴影等

#version 330 core
struct Material
{
    sampler2D texture_diffuse1;      //贴图
    sampler2D texture_specular1;     //镜面贴图
    sampler2D texture_normal1;       //法线贴图
    sampler2D texture_emission1;     //放射光贴图
    sampler2D texture_height1;       //高度贴图
};
uniform samplerCube shadowMap;
struct SunLight             //平行光
{
    vec3 direction;
    vec3 diffuse;
    vec3 specular;
};
struct PointLight           //点光源
{
    vec3 position;
    vec3 diffuse;
    vec3 specular;
    float k0, k1, k2;
    float farPlane;
};
struct SpotLight            //聚光灯
{
    vec3 position;
    vec3 direction;
    vec3 diffuse;
    vec3 specular;
    float k0, k1, k2;
    float cutOff, outCutOff;
};
uniform Material material;
uniform SunLight sunLight;
uniform PointLight pointLights[3];
uniform SpotLight spotLights[3];
vec3 CalcSunLight(SunLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
float ShadowCalculation(vec3 fragPos, PointLight light);
bool CheckNormalTex();
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec4 lightColor;
uniform vec3 viewPos;
in vec2 texIn;
in vec3 fragPosIn;
in vec3 normalIn;
in mat3 TBN;
vec2 texCoords;
void main()
{
    texCoords = texIn;
    vec3 normal = normalize(normalIn);                    //法向量
    vec3 fragPos = fragPosIn;                             //当前片段坐标
    vec3 viewDir = normalize(viewPos - fragPos);          //观察方向
    if (gl_FragCoord.x <= 800)
    {
        const float minLayers = 8;
        const float maxLayers = 36;
        float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));        //样本数
        float layerDepth = 1.0 / numLayers;               //每层样本深度
        float currentLayerDepth = 0;                      //当前测试到的样本深度
        vec3 newViewDir = transpose(TBN) * viewDir;
        vec2 deltaTexCoords = newViewDir.xy * 0.06 / numLayers;           //单层样本对应的纹理偏移

        float lastDepth = 0;
        vec2 currentTexCoords = texCoords;
        float currentDepthMapValue = texture(material.texture_height1, currentTexCoords).r;
        while(currentLayerDepth < currentDepthMapValue)                   //在找到纹理深度小于等于测试深度的采样点的时候退出循环
        {
            lastDepth = currentDepthMapValue - currentLayerDepth;
            currentLayerDepth += layerDepth;                              //继续测试下一个偏移
            currentTexCoords -= deltaTexCoords;                           //确定正确的纹理采样点
            currentDepthMapValue = texture(material.texture_height1, currentTexCoords).r;       //获取纹理深度
        }
        texCoords = currentTexCoords;

        if (lastDepth != 0)                 //插值得到最终的纹理坐标
        {
            float currentDepth = currentLayerDepth - currentDepthMapValue;
            float weight = currentDepth / (currentDepth + lastDepth);
            vec2 finalTexCoords = (currentTexCoords + deltaTexCoords) * weight + currentTexCoords * (1 - weight);
            texCoords = finalTexCoords;
        }
    }
    if (CheckNormalTex() && gl_FragCoord.x <= 800)           //如果存在法线贴图
    {
        normal = texture(material.texture_normal1, texCoords).rgb;
        normal = normalize(normal * 2.0 - vec3(1.0));
        normal = normalize(TBN * normal);
    }

    vec3 result = 0.025 * vec3(texture(material.texture_diffuse1, texCoords));
    result = result + CalcSunLight(sunLight, normal, viewDir);
    for (int i = 0; i <= 0; i++)
    {
        float shadow = ShadowCalculation(fragPos, pointLights[i]);
        result = result + (1.0 - shadow) * CalcPointLight(pointLights[i], normal, fragPos, viewDir);
    }
    result = result + CalcSpotLight(spotLights[0], normal, fragPos, viewDir);
    result = result + vec3(texture(material.texture_emission1, texCoords));

    fragColor = vec4(result, 1.0f);
    float brightness = dot(fragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
    if(brightness > 2.0 || vec3(texture(material.texture_emission1, texCoords)) != vec3(0.0))
        lightColor = vec4(fragColor.rgb, 1.0);
}

float ShadowCalculation(vec3 fragPos, PointLight light)
{
    vec3 sampleOffsetDirections[20] = vec3[]
    (
       vec3(1, 1, 1), vec3(1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), 
       vec3(1, 1, -1), vec3(1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
       vec3(1, 1, 0), vec3(1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0),
       vec3(1, 0, 1), vec3(-1, 0, 1), vec3(1, 0, -1), vec3(-1, 0, -1),
       vec3(0, 1, 1), vec3(0, -1, 1), vec3(0, -1, -1), vec3(0, 1, -1)
    );

    vec3 fragToLight = fragPos - light.position;
    float fragDepth = length(fragToLight);
    float texelSize = (1.0 + (fragDepth / light.farPlane)) / 25.0;
    float bias = 0.0004;
    float shadow = 0;
    for(int i = 0; i < 20; i++)
    {
        float closestDepth = texture(shadowMap, fragToLight + sampleOffsetDirections[i] * texelSize).r;
        closestDepth *= light.farPlane;
        if(fragDepth - bias > closestDepth)
            shadow += 0.93f;
    }
    shadow /= 20;
    return shadow;
}

vec3 CalcSunLight(SunLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    float diff = max(dot(normal, lightDir), 0.0f);
    vec3 diffuse = light.diffuse * (diff * vec3(texture(material.texture_diffuse1, texCoords)));
 
    vec3 reflectDir = normalize(lightDir + viewDir);
    float spec = pow(max(dot(normal, reflectDir), 0.0), 16);
    vec3 specular = light.specular * (spec * vec3(texture(material.texture_specular1, texCoords)));
 
    return diffuse + specular;
}
 
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
//    vec3 fragToLight = fragPos - light.position;
//    float fragDepth = length(fragToLight) / farPlane;

    vec3 lightDir = normalize(light.position - fragPos);
    float diff = max(dot(normal, lightDir), 0.0f);
    vec3 diffuse = light.diffuse * (diff * vec3(texture(material.texture_diffuse1, texCoords)));
 
    vec3 reflectDir = normalize(lightDir + viewDir);
    float spec = pow(max(dot(normal, reflectDir), 0.0), 16);
    vec3 specular = light.specular * (spec * vec3(texture(material.texture_specular1, texCoords)));
 
    float dis = length(light.position - fragPos);
    float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * (dis * dis));
 
    diffuse *= attenuation;
    specular *= attenuation;
    return diffuse + specular;
}
 
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    float theta = dot(lightDir, normalize(-light.direction));
    float lightSoft = clamp((theta - light.outCutOff) / (light.cutOff - light.outCutOff), 0.0f, 1.0f);
 
    float diff = max(dot(normal, lightDir), 0.0f);
    vec3 diffuse = light.diffuse * (diff * vec3(texture(material.texture_diffuse1, texCoords)));
 
    vec3 reflectDir = normalize(lightDir + viewDir);
    float spec = pow(max(dot(normal, reflectDir), 0.0), 16);
    vec3 specular = light.specular * (spec * vec3(texture(material.texture_specular1, texCoords)));
 
    float dis = length(light.position - fragPos);
    float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * (dis * dis));
 
    diffuse *= attenuation * lightSoft;
    specular *= attenuation * lightSoft;
    return diffuse + specular;
}

bool CheckNormalTex()
{
    vec3 normalTex = texture(material.texture_normal1, texCoords).rgb;
    if (normalTex == vec3(0.0))
        return false;
    return true;
}

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/108455092