Skip to content

Conversation

almarklein
Copy link
Member

@almarklein almarklein commented Feb 26, 2025

Alt to #989. See #1065

Intro

This drops our system of per-renderer blend modes, instead using per-object blending. We managed to keep the "weighted" and "dithered" blending modes. The multi-pass "ordered2" and "weighted_plus" are gone. We include examples that demonstrate how these can be mimicked.

The new system:

  • allows mixing objects with different blending in the same scene.
  • allows more control over blending, by controlling e.g. depth_write and other props.
  • is more performant.

But also:

  • the user may have to be more aware of opaque vs transparent fragments to avoid artifacts.

Context

You may ask why we have different blend modes in the first place. The short answer is that blending semi-transparent objects is really hard and there is not a single solution that does the trick. And then there are some more special types of blending, like additive.

In the previous system we kind of tried to offer a few blend modes that did their best to "fix" blending for the user. The new system accepts that transparency is non-trivial, relies on the user to think about the problem, and it provides the user with more tools to approach the problem.

Ordered2 blending

The previous default blending was "ordered2", which had the great feature that distinguishing between opaque and transparent fragments happens per-fragment instead of per object. It did this by rendering most objects twice,
discarding transparent fragments in the first pass, and opaque fragments in the second. This solves a range of problems, (but not everything).

The costs are quite high:

  • The code is more complex.
  • Objects are rendered twice, which is not good for performance.
  • The shader had a conditional discard, which prevents the GPU from doing early-z optimizations.

See #1003 for more details.

API changes

  • Removed WorldObject.render_mask.
  • Removed Renderer.blend_mode, use Material.blending instead.
  • Added Material.blending.
  • Added Material.transparent, default is None (auto).
  • Added Material.depth_write, default is None (auto).
  • Added `Material.depth_compare, default is "<".
  • Added Material.alpha_test.
  • Added Material.alpha_compare, default is "<".
  • Removed material.color_is_transparent for Line, Mesh, Points, Text. Use material.color.a < 1 instead.
  • Removed material.edge_color_is_transparent for Points. Use material.edge_color.a < 1 instead.
  • Removed material.outline_color_is_transparent for Text. Use material.outline_color.a < 1 instead.

Other changes

  • The renderer.sort_objects is True by default.
  • The renderer's sorting depends on opaque vs transparent objects, and the performance is improved.
  • Add support for 'multiply' and 'subtractive' blending.
  • More control over 'weighted' blending so it's use extends to other use-cases.
  • Background objects have a default render_order of -1e6, so they are usually drawn first.

@almarklein almarklein requested a review from Korijn as a code owner February 26, 2025 21:21
@almarklein almarklein marked this pull request as draft February 26, 2025 21:21
@almarklein
Copy link
Member Author

I was able to fit in the weighted blending (user sets `material.blending = "weighted"). Yes you can also mix e.g. dithered objects with weighted ones 🚀

@almarklein
Copy link
Member Author

I addressed the feedback so far.

@almarklein
Copy link
Member Author

Somewhat related, I recently found that ThreeJS also supports dithered blending! It does so via Material.alphaHash.

@almarklein
Copy link
Member Author

@Korijn I would like to move this forward.

I plan to do a few quick follow-ups on this PR because objects with mixed opaque/transparent fragments are prone to artifacts (they already were, but ordered2 avoided a subset of problems). In short, my plan is to add support for fxaa and turn off aa on lines/points by default. I have this working as a POC now and it looks great!

So what I would like to do, step by step:

  • Get this PR approved.
  • I'll draft a follow-up PR for post-effect passes, so it can receive review asap.
  • Once these two are merged I'll add fxaa. I think best is to make it optional but very easy to enable, so we enable it in all but the validation examples.
  • Then change the default values for aa on line/points.

Once that is done we have the same quality (in terms of jaggyness), with much better support for different kinds of blending, and less artifacts. 🚀

@Korijn
Copy link
Collaborator

Korijn commented Jun 12, 2025

Sorry for making you wait, I wasn't sure if you had looked into all the feedback yet. Nice work here, it's really a major improvement.

Do you think #989 and #974 are unblocked after this PR? Or not yet?

@almarklein
Copy link
Member Author

This implements most of #989. I think only the gltf loading should be added from it.

#974 should be relatively easy to fit in now. The way that the blender now sorts and categorizes objects was inspired by that pr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants