TL/DR: How do you specify a (default) texture unit for unused samplers in a sampler array?
I have the following scene in my OpenGL 4.1 application:
As you can see, there are 2 lights. Each light has its own shadow map. So my fragment shader looks like this:
#define MAX_LIGHTS 16
uniform samplerCube uShadowMaps[MAX_LIGHTS];
// sample the shadow map of the light
float Shadow(int light) {
vec3 direction = Position - uLightPositions[light];
float sampledDepth = 1.0;
switch (light) {
case 0: sampledDepth = texture(uShadowMaps[0], direction).r; break;
case 1: sampledDepth = texture(uShadowMaps[1], direction).r; break;
}
// ... the rest of the calculations ...
}
This code works because there are exactly 2 lights and each binds it's own shadow map. The code every light runs looks somewhat like this:
// make the name of the uniform
std::stringstream shadowMapName;
shadowMapName << "uShadowMaps[" << index << "]";
// bind the shadow map texture and bind the uniform
glActiveTexture(GL_TEXTURE3 + index); // index is the light number, 3 because there are 3 other textures already bound
glBindTexture(GL_TEXTURE_CUBE_MAP, shadowMapTexture);
auto position = glGetUniformLocation(shaderProgram, shadowMapName.str().c_str());
glUniform1i(position, index + 3);
However, I want to have multiple lights in the scene eventually, so I want to write my shaders so they work for more lights. I added the next switch case in the shader like this:
// sample the shadow map for the light
float Shadow(int light) {
// ...
switch (light) {
case 0: sampledDepth = texture(uShadowMaps[0], direction).r; break;
case 1: sampledDepth = texture(uShadowMaps[1], direction).r; break;
case 2: sampledDepth = texture(uShadowMaps[2], direction).r; break;
}
// ...
}
This, unfortunately, results in a black window.
I assume this is because there's nothing bound to mShaderMaps[2]
. And sure enough, if I add another light to the scene, it renders again.
So my question is: How do you specify a (default) texture unit for unused samplers?
Note: why switch
? This does not work:
// sample the shadow map for the light
float Shadow(int light) {
// ...
sampledDepth = texture(uShadowMaps[light], direction).r;
// ...
}
Because light is not a dynamically uniform expression. See GLSL, Array of textures of differing size.
EDIT
The Shadow
function is called like this:
// calculate diffuse color for the fragment
for (auto i = 0; i < min(uLightCount, MAX_LIGHTS); ++i) {
// ... calculate diffuse for the light
diffuseColor += diffuse * Shadow(i);
}
// ... some more stuff ...
// calculate specular color for the fragment
for (auto i = 0; i < min(uLightCount, MAX_LIGHTS); ++i) {
// ... calculate specular for the light
specularColor += specular * Shadow(i);
}