|
@@ -1,40 +1,38 @@
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
- <title>three.js webgl - inline tone mapping</title>
|
|
|
+ <title>three.js webgl - tone mapping</title>
|
|
|
<meta charset="utf-8">
|
|
|
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
|
<link type="text/css" rel="stylesheet" href="main.css">
|
|
|
</head>
|
|
|
- <body>
|
|
|
|
|
|
- <div id="container"></div>
|
|
|
- <div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">threejs</a> - Inline Tone Mapping (within a Material's fragment shader) without<br/>using a pre-processing step or float/half buffers by <a href="http://clara.io/" target="_blank" rel="noopener">Ben Houston</a>.</div>
|
|
|
+ <body>
|
|
|
+ <div id="info">
|
|
|
+ <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Tone Mapping<br />
|
|
|
+ Battle Damaged Sci-fi Helmet by
|
|
|
+ <a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
|
|
|
+ <a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> by <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
|
|
|
+ </div>
|
|
|
|
|
|
<script type="module">
|
|
|
|
|
|
import * as THREE from '../build/three.module.js';
|
|
|
|
|
|
- import Stats from './jsm/libs/stats.module.js';
|
|
|
-
|
|
|
import { GUI } from './jsm/libs/dat.gui.module.js';
|
|
|
import { OrbitControls } from './jsm/controls/OrbitControls.js';
|
|
|
+ import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
|
|
|
import { RGBELoader } from './jsm/loaders/RGBELoader.js';
|
|
|
|
|
|
- import { EffectComposer } from './jsm/postprocessing/EffectComposer.js';
|
|
|
- import { RenderPass } from './jsm/postprocessing/RenderPass.js';
|
|
|
- import { ShaderPass } from './jsm/postprocessing/ShaderPass.js';
|
|
|
- import { CopyShader } from './jsm/shaders/CopyShader.js';
|
|
|
+ var mesh, renderer, scene, camera, controls;
|
|
|
+ var gui,
|
|
|
+ guiWhitePoint = null,
|
|
|
+ guiExposure = null;
|
|
|
|
|
|
- var container, stats;
|
|
|
var params = {
|
|
|
- opacity: 1.0,
|
|
|
- roughness: 1.0,
|
|
|
- bumpScale: 1.0,
|
|
|
- exposure: 2.0,
|
|
|
- whitePoint: 5.0,
|
|
|
- toneMapping: "Uncharted2",
|
|
|
- renderMode: "Renderer"
|
|
|
+ exposure: 0.8,
|
|
|
+ whitePoint: 1.0, // applies to Uncharted2 only
|
|
|
+ toneMapping: 'ACESFilmic'
|
|
|
};
|
|
|
|
|
|
var toneMappingOptions = {
|
|
@@ -46,206 +44,155 @@
|
|
|
ACESFilmic: THREE.ACESFilmicToneMapping
|
|
|
};
|
|
|
|
|
|
- var camera, scene, renderer, mesh;
|
|
|
- var composer;
|
|
|
- var standardMaterial, floorMaterial;
|
|
|
-
|
|
|
init();
|
|
|
- animate();
|
|
|
+ render();
|
|
|
|
|
|
function init() {
|
|
|
|
|
|
- container = document.createElement( 'div' );
|
|
|
- document.body.appendChild( container );
|
|
|
+ renderer = new THREE.WebGLRenderer( { antialias: true } );
|
|
|
+ renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ document.body.appendChild( renderer.domElement );
|
|
|
+
|
|
|
+ renderer.toneMapping = toneMappingOptions[ params.toneMapping ];
|
|
|
+ renderer.toneMappingWhitePoint = params.whitePoint;
|
|
|
+ renderer.toneMappingExposure = params.exposure;
|
|
|
|
|
|
- camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
|
|
|
- camera.position.set( 0.0, 40, 40 * 3.5 );
|
|
|
+ renderer.outputEncoding = THREE.sRGBEncoding;
|
|
|
|
|
|
scene = new THREE.Scene();
|
|
|
|
|
|
- renderer = new THREE.WebGLRenderer();
|
|
|
-
|
|
|
- standardMaterial = new THREE.MeshStandardMaterial( {
|
|
|
- bumpScale: - 0.05,
|
|
|
- color: 0xffffff,
|
|
|
- metalness: 0.9,
|
|
|
- roughness: 0.8,
|
|
|
- premultipliedAlpha: true,
|
|
|
- transparent: true
|
|
|
- } );
|
|
|
-
|
|
|
- var textureLoader = new THREE.TextureLoader();
|
|
|
- textureLoader.load( "textures/brick_diffuse.jpg", function ( map ) {
|
|
|
-
|
|
|
- map.wrapS = THREE.RepeatWrapping;
|
|
|
- map.wrapT = THREE.RepeatWrapping;
|
|
|
- map.encoding = THREE.sRGBEncoding;
|
|
|
- map.anisotropy = 4;
|
|
|
- map.repeat.set( 9, 0.5 );
|
|
|
- standardMaterial.map = map;
|
|
|
- standardMaterial.needsUpdate = true;
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- textureLoader.load( "textures/brick_bump.jpg", function ( map ) {
|
|
|
-
|
|
|
- map.wrapS = THREE.RepeatWrapping;
|
|
|
- map.wrapT = THREE.RepeatWrapping;
|
|
|
- map.anisotropy = 4;
|
|
|
- map.repeat.set( 9, 0.5 );
|
|
|
- standardMaterial.bumpMap = map;
|
|
|
- standardMaterial.needsUpdate = true;
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- textureLoader.load( "textures/brick_roughness.jpg", function ( map ) {
|
|
|
-
|
|
|
- map.wrapS = THREE.RepeatWrapping;
|
|
|
- map.wrapT = THREE.RepeatWrapping;
|
|
|
- map.anisotropy = 4;
|
|
|
- map.repeat.set( 9, 0.5 );
|
|
|
- standardMaterial.roughnessMap = map;
|
|
|
- standardMaterial.needsUpdate = true;
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- var geometry = new THREE.TorusKnotBufferGeometry( 18, 8, 150, 20 );
|
|
|
- mesh = new THREE.Mesh( geometry, standardMaterial );
|
|
|
- mesh.castShadow = true;
|
|
|
- mesh.receiveShadow = true;
|
|
|
- scene.add( mesh );
|
|
|
-
|
|
|
- floorMaterial = new THREE.MeshStandardMaterial( {
|
|
|
- map: null,
|
|
|
- roughnessMap: null,
|
|
|
- color: 0x888888,
|
|
|
- metalness: 0.0,
|
|
|
- roughness: 1.0,
|
|
|
- side: THREE.BackSide
|
|
|
- } );
|
|
|
-
|
|
|
- var geometry = new THREE.BoxBufferGeometry( 200, 200, 200 );
|
|
|
- var floor = new THREE.Mesh( geometry, floorMaterial );
|
|
|
- floor.position.y = 50;
|
|
|
- floor.rotation.x = - Math.PI * 0.5;
|
|
|
- floor.receiveShadow = true;
|
|
|
- scene.add( floor );
|
|
|
+ camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
|
|
|
+ camera.position.set( - 1.8, 0.6, 2.7 );
|
|
|
+
|
|
|
+ controls = new OrbitControls( camera, renderer.domElement );
|
|
|
+ controls.addEventListener( 'change', render ); // use if there is no animation loop
|
|
|
+ controls.enableZoom = false;
|
|
|
+ controls.enablePan = false;
|
|
|
+ controls.target.set( 0, 0, - 0.2 );
|
|
|
+ controls.update();
|
|
|
+
|
|
|
+ var pmremGenerator = new THREE.PMREMGenerator( renderer );
|
|
|
+ pmremGenerator.compileEquirectangularShader();
|
|
|
|
|
|
new RGBELoader()
|
|
|
.setDataType( THREE.UnsignedByteType )
|
|
|
.setPath( 'textures/equirectangular/' )
|
|
|
- .load( 'venice_sunset_1k.hdr', function ( hdrEquirect ) {
|
|
|
+ .load( 'royal_esplanade_1k.hdr', function ( texture ) {
|
|
|
|
|
|
- scene.environment = pmremGenerator.fromEquirectangular( hdrEquirect ).texture;
|
|
|
+ var envMap = pmremGenerator.fromEquirectangular( texture ).texture;
|
|
|
|
|
|
- hdrEquirect.dispose();
|
|
|
+ scene.background = envMap;
|
|
|
+ scene.environment = envMap;
|
|
|
+
|
|
|
+ texture.dispose();
|
|
|
pmremGenerator.dispose();
|
|
|
|
|
|
- } );
|
|
|
+ render();
|
|
|
|
|
|
- var pmremGenerator = new THREE.PMREMGenerator( renderer );
|
|
|
- pmremGenerator.compileEquirectangularShader();
|
|
|
+ // model
|
|
|
|
|
|
- // Lights
|
|
|
+ var loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
|
|
|
+ loader.load( 'DamagedHelmet.gltf', function ( gltf ) {
|
|
|
|
|
|
- scene.add( new THREE.HemisphereLight( 0x111111, 0x000000 ) );
|
|
|
+ gltf.scene.traverse( function ( child ) {
|
|
|
|
|
|
- var spotLight = new THREE.SpotLight( 0xffffff );
|
|
|
- spotLight.position.set( 50, 100, 50 );
|
|
|
- spotLight.angle = Math.PI / 7;
|
|
|
- spotLight.decay = 2;
|
|
|
- spotLight.distance = 300;
|
|
|
- spotLight.penumbra = 0.8;
|
|
|
- spotLight.castShadow = true;
|
|
|
- scene.add( spotLight );
|
|
|
-
|
|
|
- renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
- renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
- renderer.shadowMap.enabled = true;
|
|
|
- container.appendChild( renderer.domElement );
|
|
|
+ if ( child.isMesh ) {
|
|
|
|
|
|
- renderer.outputEncoding = THREE.sRGBEncoding;
|
|
|
+ mesh = child;
|
|
|
+ scene.add( mesh );
|
|
|
|
|
|
- composer = new EffectComposer( renderer );
|
|
|
- composer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ }
|
|
|
|
|
|
- var renderScene = new RenderPass( scene, camera );
|
|
|
- composer.addPass( renderScene );
|
|
|
+ } );
|
|
|
|
|
|
- var copyPass = new ShaderPass( CopyShader );
|
|
|
- composer.addPass( copyPass );
|
|
|
+ render();
|
|
|
|
|
|
- stats = new Stats();
|
|
|
- container.appendChild( stats.dom );
|
|
|
+ } );
|
|
|
|
|
|
- new OrbitControls( camera, renderer.domElement );
|
|
|
+ } );
|
|
|
|
|
|
window.addEventListener( 'resize', onWindowResize, false );
|
|
|
|
|
|
- var gui = new GUI();
|
|
|
+ gui = new GUI();
|
|
|
+
|
|
|
+ gui.add( params, 'toneMapping', Object.keys( toneMappingOptions ) )
|
|
|
+
|
|
|
+ .onChange( function () {
|
|
|
+
|
|
|
+ updateGUI();
|
|
|
+
|
|
|
+ renderer.toneMapping = toneMappingOptions[ params.toneMapping ];
|
|
|
+ mesh.material.needsUpdate = true;
|
|
|
+ render();
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ updateGUI();
|
|
|
|
|
|
- gui.add( params, 'toneMapping', Object.keys( toneMappingOptions ) );
|
|
|
- gui.add( params, 'exposure', 0, 10 );
|
|
|
- gui.add( params, 'whitePoint', 0, 10 );
|
|
|
- gui.add( params, 'opacity', 0, 1 );
|
|
|
- gui.add( params, 'renderMode', [ 'Renderer', 'Composer' ] );
|
|
|
gui.open();
|
|
|
|
|
|
}
|
|
|
|
|
|
- function onWindowResize() {
|
|
|
+ function updateGUI() {
|
|
|
|
|
|
- var width = window.innerWidth;
|
|
|
- var height = window.innerHeight;
|
|
|
+ if ( guiWhitePoint !== null ) {
|
|
|
|
|
|
- camera.aspect = width / height;
|
|
|
- camera.updateProjectionMatrix();
|
|
|
+ gui.remove( guiWhitePoint );
|
|
|
+ guiWhitePoint = null;
|
|
|
|
|
|
- renderer.setSize( width, height );
|
|
|
- composer.setSize( width, height );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ if ( guiExposure !== null ) {
|
|
|
+
|
|
|
+ gui.remove( guiExposure );
|
|
|
+ guiExposure = null;
|
|
|
|
|
|
- //
|
|
|
+ }
|
|
|
|
|
|
- function animate() {
|
|
|
+ if ( params.toneMapping !== 'None' ) {
|
|
|
|
|
|
- requestAnimationFrame( animate );
|
|
|
+ guiExposure = gui.add( params, 'exposure', 0, 2 )
|
|
|
|
|
|
- stats.begin();
|
|
|
- render();
|
|
|
- stats.end();
|
|
|
+ .onChange( function () {
|
|
|
|
|
|
- }
|
|
|
+ renderer.toneMappingExposure = params.exposure;
|
|
|
+ render();
|
|
|
|
|
|
- function render() {
|
|
|
+ } );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( params.toneMapping === 'Uncharted2' ) {
|
|
|
|
|
|
- standardMaterial.roughness = params.roughness;
|
|
|
- standardMaterial.bumpScale = - 0.05 * params.bumpScale;
|
|
|
- standardMaterial.opacity = params.opacity;
|
|
|
+ guiWhitePoint = gui.add( params, 'whitePoint', 0, 2 )
|
|
|
|
|
|
- if ( renderer.toneMapping !== toneMappingOptions[ params.toneMapping ] ) {
|
|
|
+ .onChange( function () {
|
|
|
|
|
|
- renderer.toneMapping = toneMappingOptions[ params.toneMapping ];
|
|
|
- standardMaterial.needsUpdate = true;
|
|
|
- floorMaterial.needsUpdate = true;
|
|
|
+ renderer.toneMappingWhitePoint = params.whitePoint;
|
|
|
+ render();
|
|
|
+
|
|
|
+ } );
|
|
|
|
|
|
}
|
|
|
|
|
|
- renderer.toneMappingExposure = params.exposure;
|
|
|
- renderer.toneMappingWhitePoint = params.whitePoint;
|
|
|
+ }
|
|
|
|
|
|
- mesh.rotation.y += 0.005;
|
|
|
+ function onWindowResize() {
|
|
|
|
|
|
- if ( params.renderMode === "Composer" ) {
|
|
|
+ camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
|
|
|
- composer.render();
|
|
|
+ camera.updateProjectionMatrix();
|
|
|
|
|
|
- } else {
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
|
|
|
- renderer.render( scene, camera );
|
|
|
+ render();
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+ function render() {
|
|
|
+
|
|
|
+ renderer.render( scene, camera );
|
|
|
|
|
|
}
|
|
|
|