2
\$\begingroup\$

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);
    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    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?

UPDATE: I figured out that view matrices are superfluous in the case of program rendering the scene with shadows. One can read the adequate texel from the shadow cubemap by using one of those vectors: lightPosition - worldVertexPosition or worldVertexPosition - lightPosition (right now I don't know which is the correct one, because both gives a not complete shadowing result - the first one mirrors the shadow and doesn't display anything from the -Y light cubemap face while the second one displays the bottom face correctly, but it does right only that: >>>img here<<<)

shadowDepth = unpack(textureCube(uDepthCubemapSampler, vWorldVertex.xyz - uLightPosition));
or
shadowDepth = unpack(textureCube(uDepthCubemapSampler, uLightPosition - vWorldVertex.xyz));

UPDATE2: Finally, by trial and error, I managed to get this thing working. The problem was that the cubemap texture is mirrored, so I had to scale the regular projection matrix by (1, -1, 1) in order to un-flip it. Also, the right vector to read cubemap is vWorldVertex.xyz - uLightPosition. Now everything seems to be working nice!

\$\endgroup\$
1
\$\begingroup\$

Finally, by trial and error, I managed to get this thing working.

The problem was that the cubemap texture is mirrored, so I had to scale the regular projection matrix by (1, -1, 1) in order to un-flip it.

Also, the right vector to read the cubemap is vWorldVertex.xyz - uLightPosition.

Now everything seems to be working nice!

\$\endgroup\$
  • \$\begingroup\$ Do you have any demo online? :) \$\endgroup\$ – Maksims Mihejevs Jun 2 '14 at 15:51
  • \$\begingroup\$ Not yet, but if you are interested, here is my >code<, you can host it locally by yourself ;] \$\endgroup\$ – Winged Jun 2 '14 at 17:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.