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!