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

EXR Envmap not working on iOS #19053

Closed
4 of 13 tasks
krfft opened this issue Apr 6, 2020 · 18 comments · Fixed by #19087
Closed
4 of 13 tasks

EXR Envmap not working on iOS #19053

krfft opened this issue Apr 6, 2020 · 18 comments · Fixed by #19087

Comments

@krfft
Copy link

krfft commented Apr 6, 2020

Following the example on: https://threejs.org/examples/webgl_materials_envmaps_exr.html
and loading a EXR file as environment map with:

var pmremGenerator = new THREE.PMREMGenerator( this.renderer );
pmremGenerator.compileEquirectangularShader();
var exrCubeRenderTarget = pmremGenerator.fromEquirectangular( texture );
this.hdr = exrCubeRenderTarget.texture;

This works perfectly on all desktop browsers but on iOS the HDR is completely black and no lighting is applied to the model. This also applies to the example link on iOS: https://threejs.org/examples/webgl_materials_envmaps_exr.html

Three.js version
  • Dev
  • r115
  • ...
Browser
  • All of them
  • Chrome
  • Firefox
  • Internet Explorer
OS
  • All of them
  • Windows
  • macOS
  • Linux
  • Android
  • iOS
Hardware Requirements (graphics card, VR Device, ...)
@mrdoob
Copy link
Owner

mrdoob commented Apr 6, 2020

Any errors in the console?

@krfft
Copy link
Author

krfft commented Apr 6, 2020

[Warning] THREE.WebGLRenderer: EXT_frag_depth extension not supported. (three.module.js, line 16023)
[Warning] THREE.WebGLRenderer: WEBGL_draw_buffers extension not supported.

@krfft
Copy link
Author

krfft commented Apr 6, 2020

It seems to be the case for Android too, just tested on Chrome on Galaxy S10

@sciecode
Copy link
Contributor

sciecode commented Apr 6, 2020

I'm able to replicate this.

At a quick glance, this looked related with PMREMGenerator not being able to render to FloatType render target, which is a known limitation on mobile. However changing it to HalfFloatType also doesn't work, for whatever reason.

This doesn't look related with EXR per-se, as changing https://threejs.org/examples/webgl_materials_envmaps_hdr.html to HalfFloatTypeor FloatTypealso causes the same issue.

@Mugen87
Copy link
Collaborator

Mugen87 commented Apr 6, 2020

It seems to be the case for Android too, just tested on Chrome on Galaxy S10

Can reproduce with a Pixel 1.

@sciecode
Copy link
Contributor

sciecode commented Apr 6, 2020

I believe the problem stems from the fact that a DataTexture encoded as Uint16Array can't be correctly read by the GPU on mobile.

PMREMGenerator only copies the texture type to the render targets being used internally.
So for the case where the input HDR is in FloatType, the framebuffers are also FloatType and a mobile can't render due to spec limitation. When the input HDR type is HalfFloatType and a mobile would be able to render, it fails inputting the DataTexture to the GPU.

What I'm not sure is how we'd go about fixing this. Nothing comes to mind.

@mrdoob
Copy link
Owner

mrdoob commented Apr 7, 2020

Any ideas @elalish?

@elalish
Copy link
Contributor

elalish commented Apr 7, 2020

The PMREMGenerator is designed to work with RGBE for exactly this reason (since RGBA8 framebuffers are supported everywhere). I think the best solution would be to read in the EXR as an RGBE texture and pass that to PMREMGenerator. Unless there's a reason that would be a problem elsewhere?

@sciecode
Copy link
Contributor

sciecode commented Apr 7, 2020

It is possible to output an UnsignedByteType from EXRLoader as we do with HDR. But implies losing information of the alpha channel and some extra time processing data, but given how the texture is being run as an env map, perhaps that isn't too bad.

@WestLangley
Copy link
Collaborator

I think PMREM should support the standard HDR formats. That would include .hdr and .exr.

We also want to avoid having to decode RGBE in the shader, so support for floating point data textures is important.

@elalish
Copy link
Contributor

elalish commented Apr 7, 2020

@WestLangley I tried that way originally, but switched to RGBE because while floating point textures are widely available for decode, the same is not true for writing to framebuffers. Samsung devices can't write halffloat buffers and iphones can't write float buffers, so I ended up decoding RGBE in the shaders because that was the only way I could find to make a broadly cross-device solution.

Perhaps we should throw a warning about device compatibility when using non-8-bit formats with PMREM?

@krfft
Copy link
Author

krfft commented Apr 7, 2020

Don't know if its of any help but the same file worked with my old implementation on mobile devices:

var cubemapGenerator = new THREE.EquirectangularToCubeGenerator( texture, { resolution: 1024, type: THREE.HalfFloatType } );
var cubeMapTexture = cubemapGenerator.update( renderer );
var pmremGenerator = new THREE.PMREMGenerator( cubeMapTexture );
pmremGenerator.update( renderer );
var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
pmremCubeUVPacker.update( renderer );
hdr = pmremCubeUVPacker.CubeUVRenderTarget;

@elalish
Copy link
Contributor

elalish commented Apr 7, 2020

@krfft Yes, and the reason for that was that the old PMREMGenerator internally converted everything to RGBM (another 8-bit format). It's a little quicker to decode than RGBE, but comes at the cost of having significant interpolation artifacts at high dynamic range.

@sciecode
Copy link
Contributor

sciecode commented Apr 7, 2020

I've pushed #19070 as a temporary fix for the situation. Even if we decide to pursue other solutions in the future, there's no harm in allowing EXRLoader to output UnsignedByteType with RGBEFormat.

That way, at least, we have a viable option for people looking to use EXR envmaps with mobile support.

webgl_materials_envmaps_exr.html

@WestLangley
Copy link
Collaborator

If I am not mistaken, PMREMGenerator retains the data type passed in.

That is one reason why Loader.setDataType() is important.

I wonder if it would make sense to add PMREMGenerator.setDataType()?

@sciecode
Copy link
Contributor

sciecode commented Apr 8, 2020

I discussed privately with @WestLangley and perhaps allowing the user the ability to set the intermediary and final render target parameters ( Format, Type, Encoding ) might allow us to use regular floating point textures as an input for PMREMGenerator and still render to RGBE 8-bit RTTs correctly on mobile.

As far as I'm aware, most mobiles can read from floating point textures, the problem is usually rendering to them. Thoughts @elalish ? Do you think that would be something we can adapt ?

@elalish
Copy link
Contributor

elalish commented Apr 8, 2020

@sciecode Yes, that fits with my understanding. That sounds like a reasonable path forward; I don't have a strong opinion on the shape of three's API. If we're planning to overhaul PMREMGenerator it might also be a good time to enable different sizes (right now it's fixed to outputting a 256x256 cubemap regardless of input size). It seems like with the right choice of defaults we might even be able to make this change non-breaking, which would be nice.

@WestLangley
Copy link
Collaborator

allowing the user the ability to set the intermediary and final render target parameters ( Format, Type, Encoding )

I would allow the users to set only the data type, as I suggested above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants