three.js icon indicating copy to clipboard operation
three.js copied to clipboard

Adopting a Progressive Photorealistic Global Illumination in Three.JS

Open bhouston opened this issue 7 years ago • 38 comments

There has recently been a bunch of work towards progressive photo-realistic global illumination in WebGL. The best example I've seen of this is https://cl3ver.com renderer. There is some overlap between this and the high end rendering that Octane and other photorealistic GPU renderers do. I think it is time to add this to Three.JS.

This is a meta-task that describes what needs to be done (this is in the spirit of the PBR meta task from 2015: https://github.com/mrdoob/three.js/issues/5847)

The main components of this approach would be:

Temporal Reprojection Anti-Aliasing https://github.com/mrdoob/three.js/issues/14050

Progressive Global Illumination via Instant Radiosity/Virtual Point Lights https://github.com/mrdoob/three.js/issues/14047

Progressive Soft Shadows via Light Source Sampling https://github.com/mrdoob/three.js/issues/14048

Screen-space Reflections https://github.com/mrdoob/three.js/issues/8248

bhouston avatar May 12 '18 11:05 bhouston

Activision's Ground Truth Ambient Occlusion and Unigine's SSRTGI both look really impressive and would be great to have available.

Is the assumption that these would come after something like multiple render textures or deferred rendering is available in THREE?

gkjohnson avatar Jul 29 '18 00:07 gkjohnson

I'm surprised not to see your previous suggestion for light probes, https://github.com/mrdoob/three.js/issues/6251, in this list. Do you see these as complementary? The features here appear to me (without much background) more appropriate for viewing static models photo-realistically, whereas light probes are more of a GI approximation for dynamic objects in realtime scenes?

donmccurdy avatar Nov 19 '18 15:11 donmccurdy

Real-time light probes can be either diffuse or specular. Diffuse light probes are made obsolete by instant radiosity. Specular light probes are made obsolete via SSR.

That said, this solution is for progressive and very high quality result. If you want an instantaneous and a bit lower quality solution you should use light probes.

bhouston avatar Nov 19 '18 15:11 bhouston

How about this? https://github.com/erichlof/THREE.js-PathTracing-Renderer (Demos: https://erichlof.github.io/THREE.js-PathTracing-Renderer/)

EliasHasle avatar Jul 06 '19 08:07 EliasHasle

Hi,

At HOVER, we recently open-sourced our Ray Tracing Renderer. It's a drop-in replacement for the WebGLRenderer in three.js.

preview

Links

Note

  • You need WebGL 2.

elfrank avatar Sep 09 '19 21:09 elfrank

@elfrank Nice demo! It takes a while to get a nicer render with the raytracer than with the WebGLRenderer, though. Also, the WebGLRenderer is initialized without antialiasing, which would significantly improve the result.

EliasHasle avatar Sep 10 '19 05:09 EliasHasle

@elfrank That looks great!

Things render pretty glitchy on ChromeOS/Pixelbook though 😶

Screenshot 2019-09-09 at 23 32 47

Kinda cool though! 😁

mrdoob avatar Sep 10 '19 06:09 mrdoob

That said, this solution is for progressive and very high quality result.

Does progressive in this context mean something like what sketchfab does? It takes maybe 0.5 seconds or less there to see the high quality result after moving the camera. That's something I'd love to see made possible in three.js.

Or is more like the raytracer (or maybe a bit faster) where it takes several seconds to see a good result even on fast hardware?

looeee avatar Sep 10 '19 09:09 looeee

Does progressive in this context mean something like what sketchfab does? It takes maybe 0.5 seconds or less there to see the high quality result after moving the camera.

That's what this example does (minus the improved shadows): https://threejs.org/examples/#webgl_postprocessing_taa

mrdoob avatar Sep 10 '19 20:09 mrdoob

I think sketchfab does more than just disable AA while the camera is moving though. It seems like the reduce the resolution and possibly reduce texture quality too, and perhaps some other things that are less obvious (maybe the use progressive shadows and lighting like @bhouston is suggesting here).

I tried a quick tests to reduce resolution while OrbitControls is engaged but I couldn't get decent results or any noticeable performance gain, even in a fill rate limited scene.

looeee avatar Sep 11 '19 00:09 looeee

I think sketchfab does more than just disable AA while the camera is moving though.

Some of this has already been mentioned but here's what I noticed with sketchfabs renderer if it's worth noting:

It's most pronounced in this bacteria model but you can see that it renders transparency using some kind of noise distribution that then resolves to a smooth look. Presumably this is to help alleviate artifacts associated with transparent object render order.

It also looks like some kind of sharpen filter gets applied to the image before the accumulation kicks in but I'm not sure why. It's pretty easy to see in this car model when you zoom in and out (notice the light outlines around the dark edges of the model). Maybe someone else has an idea as to what this is for?

As was mentioned shadow map camera is subpixel jittered (much like the TAA approach, I think) to smooth shadow resolution artifacts. This can be seen pretty easily in this cyborg boy model if you pause the animation and zoom into the shirt on the right arm.

Of course there's some really nice high quality post processing going on, too.

I'm curious to what the plan should be for adding these types features into the library. As far as I'm concerned it's already possible to achieve all of this but maybe it's worth adding some facilities to make it easier to achieve? And an example that glues all the necessary pieces together?

gkjohnson avatar Sep 11 '19 04:09 gkjohnson

I've analyzed Sketchfab. It has a series of options that you can turn on and off so exactly what it does on a specific scene is dependent upon that. But it has these capabilities:

  • Sketchfab supports a series of optional and parameter post processing filters like sharpen, color correct, vignetting, sepia, chromatic aberration, etc.

  • TRAA - temporal reproduction anti-aliasing. It uses a velocity buffer of the scene to allow one to to TAA (https://threejs.org/examples/#webgl_postprocessing_taa) like effects but with a moving camera or deforming object. I have code for this and I want our team here (ThreeKit) to contribute it back to Three.js, just it is lower priority than contributing back enhanced PBR materials (e.g. https://github.com/mrdoob/three.js/issues/16977 )

  • Shadow camera jitter when the scene is static. This smoothes out the shadows. One should jitter the shadow cameras within the light volume for correct soft shadows. When moving, one should position the shadow camera at the center of the light volume or at least the functional equivalent.

  • Stochastic transparency. This is what we see in the bacteria model I believe: http://luebke.us/publications/StochTransp-slides.pdf I think Stochastic transparency can resolve to a good solution, but it looks like crap in dynamic scenes. I am unsure of its value.

(An aside: Alternatively, we (Threekit) have implemented order independent transparency based on the McGuire model, http://casual-effects.blogspot.com/2014/03/weighted-blended-order-independent.html But it is also highly problematic - it is dependent upon a lot of parameter tweaking and it is slow and needs floating point buffers. I would recommend against implement this algorithm in Three.JS as well.

I am unsure of the best approach to OIT - both Stochastic and the McGuire OIT method have significant downsides.)

A suggested plan forward:

  • I think we should add shadow camera jitter within the shadow emission volumes. It should be something one can turn on and then on each frame it jitters within that region. Then users can enable it during a TAA refinement phase. It works exceptionally well with the TAA post effect I wrote.

  • We should add TRAA and we can contribute that code. Maybe we can contribute broken code ripped out of our custom Three.JS implementation and a hero can adapt it to the mainstream Three.JS? (Interesting fact, you can not easily combine jitter shadow cameras with TRAA because TRAA uses a luminance discriminator to determine if things will blend across frames, and well shadow boundaries have significant luminance contrast -- but maybe someone can figure out a solution to this.)

Some people have combined TRAA with stochastic transparency. https://casual-effects.com/research/Wyman2017Improved/Wyman2017Improved.pdf https://developer.download.nvidia.com/assets/gameworks/downloads/regular/GDC17/RealTimeRenderingAdvances_HashedAlphaTesting_GDC2017_FINAL.pdf Maybe the game INSIDE used this approach -- one slide says stochastic everywhere: http://twvideo01.ubm-us.net/o1/vault/gdc2016/Presentations/Pedersen_LasseJonFuglsang_TemporalReprojectionAntiAliasing.pdf

bhouston avatar Sep 11 '19 12:09 bhouston

@elfrank Nice demo! It takes a while to get a nicer render with the raytracer than with the WebGLRenderer, though.

Yes, we use path tracing, which could be broadly bucketed as a "progressive photorealistic Global Illumination" technique. We are looking into faster convergence. Path tracing is expensive but there is a good amount of recent research on the matter.

Also, the WebGLRenderer is initialized without antialiasing, which would significantly improve the result.

@EliasHasle Noted! Thanks.

Things render pretty glitchy on ChromeOS/Pixelbook though 😶

That's unfortunate @mrdoob . We have experienced a few driver bugs/glitches in some devices. It's nearly possible to account for all scenarios without having access to the hardware. With that said, Pull requests are welcomed! Thoughts @jaxry ?

Or is more like the raytracer (or maybe a bit faster) where it takes several seconds to see a good result even on fast hardware?

@looeee I highly recommend this video on Path Tracing: https://www.youtube.com/watch?v=frLwRLS_ZR0&t=11s As you pointed out, the technique is inherently expensive. Hardware keeps getting faster and that helps a lot.

elfrank avatar Sep 11 '19 20:09 elfrank

I've been implementing Approximate Radiosity using Stochastic Depth Buffering, which is a version of the recent sampled Shadows PR that tunnels through surfaces to cast rays throughout the scene. This should enable reasonably fast, physically correct global illumination and area lighting (without path tracing or acceleration structures).

Here is an example scene that sort of demonstrates basic colored reflections and area lighting: https://zalo.github.io/three.js/examples/#webgl_shadowmap_progressive

zalo avatar Mar 13 '21 01:03 zalo

@zalo that runs pretty well on my cheap Android phone 😮

mrdoob avatar Mar 13 '21 12:03 mrdoob

My cheap Android phone shows it like this (with FPS eyeballed to ~10): _20210313_141212

Do you have a reference screenshot for how it should look?

EliasHasle avatar Mar 13 '21 13:03 EliasHasle

0B594B05-A2F5-4FBF-AF58-3F17439EAF1E

Took this on a Pixel 4 (the banding isn’t visible on Desktops with full float precision buffers... though I’d like to fix that with additional dithering...). @EliasHasle is there any chance you could check the console for errors and report them to me? As far as I know, the most exotic things I’m doing are float buffers and writing depth in the fragment shader...

If the latter is the issue... it may be possible to jitter depth in vertex space instead...

Performance-wise, the nice thing about this effect is that it can be turned off after a few seconds (once it’s converged) and it’s basically free from then on (as a “baked lightmap”).

EDIT:

I just added a check for the frag depth breakage (which was the issue on iOS). This looks alright in this scene, but will appear more and more incorrect as the scene complexity increases. Unfortunately I can’t think of a good alternative off the top of my head.... only bad alternatives 🙃

zalo avatar Mar 13 '21 20:03 zalo

@zalo That looks good to me. Should that become another example?

mrdoob avatar Mar 15 '21 18:03 mrdoob

Looks broken on Safari on M1 in WebGL2 mode. Screen Shot 2021-03-15 at 2 28 54 PM

bhouston avatar Mar 15 '21 18:03 bhouston

@bhouston Could you try if it's also broken on Chrome on M1?

mrdoob avatar Mar 15 '21 18:03 mrdoob

It works fine in Chrome on M1:

Screen Shot 2021-03-15 at 2 39 47 PM

bhouston avatar Mar 15 '21 18:03 bhouston

@bhouston Thanks for testing. Would you like to report the issue to Apple?

mrdoob avatar Mar 15 '21 18:03 mrdoob

This looks great -- here's an artifact I see in Chrome on 2017 Mac with a Radeon Pro 560 that I don't see in @bhouston's screenshot (notice the hard edge in the lighting):

image

gkjohnson avatar Mar 15 '21 18:03 gkjohnson

Results for Macbook Pro 2020, Radeon 5600M. Hard edge in Chrome and Firefox, no edge in Safari. There also more artefacts visible on the inside in comparison to @bhouston screenshot on M1. Screenshot 2021-03-16 at 16 40 30

carstenschwede avatar Mar 16 '21 15:03 carstenschwede

That artifact is very interesting; I think Radeon is the common element there. My wildest guess is that negative orthographic near clipping planes aren't supported on Radeon graphics cards on Mac...?

Safari was acting broken on my Mid-2015 Intel Iris Macbook, but Chrome works flawlessly. It turns out that is because renderer.extensions.has( 'EXT_frag_depth' ) returns true on Mac Safari, but it's not true! It's a lie! It also runs much more slowly than Chrome, so I'm additionally downgrading Safari to mobile settings.

Do any of those changes help on Radeon Chrome/Firefox, @gkjohnson and @carstenschwede ? If not, could you also hit the "Debug Lightmap" setting to see what that mysterious line looks like there? Perhaps also try moving the lighting/object around to see if the line moves? Updated Here: https://zalo.github.io/three.js/examples/#webgl_shadowmap_progressive

With regards to a new example, I'd really like to get the code cleaned up, add more configurability, add more bounces, and increase the complexity of the example scene/("unit test") to make this a suitable addition to three.js. The radiosity component currently triples the surface area of the lightmapping script to ~700 lines, so I'd like to par that down if possible...

zalo avatar Mar 16 '21 18:03 zalo

Looking more closely now, there are actually different edges in Safari and Chrome (Radeon 5600M, macOS).

Chrome: Screenshot 2021-03-17 at 01 00 31

Safari: Screenshot 2021-03-17 at 01 00 40

Edge does not move with lights/objects and is not present in early iterations. Screenshot 2021-03-17 at 01 07 17

carstenschwede avatar Mar 17 '21 00:03 carstenschwede

Overriding software rendering list and enabling draft extensions in Chrome/macOS/Radeon also has no effect on the edge.

carstenschwede avatar Mar 17 '21 00:03 carstenschwede

Here is a montage of how the edge appears in later iterations (Chrome/MacOS/Radeon) montage

carstenschwede avatar Mar 17 '21 15:03 carstenschwede

If this is in an example folder, rather than being a core feature, I wouldn't require it to be absolutely perfect on all devices in the first PR, because that could stall it from getting in.

bhouston avatar Mar 17 '21 16:03 bhouston

The light target is currently on top of the object. Would it make sense to add a corresponding helper to indicate the orientation of the DirectionalLight (or give the target its own control)? I assumed the control was for the origin of a PointLight and was irritated when the shadows were pointing towards it. Screenshot 2021-03-17 at 17 46 17

Also Safari appears to converge much slower which is why I think the edge is less apparent there. Increasing the LightMap resolution helps with the edge on the green plane, although they are still a few artefacts visible in the red one. Nevertheless a super impressive result - in terms of quality and convergence time! Screenshot 2021-03-17 at 18 34 31

carstenschwede avatar Mar 17 '21 17:03 carstenschwede