Omni-directional light shadow mapping with cubemaps in WebGL

Posted by Winged on Game Development See other posts from Game Development or by Winged
Published on 2014-05-30T13:48:03Z Indexed on 2014/05/30 22:11 UTC
Read the original article Hit count: 244

Filed under:
|

First of all I must say, that I have read a lot of posts describing an usage of cubemaps, but I'm still confused about how to use them.

My goal is to achieve a simple omni-directional (point) light type shading in my WebGL application.

I know that there is a lot more techniques (like using Two-Hemispheres or Camera Space Shadow Mapping) which are way more efficient, but for an educational purpose cubemaps are my primary goal.

Till now, I have adapted a simple shadow mapping which works with spotlights (with one exception: I don't know how to cut off the glitchy part beyond the reach of a single shadow map texture): >>>glitchy shadow mapping<<<

So for now, this is how I understand the usage of cubemaps in shadow mapping:

  1. Setup a framebuffer (in case of cubemaps - 6 framebuffers; 6 instead of 1 because every usage of framebufferTexture2D slows down an execution which is nicely described >>> here <<<) and a texture cubemap. Also in WebGL depth components are not well supported, so I need to render it to RGBA first.

    this.texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);
    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    for (var face = 0; face < 6; face++)
        gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, gl.RGBA, this.size, this.size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
    
    this.framebuffer = [];
    for (face = 0; face < 6; face++) {
        this.framebuffer[face] = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer[face]);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, this.texture, 0);
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthbuffer);
    
        var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER); // Check for errors
        if (e !== gl.FRAMEBUFFER_COMPLETE) throw "Cubemap framebuffer object is incomplete: " + e.toString();
    }
    
  2. Setup the light and the camera (I'm not sure if should I store all of 6 view matrices and send them to shaders later, or is there a way to do it with just one view matrix).

  3. Render the scene 6 times from the light's position, each time in another direction (X, -X, Y, -Y, Z, -Z)

    for (var face = 0; face < 6; face++) {
        gl.bindFramebuffer(gl.FRAMEBUFFER, shadow.buffer.framebuffer[face]);
        gl.viewport(0, 0, shadow.buffer.size, shadow.buffer.size);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
        camera.lookAt( light.position.add( cubeMapDirections[face] ) );
        scene.draw(shadow.program);
    }
    
  4. In a second pass, calculate the projection a a current vertex using light's projection and view matrix. Now I don't know If should I calculate 6 of them, because of 6 faces of a cubemap. ScaleMatrix pushes the projected vertex into the 0.0 - 1.0 region.

    vDepthPosition = ScaleMatrix * uPMatrixFromLight * uVMatrixFromLight * vWorldVertex;
    
  5. In a fragment shader calculate the distance between the current vertex and the light position and check if it's deeper then the depth information read from earlier rendered shadow map. I know how to do it with a 2D Texture, but I have no idea how should I use cubemap texture here. I have read that texture lookups into cubemaps are performed by a normal vector instead of a UV coordinate. What vector should I use? Just a normalized vector pointing to the current vertex? For now, my code for this part looks like this (not working yet):

    float shadow = 1.0;
    vec3 depth = vDepthPosition.xyz / vDepthPosition.w;
    depth.z = length(vWorldVertex.xyz - uLightPosition) * linearDepthConstant;
    
    float shadowDepth = unpack(textureCube(uDepthMapSampler, vWorldVertex.xyz));
    if (depth.z > shadowDepth)
            shadow = 0.5;
    

Could you give me some hints or examples (preferably in WebGL code) how I should build it?

© Game Development or respective owner

Related posts about JavaScript

Related posts about webgl