OpenGL - 将阴影立方体贴图投影到场景中

时间:2014-01-22 20:51:54

标签: opengl matrix shadow

我已经成功地将我的场景从我的光线视角渲染到深度立方体贴图上,但我不太明白我是如何将它实际投影到我的场景中的。

以下是当前情况的简短剪辑:http://youtu.be/54WXDWxqmXw

我在这里找到了一个如何实现它的实现示例:

http://www.opengl.org/discussion_boards/showthread.php/174093-GLSL-cube-shadows-projecting?p=1219162&viewfull=1#post1219162

这似乎很容易理解,所以我认为这将是一个很好的开始,但是我对矩阵有一些困难(如上面的视频所示)。

我的顶点着色器:

#version 330 core
layout(std140) uniform ViewProjection
{
    mat4 V;
    mat4 P;
};

layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 vertexUV;

out vec2 UV;
out vec4 posCs;

uniform mat4 M;
uniform mat4 lightView;

void main()
{
    mat4 MVP = P *V *M;
    gl_Position = MVP *vec4(vertexPosition,1);

    UV = vertexUV;
    posCs = V *M *vec4(vertexPosition,1);
}

Fragment Shader:

#version 330 core

in vec2 UV;
in vec4 posCs;

out vec4 color;

// Diffuse texture
uniform sampler2D renderTexture;

uniform samplerCubeShadow shadowCubeMap;

uniform mat4 lightView;
uniform mat4 lightProjection;
uniform mat4 camViewInv;

void main()
{
    color = texture2D(renderTexture,UV).rgba;

    mat4 lView = mat4(1); // The light is currently at the world origin, so we'll skip the transformation for now (The less potential error sources the better)
    vec4 posLs = lView *camViewInv *posCs;
    vec4 posAbs = abs(posLs);
    float fs_z = -max(posAbs.x,max(posAbs.y,posAbs.z));
    vec4 clip = lightProjection *vec4(0.0,0.0,fs_z,1.0);
    float depth = (clip.z /clip.w) *0.5 +0.5;
    vec4 r = shadowCube(shadowCubeMap,vec4(posLs.xyz,depth));
    color *= r;
}

(我只发布了相关部分)

lightProjection与我用于将场景渲染到立方体贴图中的投影矩阵相同。

我不完全确定'camViewInv',从我上面链接的例子中我得出了这个:

glm::mat4 camViewInv(
    camView[0][0],camView[1][0],camView[2][0],0.0f,
    camView[0][1],camView[1][1],camView[2][1],0.0f,
    camView[0][2],camView[1][2],camView[2][2],0.0f,
    camPos[0],camPos[1],camPos[2],1.0f
);

camView是摄像机的视图矩阵,并凸显摄像机的世界空间位置。

我相信其他一切都应该是不言自明的。

我看不到着色器有什么问题,但我相当确定场景是正确渲染到立方体贴图(如上面的视频所示)。也许比我更精通的人可以发现这个问题。

//更新:

有关阴影立方体贴图的创建/使用的一些其他信息:

创建立方体贴图纹理:

unsigned int frameBuffer;
glGenFramebuffers(1,&frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER,frameBuffer);

unsigned int texture;
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_CUBE_MAP,texture);

glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);
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_R,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_COMPARE_MODE,GL_COMPARE_R_TO_TEXTURE);

for(int i=0;i<6;i++)
{
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X +i,0,GL_DEPTH_COMPONENT,size,size,0,GL_DEPTH_COMPONENT,GL_FLOAT,0);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_CUBE_MAP_POSITIVE_X +i,texture,0);
    glDrawBuffer(GL_NONE);
}

光的矩阵:

glm::perspective<float>(90.f,1.f,2.f,m_distance); // Projection Matrix

// View Matrices
glm::vec3 pos = GetPosition(); // Light worldspace position
glm::lookAt(pos,pos +glm::vec3(1,0,0),glm::vec3(0,1,0));
glm::lookAt(pos,pos +glm::vec3(-1,0,0),glm::vec3(0,1,0));
glm::lookAt(pos,pos +glm::vec3(0,1,0),glm::vec3(0,0,-1))
glm::lookAt(pos,pos +glm::vec3(0,-1,0),glm::vec3(0,0,1))
glm::lookAt(pos,pos +glm::vec3(0,0,1),glm::vec3(0,1,0))
glm::lookAt(pos,pos +glm::vec3(0,0,-1),glm::vec3(0,1,0))

顶点着色器:

#version 330 core

layout(location = 0) in vec4 vertexPosition;

uniform mat4 shadowMVP;

void main()
{
    gl_Position = shadowMVP *vertexPosition;
}

Fragment Shader:

#version 330 core

layout(location = 0) out float fragmentDepth;

void main()
{
    fragmentdepth = gl_FragCoord.z;
}

1 个答案:

答案 0 :(得分:4)

我建议在世界空间中进行此操作,轻型位置通常在世界空间中定义,如果您保持这种状态,它将减少工作量。如果你在世界空间中这样做,我删除了一堆你不需要的制服。

在vtx中计算照明方向和深度。着色器:

#version 330 core
layout(std140) uniform ViewProjection
{
    mat4 V;
    mat4 P;
};

layout(location = 0) in vec4 vertexPosition; // W is automatically assigned 1, if missing.
layout(location = 1) in vec2 vertexUV;

out vec2 UV;
out vec4 lightDirDepth; // Direction = xyz, Depth = w

uniform mat4 M;

uniform vec3 lightPos;     // World Space Light Pos
uniform vec2 shadowZRange; // Near / Far clip plane distances for shadow's camera

float vecToDepth (vec3 Vec)
{
  vec3  AbsVec     = abs (Vec);
  float LocalZcomp = max (AbsVec.x, max (AbsVec.y, AbsVec.z));

  const float n = shadowZRange [0]; // Near plane when the shadow map was built
  const float f = shadowZRange [1]; // Far plane when the shadow map was built

  float NormZComp = (f+n) / (f-n) - (2.0*f*n)/(f-n)/LocalZcomp;
  return (NormZComp + 1.0) * 0.5;
}

void main()
{
    mat4 MVP = P *V *M;
    gl_Position = MVP *vertexPosition;

    UV    = vertexUV;

    vec3  lightDir   = lightPos - (M *vertexPosition).xyz;
    float lightDepth = vecToDepth (lightDir);

    lightDirDepth = vec4 (lightDir, lightDepth);
}

修改的片段着色器(使用光dir的样本立方体贴图,并测试深度):

#version 330 core

in vec2 UV;
in vec4 lightDirDepth;  // Direction = xyz, Depth = w

out vec4 color;

// Diffuse texture
uniform sampler2D         renderTexture;
uniform samplerCubeShadow shadowCubeMap;

void main()
{
    const float bias = 0.0001; // Prevent shadow acne

    color   = texture (renderTexture,UV).rgba;   
    float r = texture (shadowCubeMap, vec4 (lightDirDepth.xyz, lightDirDepth.w + bias));

    color *= r;
}

我添加了两件新制服:

  1. lightPos - 你光明的世界空间位置
  2. shadowZRange - 您构建阴影立方体贴图时近距离和远距离平面的值,打包到vec2
  3. 如果您需要我解释任何内容或者这是否会产生有意义的结果,请告诉我。