Question

I've successfully rendered my scene from my light's point of view onto a depth cubemap, but I don't quite understand how I can actually project it onto my scene.

Here's a short clip of the current situation: http://youtu.be/54WXDWxqmXw

I found an implementation example on how to do it over here:

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

It seemed fairly easy to understand, so I figured this would be a great way to start off with, but I'm having some difficulties with the matrices (As shown in the video above).

My Vertex Shader:

#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;
}

(I've only posted the relevant parts)

lightProjection is the same projection matrix that I've used to render the scene into the cubemap.

I'm not entirely sure about 'camViewInv', from the example I've linked above I came up with this:

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 being the camera's view matrix, and camPos the camera's worldspace position.

Everything else should be self-explanatory I believe.

I can't see anything wrong with the shaders, but I'm fairly certain the scene is rendered correctly to the cubemap (As shown in the video above). Maybe someone more versed than me can spot the issue.

// Update:

Some additional information about the creation / usage of the shadow cubemap:

Creating the cubemap texture:

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);
}

The light's matrices:

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))

Vertex Shader:

#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;
}
Was it helpful?

Solution

I would suggest doing this in world space, light positions are typically defined in world space and it will reduce the workload if you keep it that way. I removed a bunch of uniforms that you do not need if you do this in world space.

Compute lighting direction and depth in vtx. shader:

#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);
}

Modified Fragment Shader (sample cubemap using light dir, and test against depth):

#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;
}

I added two new uniforms:

  1. lightPos -- World space position of your light
  2. shadowZRange -- The values of your near and far plane when you built your shadow cube map, packed into a vec2

Let me know if you need me to explain anything or if this does not produce meaningful results.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top