PointLights with shadows in glsl

Hi. I’m recently trying to build upon the SimplePBR render pipeline and am trying to implement point lights with shadows. I’ve looked through many forums and even checked GitHub where someone had the same issue but is wasn’t resolved.

I’ve gotten up to a point where i get this error:

:display:gsg:glgsg(warning): Program undefined behavior warning: The current GL state uses a sampler (1) that has depth comparisons disabled, with a texture object (0) with a non-depth format, by a shader that samples it with a shadow sampler. This will result in undefined behavior.

the result of this is from this code:

uniform struct p3d_LightSourceParameters {
    vec4 position;
    vec4 diffuse;
    vec4 specular;
    vec3 attenuation;
    
    samplerCubeShadow shadowCubeMap;
    
    vec3 spotDirection;
    float spotExponent;
    float spotCosCutoff;

    sampler2DShadow shadowMap;
    mat4 shadowViewMatrix;
} p3d_LightSource[MAX_LIGHTS];


float point_shadow_caster_contrib(samplerCubeShadow shadowmapcube, vec4 shadowpos) {
    float shadow = texture(shadowmapcube, vec4(0.0));
    return shadow;
}

I’m not actually sure if it’s even possible to combine samplerCubeShadow shadowCubeMap; and sampler2DShadow shadowMap; in the same structure but it gave me no error when I did when I removed the “point_shadow_caster_contrib” function.

I’ve looked at this forum: https://discourse.panda3d.org/t/samplercubeshadow-doesnt-work-sampler-object-1-does-not-have-depth-compare-enabled/27846/6 but found no solutions that worked for me.

I’m guessing I probably am just using the “texture()” function wrong and am having cube map textures all backwards.

As described in the linked thread, Panda doesn’t enable shadow filtering on the cube map texture by default, but your shader is using a shadow sampler. Those two settings need to match.

Set this in Config.prc:

shadow-cube-map-filter true

Alternative is to change your shader to just use samplerCube and do the shadow comparison yourself.

This GitHub issue describes the problem with supporting both: p3d_LightSourceParameters shadowMap not universal. · Issue #707 · panda3d/panda3d · GitHub

I think the principles of the shader data generator are a bit clunky. It would be nice to have an analyzer that, in the context of shaders, would simply add data from the scene, and the user himself would fill in the structures he needed in the shader. For example, the analyzer would collect all the lighting nodes in a list.

I mean, this is how you can achieve the shader data generator and provide custom shaders with data. For example, you can use the cube map generation method directly in the geometric shader.

That was the GitHub issue I was referencing.

Using the new struct:

uniform struct p3d_LightSourceParameters {
    vec4 position;
    vec4 diffuse;
    vec4 specular;
    vec3 attenuation;

    //samplerCubeShadow shadowMapCube;

    vec3 spotDirection;
    float spotExponent;
    float spotCosCutoff;
    sampler2DShadow shadowMap;
    samplerCube shadowMapCube;

    mat4 shadowViewMatrix;

} p3d_LightSource[MAX_LIGHTS];

I now get this error

ERROR: :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): p3d_LightSource[].shadowMapCube should be float or vec :display:gsg:glgsg(error): GL_INVALID_OPERATION error generated. State(s) are invalid: program texture usage.

code for calculation was taken from the mentioned forums posts:

float NEAR = 1;
float FAR = 500;

float hardShadow(samplerCube tex, vec3 uv, float bias, float depth)
{
	float closestDepth = texture(tex, uv).r;

	float shadow = 0.0;
	if(depth - bias < closestDepth)
		shadow += 1.0;
	return shadow;
}

float point_shadow_caster_contrib(samplerCube shadowmapcube, vec4 shadowpos) {
    //vec3 light_space_coords = shadowpos.xyz / shadowpos.w;
    //float shadow = texture(shadowmapcube, vec4(light_space_coords, 1.0));
    vec4 coords;
	coords.xyz = shadowpos.xyz / shadowpos.w;
	float dist = max(abs(coords.x), max(abs(coords.y), abs(coords.z)));
	coords.w = (NEAR + FAR) / (FAR - NEAR) + ((-2 * FAR * NEAR) / (dist * (FAR - NEAR)));
	coords.w = coords.w * 0.5 + 0.5;
    float shadow = hardShadow( p3d_LightSource[0].shadowMapCube, coords.xyz, 1e-4, coords.w );
    //float shadow = texture(p3d_LightSource[0].shadowMapCube, coords);
    return shadow;
}

I think this error just means the ‘samplerCube shadowMapCube’ isn’t filled in by the shader generator because these two variables can’t coexist.

As you say, they cannot coexist unfortunately. You get that error because shadowMapCube is not a recognised name. It must be named shadowMap

The way around this is to pass the point light as a separate variable rather than via the setLight mechanism (which reads them into p3d_LightSource array). You can just pass the PointLight NodePath directly to a named shader input with setShaderInput, and use a different struct definition with cube shadow map to read in that shader input in the shader.