In GLTFLoader
we can load standard materials using StandardMaterial
.
However, the alpha channel of its texture doesn't be handled properly, when renderer's alpha is activated.
In OPAQUE
or MASK
material, alpha channels of texture should be negated:
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#alpha-coverage
Change the material's blend mode to ZERO-ONE (in GLTFLoader) might solve this problem?
This is how it should be (rendered in Unity):
Here's how it looks like in Three.js GLTFLoader:
This is the source code:
https://github.com/FMS-Cat/cube-gradient-alpha
Have you checked that modifying the material's blend mode fixes the problem? You can do that after the model is returned from the loader:
gltf.scene.traverse((o) => {
if (o.isMesh) {
o.material.blending = THREE.CustomBlending;
// ...
}
});
If this is a setting that GLTFLoader should be applying _only_ when the renderer's alpha property is enabled, a loader can't really know when to do that. :/
Wait uh I thought alpha is going to always be 1 regardless of map's alpha value under the ZERO-ONE blending settings... ._.
I just noticed the problem is harder one...
Priority is kinda low but we probably need to tweak our standard shader (or entire Three.js shaderchunks) to make it enable to negate all the alpha value
I'm afraid I don't understand the issue โ why do the texture's alpha values need to be negated? The link you gave to the Alpha Coverage section of the spec doesn't mention this.
In your sample source repository, which of the two models are you talking about?
Bleh I might have picked wrong words, negated -> ignored (I have a disability to my English)
You mean, we should treat the alpha as a "fourth component of the output" even if it's fully "OPAQUE
"? That sounds pretty weird for me...
I thought "rendered output is fully opaque" means output alpha is always 1.0
.
In your sample source repository, which of the two models are you talking about?
cube-gradient-alpha.glb
, has an alpha channel on its material.pbrMetallicRoughness.baseColorTexture
but its material.alphaMode
is OPAQUE
. (Yes I'm talking about weird case)
Moved the explanation project to Glitch.me
You mean that right now we should ignore the alpha component on png textures when GLTF material is OPAQUE
or MASK
?
Something like this?
#ifdef TRANSPARENT
gl_FragColor = vec4( outgoingLight, diffuseColor.a );
#else
gl_FragColor = vec4( outgoingLight, 1.0 );
#endif
This would require yet another shader permutation as we are currently not defining TRANSPARENT
.
Sounds correct. I'm afraid I have no idea at all whether that's a worthwhile change. ๐
A bit more detail can be found here:
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/AlphaBlendModeTest
I'm afraid I have no idea at all whether that's a worthwhile change.
same tbh, since it is kind of model side bad and can be workarounded by modifying models. Also if you don't use { alpha: true } on WebGLRenderer it is not an issue
I'm afraid I have no idea at all whether that's a worthwhile change.
What is the decision regarding honoring the glTF spec? Are we going to do that, or pick-and-choose which specifications to support?
Also if you don't use { alpha: true } on WebGLRenderer it is not an issue
I do not think { alpha: true }
is required to use the feature being discussed here.
What is the decision regarding honoring the glTF spec? Are we going to do that, or pick-and-choose which specifications to support?
Case by case basis I think. Depending of how painful it is ๐
I do not think
{ alpha: true }
is required to use the feature being discussed here.
Yeah, I'm surprised changing the renderer alpha changes anything in this case ๐ค
I think we could solve this by setting texture.format = THREE.RGBFormat
in GLTFLoader, when the material specifies a blendMode=OPAQUE
. If the texture is used by other materials with different blend modes, that would mean cloning it โ and at least one duplicate GPU upload. Probably better than a new shader permutation, though?
That sounds like an option.
still sounds workaroundy but what mrdoob said
Maybe material
could also have a format
property?
material.format = THREE.RGBFormat;
Why are computer graphics so complicated... ๐
I'm definitely interested in a solution here, as we've just run into this again as we try to enable <model-viewer>
to render transparently: https://github.com/GoogleWebComponents/model-viewer/pull/949
One problem with the RGBFormat approach is I think it'll only solve the OPAQUE
case, but not the MASK
case. The shader permutation solution by @mrdoob seems more general. Since the renderer's support of transparency is fixed for the life of the renderer, it seems that permutation will not cause any extra program cache misses.
One problem with the RGBFormat approach is I think it'll only solve the OPAQUE case, but not the MASK case.
Hm, good point. ๐
Might need to be a shader permutation then, yes.
In three.js, setting a cutoff for material.alphaTest
causes fragments having alpha less than the cutoff to be discarded; alphas greater than or equal to the cutoff remain _unchanged_.
It does not appear to me that this behavior is one of the options in the glTF spec...
@WestLangley By unchanged, do you mean the new fragment replaces the old? If so, I think that's exactly the meaning of glTF's MASK
. Or am I missing something?
@elalish No, it is not the same. In three.js, alphas greater than or equal to the cutoff remain unchanged, and the fragment is blended using normal alpha blending.
and the fragment is blended using normal alpha blending.
If transparent=true
, that's a desired threejs behavior. If transparent=false
, I think writing alpha to gl_FragColor is a side effect in threejs that we could change safely. glTF models never specify both alpha blending and alpha clip/test.
@WestLangley @donmccurdy Ah, thanks for the clarification. So would it be enough to just add diffuseColor.a = 1.0
after the discard? In addition to @mrdoob 's change above, of course.
Continuing the discussion from https://github.com/mrdoob/three.js/pull/18631 here:
We don't know of cases where users _need_ to use .transparent=false and .blending=AdditiveBlending both, but they certainly could, and we don't want to silently break those applications. I believe the most pragmatic fix would be to resubmit the previous changes in r115, with the addition of a warning when both of the options above are set:
THREE.WebGLRenderer: Setting .transparent=true is required when using non-opaque .blending modes.
Rationale in https://github.com/mrdoob/three.js/pull/18631#discussion_r385506836. If that change goes through and does not cause any complaints, then perhaps https://github.com/mrdoob/three.js/pull/14171 should be reverted too.
There is a more complex suggestion also in that thread, of replacing .transparent
with an .alphaMode
-like property. I think that idea shows some promise but I'm not necessarily volunteering to do all that in r115. ๐
var material = new THREE.MeshBasicMaterial( {
color: 0xff0000, // 0x00ff00 or 0x0000ff
blending: THREE.AdditiveBlending,
side: THREE.DoubleSide,
depthWrite: false
} );
There is nothing "transparent" about the above scene. The scene objects are completely opaque.
Why do you feel it is reasonable to require the users to set material.transparent
to true
in order for AdditiveBlending
to work?
I am taking .transparent
to mean, "not opaque." Occluded fragments affect the final result, and from that perspective I do not consider the objects in your example above to be opaque. The fact that depthWrite must be disabled, alone, is indicative.
That's certainly not the only meaning a user might reasonably associate with the word transparent: light passing through a glass-like material is probably a very common association, and certainly the example above has nothing to do with glass-like transparency.
However, I think the _effects_ of the .transparent
setting โ sorting differently and (if we can manage it) defaulting to .depthWrite=false
have more to do with the "not opaque" meaning than with physical transparency.
I do feel that .alphaMode
would provide a much clearer conceptual model for all of that, and flexibility for features that don't fit as well into this model. For example, "alpha hash" mode is arguably opaque, and should be sorted like opaque objects, but can certainly be used to mimic cheap physical transparency. Whether it's worth a breaking change, I'm not sure.
I do not consider the objects in your example above to be opaque
The objects in my example are completely opaque and the result is opaque.
I guess we will have to agree to disagree. :-)
The fact that depthWrite must be disabled, alone, is indicative.
depthWrite
does not have to be disabled in my particular example, but doing so can prevent artifacts if the camera moves.
//
AdditiveBlending
is commutative; NormalBlending
is not. So with AdditiveBlending
, the sort order does not matter -- unlike NormalBlending
, which must be rendered from back to front.
I am taking .transparent to mean, "not opaque." Occluded fragments affect the final result, and from that perspective I do not consider the objects in your example above to be opaque. The fact that depthWrite must be disabled, alone, is indicative.
Definitely supporting this argumentation ๐
I vote the reimplement #18631 and revert #14171 for R115
. I actually doubt somebody will complain if the mechanics from #14171 are removed.
I can live with disagreeing on the "transparent" definition. ๐ However, I do think the sorting that comes with .transparent=true
is still a property you want with other blending modes...
AdditiveBlending is commutative ... the sort order does not matter.
If that's the only blend operation anywhere in the scene, yes, but that's the exception rather than the rule. Consider this example:
You'd expect the additive material in the middle to add to the opaque object behind, and be occluded by the opaque object in front, correct? Opaque objects render front-to-back, which I'd expect to be a problem if the additive material uses .transparent=false
, but ... apparently it works either way? That surprised me when I tested it, I'm not sure what I'm missing there. ๐
All of that said, and even if I'm right, none of it will be obvious to a user who just wants to do some additive blending. I think the suggested warning helps with that, but I'd be open to other ideas! We can leave https://github.com/mrdoob/three.js/pull/14171 if you'd prefer. A warning would sort of obviate it, but I don't mind leaving that in place.
apparently it works either way? That surprised me when I tested it, I'm not sure what I'm missing there.
AdditiveBlending
works regardless of the render order as long as the depth buffer is not causing fragments to be discarded.
I'd like to make just one more point...
Please see https://github.com/mrdoob/three.js/pull/18631#discussion_r385513864.
In three.js, material.transparent = true
is just a hint to the renderer to render objects using that material in a later render queue -- and in reverse-sort order.
Following Unity's lead, we could replace material.transparent
with
material.renderQueue
having the following valid options:
THREE.BackgroundQueue
THREE.OpaqueQueue // Opaque geometry uses this queue (the default queue)
THREE.AlphaTestQueue
THREE.TransparentQueue // Anything alpha-blended goes here
THREE.OverlayQueue // Lens flares
(hehe: "OpaqueQueue")
To me, thinking about it in terms of render queues, instead of labeling the material as transparent
, makes a lot of sense. I am not sure it is worth an API change, but I much prefer to think about the issue in these terms. :-)
AdditiveBlending works regardless of the render order as long as the depth buffer is not causing fragments to be discarded.
I don't understand, sorry. I see the commutative part when operations are the same, but the following are clearly different:
THREE.TransparentQueue // Anything alpha-blended goes here
Ok, if we consider additive blending a type of alpha blending, which should be put into a transparent queue if there is one, then maybe we're on the same page enough to discuss fixes rather than terms. ๐
I find the render queue concept helpful, yes.
So if a user wanted to create a cool background using custom blending, the user would assign the material to the background queue. (In particular, the user would not be forced to place his custom-blended material into the transparent queue, where the material can never be rendered "first".)
maybe we're on the same page enough to discuss fixes rather than terms.
Let's assume we are. :-)