Skip to content

Transparency improvements #2223

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

Closed
mtsr opened this issue May 20, 2021 · 13 comments
Closed

Transparency improvements #2223

mtsr opened this issue May 20, 2021 · 13 comments
Labels
A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible

Comments

@mtsr
Copy link
Contributor

mtsr commented May 20, 2021

What problem does this solve or what need does it fill?

Currently transparency is handled by splitting entities into two groups and rendering them in sorted order:

  • opaque entities (Visible::is_transparent == false)- front-to-back;
  • transparent (Visible::is_transparent == true) - back-to-front.

Unfortunately this is not enough to handle the general case. One of the simplest problem cases is when rendering a transparent sphere with another transparent inside. Since the sorting happens by entity transform, the object inside will be incorrectly rendered first if it's behind the origin of the sphere. And it will be incorrectly rendered last if it's in front of the origin of the sphere. To render this situation correctly the back of the sphere needs to be rendered first, then the object inside and then the front of the sphere. This cannot be done by sorting entities.

A reasonable introduction to the problem and evaluation of some solutions can be read in this presentation by nvidia. This slide captures the problem in a nutshell:

image

What solution would you like?

I think we need to evaluate the costs/benefits of a few different solutions and maybe even implement more than one of them.

Order Independent Transparency seems the most general solution, but the performance cost can be significant. It seems like Weighted Blended Order Independent Transparency has the best trade-off of the common implementations.

What alternative(s) have you considered?

There's a number of different possible solutions:

Additional context

This demo shows 7 different algorithms for OIT implemented using Vulkan.

This video discusses how FrostBite uses OIT for rendering hair.

@mtsr mtsr added C-Feature A new feature, making something new possible S-Needs-Triage This issue needs to be labelled labels May 20, 2021
@mockersf mockersf added A-Rendering Drawing game state to the screen and removed S-Needs-Triage This issue needs to be labelled labels May 20, 2021
@mtsr
Copy link
Contributor Author

mtsr commented May 20, 2021

After some discussion in #rendering it seems that WBOIT indeed makes the best trade-offs for general use of the current solutions. So I'd like to suggest that as the first one to implement. The 7 vulkan implementations includes a WBOIT implementation that can be used as a reference, see the fragment shader, for example: https://github.com/nvpro-samples/vk_order_independent_transparency/blob/master/oitWeighted.frag.glsl

@cart
Copy link
Member

cart commented May 20, 2021

I'm only familiar with this topic in that I know order independent transparency can be expensive and is an active area of research. Unless WBOIT is approximately a drop-in replacement for "traditional transparency" performance-wise, I think it should probably be opt-in / not default. If that is the case, we should pick an algorithm that can be selectively enabled for specific cases that need it.

But yeah, this is definitely something we need to do. Thanks for collecting material for it!

@CptPotato
Copy link
Contributor

CptPotato commented May 21, 2021

Weight blended OIT sounded like the most promising while discussing on discord. To me it stands out because it seems to be the easiest to implement and most robust (it's basically a weighted sum over all transparent surfaces - no sorting, no limits to how many transparent layers are possible, etc.). Performance wise I don't see it having much of an impact. The other techniques seem much more elaborate here as they actually perform sorting or some separation of layers. The main downside is that WBOIT is incorrect but it still produces a "plausible" looking result.

Unless I'm missing something, compared to traditional transparency it requires one additional fullscreen rendertarget where everything transparent renders to (additively) and one additional compositing pass to resolve the transparency buffer and blend it with the opaque buffer (post process).

I also thought about it in terms of playing nice with the rest of the rendering and didn't come up with any blockers. Even from the MSAA perspective it should work very well (the compositing step will also resolve MSAA in this case).

@mtsr
Copy link
Contributor Author

mtsr commented May 21, 2021

'Traditional' transparency already breaks down for the transparent sphere, though. So it quickly requires extra effort from the user to make it work. Let alone what happens with more complex meshes.

@cart
Copy link
Member

cart commented May 23, 2021

'Traditional' transparency already breaks down for the transparent sphere, though. So it quickly requires extra effort from the user to make it work. Let alone what happens with more complex meshes.

But if order-independent transparency is too expensive, some common cases like "drawing lots of grass on the ground" might be very slow by default (although "traditional transparency" also isn't ideal for that use case afaik). The only thing that I currently know performs well enough to be a default is "traditional transparency". But we should implement as many options as we can, compare them, then make a call. This is a case where giving people lots of options is a good idea anyway, so it wouldn't be wasted effort.

@aevyrie
Copy link
Member

aevyrie commented May 27, 2021

I think this bug in sponza might be caused by entity transform ordering:
image

The column is visible behind the vegetation, but the banner is not. It could be an outright sorting bug though, the banner doesn't have any transparency and should be visible.

This is how it renders in windows 3d viewer:
image

I can open an issue if this is unrelated.

@alice-i-cecile
Copy link
Member

Closely related to #64.

@superdump
Copy link
Contributor

Noting two things:

  • The bugs in Sponza are fixed. They used alpha mask materials that are opaque when the alpha is above a threshold and fully transparent below it.
  • A noted shortcoming of WBOIT is that it doesn’t work when alpha is close to opaque. Quoting from nvidia:

However, it can also diverge from the ground truth in scenarios where order strongly affects the result, such as when opacity is high.

@mtsr
Copy link
Contributor Author

mtsr commented Oct 23, 2023

This isn't relevant to me right now and it doesn't seem like it's something bevy needs right now either.

@mtsr mtsr closed this as completed Oct 23, 2023
@alice-i-cecile
Copy link
Member

Order-independent-transparency would still be a welcome addition. We can track that separately when that comes up though.

@superdump
Copy link
Contributor

I don’t think this should be closed. And I know @IceSentry has been working on OIT.

@superdump superdump reopened this Oct 23, 2023
@IceSentry
Copy link
Contributor

Yep, I'm planning on upstreaming my work during the 0.13 dev cycle

github-merge-queue bot pushed a commit that referenced this issue Apr 15, 2024
[Alpha to coverage] (A2C) replaces alpha blending with a
hardware-specific multisample coverage mask when multisample
antialiasing is in use. It's a simple form of [order-independent
transparency] that relies on MSAA. ["Anti-aliased Alpha Test: The
Esoteric Alpha To Coverage"] is a good summary of the motivation for and
best practices relating to A2C.

This commit implements alpha to coverage support as a new variant for
`AlphaMode`. You can supply `AlphaMode::AlphaToCoverage` as the
`alpha_mode` field in `StandardMaterial` to use it. When in use, the
standard material shader automatically applies the texture filtering
method from ["Anti-aliased Alpha Test: The Esoteric Alpha To Coverage"].
Objects with alpha-to-coverage materials are binned in the opaque pass,
as they're fully order-independent.

The `transparency_3d` example has been updated to feature an object with
alpha to coverage. Happily, the example was already using MSAA.

This is part of #2223, as far as I can tell.

[Alpha to coverage]: https://en.wikipedia.org/wiki/Alpha_to_coverage

[order-independent transparency]:
https://en.wikipedia.org/wiki/Order-independent_transparency

["Anti-aliased Alpha Test: The Esoteric Alpha To Coverage"]:
https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f

---

## Changelog

### Added

* The `AlphaMode` enum now supports `AlphaToCoverage`, to provide
limited order-independent transparency when multisample antialiasing is
in use.
@alice-i-cecile
Copy link
Member

OIT has been merged; closing as resolved :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible
Projects
None yet
Development

No branches or pull requests

8 participants