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

Material: transparent=true has no effect on glTF materials (since r132) #22598

Closed
konradkissener opened this issue Sep 28, 2021 · 20 comments · Fixed by #23166
Closed

Material: transparent=true has no effect on glTF materials (since r132) #22598

konradkissener opened this issue Sep 28, 2021 · 20 comments · Fixed by #23166
Milestone

Comments

@konradkissener
Copy link

konradkissener commented Sep 28, 2021

With Three.js r132 I can no longer make my models transparent. I export Models in Blender (2.92.0) as .glb file. I then import via GLTFLoader and set the opacity in JavaScript. I am also unable to make the object transparent using the three.js editor (drag and drop .glb in the editor).

Steps to reproduce the behavior:

  1. Export "Blender cube" as .glb file
  2. Load the .glb via GLTFLoader
  3. Set opacity/transparency on the MeshStandardMaterial

(expect that model ist transparent)

Code

new GLTFLoader().load("cube.glb", (gltf) => {
    const cube = gltf.scene.getObjectByName("Cube");
    if (
      cube instanceof THREE.Mesh &&
      cube.material instanceof THREE.MeshStandardMaterial
    ) {
      scene.add(cube);
      cube.material.opacity = 0.5;
      cube.material.color.set("#0000ff");
      cube.material.transparent = true;
      cube.material.depthWrite = false;
      cube.material.side = THREE.FrontSide;
    }
  });

Expected behavior

With r131, the sample project looks like this:

r131

https://codesandbox.io/s/threejs-transparency-r131-rth6g

Screenshots

With r132, the sample project looks like this:

r132

https://codesandbox.io/s/threejs-transparency-r132-1hs0v

Platform:

  • Device: Desktop
  • OS: MacOS
  • Browser: Chrome
  • Three.js version: r132.2
@donmccurdy
Copy link
Collaborator

In the latest version you may also need to set material.format = THREE.RGBAFormat to enable transparency. I'm not sure whether that change will remain in future versions though, see #22428 (comment).

@konradkissener
Copy link
Author

Thank you @donmccurdy, your proposed solution solves the issue for the JavaScript case.

Should I close the issue, though? The documentation for Material and MeshStandardMaterial does not mention the .format attribute and the behaviour in the three.js editor is quite confusing, as also mentioned by @marcatec in #22428 (comment)

@WestLangley
Copy link
Collaborator

Should I close the issue, though?

No. This has to be fixed.

@donmccurdy
Copy link
Collaborator

In the short term maybe reverting material.format would be justified? It seems like it might be catching more people by surprise than it helped, unfortunately.

Of the alternatives I think that #22414 (comment) (or something like it) is currently my favorite, turning .transparent into a setter with multiple effects. Happy users can continue using it; users with more advanced needs can dive into the the lower-level settings instead.

@optimus007
Copy link

optimus007 commented Oct 2, 2021

to summarise

  • material.transparent : Boolean // master transparency control BUT _alphaTest will still work if this is false

  • material.opacity : Float 0-1 // Only works if material.format=THREE.RGBAFormat

  • material.alphaMap : greyscale opacity map // works if material.transparent=true & material.format is THREE.RGBAFormat NOTE: GLTF exporter does not export this channel & USDZ exporter does.

  • Alpha channel of material.map // ( transparent png image) will work if material.transparent=true & material.format is irrelevant here

  • material.format : THREE.format // Is THREE.RGBFormat for imported gltf files and is THREE.RGBAFormat when a new THREE.MeshStandardMaterial is created .

  • material._alphaTest : Float 0-1 // clips the transparent part before this threshold value. Side note: this was material.alphaTest before now its material._alphaTest but using matrial.alphaTest still works

is this correct ?

@donmccurdy
Copy link
Collaborator

donmccurdy commented Oct 3, 2021

I think _alphaTest is a private variable, it's better to use alphaTest (no change here).

Right now, GLTFLoader sets material.format to RGBFormat when loading a material that should (as defined in the glTF file) be fully opaque: no alpha test, no alpha blending, etc. To enable those effects after loading a glTF file, material.format must be changed to RGBAFormat. It doesn't seem like we're quite happy with this solution, so I would expect this to change.

I think of .transparent as existing only to enable alpha blending, e.g. semi-transparency that does not use physically-based methods like transmission. For all other forms of "not fully opaque" — like alpha hash/dither, transmission, and alpha test — .transparent should remain false.

@pailhead
Copy link
Contributor

pailhead commented Oct 5, 2021

In the short term maybe reverting material.format would be justified?

I find this phenomenon a bit odd. Would this basically invalidate a version(s) of three.js (at least from the perspective of this feature)? Would it be possible to label some releases as "broken" or something along those lines?

Ie. for all intents and purposes, if my project uses transparent objects, i might not be able to use 133, but based on the conversation here, 134 may work exactly like 132.

I ran into this somewhat when working with shader chunks, some files can be pretty long lived between versions, but then could also start changing in a few consecutive versions.

@MCArth
Copy link

MCArth commented Dec 1, 2021

I'm new to three.js - but dropping in to say that I recently experienced this and it was a nightmare to debug - I thought it was an issue with the model. This should definitely be reverted or otherwise fixed.

@WestLangley WestLangley added this to the r136 milestone Dec 1, 2021
@donmccurdy
Copy link
Collaborator

donmccurdy commented Dec 3, 2021

To recap briefly –

When a material is opaque (which is not the same as .transparent = false!) we want to ignore alpha in output_fragment. When a material is transparent via alpha blending (.transparent = true), we of course need to use alpha in output_fragment.

Unfortunately, we can't solely determine whether to write alpha based on state of .transparent. Additive alpha blending also requires alpha, but may use .transparent = false.

This brings us to a conflict — intuitively and historically, .transparent = true should enable classic alpha blend transparency. But we need to expose an additional option via material.format (perhaps this should be changed), that determines whether alpha is written.

From here, a few ideas:

(A) Replace .format = RGBFormat with .alphaWrite = false? Doesn't fix anything, but does seem like a clearer name.

(B) Ignore .format/.alphaWrite when .transparent=true. We need the option to toggle it for additive blending when .transparent=false but there's no need to choose when .transparent=true.

(C) Turn .transparent into a getter/setter with multiple effects. In the past we've considered making .depthWrite=false the default for transparent objects but couldn't decide on an API to do so — this could be one way.

class Material {

  set transparent ( transparent ) {
    this.alphaMode = AlphaBlend;
    this.alphaWrite = true;
    this.depthWrite = false;
  }

  get transparent () {
    return this.alphaMode === AlphaBlend && this.alphaWrite === true; 
  }

}

If any of these are acceptable, I'm glad to open a PR.

Related issues: #15483 #18631 #21744 #22196 #22414 #22428

@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 3, 2021

Replace .format = RGBFormat with .alphaWrite = false? Doesn't fix anything, but does seem like a clearer name.

Indeed! I like alphaWrite as a notation. Sounds like a good addition next to depthWrite.

@takahirox
Copy link
Collaborator

@donmccurdy

Thanks for recapping this! I wanted to join the discussion but it was hard to read through the long discussion. The recap help me a lot.

In the three suggestions I want to vote for .alphaWrite because it's clearest, simplest, and most straightforward to me. Others, ignoring some exposed properties in some conditions or having an effect to some exposed properties by setting a property, might be sometimes a bit confusing to users. .format = RGBFormat may be also confusing to users, they may think .format can take other formats like AlphaFormat, DepthFormat or so on, but it may be not what we really want to do.

@donmccurdy
Copy link
Collaborator

I think (a) is not quite enough by itself — When a user loads an opaque glTF material, .format = RGBFormat (or .alphaWrite = false) will be set by default. If the user modifies that material to set .transparent = true, something should happen. That something could be (b) or (c) above, or just logging a warning like THREE.Material: .transparent=true will have no effect when combined with .alphaWrite=false..

Longer discussion in #18631 (comment).

@takahirox
Copy link
Collaborator

If the user modifies that material to set .transparent = true, something should happen.

OK, that makes sense to me.

or just logging a warning like THREE.Material: .transparent=true will have no effect when combined with .alphaWrite=false..

That's one of the good options to me.

And, I'm thinking of the option B. Although I said

ignoring some exposed properties in some conditions ... might be sometimes a bit confusing to users

I started to feel it may be ok. We already have some properties which have an effect only if some other properties have certain values, e.g. .blendSrc (and others) have an effect only if .blend is CustomBlending.

Is there any concerns of ignoring .alphaWrite when .transparent = true? Is there any potential use cases of .alphaWrite = false && .transparent = true?

@makc
Copy link
Contributor

makc commented Dec 13, 2021

@takahirox

any potential use cases of .alphaWrite = false && .transparent = true?

maybe to always draw one object after another

@donmccurdy
Copy link
Collaborator

any potential use cases of .alphaWrite = false && .transparent = true?

maybe to always draw one object after another

I'd rather we not create any expectation that .alphaWrite = false && .transparent = true will do something useful... The .transparent property is doing a lot already. If "2nd opaque render pass" was a feature we needed, this doesn't seem like the API we'd choose.

@takahirox
Copy link
Collaborator

I'd rather we not create any expectation that .alphaWrite = false && .transparent = true will do something useful...

OK, so the option B may be good to me. What do you folks think?

@mrdoob mrdoob modified the milestones: r136, r137 Dec 24, 2021
@bencresty
Copy link

bencresty commented Jan 5, 2022

My 2 cts:

Today I bumped into this issue. I was surprised to see that transparent = true together with setting the opacity didn't work on models imported from gltf/glb. While when hard setting the alpha channel of gl_FragColor when overriding output_fragment shaderChunk with onBeforeCompile transparency just worked as expected when transparent = true on the material.

IMO the way it is now is very confusing and counterintuitive. I would expect materials coming from GLTF to work the same way with transparency as materials created by code by ourselves in threejs and think it's a little overkill and confusing to need to set three (!) variables on a material, just to make it transparent.

@pailhead
Copy link
Contributor

pailhead commented Jan 5, 2022

@bencresty

From reading this it doesn’t sound like this interface is here to stay, it may only be present in a few versions of three, but not before or after those few versions. I imagine that without something like typescript, one of these three properties simply won’t do anything in some near future, it doesn’t sound like you would have to change code.

@donmccurdy donmccurdy changed the title MeshStandardMaterial cannot be made transparent when model was imported via GLTFLoader (since r132) Material: transparent=true has no effect on glTF materials (since r132) Jan 7, 2022
@donmccurdy
Copy link
Collaborator

@harriputterr
Copy link

In the latest version you may also need to set material.format = THREE.RGBAFormat to enable transparency. I'm not sure whether that change will remain in future versions though, see #22428 (comment).

Love you don! ahahah Thank you it worked for me :)

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 a pull request may close this issue.