Shadow Map -- 點陰影(全方位)

  

  昨晚終於把點陰影(深度CubeMap)程序調通了,思想不難,基本就是在上節定向光陰影基礎上稍做修改,可是CG程序不太方便Debug,須要輸出中間效果圖進行判斷,耽擱了一下子。函數

  過程以下:測試

  

  一、將深度渲染到CubeMap上ui

  爲了之後使用方便,在Texture文件中擴展功能,添加一個生成CubeMap的函數this

GLuint WKS::CubeMap::GenDepthCubeMap(GLuint width, GLuint height) {
    glGenTextures(1, &this->textureID);
    glBindTexture(GL_TEXTURE_CUBE_MAP, this->textureID);
    for (GLuint i = 0; i < 6; i++) {
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, width, 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 this->textureID;
}

  一樣爲了之後方便,在DefferredShading文件中擴展功能,添加附加深度CubeMap到幀緩衝的函數spa

       (注:暫時遇到一點奇怪的問題,同時添加顏色緩衝紋理和2D深度緩衝紋理(或深度緩衝對象RBO)是沒有問題的。可是若是是深度CubeMap,同時再添加顏色緩衝紋理就會報錯,如今沒找到解決辦法,不過作陰影渲染只須要深度CubeMap,額……)3d

void DeferredShading::setupDepthBufferByCubeMap(GLuint texId) {
    this->BindGBuffer();
    glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texId, 0);
    //檢查幀緩衝是否完整
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Depth CubeMap is not added to FrameBuffer correctly!" << std::endl;
    else std::cout << "Successful:: Depth CubeMap is added to FrameBuffer correctly" << std::endl;
    this->BindDefaultBuffer();
}

  既然渲染深度到CubeMap,那麼就須要6個方向的view矩陣,並傳入shader,故新建了個ShadowMap文件完成這些操做。code

//ShadowMap.h

#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "../BaseFile/Shader.h"

class ShadowMap
{
public:
    ShadowMap();
    ~ShadowMap();
    void updateLightSpaceMatrix(glm::vec3 lightPos);
    void transmitMat2Shader(Shader* shader);

private:
    void setup();

private:
    glm::mat4 shadowProj;
    std::vector<glm::mat4> shadowTransforms;
};

  

//ShadowMap.cpp

#include "./ShadowMap.h"


ShadowMap::ShadowMap()
{
    this->setup();
}

ShadowMap::~ShadowMap()
{
}

void ShadowMap::setup() {
    this->shadowProj = glm::perspective(glm::radians(90.0f), 800.0f / 800, 1.0f, 100.0f);
}

void ShadowMap::updateLightSpaceMatrix(glm:: vec3 lightPos) {
    this->shadowTransforms.clear();
    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)));
}

void ShadowMap::transmitMat2Shader(Shader* shader) {
    shader->use();
    for (int i = 0; i < 6; i++) {
        std::string name = "shadowMatrices["+std::to_string(i)+"]";
        shader->setMat4(name, this->shadowTransforms[i]);
    }
}

  如今來看看着色器咋樣的^_^orm

  咱們沒必要在 OpenGL 程序中控制 View ,渲染六遍,將六個方向的深度分別渲染到 CubeMap 的各個面上。幾何着色器能夠幫助完成這一點,幾何着色器中有一個內建變量 gl_Layer ,能夠控制當前渲染的圖元輸出到CubeMap哪個面。故咱們只須要將每一個圖元在幾何着色器中渲染六次,且每次指定輸出到一個面,固然得配合傳入的對應的 view * projection 矩陣,這樣就實現渲染六個方向的深度到CubeMap。對象

  • 頂點着色器:只需將頂點座標變換到世界座標(乘以 model matrix),輸出到幾何着色器。
#version 330 core
layout (location = 0) in vec3 position;

uniform mat4 model;

void main()
{
    gl_Position = model * vec4(position, 1.0);
}
  • 幾何着色器:須要傳入六個view*projection矩陣,經過 gl_Layer 內建變量生成每一個方向的深度圖。
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices=18) out;

uniform mat4 shadowMatrices[6];

out vec4 FragPos; // FragPos from GS (output per emitvertex)

void main()
{
    for(int face = 0; face < 6; ++face)
    {
        gl_Layer = face; // built-in variable that specifies to which face we render.
        for(int i = 0; i < 3; ++i) // for each triangle's vertices
        {
            FragPos = gl_in[i].gl_Position;
            gl_Position = shadowMatrices[face] * FragPos;
            EmitVertex();
        }    
        EndPrimitive();
    }
}
  • 片斷着色器:傳入光源位置、遠平面距離,計算 當前片元到光源的距離 / 遠平面距離,輸出到深度值。
#version 330 core
in vec4 FragPos;

uniform vec3 lightPos;
uniform float far_plane;

void main()
{
    // get distance between fragment and light source
    float lightDistance = length(FragPos.xyz - lightPos);

    // map to [0;1] range by dividing by far_plane
    lightDistance = lightDistance / far_plane;
    // write this as modified depth
    gl_FragDepth = lightDistance;
}

  這樣就完成了深度CubeMap生成。blog

  爲了測試深度CubeMap是否正確,能夠用天空盒的方式顯示生成的CubeMap。

  固然了也能夠在後續的渲染陰影中,用採樣CubeMap的深度值代替顏色值顯示深度,這樣還能夠稍做修改顯示實際的深度值(當前片元與光源的距離值),二者直接對比,能夠檢測第一步(First Pass)是否有問題。

  展現一下我實現的採樣CubeMap深度值顯示(左圖)與 實際深度值顯示(右圖)的對比:

     

  

  二、使用深度CubeMap渲染全方位陰影

  這一部分就很簡單了,很往常渲染基本同樣,只須要添加陰影判斷。

  在片斷着色器中傳入深度CubeMap:

uniform samplerCube shadowMap;

  計算當前的片元的深度,並判斷是否在陰影中:

float ShadowCalculation(vec3 fragPos)
{
    vec3 light2Frag=fragPos-spotLight.position;
    float closestDepth = texture(shadowMap, light2Frag).r;
    closestDepth*=far_plane;
    float currentDepth=length(light2Frag);
    float shadow = currentDepth-0.05f > closestDepth  ? 1.0 : 0.0;
    return shadow;
}

  使用這個陰影判斷值計算光照值:

color=vec4(result*(1.0f-shadow)+ambient,1.0f);

  效果圖:

    

  OK,至此完成了Shadow Map的總結啦 ^_^。

相關文章
相關標籤/搜索