Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shaded Transparent Material Is Drawn On Top Of Unshaded Transparent Material With Two Directional Lights #47078

Closed
mortarroad opened this issue Mar 16, 2021 · 2 comments

Comments

@mortarroad
Copy link
Contributor

mortarroad commented Mar 16, 2021

Godot version:
v3.2.4-rc5

OS/device including version:
Windows 64
GLES 3
GeForce GTX 760

Issue description:
Transparent materials with shading enabled (no unshaded) are drawn using the additive blend mode, even if the blend mode is set to blend_mix.
This only occurs in the presence of more than one directional light. (With spot lights, this does not happen.)
This seems to be the way directional lights are implemented, i.e. they are rendered in multiple passes.

This wouldn't be so bad, but they are also drawn AFTER (i.e. on top of) other transparent materials, even if those are unshaded and have a higher priority.
I would expect the unshaded material (or any transparent material with higher priority) to be drawn afterwards, which would avoid this issue.

A bit of background:
This happened to me when I was using multiple particle systems.
One for clouds, which are affected by light, and
another particle system, with small mostly opaque particles, which should be drawn in front.

Steps to reproduce:
Create two objects, with a ShaderMaterial each.
Add ALPHA=1.0; to both, in order to make them transparent materials.
Add render_mode unshaded; to one of them.
Add two directional lights.

Minimal reproduction project:
draw_order_bug.zip

As you can see, the green plane is visible through the red planes.
If you disable one of the directional lights, the problem goes away.
Changing priority does not affect it.

@mortarroad
Copy link
Contributor Author

I suspect this piece of code is the issue:

if (state.directional_light_count == 0) {
directional_light = NULL;
_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, sky, false, true, false, false, use_shadows);
} else {
for (int i = 0; i < state.directional_light_count; i++) {
directional_light = directional_lights[i];
_setup_directional_light(i, p_cam_transform.affine_inverse(), use_shadows);
_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, sky, false, true, false, i > 0, use_shadows);
}

If there are multiple direct lights, multiple passes are drawn. The priorities are only considered within one pass, i.e. objects with a lower priority can be drawn over objects with higher priority in the next pass.
This is normally not an issue, since all objects are drawn with the mix_add blend mode (except in the first pass), so the order does not matter.
However, since unshaded objects are only drawn in the first pass, they will always end up behind.

As I see it, there are multiple ways to go about fixing this:

  1. Reorder the loops: Render the 1st object with all lights, then render the 2nd object with all lights, ...
    (Instead of rendering all objects with the first light, then rendering all objects with the second, ...)
    This will likely lead to a few more state changes, but that can be optimized by splitting the list into "chunks", in which the order does not matter. I.e. if there are no unshaded objects, it would result in only one chunk that can be processed as is.
  2. Implement a way to render directional lights in a single pass. This should be possible, if at most one of them uses shadows.
  3. Document this issue as a limitation, and recommend putting transparent objects on a layer with at most one directional light.

@akien-mga
Copy link
Member

Fixed by #47129.

@akien-mga akien-mga modified the milestones: 3.3, 3.4 Apr 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants