p3d_LightSource[0] is white when no lights are present?

Help me to understand what’s going on, please. Here’s the minimal code:

Python code:

from direct.showbase.ShowBase import ShowBase
from panda3d.core import *

base = ShowBase()

model = base.loader.loadModel("box")
model.setPos(0, 10, 0)
model.reparentTo(base.render)

shader = Shader.load(Shader.SL_GLSL, "example.vert.glsl", "example.frag.glsl")
model.setShader(shader)

base.run()

Vertex shader:

#version 150

uniform mat4 p3d_ModelViewProjectionMatrix;

in vec4 p3d_Vertex;

void main() {
  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
}

Fragment shader:

#version 150

#define NUM_LIGHTS 2

uniform struct p3d_LightSourceParameters {
  vec4 color;
}p3d_LightSource[NUM_LIGHTS];

out vec4 fragColor;

void main() {
  fragColor = p3d_LightSource[0].color;
}

Notice that:

  1. There are no lights in the scene
  2. I explicitly set color of the fragment to p3d_LightSource[0].color; in the fragment shader.

I would expect that since there are no lights in the scene, the resulting colour would be black. But it’s white.

  1. If I change fragColor to p3d_LightSource[1].color; the resulting color would be black, as expected.

  2. If I add a point light to the scene, and change its color to green, the resulting colour would be green, as expected.

p_light_node = PointLight("p_light")
p_light = base.render.attachNewNode(p_light_node)
p_light_node.setColor(LColor(0.0, 1.0, 0.0, 1.0))
p_light.setPos(0, 10, 0)
base.render.setLight(p_light)

So there’s always a default light present. Is it a bug, or a feature?

If this is a feature, what’s the purpose of it?

And how would one make an unlit scene, if there’s always an implicit light present in the shader?

This is a feature, and it’s here to match the OpenGL behaviour: if there’s no light, the expectation is that you want a scene without lighting, so it applies an all-white light. This way, applications that do not require lighting will still have their scene show up.

To create an unlit (black) scene, add an ambient light with color (0, 0, 0, 0).

Great, thanks for the explanation. I am not very familiar with OpenGL, my bad. I thought it was a bug.

P.S. Not a Panda-related question, but am I calculating light wrong?

For a basic diffuse lighting, I add ambient to diffuse and multiply by texture color:
fragColor = (ambient + diffuse) * texColor;

Even with ambient (0, 0, 0, 1) this will still produce some color other than black.

I would need to see the rest of your shader code. How are ambient and diffuse defined?

Sure. Here’s the fragment shader code:

#version 150

#define NUMBER_OF_LIGHTS 1

uniform sampler2D p3d_Texture0;
uniform mat3 p3d_NormalMatrix;
uniform struct {
  vec4 ambient;
} p3d_LightModel;
uniform struct p3d_LightSourceParameters {
  vec4 color;
  vec4 position;
} p3d_LightSource[NUMBER_OF_LIGHTS];

in vec2 texcoord;
in vec3 normal;
in vec4 vertexPos;

out vec4 fragColor;

void main() {
  vec4 texColor = texture(p3d_Texture0, texcoord);

  float ambientStrength = 1.0;
  vec4 ambient = p3d_LightModel.ambient * ambientStrength;
  
  // Lights in view space, only one light
  vec4 lightPos = p3d_LightSource[0].position;
  vec3 lightDir = normalize(lightPos.xyz - vertexPos.xyz * lightPos.w);
  
  vec3 norm = normalize(p3d_NormalMatrix * normal);
  float diffuseStrength = max(dot(norm, lightDir), 0.0);
  vec4 diffuse = p3d_LightSource[0].color * diffuseStrength;
  
  fragColor = (ambient + diffuse ) * texColor;
}

But I think it doesn’t matter here, because (ambient + diffuse) will always give (1, 1, 1, 1) if the default light is white and ambient is black. But maybe I am wrong.

I think in this case, you usually do diffuse * texColor + ambient.

But it won’t cancel out the default white light, too…

I think ambient is usually additive? If you don’t want it to be additive, you could multiply instead: ambient * diffuse * texColor

p3d_LightSource[0].diffuse is white even if there is a black ambient light? Hmm, I may have been wrong. I think it counts only non-ambient lights for this criteria. My apologies.

I wonder if this should be considered a bug.

Yes, it is - at least in GLSL shaders. I haven’t tried CG. The only way to remove it is to add a black point or directional light.