Advertisement

Pre-Calculating Lightmaps using the depth buffer for shadowing

Started by May 22, 2023 12:43 PM
5 comments, last by danqua 1 year, 5 months ago

Hello everyone,

I am struggling with calculating the lightmap for my game. A level just consists of surfaces for walls/floors/ceilings (like in Wolfenstein3D).

Currently the pipeline looks as the following:

  1. render the scene into the depth buffer from the light source's point of view
  2. iterate over all surfaces (each surfaces consists of an 8x8 grid of lumels)
  3. for each lumel check the distance D to the light source
  4. find the point Q in the near plane of the light source
  5. transform Q into screen space and look inside the depth buffer at that position
  6. if depth value is less then D then lumel is in shadow

I guess that I am doing something wrong transforming Q into screen space.

// ...
float u = ((float)x + 0.5f) / lmWidth;
float v = ((float)y + 0.5f) / lmHeight;

glm::vec3 lumel = surface.vertices[0].position + u * uAxis + v * vAxis;
glm::vec3 lightLumelDir = glm::normalize(lumel - lightPosition);

// ==== EDIT: It isn't the near plane to get the actual point on the plane
// glm::vec3 planePositionWorld = lightPosition + near * lightLumelDir;

float dist = 0.0f;
glm::intersectRayPlane(lightPosition, lightLumelDir, lightPosition + near * lightNormal, nearPlane.normal, dist);
glm::vec3 planePositionWorld = lightPosition + dist * lightLumelDir;
// EDIT ====

glm::vec4 position = projection * view * glm::vec4(planePositionWorld, 1.0f);
glm::vec4 positionNDC = position / position.w;

float nx = positionNDC.x;
float ny = positionNDC.y;

if (nx < -1.0f || nx > 1.0f || ny < -1.0f || ny > 1.0f) continue;

// Screen space coordinates
float sx = ((nx + 1.0f) / 2.0f) * frameBufferWidth;
float sy = ((ny + 1.0f) / 2.0f) * frameBufferHeight;

float depth = 0.0f;
glReadPixels((int)sx, (int)sy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);

// Check distance between lumel and depth value
float lumelDistance = glm::distance(lumel, lightPosition);

if (depth < lumelDistance) continue;

// light intensity calculation
// ...

Of course, without the check in point 4 I get a nicely shaded lightmap ? - without shadows :(

Thanks,
danqua

I was thinking from the wrong “perspective”.

Got it working by projecting the lumel into the light sources view.

glm::vec4 position = projection * view * glm::vec4(lumel, 1.0f);
glm::vec4 ndc = position / position.w;

if (glm::abs(ndc.x) > 1.0f || glm::abs(ndc.y) > 1.0f || glm::abs(ndc.z) > 1.0f) continue;

int sx = (int)(((ndc.x + 1.0f) / 2.0f) * 1024.0f);
int sy = (int)(((ndc.y + 1.0f) / 2.0f) * 1024.0f);

float depth = LinearizeDepth(depthBuffer[sx + sy * 1024], near, far);

// Check distance between lumel and depth value
if (depth < position.z) continue;
float lumelDistance = glm::distance(lumel, lightPosition);

Advertisement

Calculating proper lightmaps requires path tracing or some other similar solution for physically-based indirect lighting. What you have is just calculating the direct light, which won't produce satisfactory results, especially with shadows. In the simplest sense, path tracing would just involve emitting random rays from light sources and bouncing them around the scene, adding up how much light intensity is incident on all surfaces in the light map.

Aressera said:

Calculating proper lightmaps requires path tracing or some other similar solution for physically-based indirect lighting. What you have is just calculating the direct light, which won't produce satisfactory results, especially with shadows. In the simplest sense, path tracing would just involve emitting random rays from light sources and bouncing them around the scene, adding up how much light intensity is incident on all surfaces in the light map.

You're absolutely right. I wanted to get there eventually (to some extent), but for now I tried to achieve good looking results and get things going like uv extension for texture filtering etc.

I read somewhere on this forum that someone uses this technique for pre-calculating lighting in a scene on level loading and I thought I give it a go. Unfortunately I am still facing some issues because of depth resolution and perspective inaccuracies I assume.

I actually did ray tracing (not path) in the beginning, but I had problems determine lumels that lie in shadow because of the extended uv space, which in my case had wall surfaces exceed the floor and ceiling surfaces resulting in ray intersections I didn't want.

But in the end I just want a simple lighting solution with shadows that doesn't require a lot of textures.

danqua said:
I read somewhere on this forum that someone uses this technique for pre-calculating lighting in a scene on level loading and I thought I give it a go. Unfortunately I am still facing some issues because of depth resolution and perspective inaccuracies I assume.

The problem you have is bias - I'm not sure whether you're using shadow maps or ray tracing - but:

  • In case of shadow maps when you're comparing depth in shadow map to actual depth from light, you are often subtracting/adding some bias value. This bias value is most likely the cause you see in your left red circles
  • In case of rays - how do you calculate origins fired towards light? If you push them towards the light “a little bit” - that's the bias - you will need to push them less

Apart from bias - this may also be related to the precision of your shadow map (if you're using one - try switching from R8 or R16 to R32F formats (full 32-bit floating-point to store depth)).

It can also be related to your ray-triangle (or ray-whatever) intersection in case you're doing ray tracing - there are often bias values introduced, or simply origin and then non-0 min t value - similar to near plane

Sorry - not having more ideas right now when looking at the image (it's past 2am here, I'll give it a proper read once I get some sleep).

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

@Vilem Otte Thanks for your suggestions. I use the same approach like calculating a shadow map. I use the depth buffer to check if certain lumels are visible from the light sources and if they are, I color them accordingly. For the depth buffer - I use the default depth buffer. If I'm not mistaken it has a bit-depth of 24, so this should be plenty I guess. And I also use a bias you have mentioned.

What really got me to a point where I am quite happy with the result is tweaking the near and far plane when rendering the depth. There are still some small light glitches here and there, but when combined with the actual textures it isn't that bad anymore. I made a small fly-through so you can see my results. For now I guess I move on and got my hands dirty on a small room/sector system and an editor.

This topic is closed to new replies.

Advertisement