You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

271 lines
8.2 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - progressive lightmap accumulation</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">three.js</a> - Progressive Lightmaps by <a href="https://github.com/zalo" target="_blank" rel="noopener">zalo</a><br/>
[Inspired by <a href="http://madebyevan.com/shaders/lightmap/" target="_blank" rel="noopener">evanw's Lightmap Generation</a>]
</div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
import { ProgressiveLightMap } from 'three/addons/misc/ProgressiveLightMap.js';
// ShadowMap + LightMap Res and Number of Directional Lights
const shadowMapRes = 512, lightMapRes = 1024, lightCount = 8;
let camera, scene, renderer, controls, control, control2,
object = new THREE.Mesh(), lightOrigin = null, progressiveSurfacemap;
const dirLights = [], lightmapObjects = [];
const params = { 'Enable': true, 'Blur Edges': true, 'Blend Window': 200,
'Light Radius': 50, 'Ambient Weight': 0.5, 'Debug Lightmap': false };
init();
createGUI();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
document.body.appendChild( renderer.domElement );
// camera
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 100, 200 );
camera.name = 'Camera';
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x949494 );
scene.fog = new THREE.Fog( 0x949494, 1000, 3000 );
// progressive lightmap
progressiveSurfacemap = new ProgressiveLightMap( renderer, lightMapRes );
// directional lighting "origin"
lightOrigin = new THREE.Group();
lightOrigin.position.set( 60, 150, 100 );
scene.add( lightOrigin );
// transform gizmo
control = new TransformControls( camera, renderer.domElement );
control.addEventListener( 'dragging-changed', ( event ) => {
controls.enabled = ! event.value;
} );
control.attach( lightOrigin );
scene.add( control );
// create 8 directional lights to speed up the convergence
for ( let l = 0; l < lightCount; l ++ ) {
const dirLight = new THREE.DirectionalLight( 0xffffff, 1.0 / lightCount );
dirLight.name = 'Dir. Light ' + l;
dirLight.position.set( 200, 200, 200 );
dirLight.castShadow = true;
dirLight.shadow.camera.near = 100;
dirLight.shadow.camera.far = 5000;
dirLight.shadow.camera.right = 150;
dirLight.shadow.camera.left = - 150;
dirLight.shadow.camera.top = 150;
dirLight.shadow.camera.bottom = - 150;
dirLight.shadow.mapSize.width = shadowMapRes;
dirLight.shadow.mapSize.height = shadowMapRes;
lightmapObjects.push( dirLight );
dirLights.push( dirLight );
}
// ground
const groundMesh = new THREE.Mesh(
new THREE.PlaneGeometry( 600, 600 ),
new THREE.MeshPhongMaterial( { color: 0xffffff, depthWrite: true } )
);
groundMesh.position.y = - 0.1;
groundMesh.rotation.x = - Math.PI / 2;
groundMesh.name = 'Ground Mesh';
lightmapObjects.push( groundMesh );
scene.add( groundMesh );
// model
function loadModel() {
object.traverse( function ( child ) {
if ( child.isMesh ) {
child.name = 'Loaded Mesh';
child.castShadow = true;
child.receiveShadow = true;
child.material = new THREE.MeshPhongMaterial();
// This adds the model to the lightmap
lightmapObjects.push( child );
progressiveSurfacemap.addObjectsToLightMap( lightmapObjects );
} else {
child.layers.disableAll(); // Disable Rendering for this
}
} );
scene.add( object );
object.scale.set( 2, 2, 2 );
object.position.set( 0, - 16, 0 );
control2 = new TransformControls( camera, renderer.domElement );
control2.addEventListener( 'dragging-changed', ( event ) => {
controls.enabled = ! event.value;
} );
control2.attach( object );
scene.add( control2 );
const lightTarget = new THREE.Group();
lightTarget.position.set( 0, 20, 0 );
for ( let l = 0; l < dirLights.length; l ++ ) {
dirLights[ l ].target = lightTarget;
}
object.add( lightTarget );
if ( typeof TESTING !== 'undefined' ) {
for ( let i = 0; i < 300; i ++ ) {
render();
}
}
}
const manager = new THREE.LoadingManager( loadModel );
const loader = new GLTFLoader( manager );
loader.load( 'models/gltf/ShadowmappableMesh.glb', function ( obj ) {
object = obj.scene.children[ 0 ];
} );
// controls
controls = new OrbitControls( camera, renderer.domElement );
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;
controls.screenSpacePanning = true;
controls.minDistance = 100;
controls.maxDistance = 500;
controls.maxPolarAngle = Math.PI / 1.5;
controls.target.set( 0, 100, 0 );
window.addEventListener( 'resize', onWindowResize );
}
function createGUI() {
const gui = new GUI( { name: 'Accumulation Settings' } );
gui.add( params, 'Enable' );
gui.add( params, 'Blur Edges' );
gui.add( params, 'Blend Window', 1, 500 ).step( 1 );
gui.add( params, 'Light Radius', 0, 200 ).step( 10 );
gui.add( params, 'Ambient Weight', 0, 1 ).step( 0.1 );
gui.add( params, 'Debug Lightmap' );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function render() {
// Update the inertia on the orbit controls
controls.update();
// Accumulate Surface Maps
if ( params[ 'Enable' ] ) {
progressiveSurfacemap.update( camera, params[ 'Blend Window' ], params[ 'Blur Edges' ] );
if ( ! progressiveSurfacemap.firstUpdate ) {
progressiveSurfacemap.showDebugLightmap( params[ 'Debug Lightmap' ] );
}
}
// Manually Update the Directional Lights
for ( let l = 0; l < dirLights.length; l ++ ) {
// Sometimes they will be sampled from the target direction
// Sometimes they will be uniformly sampled from the upper hemisphere
if ( Math.random() > params[ 'Ambient Weight' ] ) {
dirLights[ l ].position.set(
lightOrigin.position.x + ( Math.random() * params[ 'Light Radius' ] ),
lightOrigin.position.y + ( Math.random() * params[ 'Light Radius' ] ),
lightOrigin.position.z + ( Math.random() * params[ 'Light Radius' ] ) );
} else {
// Uniform Hemispherical Surface Distribution for Ambient Occlusion
const lambda = Math.acos( 2 * Math.random() - 1 ) - ( 3.14159 / 2.0 );
const phi = 2 * 3.14159 * Math.random();
dirLights[ l ].position.set(
( ( Math.cos( lambda ) * Math.cos( phi ) ) * 300 ) + object.position.x,
Math.abs( ( Math.cos( lambda ) * Math.sin( phi ) ) * 300 ) + object.position.y + 20,
( Math.sin( lambda ) * 300 ) + object.position.z
);
}
}
// Render Scene
renderer.render( scene, camera );
}
function animate() {
requestAnimationFrame( animate );
render();
}
</script>
</body>
</html>