Search code examples
openglglslshadowtexture-mapping

Sending 2D and 3D shadowmaps to shaders


I'm trying to implement shadow mapping for my simple engine and i've figured out I should combine Omnidirectional Shadow Mapping (cubemap for point lights) with 2d mapping (for directional and spot lights).

My uniform block looks like this:

#define MAX_LIGHTS 128
//...
struct Light
{
   //light data...
};
//...
layout (std140) uniform Lights
{
    int lightCount; //how many lights were passed into the shader (from 0 to MAX_LIGHTS)
    Light lights[MAX_LIGHTS];
};

I have two questions for you.

  1. Are sampler objects costly? Is the following code optimal for multiple lights?

    sampler2D shadowMaps2D[MAX_LIGHTS];
    samplerCube shadowCubemaps[MAX_LIGHTS];
    //...
    if (lights[index].type == POINT_LIGHT)
        CalculateShadow(shadowCubemaps[lights[index].shadowMapNr]);
    else
        CalculateShadow(shadowMaps2D[lights[index].shadowMapNr]);
    

    Only lightCount amount of the objects would be actually filled with a texture. We're stuck with a lot of undefined samplers and I think it can cause some problems.

  2. If I understand correctly, I mustn't declare sampler in uniform blocks. So am I really forced to cycle through all of my shaders and update samplers each time the shadow maps get updated? It's a waste of time!


Solution

  • Are sampler objects costly?

    That question is a bit misleading since the sampler data types in GLSL are only opaque handles which reference the texture units. What is costly is the actual sampling operation. Also, the number of texture units a particular shade stage is limited. The spec only guarantees 16. Since you can't reuse the same unit for different sampler types, this would limit your MAX_LIGHTS to just 8.

    However, one seldom needs arrays of samplers. Instead, you can use array textures, which will allow you to store all of your shadow maps (per texture type) in a single texture object, and you will need only one sampler for it.

    Having said all that I still think that your light count is completely unrealistic. Applying 128 shadow maps in real time won't work even on the fastest GPUs out there...

    If I understand correctly, I mustn't declare sampler in uniform blocks.

    Correct.

    So am I really forced to cycle through all of my shaders and update samplers each time the shadow maps get updated? It's a waste of time!

    No. The sampler uniforms only need to be updated if the index of the texture unit you want to sample from changes (which is ideally never). Not when a different texture is bound, and not when some texture contents change.