godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.18k stars 21.2k forks source link

SpatialMaterial "shadow to opacity" multiplied incorrectly by ambient light color and energy #62257

Open Wolfe2x7 opened 2 years ago

Wolfe2x7 commented 2 years ago

Godot version

3.5 RC4

System information

Linux Mint 20.2; GTX 1660Ti; GLES3; Nvidia 470.103.01

Issue description

With the "Shadow to Opacity" SpatialMaterial flag set, the opacity is multiplied by the ambient light color and energy. Shadows are only opaque if illuminated by ambient light. In a scene with less/no ambient light, the shadowed areas are translucent or invisible.

All other forms of light subtract from the material's alpha, whether the light casts shadows or not. The docs recommend this setting for displaying shadows over invisible surfaces for augmented reality. However, working as it does, you can only fill those shadows by flooding the scene's shading with bright ambient light. That doesn't seem right...?

The behavior I expected goes about like this:

shader_type spatial;

void light() {
    DIFFUSE_LIGHT = vec3(0.0);
    ALPHA = 1.0 - ATTENUATION.z;
}

Steps to reproduce

  1. Cast a shadow onto a SpatialMaterial with the flag "Shadow to Opacity"
  2. Remove sky contribution to ambient lighting
  3. Set ambient light to black and/or no energy; shadow is not visible
  4. Set ambient light to white and energy = 1.0; shadow is fully visible, scene shading is washed out

Minimal reproduction project

shadow_opacity.zip

clayjohn commented 2 years ago

Cc @BastiaanOlij

lostminds commented 1 year ago

This issue, or a similar related one is present in 4.0.2.stable. But I think it's not just that it's incorrectly multiplied by the ambient light (even if that's also true). What I would expect from the tooltip is that the shadowed area would just be translated into alpha. And after that I could set alpha transparency for the material and use that alpha as the mask, and configure whatever blend mode I'd like.

Instead it seems the option "shadow to opacity" doesn't result in an alpha mask at all, and instead does some custom blending where the opacity is partially based on some shadow calculation (though as you can see not the rendered soft shadow) but also the albedo color of the material. So you can't have dark faked shadows using this option, since setting the material color to black also results in a fully transparent shadow.

https://github.com/godotengine/godot/assets/17763524/933fcdd0-fd15-47b6-8efc-9032c5f91346

lostminds commented 1 year ago

I looked into this a little more and found that you can now output ALPHA in the light() function, I'm guessing for the very purpose of making shadow catchers like this. I made a simple shadow catcher shader using just ATTENUATION to detect shadow (which works ok, even though it would be good to have access to just shadow without light falloff in the same variable) which results in something much closer to how I think the "shadow to opacity" setting is supposed to work?