Cube-Map Shadow Mapping

I’m slowly making progress with my game engine, but I can’t seem to get omni-directional shadow maps to work.

The shading for each point light is done in two passes:
It renders the depth component 6 times in each direction (for the cube that is). One framebuffer is used to draw to a cube map. Then it passes the scene again with the light’s calculation, sampling the cube-map texture.

Here’s what I understand & have so far,

To initialize the OpenGL cubemap, there’s 6 enums for 6 textures with one binding:

GL13.GL_TEXTURE_CUBE_MAP
GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X​
GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_X​
GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Y​
GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y​
GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Z​
GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z​

And the per-cubemap-face constants:

private final static int[] GL_CUBEMAP_ENUM = {
	GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
	GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
	GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
	GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
	GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
	GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
};

private final static Vector3f[] DIRECTIONS = new Vector3f[] {
	new Vector3f( 1.0f, 0.0f, 0.0f),
	new Vector3f(-1.0f, 0.0f, 0.0f),
	new Vector3f( 0.0f, 1.0f, 0.0f),
	new Vector3f( 0.0f,-1.0f, 0.0f),
	new Vector3f( 0.0f, 0.0f, 1.0f),
	new Vector3f( 0.0f, 0.0f,-1.0f),
};

private final static Vector3f[] DIRECTIONS_UP = new Vector3f[]{
	new Vector3f(0.0f,-1.0f, 0.0f),
	new Vector3f(0.0f,-1.0f, 0.0f),
	new Vector3f(0.0f, 0.0f,-1.0f),
	new Vector3f(0.0f, 0.0f, 1.0f),
	new Vector3f(0.0f,-1.0f, 0.0f),
	new Vector3f(0.0f,-1.0f, 0.0f),
};

And were I loop through the cubemap to initialize it:

depthCubeMap = new Texture(GL13.GL_TEXTURE_CUBE_MAP, WorldLightBatchObject.SHADOW_MAP_TMUNIT, GL11.GL_NEAREST, GL11.GL_REPEAT);
depthCubeMap.bind();
for(int i = 0; i < 6; i++){
	GL13.glActiveTexture(GL13.GL_TEXTURE0 + WorldLightBatchObject.SHADOW_MAP_TMUNIT);
	GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL14.GL_DEPTH_COMPONENT24, (int) (WorldGameContext.SHADOW_MAP_MUL * 1024), (int) (WorldGameContext.SHADOW_MAP_MUL * 1024), 0, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, (ByteBuffer) null);
}

Initializing the framebuffer & the perspecive matrix

depthFrame = new Frame(GL30.GL_FRAMEBUFFER);
depthFrame.bind();

depthProjection = Projection.createPerspective(90f, 1.f, 0.01f, 100f);

And to render to the depth-map:

GL11.glPushAttrib(GL11.GL_VIEWPORT_BIT | GL11.GL_ENABLE_BIT);
GL11.glViewport(0, 0, (int) (WorldGameContext.SHADOW_MAP_MUL * 1024), (int) (WorldGameContext.SHADOW_MAP_MUL * 1024));
	for(int i = 0; i < 6; i++){
		GL11.glColorMask(false, false, false, false);
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		
		GL11.glClearColor(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
		depthFrame.bind();
		GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL_CUBEMAP_ENUM[i], depthCubeMap.getID(), 0);
		GL11.glDrawBuffer(GL11.GL_NONE);
		GL11.glReadBuffer(GL11.GL_NONE);
		GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
		
		Matrix4f depthTransform = getLightMatrix(i);
		depthProgram.use();
		depthProgram.setUniformM4("u_tLight", false, depthTransform);
		depthProgram.setUniformf("u_lPosition", position.x, position.y, position.z);
		rootObject.render(null, depthProgram);
		GL11.glColorMask(true, true, true, true);
	}
GL11.glPopAttrib();
GL11.glClearDepth(1.0d);

And to find the light projection/view matrix per-face, (I’m certain this one’s right, worked with the spotlight…)

private Matrix4f getLightMatrix(int index){
	Vector3f direction = DIRECTIONS[index];		
	Vector3f up        = DIRECTIONS_UP[index];
	
	Matrix4f out = ViewTransformComponent.lookat(position, direction, up);
	out.mul(depthProjection);
	return out;
}

Here’s what I use in the light pass, to multiply with the light fragment
v_position is in world space (object matrix * vertex) and l_position is traditionally in world space

uniform samplerCube u_sDepthMap;

float shadowVisibility(vec3 l_position, vec3 v_position){
	vec3 l_direction = l_position - v_position;
	
	float depth  = length(l_direction);
	float sample = textureCube(u_sDepthMap, normalize(l_direction)).z;
	
	if(sample <= depth + 0.00005) return 1;
	else                          return 0;
};

And the fragment shader for the ‘depth’ pass, same world space calculation as before just with the light’s matrix.

#include "./asset/shader/global.fsh"

varying vec3 v_lPosition_ws;
varying vec3 v_vPosition_ws;

vec4 calcPixel()
{
	return vec4(length(v_vPosition_ws - v_lPosition_ws));
}

Vertex shader for the ‘depth’ pass

#include "./asset/shader/global.vsh"

uniform mat4 u_tLight;
uniform vec3 u_lPosition;

varying vec3 v_lPosition_ws;
varying vec3 v_vPosition_ws;

void main(void)
{
	v_lPosition_ws = u_lPosition;
	v_vPosition_ws = vec3(u_tObject * gl_Vertex);
	gl_Position = u_tLight * vec4(v_vPosition_ws, gl_Vertex.w);
}

Stack overflow doesn’t know what’s up, but here’s the output:

As you can see in the small circle is an error I noticed happening. But in the large circle the shadows not projecting. More code will be provided on request. Thanks in advance!

For shadow mapping you don’t need any pixel shader. You just render to hardware depth buffer and use that as depth texture. You do not want distance to camera but the unmodified depth.

But what about the cube maps? I still barely understand this, but doesn’t cubemaps sample using a direction? In another tutorial they said to use a fragment shader with the length of the light in world space to the position in world space. Do you know any good tutorials / books that talk about cubemap shadow mapping? Also, how would I render the hardware depth buffer?

Yes for point lights you need a cubemap. That’s how I would do it. There’s another method but I don;t remember what it’s called

[quote]There’s another method but I don;t remember what it’s called
[/quote]
You likely mean Dual Paraboloid Mapping and it being applied to shadow mapping?