three-gpu-pathtracer
three-gpu-pathtracer copied to clipboard
Add OIDN Denoiser.
This PR Adds the OIDN Denoiser (Denoiser) into the core three-gpu-pathtracer.
It works, and looks decent but there are clear issues to resolve.
API Added
-
maxSamples
Pathtracer now stops when reaching this number. Also is the number the system denoises at -
enableDenoiser
If we are using the denoiser (NOTE, the denosier actually initializes regardless of this for webGL compat) -
weightsUrl
Lets you bypass the jsDelivr URL for your own location of the TZAs. Also how you could pass your own weights in general -
denoiserQuality
Fast by default, Balanced is better. "high" is really heavy and not supported in the Denoiser yet -
useAux
Whether or not you want to use just the pathtraced input (fast, but bad) or with the Aux -
renderAux
If you want to view the aux inputs set this toalbedo
ornormal
null
gets you the denoised output -
cleanAux
If you are 100% the aux inputs are clean, ANY noise will be projected into the output (NOTE, upstream fix may be needed) -
externalAux
If the user is supplying their own Aux Textures. disables automatic generation -
denoiserDebugging
Sets the denoiser to log more things including an output report.
State API Properties (read-only)
-
isDenoising
If the denoiser is running -
isDenoised
if the current output is the denoised output
Methods
-
generateAux()
runs the albedo/normals pass on the current scene. returns{ albedo: THREE.Texture, normal: THREE.Texture }
-
setAuxTextures(albedoTexture, normalTexture)
if the user wants to set the aux themselves, like in a pipeline or has a deferred setup. Deactiviates internal automatic creation (this.externalAux = true
) -
denoiseSample(inputTexture, albedoTexture, normalTexture)
loads automatically or can be overridden letting you send to the denoiser directly to theDenoiserManager
Not sure about exposing this, called internally byrenderSample
Changes to Core
- Not much was done to the core itself. The main thing is the addition of another fullScreenQuad pass that blends the pathtracer output with the denoised (or aux if you're debugging) outputs.
- The second significant change is the addition of
maxSamples
and it's stopping the pathtracer when reached. - There were other changes to better support the denoiser, but nothing breaking.
One change was added to the BlendingMaterial
to allow a conversion to happen to texture2. While we resolve the colorspace issues it may be useful.
Additions
- AlbedoNormalPass: Generates Albedo & Normal textures based on the current scene
- DenoiserManager: Holds all things related to the denoiser and interfaces with the
Denoiser
class directly
Flow With Denoising
Assuming Denoiser is enabled and all options default/set
- Pathtracer runs until
maxSamples
is hit - Denoiser process started. Block future
denoiseSample
calls until finished - Albedo and Normal Textures Generated (only when needed by the denoiser, not every frame)
- RawPathtracerTexture (
pathtracer.target.texture
), AlbedoTexture, and NormalTexture sent to the DenoiserManager. - If the renderer size has changed, regenerate renderTargets and set Denoiser sizes.
- Render the RawPathtracer texture to a Quad with same tonemapping as default PT so resulting texture matches and within [0, 1]
- Extract raw
WebGLTextures
from internals of input textures (THREE.Texture
s) - Set those Inputs on the denoiser
- Execute the denoiser, return
denoisedWebGLTexture
- If not already created, create and load a ThreeJS texture that is properly initialized and setup within threejs internals
outputTexture
( one of the many steps to get WebGL/Three to play nice) - Merge the
denoisedWebGLTexture
with the outputTexture -
denoisedTexture
is held in the class ready to be rendered - denoiser marked finished, results say it is now denoised
- In the next
renderSample
call now thatisDenoised
is true, render usingDenoiserManager.renderOutput()
- Quad material now set to
BlendingMaterial
- Blend between previous PT Output (results of step 6) and the
denoisedTexture
(Note: if RenderAux is passed, will render the aux texture provided so you can see either the albedo or normal textures visually. Not sure the normals render how you'd expect, as they should be outputting in [-1, 1])
If reset is called it all drops back to normal, hides the outputs etc and starts over.
Known Issues
1. ColorSpaces/tonemapping/conversion: Something is clearly not setup right through the pipeline regarding colors. The denoiser DOES NOT CARE what colorspace you input. Whatever you input, it will output. Color and Albedo inputs should be in the same colorspace, and normals are expected to be in linear. The output looks correct but dark. converting makes it look flat.
This will take tweaking and someone smarter than me with regards to color to follow the renderTargets (all setup with THREE.SRGBColorSpace
and what we should convert/adjust
2. Normal Generation: I have a script setup to use worldspace or viewspace normals and output to [-1, 1]. I don't know if colorSpace/RT's might be effecting this (I don't think it matters). Also, I have added code to accept normalMaps on meshes so they can be used instead of the raw surface. I tried converting the NormalMaps to worldspace with tragic results. For the moment If an object has a map I use it.
Something very important to point out about normals and the denoiser. The denoiser does not actually use them for normal values or any kind of light calculations whatsoever. They can be in world/local space. The normal maps just help define edges and breaks in materials.
3. Albedo Generation: In The OIDN Docs they go into detail about the albedos. In general, most matte surfaces can use just the basic color output or textures. This isn't true for highly reflective surfaces (like the floor). Here albedo wants something different, you can read the docs and it says something to the effect that the reflected surface should have the albedo of whatever they are reflecting, or a full 1 value. It gets even weirder for reflections.
4. Edges with floor and background. Looking at original OIDN examples they do not expect black backgrounds or hard edges where soft edges blend with the original background. The normals/albedo read those as hard crisp edges or flat planes. So the gradient background gets flattened into a weird flat gradient and the floor has sharp edges. So with threejs backgrounds or envMaps we should generate something for both of these passes and include any floor/horizons.
The only one of these issues I see as a blocker is the colorspaces. Generating albedos and normals etc we can work on for a while and it would be fine to release improvements as updates. But gotta get closer on the color outputs IMO.
Other Changes
I updated the plain example (index) to include denoiser props and some other slight adjustments. I added a rawOutput canvas to the html which is very easy to setup when debugging to see exactly what the denoiser is outputting and confirm your inputs/outputs.
Notes
I tried commenting a lot to explain what is happening, and I realize my commits are terribly labeled. I was focusing on getting this added as the main thing and not making a lot of gradual changes.