|
@@ -20,27 +20,28 @@
|
|
|
<script type="module">
|
|
|
|
|
|
import * as THREE from 'three';
|
|
|
- import { pass, mrt, output, transformedNormalView } from 'three/tsl';
|
|
|
+ import { pass, mrt, output, transformedNormalView, texture, ao } from 'three/tsl';
|
|
|
|
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
|
|
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
|
|
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
|
+ import { SimplexNoise } from 'three/addons/math/SimplexNoise.js';
|
|
|
|
|
|
import Stats from 'three/addons/libs/stats.module.js';
|
|
|
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
|
|
|
|
|
let camera, scene, renderer, postProcessing, controls, clock, stats, mixer;
|
|
|
|
|
|
- let aoPass;
|
|
|
+ let aoPass, blendPassAO, blendPassDenoise, scenePassColor;
|
|
|
|
|
|
const params = {
|
|
|
- blendIntensity: 1,
|
|
|
distanceExponent: 1,
|
|
|
distanceFallOff: 1,
|
|
|
radius: 0.25,
|
|
|
scale: 1,
|
|
|
thickness: 1,
|
|
|
+ denoised: true,
|
|
|
enabled: true
|
|
|
};
|
|
|
|
|
@@ -57,10 +58,10 @@
|
|
|
clock = new THREE.Clock();
|
|
|
|
|
|
const hdrloader = new RGBELoader();
|
|
|
- const texture = await hdrloader.loadAsync( 'textures/equirectangular/quarry_01_1k.hdr' );
|
|
|
- texture.mapping = THREE.EquirectangularReflectionMapping;
|
|
|
+ const envMap = await hdrloader.loadAsync( 'textures/equirectangular/quarry_01_1k.hdr' );
|
|
|
+ envMap.mapping = THREE.EquirectangularReflectionMapping;
|
|
|
|
|
|
- scene.environment = texture;
|
|
|
+ scene.environment = envMap;
|
|
|
|
|
|
renderer = new THREE.WebGPURenderer();
|
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
@@ -86,13 +87,22 @@
|
|
|
normal: transformedNormalView
|
|
|
} ) );
|
|
|
|
|
|
- const scenePassColor = scenePass.getTextureNode( 'output' );
|
|
|
+ scenePassColor = scenePass.getTextureNode( 'output' );
|
|
|
const scenePassNormal = scenePass.getTextureNode( 'normal' );
|
|
|
const scenePassDepth = scenePass.getTextureNode( 'depth' );
|
|
|
|
|
|
- aoPass = scenePassColor.ao( scenePassDepth, scenePassNormal, camera );
|
|
|
+ // ao
|
|
|
|
|
|
- postProcessing.outputNode = aoPass;
|
|
|
+ aoPass = ao( scenePassDepth, scenePassNormal, camera );
|
|
|
+ blendPassAO = aoPass.getTextureNode().mul( scenePassColor );
|
|
|
+
|
|
|
+ // denoise (optional)
|
|
|
+
|
|
|
+ const noiseTexture = texture( generateNoise() );
|
|
|
+ const denoisePass = aoPass.getTextureNode().denoise( scenePassDepth, scenePassNormal, noiseTexture, camera );
|
|
|
+ blendPassDenoise = denoisePass.mul( scenePassColor );
|
|
|
+
|
|
|
+ postProcessing.outputNode = blendPassDenoise;
|
|
|
|
|
|
//
|
|
|
|
|
@@ -119,34 +129,43 @@
|
|
|
|
|
|
const gui = new GUI();
|
|
|
gui.title( 'AO settings' );
|
|
|
- gui.add( params, 'blendIntensity' ).min( 0 ).max( 1 ).onChange( updateParameters );
|
|
|
gui.add( params, 'distanceExponent' ).min( 1 ).max( 4 ).onChange( updateParameters );
|
|
|
gui.add( params, 'distanceFallOff' ).min( 0.01 ).max( 1 ).onChange( updateParameters );
|
|
|
gui.add( params, 'radius' ).min( 0.01 ).max( 1 ).onChange( updateParameters );
|
|
|
gui.add( params, 'scale' ).min( 0.01 ).max( 2 ).onChange( updateParameters );
|
|
|
gui.add( params, 'thickness' ).min( 0.01 ).max( 2 ).onChange( updateParameters );
|
|
|
+ gui.add( params, 'denoised' ).onChange( updatePassChain );
|
|
|
+ gui.add( params, 'enabled' ).onChange( updatePassChain );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function updatePassChain() {
|
|
|
+
|
|
|
+ if ( params.enabled === true ) {
|
|
|
|
|
|
- gui.add( params, 'enabled' ).onChange( ( value ) => {
|
|
|
-
|
|
|
- if ( value === true ) {
|
|
|
+ if ( params.denoised === true ) {
|
|
|
|
|
|
- postProcessing.outputNode = aoPass;
|
|
|
+ postProcessing.outputNode = blendPassDenoise;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- postProcessing.outputNode = scenePassColor;
|
|
|
+ postProcessing.outputNode = blendPassAO;
|
|
|
|
|
|
}
|
|
|
|
|
|
- postProcessing.needsUpdate = true;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ postProcessing.outputNode = scenePassColor;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ postProcessing.needsUpdate = true;
|
|
|
|
|
|
- } );
|
|
|
|
|
|
}
|
|
|
|
|
|
function updateParameters() {
|
|
|
|
|
|
- aoPass.blendIntensity.value = params.blendIntensity;
|
|
|
aoPass.distanceExponent.value = params.distanceExponent;
|
|
|
aoPass.distanceFallOff.value = params.distanceFallOff;
|
|
|
aoPass.radius.value = params.radius;
|
|
@@ -155,6 +174,38 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
+ function generateNoise( size = 64 ) {
|
|
|
+
|
|
|
+ const simplex = new SimplexNoise();
|
|
|
+
|
|
|
+ const arraySize = size * size * 4;
|
|
|
+ const data = new Uint8Array( arraySize );
|
|
|
+
|
|
|
+ for ( let i = 0; i < size; i ++ ) {
|
|
|
+
|
|
|
+ for ( let j = 0; j < size; j ++ ) {
|
|
|
+
|
|
|
+ const x = i;
|
|
|
+ const y = j;
|
|
|
+
|
|
|
+ data[ ( i * size + j ) * 4 ] = ( simplex.noise( x, y ) * 0.5 + 0.5 ) * 255;
|
|
|
+ data[ ( i * size + j ) * 4 + 1 ] = ( simplex.noise( x + size, y ) * 0.5 + 0.5 ) * 255;
|
|
|
+ data[ ( i * size + j ) * 4 + 2 ] = ( simplex.noise( x, y + size ) * 0.5 + 0.5 ) * 255;
|
|
|
+ data[ ( i * size + j ) * 4 + 3 ] = ( simplex.noise( x + size, y + size ) * 0.5 + 0.5 ) * 255;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const noiseTexture = new THREE.DataTexture( data, size, size );
|
|
|
+ noiseTexture.wrapS = THREE.RepeatWrapping;
|
|
|
+ noiseTexture.wrapT = THREE.RepeatWrapping;
|
|
|
+ noiseTexture.needsUpdate = true;
|
|
|
+
|
|
|
+ return noiseTexture;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
function onWindowResize() {
|
|
|
|
|
|
const width = window.innerWidth;
|