Jelajahi Sumber

Examples: Add webgl_loader_gainmap (#27183)

* initial work for adding gainmap loader example

* added tags

* better example with file size and resolution comparison

* updated example to latest gainmap-js release 2.0.3 which fixes some compatibility problems in some browsers and platforms + adds progress handler for separate gainmap data

* should solve test failing problems

* updated loader name to HDRJPGLoader

* renamed vars for clarity

* some more renaming

* restored magFilter configuration for HDR
Daniele Pelagatti 1 tahun lalu
induk
melakukan
0582b0000c

+ 1 - 0
examples/files.json

@@ -82,6 +82,7 @@
 		"webgl_loader_collada_skinning",
 		"webgl_loader_collada_skinning",
 		"webgl_loader_draco",
 		"webgl_loader_draco",
 		"webgl_loader_fbx",
 		"webgl_loader_fbx",
+		"webgl_loader_gainmap",
 		"webgl_loader_fbx_nurbs",
 		"webgl_loader_fbx_nurbs",
 		"webgl_loader_gcode",
 		"webgl_loader_gcode",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf",

TEMPAT SAMPAH
examples/screenshots/webgl_loader_gainmap.jpg


+ 1 - 0
examples/tags.json

@@ -40,6 +40,7 @@
 	"webgl_lines_fat": [ "gpu", "stats", "panel" ],
 	"webgl_lines_fat": [ "gpu", "stats", "panel" ],
 	"webgl_lines_fat_raycasting": [ "gpu", "stats", "panel", "raycast" ],
 	"webgl_lines_fat_raycasting": [ "gpu", "stats", "panel", "raycast" ],
 	"webgl_loader_ttf": [ "text", "font" ],
 	"webgl_loader_ttf": [ "text", "font" ],
+	"webgl_loader_gainmap": [ "external", "hdr", "gainmap", "ultrahdr" ],
 	"webgl_loader_pdb": [ "molecules", "css2d" ],
 	"webgl_loader_pdb": [ "molecules", "css2d" ],
 	"webgl_loader_ldraw": [ "lego" ],
 	"webgl_loader_ldraw": [ "lego" ],
 	"webgl_loader_ifc": [ "external" ],
 	"webgl_loader_ifc": [ "external" ],

TEMPAT SAMPAH
examples/textures/gainmap/spruit_sunrise_1k.hdr


TEMPAT SAMPAH
examples/textures/gainmap/spruit_sunrise_4k-gainmap.webp


TEMPAT SAMPAH
examples/textures/gainmap/spruit_sunrise_4k.jpg


+ 29 - 0
examples/textures/gainmap/spruit_sunrise_4k.json

@@ -0,0 +1,29 @@
+{
+  "gainMapMax": [
+    15.99929538702341,
+    15.99929538702341,
+    15.99929538702341
+  ],
+  "gainMapMin": [
+    0,
+    0,
+    0
+  ],
+  "gamma": [
+    1,
+    1,
+    1
+  ],
+  "hdrCapacityMax": 15.99929538702341,
+  "hdrCapacityMin": 0,
+  "offsetHdr": [
+    0.015625,
+    0.015625,
+    0.015625
+  ],
+  "offsetSdr": [
+    0.015625,
+    0.015625,
+    0.015625
+  ]
+}

TEMPAT SAMPAH
examples/textures/gainmap/spruit_sunrise_4k.webp


+ 326 - 0
examples/webgl_loader_gainmap.html

@@ -0,0 +1,326 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - gainmap hdr</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">
+		<style>
+			.lbl {
+				color: #fff;
+				font-size: 16px;
+				font-weight: bold;
+				position: absolute;
+				bottom: 0px;
+				z-index: 100;
+				text-shadow: #000 1px 1px 1px;
+				background-color: rgba(0,0,0,0.85);
+				padding: 1em;
+			}
+
+			#lbl_left {
+				text-align:left;
+				left:0px;
+			}
+		</style>
+	</head>
+
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - gain map (ultra hdr) loader <br/>
+			Gain map images converted from hdr with <a href="https://gainmap-creator.mono-grid.com/" target="_blank" rel="noopener">Gain map converter</a>. <br />
+			See external <a href="https://github.com/MONOGRID/gainmap-js" target="_blank" rel="noopener">gainmap-js</a> for more information on how to use and create gain map images.
+		</div>
+
+		<div id="lbl_left" class="lbl"></div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/",
+					"@monogrid/gainmap-js": "https://unpkg.com/@monogrid/[email protected]/dist/decode.js"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import Stats from 'three/addons/libs/stats.module.js';
+
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+
+			import { GainMapLoader, HDRJPGLoader } from '@monogrid/gainmap-js';
+
+			const params = {
+				envMap: 'HDR JPG',
+				roughness: 0.0,
+				metalness: 0.0,
+				exposure: 1.0,
+				debug: false
+			};
+
+			let container, stats;
+			let camera, scene, renderer, controls;
+			let torusMesh, planeMesh;
+			let hdrJpg, hdrJpgPMREMRenderTarget, hdrJpgEquirectangularMap;
+			let gainMap, gainMapPMREMRenderTarget, gainMapBackground;
+			let hdrPMREMRenderTarget, hdrEquirectangularMap;
+
+			
+			const fileSizes = {};
+			const resolutions = {};
+
+			init();
+			animate();
+
+			function init() {
+
+				const lbl = document.getElementById( 'lbl_left' );
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set( 0, 0, 120 );
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x000000 );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+
+				//
+
+				let geometry = new THREE.TorusKnotGeometry( 18, 8, 150, 20 );
+				// let geometry = new THREE.SphereGeometry( 26, 64, 32 );
+				let material = new THREE.MeshStandardMaterial( {
+					color: 0xffffff,
+					metalness: params.metalness,
+					roughness: params.roughness
+				} );
+
+				torusMesh = new THREE.Mesh( geometry, material );
+				scene.add( torusMesh );
+
+
+				geometry = new THREE.PlaneGeometry( 200, 200 );
+				material = new THREE.MeshBasicMaterial();
+
+				planeMesh = new THREE.Mesh( geometry, material );
+				planeMesh.position.y = - 50;
+				planeMesh.rotation.x = - Math.PI * 0.5;
+				scene.add( planeMesh );
+
+
+				const pmremGenerator = new THREE.PMREMGenerator( renderer );
+				pmremGenerator.compileEquirectangularShader();
+
+				THREE.DefaultLoadingManager.onLoad = function ( ) {
+
+					pmremGenerator.dispose();
+
+				};
+
+
+
+				hdrJpg = new HDRJPGLoader( renderer )
+					.load( 'textures/gainmap/spruit_sunrise_4k.jpg', function ( ) {
+
+						resolutions[ 'HDR JPG' ] = hdrJpg.width + 'x' + hdrJpg.height;
+						displayStats( 'HDR JPG' );
+			
+						hdrJpgPMREMRenderTarget = pmremGenerator.fromEquirectangular( hdrJpg.renderTarget.texture );
+			
+						hdrJpgEquirectangularMap = hdrJpg.toDataTexture();
+						hdrJpgEquirectangularMap.mapping = THREE.EquirectangularReflectionMapping;
+						hdrJpgEquirectangularMap.minFilter = THREE.LinearFilter;
+						hdrJpgEquirectangularMap.magFilter = THREE.LinearFilter;
+						hdrJpgEquirectangularMap.generateMipmaps = false;
+
+						hdrJpgEquirectangularMap.needsUpdate = true;
+
+					}, function ( progress ) {
+
+						fileSizes[ 'HDR JPG' ] = humanFileSize( progress.total );
+			
+					} );
+
+				gainMap = new GainMapLoader( renderer )
+					.load( [
+						'textures/gainmap/spruit_sunrise_4k.webp',
+						'textures/gainmap/spruit_sunrise_4k-gainmap.webp',
+						'textures/gainmap/spruit_sunrise_4k.json'
+					], function ( ) {
+
+						resolutions[ 'Webp Gain map (separate)' ] = gainMap.width + 'x' + gainMap.height;
+			
+						gainMapPMREMRenderTarget = pmremGenerator.fromEquirectangular( gainMap.renderTarget.texture );
+
+						gainMapBackground = gainMap.toDataTexture();
+						gainMapBackground.mapping = THREE.EquirectangularReflectionMapping;
+						gainMapBackground.minFilter = THREE.LinearFilter;
+						gainMapBackground.magFilter = THREE.LinearFilter;
+						gainMapBackground.generateMipmaps = false;
+
+						gainMapBackground.needsUpdate = true;
+			
+					}, function ( progress ) {
+
+						fileSizes[ 'Webp Gain map (separate)' ] = humanFileSize( progress.total );
+
+					} );
+
+				hdrEquirectangularMap = new RGBELoader()
+					.load( 'textures/gainmap/spruit_sunrise_1k.hdr', function ( ) {
+
+						resolutions[ 'HDR' ] = hdrEquirectangularMap.image.width + 'x' + hdrEquirectangularMap.image.height;
+			
+						hdrPMREMRenderTarget = pmremGenerator.fromEquirectangular( hdrEquirectangularMap );
+
+						hdrEquirectangularMap.mapping = THREE.EquirectangularReflectionMapping;
+						hdrEquirectangularMap.minFilter = THREE.LinearFilter;
+						hdrEquirectangularMap.magFilter = THREE.LinearFilter;
+						hdrEquirectangularMap.needsUpdate = true;
+
+					}, function ( progress ) {
+
+						fileSizes[ 'HDR' ] = humanFileSize( progress.total );
+
+					} );
+
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.minDistance = 50;
+				controls.maxDistance = 300;
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				const gui = new GUI();
+
+				gui.add( params, 'envMap', [ 'HDR JPG', 'Webp Gain map (separate)', 'HDR' ] ).onChange( displayStats );
+				gui.add( params, 'roughness', 0, 1, 0.01 );
+				gui.add( params, 'metalness', 0, 1, 0.01 );
+				gui.add( params, 'exposure', 0, 2, 0.01 );
+				gui.add( params, 'debug' );
+				gui.open();
+			
+				function displayStats( value ) {
+
+					lbl.innerHTML = value + ' size : ' + fileSizes[ value ] + ', Resolution: ' + resolutions[ value ];
+
+				}
+
+			}
+
+
+			function humanFileSize( bytes, si = true, dp = 1 ) {
+
+				const thresh = si ? 1000 : 1024;
+
+				if ( Math.abs( bytes ) < thresh ) {
+
+					return bytes + ' B';
+
+				}
+
+				const units = si
+					? [ 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ]
+					: [ 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' ];
+				let u = - 1;
+				const r = 10 ** dp;
+
+				do {
+
+					bytes /= thresh;
+					++ u;
+
+				} while ( Math.round( Math.abs( bytes ) * r ) / r >= thresh && u < units.length - 1 );
+
+				return bytes.toFixed( dp ) + ' ' + units[ u ];
+
+			}
+
+
+			function onWindowResize() {
+
+				const width = window.innerWidth;
+				const height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				stats.begin();
+				render();
+				stats.end();
+
+			}
+
+			function render() {
+
+				torusMesh.material.roughness = params.roughness;
+				torusMesh.material.metalness = params.metalness;
+
+				let pmremRenderTarget, equirectangularMap;
+
+				switch ( params.envMap ) {
+
+					case 'HDR JPG':
+						pmremRenderTarget = hdrJpgPMREMRenderTarget;
+						equirectangularMap = hdrJpgEquirectangularMap || hdrJpg.renderTarget.texture;
+						break;
+					case 'Webp Gain map (separate)':
+						pmremRenderTarget = gainMapPMREMRenderTarget;
+						equirectangularMap = gainMapBackground || gainMap.renderTarget.texture;
+						break;
+					case 'HDR':
+						pmremRenderTarget = hdrPMREMRenderTarget;
+						equirectangularMap = hdrEquirectangularMap;
+						break;
+
+				}
+
+				const newEnvMap = pmremRenderTarget ? pmremRenderTarget.texture : null;
+
+				if ( newEnvMap && newEnvMap !== torusMesh.material.envMap ) {
+
+					torusMesh.material.envMap = newEnvMap;
+					torusMesh.material.needsUpdate = true;
+
+					planeMesh.material.map = newEnvMap;
+					planeMesh.material.needsUpdate = true;
+
+				}
+
+				torusMesh.rotation.y += 0.005;
+				planeMesh.visible = params.debug;
+
+				scene.background = equirectangularMap;
+				renderer.toneMappingExposure = params.exposure;
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>