-
-
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
WebGLRenderer: Render transparent doublesided in two calls #21967
Conversation
This approach superior to setting It just results in two draw calls instead of a single one. Rendering twice should not be required in the opaque case. |
it should not, but unfortunately you have these https://github.com/mrdoob/three.js/issues?q=is%3Aissue+gl_FrontFacing |
Render calls doubles for double side materials. Wondering the performance impact... |
Yeah, there will be a performance impact in certain scenarios. However, the renderer will produce better results with transparent double-sided materials. It's some sort of trade-off. If we want to prioritize rendering quality over performance, this PR should be merged.
Maybe doing this for now? if ( material.side === DoubleSide && material.transparent === true ) { |
Yeah... I was pretty eager to remove all this complexity but I agree, it's better to only do this for transparent materials.
These were solved with the workaround in #21205.
Yep! |
Three.js is primary designed for web 3d. Low-end devices can access the Three.js application. For such devices, the application authors or users may want to prioritize the performance over the visual quality. Actually I as a Mozilla Hubs team member would like to choose the performance for low-end devices. I also know there are users who wants better quality. So ideally there is an option to choose which. (But if we do only for transparent double side materials, it might be acceptable?) Personally I like having the option, but I don't have the performance impact numbers and how much popular transparent + double side materials are now so I'm not really sure whether I should convince or not. |
@takahirox Yeah, I would be very curious to see a use case that gets affected by this 🤔 |
Any planar geometry using transparency that can be viewed from both sides, I think, will incur double draw calls with no possible benefit. I know I personally have many UI/text/image elements that fit that scenario, including every troika-three-text object using its default material (I could change that default but there are legit cases for seeing text from behind). It's a nice enhancement for those cases where it makes a difference but there are plenty where it doesn't, so being able to prevent the double draw is pretty necessary IMO. I'd vote for opt-in personally. |
No... I tried to solve in a different way and then I learnt that the issues was that some GPU drivers do not support |
Indeed. I can see that use case. Before adding more API surface would be good if we can investigate if the performance impact is worth it though.
Plenty... So far I'm only aware of 1 use case. |
Thank you @mrdoob, this is fantastic! We have been doing this manually in model-viewer for some time and this will let us simplify the code considerably. Regarding performance, we don't have clear numbers to show either, but no one has complained. I certainly haven't noticed a big problem; keep in mind that yes, it's an extra draw call, but it's no more fragments being shaded. When you're performance limited in PBR, it's usually because of too many fragments, as that's where the expensive shaders are. And as for quality; the artifacts are so glaring without this approach that I think it's well worth a little performance hit (the same logic we use in the rest of the PBR shaders). |
😆 Haha ok I'll give you that, if you consider all uses of planes a single use case. I still think there are "plenty" of scenarios in which you'd want to use planar geometries that way.
I'll attempt to get you some hard numbers for one of my scenes. It's in XR, where I'm already highly draw call limited, and any draw call is actually 2, so my gut says this should be measurable but that's worthless without data to prove it of course. |
with this change have some trouble on many Textures on a Planes DoubleSided and transparent (images) would be good if i can disable the double drawcall in this Scenario |
@arpu any chance you can share screenshots? |
not sure what screenshot you request @mrdoob workflow is user can upload images to vrland on server side they are packed in a glb with a plane doubeSide and transparent true screenshot from https://vrland.io/2xQTLN/cypher-chk |
I'm just curious to see what you mean with "trouble" here:
|
thats the same as lojjic issue above |
@mrdoob hope this makes it more clear the difference in drawcalls with no visual benefits |
http://prntscr.com/1ibwhup |
It's true that with the current Hence, it's a valid use case to render grass as transparent objects for better quality. |
@KiborgMaster You have 250 draw calls per frame? |
Yes on build 129 |
In general, for those who are looking for a solution, while the developers will decide how best to make it manageable, here is my solution, you can invent the property of the object yourself, or even comment out or remove the code, everyone will decide for himself how it is more convenient for him to do it. String 19259 i add object.userData.hasOwnProperty('twoCalls') |
That is, you just need to come up with some kind of property and, due to it, activate this processing option if someone needs it. |
Use instancing or something. |
Yeah, the new implementation for rendering doublesided transparent objects makes it even more important to use concepts like instanced rendering or batching to improve performance. |
You have not modified your code correctly. The official build looks like so: three.js/src/renderers/WebGLRenderer.js Lines 1355 to 1371 in efd6466
|
And yes, exactly, I accidentally duplicated data, my mistake. |
@KiborgMaster funny how you had to switch to solid grey floor texture to demonstrate aliasing issue, where your original screenshos had this: but I agree that
for the sake of keeping everyone happy |
Ok so draw calls are exactly the same in the scene I was originally testing so I suppose this PR is not the culprit. Perhaps #22244 will resolve my issue. Edit* Yup, it was #22244 |
Sorry for the very long delay, I went through a job change and no longer had access to my XR scenes. Instead I've created this simplified test case: https://gyprf.csb.app/ The scene is 25 troika-three-text instances - that's a modest number you could easily reach when building a simple UI/dataviz. I tested it twice, only changing the The timings below are an average-ish measurement from Chrome devtools profiler for the Desktop: Ryzen 3700X, NVidia RTX 2070
Phone: Pixel 5
|
Is there a way to disable this behavior and let the user choose ? |
There is no such possibility yet, just make edits to the code on your own, I showed this in the messages above, I hope this will be done in the new version and double rendering will be involved only if there is a specific property specified for this that will activate it. |
wait, I misread 0.8 as 0.08 :( |
And here is a mini-video of a larger card, imagine here to enable double processing, even when recording a video, I have a slight freeze. |
@KiborgMaster p.s. just in case,
=larger map, (card and map are the same word in rus., everyone) |
ok, everyone, I just prayed to the God of Hacks, and - here is your workaround: https://jsfiddle.net/ny7uco8a/ → 3 calls (2 + 1 opaque) power to the people! 🙏 |
The I did a profiling on the my batch particle renderer too. It seems the doubling draw call counts is not the reason for performance issue. Because the material.version was set twice for each material per frame. Inside the WebGLRenderer, It calls getProgramCacheKey if the material.version is renewed. getProgramCacheKey takes 30% of my application scripting time. That's why it's so slow. |
I too am noticing significantly slow getProgramCacheKey in the latest version of three. Similar results of about 30% of js time spent on that function according to chrome profiler. Stuck on earlier build until resolved. There was PR made not too long ago that altered that function aiming to improve it's performance. I assume that made it worse but hard to say without additional testing. |
One problem here may now be that 13X was out in the wild long enough for people to start projects with it. If someone's project is rendering the torus from the OP reverting this may break it. Instead of forcing the user to do A or B, it may be better to let the user choose between A or B. Webgl has some hints like this eg I too would like to cast my vote for the previous behavior. Rather than hard coding this functionality, i think it may be possible to achieve it at an app level? |
I recently updated ThreeJS from r121 -> 139 and the FPS's dropped by half. Please consider making this feature optional.🙏 |
This makes using 👍 for opt-in |
I think we finally figured out a good solution for this: #25165
If anyone has suggestions for a better name, we're all ears! |
Description
Another benefit of being able to have multiple programs per material is that we can now render doublesided materials in two calls (back side first and then front side).
This produces less confusing renders.
Before:
After (not perfect, but way less confusing):
At the moment the user has to duplicate meshes to achieve this.
The transmission example does it:
https://threejs.org/examples/webgl_materials_physical_transmission.html
With this change we'd be able to simplify shaders a little bit too:
/cc @WestLangley @Mugen87 @elalish