Skip to content

Conversation

@richardmonette
Copy link
Contributor

@richardmonette richardmonette commented Mar 26, 2018

Fixes #13574

screen shot 2018-03-26 at 4 17 03 pm

Summary

This PR adds an example of an IBL (Image based lighting) pipeline based around an equiangular formatted EXR.

Since we already have a nice PMREMGenerator, I have taken the approach of adding a EquiangularToCubeGenerator which uses the GPU to convert an equiangular format texture into a cube map. The highlight here is the equiangular EXR workflow, but I have also included in the example using a PNG equiangular map, just to demonstrate the EquiangularToCubeGenerator conversion is also possible in a low dynamic range context.

Performance

Test

console.time('EquiangularToCubeGenerator');

var cubemapGenerator = new THREE.EquiangularToCubeGenerator( texture, 512 );
var cubeMapTexture = cubemapGenerator.generate( renderer );

console.timeEnd('EquiangularToCubeGenerator');

Result

EquiangularToCubeGenerator: 28.334716796875ms

@mrdoob @WestLangley

@looeee
Copy link
Collaborator

looeee commented Mar 26, 2018

Live version. Looks amazing btw 😍

Is there a reason why PNG seems so overexposed compared to EXR?

@richardmonette
Copy link
Contributor Author

@looeee Without getting super into the details, perhaps suffice to say the PNG version is using LDR (low dynamic range) so its never going to look correct, you can mess with the exposure a bit to adjust, but the primary goal here is to establish the correct pipeline using HDR (high dynamic range) light probes as the basis of the IBL (image base lighting) pipeline.


standardMaterial = new THREE.MeshStandardMaterial( {
map: null,
color: 0x000000,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why black? I suggest white -- and reduce the default exposure, accordingly

scene.add( torusMesh1 );
objects.push( torusMesh1 );

floorMaterial = new THREE.MeshStandardMaterial( {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think MeshBasicMaterial is more appropriate, here, as the purpose is only to display the mips.

@WestLangley
Copy link
Collaborator

Sweet!

For a future PR, if you have any ideas on how to reduce the memory stats, that would be great.

memory:
    geometries: 318
    textures: 18

@richardmonette
Copy link
Contributor Author

richardmonette commented Mar 26, 2018

@WestLangley Thank you for your comments! I've made the changes you recommended and I think it even slightly improves the visual quality. Good call with the MeshBasicMaterial, its unlit rendering is exactly what we needed for the debug texture.

@mrdoob mrdoob added this to the r92 milestone Mar 27, 2018
@WestLangley
Copy link
Collaborator

@looeee I took the liberty of editing your temporary live link as it was not accommodating the additional commits. :)

@mrdoob
Copy link
Owner

mrdoob commented Mar 27, 2018

@WestLangley Thanks! The new link looks much better indeed!

];

this.camera = new THREE.PerspectiveCamera( 90, 1, 0.1, 10 );
this.planeMesh = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), this.getShader() );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would choose BoxBufferGeometry here.

envMapIntensity: 1.0
} );

var geometry = new THREE.TorusKnotGeometry( 18, 8, 150, 20 );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here: TorusKnotBufferGeometry

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 27, 2018

I have a black screen and the following warnings in the console.

[.Offscreen-For-WebGL-0x55fb20407c10]GL ERROR :GL_INVALID_FRAMEBUFFER_OPERATION : glClear: framebuffer incomplete (check)
[.Offscreen-For-WebGL-0x55fb20407c10]GL ERROR :GL_INVALID_FRAMEBUFFER_OPERATION : glDrawArrays: framebuffer incomplete (check)

Tested with Chromium 64.0.3282.167 on Ubuntu 16.04. FF 59.0.1 shows a black screen, too (with similar warnings).

Other HDR examples (like https://threejs.org/examples/webgl_materials_envmaps_hdr.html) seem to work on my machine:

@richardmonette
Copy link
Contributor Author

@Mugen87 Thank you for your review. I have made the following changes, based upon your suggestions:

Would it be possible for you to please confirm that's working for you too now? Thanks!

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 27, 2018

Great 👍 . The example now works in Chrome and FF on my machine.

\n\
gl_FragColor = vec4( color, 1.0 );\n\
}",
blending: THREE.CustomBlending,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please explain the reason for the custom blending?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @WestLangley , I'm not specifically familiar with the THREE.ShaderMaterial so I just took that from an example. If its an option, this could probably just be left as the defaults.


this.camera = new THREE.PerspectiveCamera( 90, 1, 0.1, 10 );
this.planeMesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 1, 1, 1 ), this.getShader() );
this.planeMesh.material.side = THREE.DoubleSide;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it planeMesh and why is it double-sided?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

planeMesh should be renamed to boxMesh, it's double sided because the camera is inside the box, i.e. it needs to render and not be backface culled.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

side: THREE.BackSide;

As you know, users copy code, so let's make it clear. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍(btw, appreciate you taking the time to work through the code with me!)

this.planeMesh.material.side = THREE.DoubleSide;
this.scene = new THREE.Scene();
this.scene.add( this.planeMesh );
this.scene.add( this.camera );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think adding the camera to the scene is required.


this.camera.position.set(0, 0, 0);
this.camera.up.set(v.u[0], v.u[1], v.u[2]);
this.camera.lookAt(new THREE.Vector3(v.t[0], v.t[1], v.t[2]));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.camera.lookAt( v.t[ 0 ], v.t[ 1 ], v.t[ 2 ] );

@WestLangley
Copy link
Collaborator

How are you handing the encoding? Are you assuming .exr is in linear space? .png in sRGB?

@richardmonette
Copy link
Contributor Author

EXR is in linear, yes.

@WestLangley
Copy link
Collaborator

Is there a reason why PNG seems so overexposed compared to EXR?

Likely the encoding.

Can you explicitly set the proper encodings in your example? THREE.sRGBEncoding for .png, THREE.LinearEncoding for .exr.

@mrdoob
Copy link
Owner

mrdoob commented Mar 27, 2018

Unrelated to this PR, but I think there is something wrong with the pmrem code. By changing the roughness of the material we can see that the levels are not aligned (the lights in the reflection changes position).

/cc @bhouston

@richardmonette
Copy link
Contributor Author

Unrelated to this PR, but I think there is something wrong with the pmrem code. By changing the roughness of the material we can see that the levels are not aligned (the lights in the reflection changes position).

https://github.com/mrdoob/three.js/blob/dev/examples/js/pmrem/PMREMGenerator.js#L103

That's something I am looking at in my next PR... 😄

@mrdoob
Copy link
Owner

mrdoob commented Mar 27, 2018

That's something I am looking at in my next PR... 😄

Sweet!

I think this PR only needs @WestLangley last comment addressed and it's ready to go.

@richardmonette
Copy link
Contributor Author

richardmonette commented Mar 27, 2018

@WestLangley I've explicitly set the proper encodings as you suggested and things have come even further into line 👍

hdr vs ldr

Left: Low dynamic range / PNG, Right: High dynamic range / EXR

You'll notice when adjusting exposure (especially where the EXR has more detail in the shadows) that the EXR / high dynamic range version works better, but there isn't the strange visual difference anymore 👌

@WestLangley
Copy link
Collaborator

Yes, the rendering is looking more reasonable now. :)

What I don't yet understand is why generateMipmaps is false for .exr and true for .png -- and what it should be. That can wait for another PR, though.

/ping @bhouston

@bhouston
Copy link
Contributor

What I don't yet understand is why generateMipmaps is false for .exr and true for .png -- and what it should be. That can wait for another PR, though.

Actually it isn't because of EXR or PNG but rather RGBE can not be linearly interpolated as it isn't a monotonic encoding, where as RGBM is a relatively monotonic encoding.

@bhouston
Copy link
Contributor

Unrelated to this PR, but I think there is something wrong with the pmrem code. By changing the roughness of the material we can see that the levels are not aligned (the lights in the reflection changes position).

This is caused by the sample pattern not being fully symmetric.

@bhouston
Copy link
Contributor

bhouston commented Mar 28, 2018

If the PNG in this example is truly LDR and there is no encoding (e.g. RGBE, RGBM, RGBD, etc.) then it can be linear interp as well like the EXR HDR. There is no need to work around non-monotonic RGBA8 encodings for RGB HDR values.

@richardmonette
Copy link
Contributor Author

richardmonette commented Mar 28, 2018

This is caused by the sample pattern not being fully symmetric.

I've got some ideas about using a low discrepancy sampler, such as a Hammersley, to improve quality as well, for a follow up PR.

@bhouston
Copy link
Contributor

I've got some ideas about using a low discrepancy sampler, such as a Hammersley, to improve quality as well, for a follow up PR.

Okay. I'm interested. I think that the issue is the current reliance on a GPU-based random number generator which is not uniform, nor is it consistent across different GPU families. I once had hope that there could be a reliable, high quality GPU-based "random" number generator -- test cases here https://threejs.org/examples/#webgl_postprocessing_procedural -- but I have since given up hope. I think a Hammersley distribution or a 2D Poisson disk would work great here.

You also hint that you can improve the theoretical foundation of this convolver, that would be really amazing. The problem with this one is that it uses the result of the previous level to create the next to get speed and reduce the number of samples required, thus it is a little tricky.

@bhouston
Copy link
Contributor

I would rename this example to webgl_materials_envmaps_exr.html so that it is beside the existing weblg_materials_envmaps_hdr.html and weblg_materials_envmaps.html as they are all related.

@mrdoob mrdoob merged commit 1ec3ee2 into mrdoob:dev Mar 29, 2018
@mrdoob
Copy link
Owner

mrdoob commented Mar 29, 2018

Thanks!

@bhouston
Copy link
Contributor

This was a great PR and work @richardmonette, very impressive.

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 this pull request may close these issues.

6 participants