Notes on the light function
A post for collecting my thoughts on light shaders. I will add more to it in the future
Recieving shadows
As per the docs, the shadow value is multiplied into ATTENUATION
. This can be useful for achieving a mostly unshaded look while still supporting shadows
void light () {
DIFFUSE_LIGHT += ATTENUATION;
}
Ambient light
Ambient lighting from the Environment can be disabled via render_mode ambient_light_disabled;
or IRRADIANCE = vec4(0,0,0,1);
. You probably wanted that for stylization purposes, and now everything in shadow will be rendered black. To fix it you need to add some ambient light in it’s place. It would be wise to do this in fragment() instead of light()
void fragment() {
EMISSION = ALBEDO * vec3(0.1, 0.2, 0.3);
}
[DRAFT] Freeing up the roughness channel
In lack of stencil buffers or g-buffers. It’s wise to make the most of what you’ve got. If your game is toon/cel shaded, keep reading. You can probably get away with losing the roughness channel. Don’t worry, you can still make shiny objects happen with a custom shader!
Here’s how it works:
– Make all objects have a roughness value of 1.0
– On any opaque object in the scene, set ROUGHNESS
– Sample the normal_roughness_texture and extract the roughness component (w)
In the screen shader, you can use 1-x to invert the mask.
As is, render_mode depth_prepass_alpha must be disabled for this to work. See the workaround below if you want to enable depth_prepass_alpha as well.
[DRAFT] Supporting cast shadows when screen reading
The. It can help to know if the shadow pass is being rendered, shoutout to this article for that. I’ve discovered an efficient workaround which allows the shader to discard during the alpha pre-pass but not in the shadow map.
void fragment(){ ALPHA = 0.3; } void light(){ ALPHA=1.0; }
It’s pretty insane that this works. Haha
Operating on the final sum
Spatial shaders provide a lot of information about the light sources, but no way to get the final shading value. You may wish to create an effect based upon the final shading of the fragment, not just the individual contribution of each light. The general approach I’ve seen is to do such effects as a post-process, but that limits us to working in screen space. Luckily, it’s pretty easy to subvert the purpose of the output, and process the final shading value in addition to each light. In the example code, I treat DIFFUSE_LIGHT
like a spare variable, and only allow SPECULAR_LIGHT
to contribute to the final color.
Sources
Spatial light built-ins
The default light function source code
Shader code
shader_type spatial;
void light() {
// light we wish to accumulate
DIFFUSE_LIGHT += ALBEDO * ATTENUATION * LIGHT_COLOR * max(dot(LIGHT,NORMAL), 0.0);
// assign the result
SPECULAR_LIGHT = round(DIFFUSE_LIGHT*16.0 - 0.5)/16.0; // posterizes the final color
//SPECULAR_LIGHT = DIFFUSE_LIGHT; // do nothing to the result
// cancel all contribution of the diffuse_light variable
SPECULAR_LIGHT -= ALBEDO * DIFFUSE_LIGHT;
}
I’ve the same issue explained here. So I’ve modified Godot 4.1 to solve this.
spamrakuen/godot at 4.1MOD (github.com)
Future Shader Templetes will allow this without any MOD.
Here is a talk of Clay John were its explained
https://m.youtube.com/watch?v=MW3IFMvDTCY
This is immensely helpful, keep ’em coming 🙌