-
-
Notifications
You must be signed in to change notification settings - Fork 35.5k
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
GLTFLoader: Use depthWrite=false for transparent materials #18235
GLTFLoader: Use depthWrite=false for transparent materials #18235
Conversation
depthWrite (true or false) should be used per models (mesh or material). |
@RemusMar if you're OK with my claim that a. Within the current API — with b. We could document the workarounds and limitations of transparent rendering somewhere, and include the recommendation that "transparent materials should generally set depthWrite=false" as best practice. c. We could apply that best practice to new transparent materials created by three.js on behalf of users, e.g. by loaders. I'd suggest a combination of (b) and (c) right now. I'm not opposed to making this same change on all loaders, and perhaps elsewhere. But I'd rather discuss the PR first, before editing dozens of other loaders. |
Don, I have always disliked forced settings via loaders. cheers |
To give you a better picture, check out this example:
|
In glTF, at least, there is a separate alpha mode ("MASK") for the Defaults are a way of giving users settings that work well in most cases. Users will continue to be able to change this setting, but fewer users will need to do so with good defaults. I'm trying to reduce surprises and inconsistency when bringing models in from outside software. That is the responsibility of a loader — to bring a model into three.js, matching the expectations of the author as closely as possible. I'm not opposed to changing the API rather than the loaders. But I've thought about it a bit, and I'm unable to come up with a better API. Mostly unrelated to this PR, but I am interested in introducing an alpha hash or alpha dither transparency mode to three.js, which would not have this depthWrite limitation (but has some other quirks). I understand your dislike for having different defaults in loaders than those of the underlying three.js library. However, I do believe that if we're going to advise users to set depthWrite=false when they create transparent materials (and we should), then the loaders should do that as well. |
I don't mean to impose with my opinion, but in the specific case of glTF there seems to be a general interpretation of the standard regarding transparent materials. Software like Blender since ~2.81 is exporting very close to that interpretation (thanks to the direct contribution from Khronos Group developers), and most software me and my team have tested seems to handle transparency on imported models the same way Blender is exporting them. When we, as developers, use a library or viewer, we generally want the default settings to be such that they import our models as closely as possible to the way we exported them without needing extra adjustments on a per-model basis. Currently however, three.js requires specific adjustments to correctly load models that we see as closely adhering to the generally accepted interpretation of the standard. While this patch may not be the ideal implementation it would seem to provide default with a closer representation of what we are exporting without requiring additional code. Just to reiterate, I'm specifically talking about importing glTF files, NOT about three.js defaults. |
The best approach is to modify your GLTF importer to fit your needs. |
Don't get me wrong! |
@donmccurdy what do you think about making this change in the engine directly? |
@mrdoob I think it would be a good default behavior for the engine, but I'm not sure how the API should be changed to allow that (while keeping the ability to change that default). Comparing the engines mentioned above:
The most straight-forward change would be to introduce a separate property, such as If it matters, I'm also mulling over the idea of proposing an enum AlphaMode {
ALPHA_BLEND,
ALPHA_BLEND_DEPTH_WRITE,
ALPHA_HASH,
ALPHA_DITHER
} ... but that's not great either, because really depth writes could be enabled or disabled regardless of blend mode. :/ |
Maybe 90% of the answer here is more documentation about transparency 😅 |
Again, the default value is TRUE and they don't force FALSE for transparent materials (because it's not always a winner). |
Again, please excuse my opinion (as someone who isn't an active contributor to the project) here. The way it should be is whatever is most adherent to the standard. With glTF specifically this hasn't been completely clearly defined until recently - and with that increasing clarity in the definition it seems most projects are moving toward a single interpretation (and I'm thinking @donmccurdy would agree with this assessment?). As for changing engine defaults... I don't know if that's a good idea if it ends up breaking a lot of existing projects which use three.js. three.js isn't built to be just a model viewer after all - as I see it importing models seems to be more secondary to it primarily being a web-based visualization engine. So if the changes can be isolated to the glTF importer that would likely be ideal. Or, if improvements to the engine are spurred by this then that would be fantastic. But I think the position @RemusMar is trying to convey is that if the engine is changed in some significant way just to import glTF then that's a bad thing - and I would generally agree with that. On the other hand, I completely expect wider glTF adoption in the near future and if three.js is the only engine that can't cleanly import the majority of models with defaults then that would be detrimental to adoption and would likely come with more "this works on X but not three.js" complaint spam [like mine 😅] on the issue tracker. |
@1147079942 this PR doesn't fix all possible issues with transparency, and no perfect solution in WebGL exists. If you'd like to open a thread on the forums (discourse.threejs.org/) and share enough to reproduce the issue (code, model, demo) I may be able to suggest workarounds for your particular scene.
@RemusMar nowhere in this thread has anyone suggested forcing transparent materials to use depthWrite=false. It is, and will remain, configurable. I am suggesting that we change a default so that it's right most of the time, not wrong most of the time. In either case, some users will need to change it. If transparent materials could default I do feel that if GLTFLoader (or other loaders, for that matter) are going to create transparent materials on behalf of users, they should use best practices for those materials, and |
Don, This topic is not about best practices. |
@RemusMar It is not just Blender, and in the specific case of glTF there has been a lot of discussion about how to handle transparency which has led to a fairly clearly defined implementation of the standard which most (and when I say most I mean every engine other than three.js that my team has tested) are implementing it something like what Don is presenting.
I'm fairly certain it's entirely about best practices specific to handling glTF. To quote Don, with emphasis:
In the case of glTF, as it stands now/as I understand it/as the standard is being generally interpreted and implemented, depthWrite=false is going to have a much higher chance of being correct than otherwise. |
For the last time: depthWrite=false is GAMBLING and not a solution. |
@RemusMar Sorry, false absolutely looks better to me. Both are broken - one is just less broken - and the one that looks less broken to me is depthWrite=false. Then again, perhaps "your model is not properly designed"... |
@RemusMar I've collected defaults and documentation from three other engines, and examples from five user-reported issues, in this thread. I find these to be compelling evidence that the proposed change is both best practice, and improves considerably more than 50% of cases. Transparency on what should be an opaque object (the hairtie) is not helping here: Not to say you or the artist are incorrect for doing this — choose a workflow that works for your application — but if you wanted this model to be portable and to look right in multiple engines, you would need If you import to BabylonJS or Blender, you'll again see
To put it another way, if you put a transparent object inside another transparent object you would expect to see both objects. This is more physically intuitive. There do exist cases where you wouldn't want that, and might choose |
@RemusMar If you really think it's a 50/50 gamble, despite multiple counterexamples, then you can at least rest easy that 50/50 means a net-neutral effect here. This conversation is not productive, and I'm not interested in spending time persuading you, personally, to agree with me or to use settings you do not want to use. Choose the settings you want for your own use. You've expressed your disapproval of the PR. I acknowledge that but disagree, and I don't think I have any more to say about the topic. |
Yes, the changes would be extensive, but maybe the basic API could be something like this: Define Then, in the renderer, depthBuffer.setMask( ( material.depthWrite === null ) ? ! material.isTransparent : material.depthWrite ); |
I was considering this too. We may want to try that at some point... But I think for now it's better to make sure we render GLTF as they're supposed to be rendered. |
@donmccurdy Sorry for the delay dealing with this one. Do you mind resolving the conflicts? |
0c56a58
to
21498d1
Compare
@mrdoob Done. ✅ |
Thanks! |
Um, it seems this change has broken webgl_lightshafts. It was necessary to fix it manually, see #18708 (comment). @donmccurdy How would you highlight this change in the migration guide? Is there something we can recommend users if they encounter similar issues like with the tree model from |
As you found since the model didn't render right initially, and already needed these overrides... child.material.transparent = false;
child.material.alphaTest = 0.5; ...this model needed to be using an Alpha Clip (.alphaTest>0) alpha mode, rather than Alpha Blending (.transparent=true). Importing to Blender, it looked broken in the same way. Seems to be an occasional issue in Sketchfab exports. The best fix is the same now as it was before; to use a masked alpha mode: We should highlight that this setting has changed in the migration guide for sure, but as far as specific recommendations I think we could probably benefit from having a whole page in the documentation about transparency... |
@donmccurdy thanks for you continuing support on all things WebXR - probably never going to be a one size fits all for these things - as long as there is a doc somewhere via a google search on what to try in each case then that's cool - it's called development for a reason right :P |
The default values are for the case if you just create a material by yourself without parameters. Loaders configure many material properties, not just |
Agreed with @Mugen87. However, a note suggesting to users that |
This is worst when you need put an animator with opacity on materials as glasses. Any suggestions? |
@vadermemo please use the forum (https://discourse.threejs.org/) for help. You'll need to share additional details about your question as well, I think. |
Hi @donmccurdy, thanks for the tip. Here is the big question => https://discourse.threejs.org/t/opacity-transparent-animation-big-problem/20149 |
Based in part on #17706 and donmccurdy/three-gltf-viewer#169, and more importantly by what other engines appear to be doing, I've come to the conclusion that we should set
depthWrite=false
for transparent materials in GLTFLoader.References
Examples
depthWrite=false
.BLEND
materials? #17706depthWrite=false
.depthWrite
disabled.depthWrite
disabled.For an illustration of where this makes something worse, here's the Glennfiddich Bottle from below. You see the stem of the bottle through the base of the bottle:
Exceptions
The "Glennfiddich Bottle" and "Curtains" examples above are (IMHO) not modeled appropriately for realtime rendering. The latter could be fixed without too much trouble, but I'm not sure how I'd fix the former. Enabled or disabled, depthWrite doesn't fix either.
The resources below describe a few situations where developers really may want to use
depthWrite=true
on transparent materials. Compared to the precedent set by other engines above, these exceptions seem minor enough to justify changing the default: