简体   繁体   中英

Shadow Mapping is not projected correctly

I'm having troubles with the implementation of shadow mapping in my OpenGL graphic engine.

At first stage, I render the shadow map into a frame buffer object (with a depth texture attached to it) from the light point of view:

Vector3f lightPosition = Vector3f(mainLight->getPosition().x, mainLight->getPosition().y, mainLight->getPosition().z);

shadowMapFBO->BindForWriting();

glUseProgramObjectARB(0);

glViewport(0, 0, screenWidth * SHADOW_Q, screenHeight * SHADOW_Q);

glClear(GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);

glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); //Avoid self shadowing

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-20, 20, -20, 20, 0.0f, +300.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(lightPosition.X, lightPosition.Y, lightPosition.Z, 0, 0, 0, 0, 1, 0);

drawSceneTree();

setTextureMatrix();

I draw all the scene by using the function drawSceneTree(), and I store the light matrix into the OpenGL TEXTURE7 using the function setTextureMatrix(), which content is this:

static double modelView[16];
static double projection[16];

const GLdouble bias[16] = {
    0.5, 0.0, 0.0, 0.0,
    0.0, 0.5, 0.0, 0.0,
    0.0, 0.0, 0.5, 0.0,
    0.5, 0.5, 0.5, 1.0
};

// Grab modelview and transformation matrices
glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
glGetDoublev(GL_PROJECTION_MATRIX, projection);

glActiveTextureARB(GL_TEXTURE7);
glMatrixMode(GL_TEXTURE);

glLoadIdentity();
glLoadMatrixd(bias);

// concatating all matrice into one.
glMultMatrixd(projection);
glMultMatrixd(modelView);


// Go back to normal matrix mode
glMatrixMode(GL_MODELVIEW);

Then I render the scene from the camera point of view, and using a shader for rendering the shadows:

glViewport(0, 0, screenWidth, screenHeight);

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

// Clear previous frame values
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_LIGHTING);

//Using the shadow shader
glUseProgram(shadowMapFBO->getShader());
glUniform1iARB(shadowMapFBO->getShadowMapUniform(), 7);
glUniform1iARB(shadowMapFBO->getTextureUniform(), 0);

shadowMapFBO->BindForReading(7);

setupMatrices(0, 4, -13, 0, 5, 0);

glEnable(GL_CULL_FACE);

glCullFace(GL_BACK);
drawSceneTree();

The setupMatrices() function set the projection and modelview matrix. I bind the shadowMapFBO into the OpenGL TEXTURE7, using the function BindForReading(7), which content is:

glActiveTexture(GL_TEXTURE0 + TextureUnit); 
glBindTexture(GL_TEXTURE_2D, depthTextureId);

Finally, the vertex and fragment shaders are these:

Vertex:

varying vec4 ShadowCoord;

varying vec3 normal;
varying vec3 vertex_to_light_vector;
varying vec2 texture_coordinate;

void main()
{
        ShadowCoord = gl_TextureMatrix[7] * gl_Vertex;

        gl_Position = ftransform();

        gl_FrontColor = gl_Color;

        normal = gl_NormalMatrix * gl_Normal;

        vec4 vertex_in_modelview_space = gl_ModelViewMatrix * gl_Vertex;

        vertex_to_light_vector = vec3(gl_LightSource[0].position -vertex_in_modelview_space);

        texture_coordinate = vec2(gl_MultiTexCoord0);
}

Fragment:

uniform sampler2D ShadowMap;
uniform sampler2D Texture;

varying vec4 ShadowCoord;
varying vec3 normal;
varying vec3 vertex_to_light_vector;
varying vec2 texture_coordinate;


void main()
{   
    const vec4 AmbientColor = vec4(0.7, 0.7, 0.7, 1.0);
    const vec4 DiffuseColor = vec4(0.5, 0.5, 0.5, 1.0);

    vec3 normalized_normal = normalize(normal);
    vec3 normalized_vertex_to_light_vector = normalize(vertex_to_light_vector);

    float DiffuseTerm = clamp(dot(normal, vertex_to_light_vector), 0.0, 1.0);


    vec4 shadowCoordinateWdivide = ShadowCoord / ShadowCoord.w ;

    // Used to lower moiré pattern and self-shadowing
    shadowCoordinateWdivide.z += 0.0005;

    float distanceFromLight = texture2D(ShadowMap,shadowCoordinateWdivide.st).z;


    float shadow = 1.0;
    if (ShadowCoord.w > 0.0)
        shadow = distanceFromLight < shadowCoordinateWdivide.z ? 0.5 : 1.0 ;


    gl_FragColor = texture2D(Texture, texture_coordinate) * shadow * (AmbientColor + DiffuseColor * DiffuseTerm);
    gl_FragColor.a = 1.0;

}

And I'm getting the shadow of the game's character projected in all objects of the scene. I've recorded it in a gif:

https://dl.dropboxusercontent.com/u/658766/captura.gif

PS: The shadow map is also rendered on screen for debug purposes, at the right upper corner.

Your shadow coordinate has to be processed by ModelWold matrix (without projection nor view matrices). In my shaders, I compute it with this:

 ShadowCoord = gl_TextureMatrix[7] *  inverse(modelView) * gl_ModelViewMatrix * gl_Vertex;

But you may pass it to your shader as an uniform. This is the transformation matrix applied to each individual objects in your scene after the ModelViewProjectionMatrix has been set to look at origin. (there is surely a smarter way for handling this, but I've not dig much) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM