Skip to content

Commit b82af09

Browse files
authored
Merge pull request #249 from gkjohnson/denoise
Add denoiser round 2
2 parents ba6f88a + 82b8a13 commit b82af09

File tree

4 files changed

+197
-6
lines changed

4 files changed

+197
-6
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,29 @@ _extends MaterialBase_
566566
}
567567
```
568568

569+
## DenoiseMaterial
570+
571+
_extends MaterialBase_
572+
573+
Denoise material based on [BrutPitt/glslSmartDeNoise](https://github.com/BrutPitt/glslSmartDeNoise) intended to be the final pass to the screen. Includes tonemapping and color space conversions.
574+
575+
**Uniforms**
576+
577+
```js
578+
{
579+
580+
// sigma - sigma Standard Deviation
581+
// kSigma - sigma coefficient
582+
// kSigma * sigma = radius of the circular kernel
583+
sigma = 5.0 : Number,
584+
kSigma = 1.0 : Number,
585+
586+
// edge sharpening threshold
587+
threshold = 0.03 : Number,
588+
589+
}
590+
```
591+
569592
## RenderTarget2DArray
570593

571594
_extends WebGLArrayRenderTarget_

example/materialBall.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import * as THREE from 'three';
22
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
33
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
44
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
5-
import { PathTracingRenderer, PhysicalPathTracingMaterial, PhysicalCamera, BlurredEnvMapGenerator, EquirectCamera } from '../src/index.js';
5+
import { PathTracingRenderer, PhysicalPathTracingMaterial, PhysicalCamera, BlurredEnvMapGenerator, EquirectCamera, DenoiseMaterial } from '../src/index.js';
66
import { PathTracingSceneWorker } from '../src/workers/PathTracingSceneWorker.js';
77
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
88
import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
99
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
1010

11-
let renderer, controls, sceneInfo, ptRenderer, activeCamera, fsQuad, materials;
11+
let renderer, controls, sceneInfo, ptRenderer, activeCamera, blitQuad, denoiseQuad, materials;
1212
let perspectiveCamera, orthoCamera, equirectCamera;
1313
let envMap, envMapGenerator, scene;
1414
let samplesEl;
@@ -78,6 +78,10 @@ const params = {
7878

7979
multipleImportanceSampling: true,
8080
stableNoise: false,
81+
denoiseEnabled: true,
82+
denoiseSigma: 2.5,
83+
denoiseThreshold: 0.1,
84+
denoiseKSigma: 1.0,
8185
environmentIntensity: 1,
8286
environmentRotation: 0,
8387
environmentBlur: 0.0,
@@ -144,11 +148,13 @@ async function init() {
144148
ptRenderer.material.setDefine( 'FEATURE_MIS', Number( params.multipleImportanceSampling ) );
145149
ptRenderer.tiles.set( params.tiles, params.tiles );
146150

147-
fsQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {
151+
blitQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {
148152
map: ptRenderer.target.texture,
149153
blending: THREE.CustomBlending,
150154
} ) );
151155

156+
denoiseQuad = new FullScreenQuad( new DenoiseMaterial() );
157+
152158
controls = new OrbitControls( perspectiveCamera, renderer.domElement );
153159
controls.addEventListener( 'change', () => {
154160

@@ -273,7 +279,7 @@ async function init() {
273279
ptFolder.add( params, 'acesToneMapping' ).onChange( value => {
274280

275281
renderer.toneMapping = value ? THREE.ACESFilmicToneMapping : THREE.NoToneMapping;
276-
fsQuad.material.needsUpdate = true;
282+
blitQuad.material.needsUpdate = true;
277283

278284
} );
279285
ptFolder.add( params, 'stableNoise' ).onChange( value => {
@@ -315,6 +321,24 @@ async function init() {
315321

316322
} );
317323

324+
const denoiseFolder = gui.addFolder( 'Denoising' );
325+
denoiseFolder.add( params, 'denoiseEnabled' );
326+
denoiseFolder.add( params, 'denoiseSigma', 0.01, 12.0 ).onChange( value => {
327+
328+
denoiseQuad.material.sigma = value;
329+
330+
} );
331+
denoiseFolder.add( params, 'denoiseThreshold', 0.01, 1.0 ).onChange( value => {
332+
333+
denoiseQuad.material.threshold = value;
334+
335+
} );
336+
denoiseFolder.add( params, 'denoiseKSigma', 0.0, 12.0 ).onChange( value => {
337+
338+
denoiseQuad.material.kSigma = value;
339+
340+
} );
341+
318342
const envFolder = gui.addFolder( 'Environment' );
319343
envFolder.add( params, 'environmentIntensity', 0, 10 ).onChange( () => {
320344

@@ -624,9 +648,11 @@ function animate() {
624648

625649
}
626650

651+
const quad = params.denoiseEnabled ? denoiseQuad : blitQuad;
652+
627653
renderer.autoClear = false;
628-
fsQuad.material.map = ptRenderer.target.texture;
629-
fsQuad.render( renderer );
654+
quad.material.map = ptRenderer.target.texture;
655+
quad.render( renderer );
630656
renderer.autoClear = true;
631657

632658
samplesEl.innerText = `Samples: ${ Math.floor( ptRenderer.samples ) }`;

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export * from './utils/BlurredEnvMapGenerator.js';
2424
export * from './utils/IESLoader.js';
2525

2626
// materials
27+
export * from './materials/DenoiseMaterial';
2728
export * from './materials/MaterialBase.js';
2829
export * from './materials/PhysicalPathTracingMaterial.js';
2930

src/materials/DenoiseMaterial.js

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { NoBlending } from 'three';
2+
import { MaterialBase } from './MaterialBase.js';
3+
4+
export class DenoiseMaterial extends MaterialBase {
5+
6+
constructor( parameters ) {
7+
8+
super( {
9+
10+
blending: NoBlending,
11+
12+
transparent: false,
13+
14+
depthWrite: false,
15+
16+
depthTest: false,
17+
18+
defines: {
19+
20+
USE_SLIDER: 0,
21+
22+
},
23+
24+
uniforms: {
25+
26+
sigma: { value: 5.0 },
27+
threshold: { value: 0.03 },
28+
kSigma: { value: 1.0 },
29+
30+
map: { value: null },
31+
32+
},
33+
34+
vertexShader: /* glsl */`
35+
36+
varying vec2 vUv;
37+
38+
void main() {
39+
40+
vUv = uv;
41+
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
42+
43+
}
44+
45+
`,
46+
47+
fragmentShader: /* glsl */`
48+
49+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
50+
// Copyright (c) 2018-2019 Michele Morrone
51+
// All rights reserved.
52+
//
53+
// https://michelemorrone.eu - https://BrutPitt.com
54+
//
55+
56+
// twitter: @BrutPitt - github: BrutPitt
57+
//
58+
// https://github.com/BrutPitt/glslSmartDeNoise/
59+
//
60+
// This software is distributed under the terms of the BSD 2-Clause license
61+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
62+
63+
uniform sampler2D map;
64+
65+
uniform float sigma;
66+
uniform float threshold;
67+
uniform float kSigma;
68+
69+
varying vec2 vUv;
70+
71+
#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439
72+
#define INV_PI 0.31830988618379067153776752674503
73+
74+
// Parameters:
75+
// sampler2D tex - sampler image / texture
76+
// vec2 uv - actual fragment coord
77+
// float sigma > 0 - sigma Standard Deviation
78+
// float kSigma >= 0 - sigma coefficient
79+
// kSigma * sigma --> radius of the circular kernel
80+
// float threshold - edge sharpening threshold
81+
vec4 smartDeNoise( sampler2D tex, vec2 uv, float sigma, float kSigma, float threshold ) {
82+
83+
float radius = round( kSigma * sigma );
84+
float radQ = radius * radius;
85+
86+
float invSigmaQx2 = 0.5 / ( sigma * sigma );
87+
float invSigmaQx2PI = INV_PI * invSigmaQx2;
88+
89+
float invThresholdSqx2 = 0.5 / ( threshold * threshold );
90+
float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold;
91+
92+
vec4 centrPx = texture2D( tex, uv );
93+
centrPx.rgb *= centrPx.a;
94+
95+
float zBuff = 0.0;
96+
vec4 aBuff = vec4( 0.0 );
97+
vec2 size = vec2( textureSize( tex, 0 ) );
98+
99+
vec2 d;
100+
for ( d.x = - radius; d.x <= radius; d.x ++ ) {
101+
102+
float pt = sqrt( radQ - d.x * d.x );
103+
104+
for ( d.y = - pt; d.y <= pt; d.y ++ ) {
105+
106+
float blurFactor = exp( - dot( d, d ) * invSigmaQx2 ) * invSigmaQx2PI;
107+
108+
vec4 walkPx = texture2D( tex, uv + d / size );
109+
walkPx.rgb *= walkPx.a;
110+
111+
vec4 dC = walkPx - centrPx;
112+
float deltaFactor = exp( - dot( dC.rgba, dC.rgba ) * invThresholdSqx2 ) * invThresholdSqrt2PI * blurFactor;
113+
114+
zBuff += deltaFactor;
115+
aBuff += deltaFactor * walkPx;
116+
117+
}
118+
119+
}
120+
121+
return aBuff / zBuff;
122+
123+
}
124+
125+
void main() {
126+
127+
gl_FragColor = smartDeNoise( map, vec2( vUv.x, vUv.y ), sigma, kSigma, threshold );
128+
#include <tonemapping_fragment>
129+
#include <encodings_fragment>
130+
131+
}
132+
133+
`
134+
135+
} );
136+
137+
this.setValues( parameters );
138+
139+
}
140+
141+
}

0 commit comments

Comments
 (0)