mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.41k stars 35.35k forks source link

Transparent model does not render on some machines #26167

Open bennlich opened 1 year ago

bennlich commented 1 year ago

Description

I have a GLB of some transparent cloud shapes. On some computers (Intel macbook), they render fine:

Screen Shot 2023-05-29 at 1 25 11 PM

On other devices (Macbook w/ M2 chip, debian laptop, 10th gen ipad), they do not seem to render at all:

Screenshot 2023-05-30 at 1 52 50 AM

Reproduction steps

  1. Download the GLB https://nac-prod.sfo2.cdn.digitaloceanspaces.com/1607/clouds-l_nvBBMv.glb
  2. Plop it into mccurdy's wonderful viewer: https://gltf-viewer.donmccurdy.com/

Code

N/A

Live example

See steps above

Screenshots

No response

Version

r151

Device

Desktop, Mobile

Browser

Chrome, Safari

OS

MacOS, Linux, iOS

donmccurdy commented 1 year ago

Notable material properties:

A non-zero IOR is probably intended here, perhaps between 1.0 and 1.33. Based on the MeshPhysicalMaterial documentation, I'm not sure if three.js supports .ior = 0? glTF does allow it, and it can be helpful in converting spec/gloss to metal/rough materials, though very high IOR values will work just as well. That the material disappears on certain devices and not others is a surprise to me....

gkjohnson commented 1 year ago

I'm not sure if three.js supports .ior = 0? glTF does allow it...

What is ior = 0 supposed to mean? With refractive indices it's common to work with ratios between the external and inner medium light is being transmitted through. So you'll have to do things like 1 / ior in some cases - such as here:

https://github.com/mrdoob/three.js/blob/129635bdb5cbfa11f1b31522889846e862da43d0/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js#L124

I wouldn't be surprised if this gives undefined or inconsistent results. I do see in the spec that the valid value ranges are >= 1.0 or 0 explicitly. It's possible our shader needs a special case to handle a 0 ior - though it's not immediately clear to me from the spec what the behavior is supposed to be.

donmccurdy commented 1 year ago

From the spec —

Valid values for ior are numbers greater than or equal to 1. In addition, a value of 0 is allowed. This value gives full weight to layer, i.e., the Fresnel term evaluates to 1 independent of the view or light direction. It is useful in combination with KHR_materials_specular to seamlessly support the specular-glossiness workflow.

The Fresnel term also evaluates to 1 for very large IOR values. However, KHR_materials_specular is not involved here. I'm not sure what use ior=0 has within a transmissive material.

RemusMar commented 1 year ago

Valid values for ior are numbers greater than or equal to 1. In addition, a value of 0 is allowed.

Those specs are quite obfuscated (I don't want to say bad designed). That 0 should not be there.

mrdoob commented 1 year ago

@elalish Do you know the reasoning behind that 0?

bennlich commented 1 year ago

I re-exported the model with an IOR of 1 and verified that it renders on one of the machines where it was previously not rendering.

RemusMar commented 1 year ago

I re-exported the model with an IOR of 1 and verified that it renders on one of the machines where it was previously not rendering.

That's because ThreeJS does not support ior = 0 And honestly, mixing a range with a flag in the GLTF spec was a bad idea.

donmccurdy commented 1 year ago

That glTF allows ior = 0 does not mean MeshPhysicalMaterial must do so, we can map it to whatever parameterization we find appropriate for our user-facing Material API, or not. But we should try to understand the purpose of the value in the specification — it has an important role in some contexts. I'm not sure whether transmissive materials are one of those contexts.

WestLangley commented 1 year ago

FWIW

Valid values for ior are numbers greater than or equal to 1. In addition, a value of 0 is allowed. This value gives full weight to layer, i.e., the Fresnel term evaluates to 1 independent of the view or light direction. It is useful in combination with KHR_materials_specular to seamlessly support the specular-glossiness workflow.

It also says,

This extension must not be used on a material that also uses KHR_materials_pbrSpecularGlossiness.

ref: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_ior

donmccurdy commented 1 year ago

The implication above is that ior=0 or ior=Infinity can be used to losslessly convert from the spec/gloss PBR workflow (KHR_materials_pbrSpecularGlossiness) to a standard metal/rough PBR workflow. The conversion tools recommended in https://github.com/mrdoob/three.js/pull/24950 assign ior=1000. Longer discussion in https://github.com/KhronosGroup/glTF/pull/1719#issuecomment-674365677. None of those concerns appear relevant to transmissive transparency, however.

donmccurdy commented 1 year ago

As a test, I've modified the TransmissionRoughnessTest sample to include ior=0 and ior=1000. The last row, unlabeled, is opaque with default IOR.

TransmissionIORTest.glb.zip

engine screenshot
three three
babylon babylon
khronos khronos
playcanvas playcanvas

If we don't want to support ior=0 as an input to MeshPhysicalMaterial (which is fine with me), then perhaps we should just have GLTFLoader map ior=0 to ior=[large value] instead. The effect is the same.