Three.js: EXR example is broken on latest Chrome with Windows

Created on 13 Mar 2019  路  12Comments  路  Source: mrdoob/three.js

Hi all,

This literally broke overnight - it worked all day yesterday. Update to the latest chrome and this example will be pitch black (because geometry itself is black on the black background):
https://threejs.org/examples/webgl_materials_envmaps_exr.html

The recurring error pattern in the dev console is:

[.WebGL-000001B8882DE700] GL_INVALID_FRAMEBUFFER_OPERATION: Framebuffer is incomplete.
webgl_materials_envmaps_exr.html:1 [.WebGL-000001B8882DE700] GL_INVALID_FRAMEBUFFER_OPERATION: Draw framebuffer is incomplete

If you switch to the PNG in the above example - all looks good, so it's just EXR.

HDR example works too
https://threejs.org/examples/webgl_materials_envmaps_hdr.html
so it's an issue for just EXR one.

Three.js version
  • [ ] Dev
  • [x] r102, but is also reproducible on at least r98
  • [ ] ...
Browser
  • [x] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer
OS
  • [] All of them
  • [x] Windows
  • [ ] macOS
  • [ ] Linux
  • [ ] Android
  • [ ] iOS

NOTE: this seems to be a Windows-only issue, as the latest Chrome on iOS works.

This may be related to https://github.com/mrdoob/three.js/issues/15343

Browser Issue

All 12 comments

NOTE: this seems to be a Windows-only issue, as the latest Chrome on iOS works.

True. I'm unable to reproduce on macOS with Chrome 73.0.3683.75.

Can you please check out if the workaround shared by @silvainSayduck does help?

https://github.com/mrdoob/three.js/issues/15343#issuecomment-457528822

What seems to work for us is changing the type in line
let cubemapGenerator = new THREE.EquirectangularToCubeGenerator(texture, { resolution: 512, type: THREE.HalfFloatType });
from
THREE.HalfFloatType to THREE.FloatType

EXR lighting does look a bit different on r102 for same EXRs with the above change, but it's still better than black models.

Ferrari

Just tried the workaround suggested by @silvainSayduck - kept getting this error:

MYC3D.js:1639 Uncaught DOMException: Failed to construct 'ImageData': The input data length is not equal to (4 * width * height).

for the line:

const imageData = new ImageData(new Uint8ClampedArray(dataTexture.image.data.map((value) => value * 255)), dataTexture.image.width, dataTexture.image.height);

Apparently constructing ImageData requires dataTexture.image.data to be equal to 4 * width * height?

Um, the original type of the loaded EXR texture is THREE.FloatType. THREE.CubemapGenerator respects the original type of the texture whereas THREE.EquirectangularToCubeGenerator allows to overwrite it (e.g. with THREE.HalfFloatType).

@WestLangley Does it makes sense to provide this flexibility? Maybe it's best to switch to THREE.CubemapGenerator in this example...

Just tried the workaround suggested by @silvainSayduck - kept getting this error:

MYC3D.js:1639 Uncaught DOMException: Failed to construct 'ImageData': The input data length is not equal to (4 * width * height).

for the line:

const imageData = new ImageData(new Uint8ClampedArray(dataTexture.image.data.map((value) => value * 255)), dataTexture.image.width, dataTexture.image.height);

Apparently constructing ImageData requires dataTexture.image.data to be equal to 4 * width * height?

I noticed that this happened for some EXR files on my end too. It's when they are in RGB format. ImageData expects data to be RGBA. Here's the updated workaround:

const originalData = dataTexture.image.data;
const imageWidth = dataTexture.image.width;
const imageHeight = dataTexture.image.height;
let data;
if (originalData.length === imageHeight * imageWidth * 3) {
  // RGB format, need to insert 255 values for alpha channel
  data = new Float32Array(4 * imageWidth * imageHeight);
  data.fill(255);
  originalData.forEach((value, ix) => {
    const destinationIndex = ix + Math.floor(ix / 3);
    data.fill(value * 255, destinationIndex, destinationIndex + 1);
  });
} else {
  data = originalData.map(value => value * 255);
}
const imageData = new ImageData(
  new Uint8ClampedArray(data),
  imageWidth,
  imageHeight
);
texture = new THREE.Texture(imageData);
texture.needsUpdate = true;

I think you should be able to just do texture.format = THREE.RGBAFormat;.

Thanks for jumping in! However, I'm not sure I understand on which texture this should be applied?

To recreate the Texture that works on Android, I need to create the ImageData, and that expects a Float32Array of length imageWidth * imageHeight * 4. The data in originalData is of length imageWidth * imageHeight * 3 if the EXR file is RGB.

Do you mean that applying dataTexture.format = THREE.RGBAFormat would somehow update dataTexture.image.data? Or that by setting dataTexture.format = THREE.RGBAFormat, the dataTexture would then work directly on Android without having to recreate a Texture at all?

Because from what I understand, it seems that you're saying to apply texture.format = THREE.RGBAFormat on the final texture (two last lines of my snippet). But that Texture can't even be created without the new Float32Array...

Chrome 74 is out. Has this been fixed?

Yes, this is now fixed and can be closed.
Thanks everyone.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

clawconduce picture clawconduce  路  3Comments

fuzihaofzh picture fuzihaofzh  路  3Comments

donmccurdy picture donmccurdy  路  3Comments

makc picture makc  路  3Comments

boyravikumar picture boyravikumar  路  3Comments