|
@@ -44,8 +44,8 @@
|
|
"three": "../build/three.module.js",
|
|
"three": "../build/three.module.js",
|
|
"three/addons/": "./jsm/",
|
|
"three/addons/": "./jsm/",
|
|
"three/examples/": "./",
|
|
"three/examples/": "./",
|
|
- "three-gpu-pathtracer": "https://cdn.jsdelivr.net/npm/[email protected]0/build/index.module.js",
|
|
|
|
- "three-mesh-bvh": "https://cdn.jsdelivr.net/npm/[email protected].3/build/index.module.js"
|
|
|
|
|
|
+ "three-gpu-pathtracer": "https://cdn.jsdelivr.net/npm/[email protected]1/build/index.module.js",
|
|
|
|
+ "three-mesh-bvh": "https://cdn.jsdelivr.net/npm/[email protected].4/build/index.module.js"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
</script>
|
|
@@ -60,14 +60,12 @@
|
|
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
|
|
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
|
|
import { LDrawLoader } from 'three/addons/loaders/LDrawLoader.js';
|
|
import { LDrawLoader } from 'three/addons/loaders/LDrawLoader.js';
|
|
import { LDrawUtils } from 'three/addons/utils/LDrawUtils.js';
|
|
import { LDrawUtils } from 'three/addons/utils/LDrawUtils.js';
|
|
- import { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';
|
|
|
|
|
|
|
|
- import { PhysicalPathTracingMaterial, PathTracingRenderer, MaterialReducer, BlurredEnvMapGenerator, PathTracingSceneGenerator, GradientEquirectTexture } from 'three-gpu-pathtracer';
|
|
|
|
|
|
+ import { WebGLPathTracer, BlurredEnvMapGenerator, GradientEquirectTexture } from 'three-gpu-pathtracer';
|
|
|
|
|
|
let progressBarDiv, samplesEl;
|
|
let progressBarDiv, samplesEl;
|
|
let camera, scene, renderer, controls, gui;
|
|
let camera, scene, renderer, controls, gui;
|
|
- let pathTracer, sceneInfo, fsQuad, floor;
|
|
|
|
- let delaySamples = 0;
|
|
|
|
|
|
+ let pathTracer, floor, gradientMap;
|
|
|
|
|
|
const params = {
|
|
const params = {
|
|
enable: true,
|
|
enable: true,
|
|
@@ -89,7 +87,6 @@
|
|
};
|
|
};
|
|
|
|
|
|
init();
|
|
init();
|
|
- render();
|
|
|
|
|
|
|
|
function init() {
|
|
function init() {
|
|
|
|
|
|
@@ -97,42 +94,32 @@
|
|
camera.position.set( 150, 200, 250 );
|
|
camera.position.set( 150, 200, 250 );
|
|
|
|
|
|
// initialize the renderer
|
|
// initialize the renderer
|
|
- renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true, premultipliedAlpha: false } );
|
|
|
|
|
|
+ renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true, preserveDrawingBuffer: true, premultipliedAlpha: false } );
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
- renderer.setClearColor( 0xdddddd );
|
|
|
|
document.body.appendChild( renderer.domElement );
|
|
document.body.appendChild( renderer.domElement );
|
|
|
|
|
|
- const gradientMap = new GradientEquirectTexture();
|
|
|
|
|
|
+ gradientMap = new GradientEquirectTexture();
|
|
gradientMap.topColor.set( 0xeeeeee );
|
|
gradientMap.topColor.set( 0xeeeeee );
|
|
gradientMap.bottomColor.set( 0xeaeaea );
|
|
gradientMap.bottomColor.set( 0xeaeaea );
|
|
gradientMap.update();
|
|
gradientMap.update();
|
|
|
|
|
|
// initialize the pathtracer
|
|
// initialize the pathtracer
|
|
- pathTracer = new PathTracingRenderer( renderer );
|
|
|
|
- pathTracer.camera = camera;
|
|
|
|
- pathTracer.alpha = true;
|
|
|
|
|
|
+ pathTracer = new WebGLPathTracer( renderer );
|
|
|
|
+ pathTracer.filterGlossyFactor = 1;
|
|
|
|
+ pathTracer.minSamples = 3;
|
|
|
|
+ pathTracer.renderScale = params.resolutionScale;
|
|
pathTracer.tiles.set( params.tiles, params.tiles );
|
|
pathTracer.tiles.set( params.tiles, params.tiles );
|
|
- pathTracer.material = new PhysicalPathTracingMaterial( {
|
|
|
|
- filterGlossyFactor: 0.5,
|
|
|
|
- backgroundMap: gradientMap,
|
|
|
|
- } );
|
|
|
|
- pathTracer.material.setDefine( 'FEATURE_MIS', 1 );
|
|
|
|
-
|
|
|
|
- fsQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {
|
|
|
|
- map: pathTracer.target.texture,
|
|
|
|
- blending: THREE.CustomBlending
|
|
|
|
- } ) );
|
|
|
|
|
|
|
|
// scene
|
|
// scene
|
|
scene = new THREE.Scene();
|
|
scene = new THREE.Scene();
|
|
|
|
+ scene.background = gradientMap;
|
|
|
|
|
|
controls = new OrbitControls( camera, renderer.domElement );
|
|
controls = new OrbitControls( camera, renderer.domElement );
|
|
controls.addEventListener( 'change', () => {
|
|
controls.addEventListener( 'change', () => {
|
|
|
|
|
|
- delaySamples = 5;
|
|
|
|
- pathTracer.reset();
|
|
|
|
|
|
+ pathTracer.updateCamera();
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
@@ -161,7 +148,7 @@
|
|
progressBarDiv.innerText = 'Loading...';
|
|
progressBarDiv.innerText = 'Loading...';
|
|
|
|
|
|
let model = null;
|
|
let model = null;
|
|
- let envMap = null;
|
|
|
|
|
|
+ let environment = null;
|
|
|
|
|
|
updateProgressBar( 0 );
|
|
updateProgressBar( 0 );
|
|
showProgressBar();
|
|
showProgressBar();
|
|
@@ -173,12 +160,22 @@
|
|
.loadAsync( 'models/7140-1-X-wingFighter.mpd_Packed.mpd', onProgress )
|
|
.loadAsync( 'models/7140-1-X-wingFighter.mpd_Packed.mpd', onProgress )
|
|
.then( function ( legoGroup ) {
|
|
.then( function ( legoGroup ) {
|
|
|
|
|
|
|
|
+ // Convert from LDraw coordinates: rotate 180 degrees around OX
|
|
legoGroup = LDrawUtils.mergeObject( legoGroup );
|
|
legoGroup = LDrawUtils.mergeObject( legoGroup );
|
|
legoGroup.rotation.x = Math.PI;
|
|
legoGroup.rotation.x = Math.PI;
|
|
|
|
+ legoGroup.updateMatrixWorld();
|
|
|
|
+ model = legoGroup;
|
|
|
|
|
|
- // adjust the materials to use transmission, be a bit shinier
|
|
|
|
legoGroup.traverse( c => {
|
|
legoGroup.traverse( c => {
|
|
|
|
|
|
|
|
+ // hide the line segments
|
|
|
|
+ if ( c.isLineSegments ) {
|
|
|
|
+
|
|
|
|
+ c.visible = false;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // adjust the materials to use transmission, be a bit shinier
|
|
if ( c.material ) {
|
|
if ( c.material ) {
|
|
|
|
|
|
c.material.roughness *= 0.25;
|
|
c.material.roughness *= 0.25;
|
|
@@ -190,6 +187,7 @@
|
|
|
|
|
|
newMaterial.opacity = 1.0;
|
|
newMaterial.opacity = 1.0;
|
|
newMaterial.transmission = 1.0;
|
|
newMaterial.transmission = 1.0;
|
|
|
|
+ newMaterial.thickness = 1.0;
|
|
newMaterial.ior = 1.4;
|
|
newMaterial.ior = 1.4;
|
|
newMaterial.roughness = oldMaterial.roughness;
|
|
newMaterial.roughness = oldMaterial.roughness;
|
|
newMaterial.metalness = 0.0;
|
|
newMaterial.metalness = 0.0;
|
|
@@ -207,12 +205,6 @@
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
- model = new THREE.Group();
|
|
|
|
- model.add( legoGroup );
|
|
|
|
-
|
|
|
|
- // Convert from LDraw coordinates: rotate 180 degrees around OX
|
|
|
|
- model.updateMatrixWorld();
|
|
|
|
-
|
|
|
|
} )
|
|
} )
|
|
.catch( onError );
|
|
.catch( onError );
|
|
|
|
|
|
@@ -225,16 +217,19 @@
|
|
const envMapGenerator = new BlurredEnvMapGenerator( renderer );
|
|
const envMapGenerator = new BlurredEnvMapGenerator( renderer );
|
|
const blurredEnvMap = envMapGenerator.generate( tex, 0 );
|
|
const blurredEnvMap = envMapGenerator.generate( tex, 0 );
|
|
|
|
|
|
- scene.environment = blurredEnvMap;
|
|
|
|
- envMap = blurredEnvMap;
|
|
|
|
|
|
+ environment = blurredEnvMap;
|
|
|
|
|
|
- } );
|
|
|
|
|
|
+ } )
|
|
|
|
+ .catch( onError );
|
|
|
|
|
|
await Promise.all( [ envMapPromise, ldrawPromise ] );
|
|
await Promise.all( [ envMapPromise, ldrawPromise ] );
|
|
|
|
|
|
hideProgressBar();
|
|
hideProgressBar();
|
|
document.body.classList.add( 'checkerboard' );
|
|
document.body.classList.add( 'checkerboard' );
|
|
|
|
|
|
|
|
+ // set environment map
|
|
|
|
+ scene.environment = environment;
|
|
|
|
+
|
|
// Adjust camera
|
|
// Adjust camera
|
|
const bbox = new THREE.Box3().setFromObject( model );
|
|
const bbox = new THREE.Box3().setFromObject( model );
|
|
const size = bbox.getSize( new THREE.Vector3() );
|
|
const size = bbox.getSize( new THREE.Vector3() );
|
|
@@ -244,13 +239,16 @@
|
|
controls.position0.set( 2.3, 1, 2 ).multiplyScalar( radius ).add( controls.target0 );
|
|
controls.position0.set( 2.3, 1, 2 ).multiplyScalar( radius ).add( controls.target0 );
|
|
controls.reset();
|
|
controls.reset();
|
|
|
|
|
|
|
|
+ // add the model
|
|
|
|
+ scene.add( model );
|
|
|
|
+
|
|
// add floor
|
|
// add floor
|
|
floor = new THREE.Mesh(
|
|
floor = new THREE.Mesh(
|
|
new THREE.PlaneGeometry(),
|
|
new THREE.PlaneGeometry(),
|
|
new THREE.MeshStandardMaterial( {
|
|
new THREE.MeshStandardMaterial( {
|
|
side: THREE.DoubleSide,
|
|
side: THREE.DoubleSide,
|
|
- roughness: 0.01,
|
|
|
|
- metalness: 1,
|
|
|
|
|
|
+ roughness: params.roughness,
|
|
|
|
+ metalness: params.metalness,
|
|
map: generateRadialFloorTexture( 1024 ),
|
|
map: generateRadialFloorTexture( 1024 ),
|
|
transparent: true,
|
|
transparent: true,
|
|
} ),
|
|
} ),
|
|
@@ -258,50 +256,14 @@
|
|
floor.scale.setScalar( 2500 );
|
|
floor.scale.setScalar( 2500 );
|
|
floor.rotation.x = - Math.PI / 2;
|
|
floor.rotation.x = - Math.PI / 2;
|
|
floor.position.y = bbox.min.y;
|
|
floor.position.y = bbox.min.y;
|
|
- model.add( floor );
|
|
|
|
- model.updateMatrixWorld();
|
|
|
|
-
|
|
|
|
- // de-duplicate and reduce the number of materials used in place
|
|
|
|
- const reducer = new MaterialReducer();
|
|
|
|
- reducer.process( model );
|
|
|
|
|
|
+ scene.add( floor );
|
|
|
|
|
|
// reset the progress bar to display bvh generation
|
|
// reset the progress bar to display bvh generation
|
|
progressBarDiv.innerText = 'Generating BVH...';
|
|
progressBarDiv.innerText = 'Generating BVH...';
|
|
updateProgressBar( 0 );
|
|
updateProgressBar( 0 );
|
|
|
|
|
|
- const generator = new PathTracingSceneGenerator();
|
|
|
|
- const result = generator.generate( model );
|
|
|
|
-
|
|
|
|
- // add the model to the scene
|
|
|
|
- sceneInfo = result;
|
|
|
|
- model.traverse( c => {
|
|
|
|
-
|
|
|
|
- if ( c.isLineSegments ) {
|
|
|
|
-
|
|
|
|
- c.visible = false;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- } );
|
|
|
|
- scene.add( model );
|
|
|
|
-
|
|
|
|
- // update the material
|
|
|
|
- const { bvh, textures, materials } = result;
|
|
|
|
- const geometry = bvh.geometry;
|
|
|
|
- const material = pathTracer.material;
|
|
|
|
-
|
|
|
|
- material.bvh.updateFrom( bvh );
|
|
|
|
- material.attributesArray.updateFrom(
|
|
|
|
- geometry.attributes.normal,
|
|
|
|
- geometry.attributes.tangent,
|
|
|
|
- geometry.attributes.uv,
|
|
|
|
- geometry.attributes.color,
|
|
|
|
- );
|
|
|
|
- material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
|
|
|
|
- material.textures.setTextures( renderer, 2048, 2048, textures );
|
|
|
|
- material.materials.updateFrom( materials, textures );
|
|
|
|
- pathTracer.material.envMapInfo.updateFrom( envMap );
|
|
|
|
- pathTracer.reset();
|
|
|
|
|
|
+ pathTracer.setScene( scene, camera );
|
|
|
|
+ render();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -309,19 +271,17 @@
|
|
|
|
|
|
const w = window.innerWidth;
|
|
const w = window.innerWidth;
|
|
const h = window.innerHeight;
|
|
const h = window.innerHeight;
|
|
- const scale = params.resolutionScale;
|
|
|
|
const dpr = window.devicePixelRatio;
|
|
const dpr = window.devicePixelRatio;
|
|
|
|
|
|
- pathTracer.setSize( w * scale * dpr, h * scale * dpr );
|
|
|
|
- pathTracer.reset();
|
|
|
|
-
|
|
|
|
renderer.setSize( w, h );
|
|
renderer.setSize( w, h );
|
|
- renderer.setPixelRatio( window.devicePixelRatio * scale );
|
|
|
|
|
|
+ renderer.setPixelRatio( dpr );
|
|
|
|
|
|
const aspect = w / h;
|
|
const aspect = w / h;
|
|
camera.aspect = aspect;
|
|
camera.aspect = aspect;
|
|
camera.updateProjectionMatrix();
|
|
camera.updateProjectionMatrix();
|
|
|
|
|
|
|
|
+ pathTracer.updateCamera();
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
function createGUI() {
|
|
function createGUI() {
|
|
@@ -338,25 +298,31 @@
|
|
gui.add( params, 'toneMapping' );
|
|
gui.add( params, 'toneMapping' );
|
|
gui.add( params, 'transparentBackground' ).onChange( v => {
|
|
gui.add( params, 'transparentBackground' ).onChange( v => {
|
|
|
|
|
|
- pathTracer.material.backgroundAlpha = v ? 0 : 1;
|
|
|
|
- renderer.setClearAlpha( v ? 0 : 1 );
|
|
|
|
|
|
+ scene.background = v ? null : gradientMap;
|
|
|
|
+ pathTracer.updateEnvironment();
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+ gui.add( params, 'resolutionScale', 0.1, 1.0, 0.1 ).onChange( v => {
|
|
|
|
+
|
|
|
|
+ pathTracer.renderScale = v;
|
|
pathTracer.reset();
|
|
pathTracer.reset();
|
|
|
|
|
|
} );
|
|
} );
|
|
- gui.add( params, 'resolutionScale', 0.1, 1.0, 0.1 ).onChange( onWindowResize );
|
|
|
|
- gui.add( params, 'tiles', 1, 3, 1 ).onChange( v => {
|
|
|
|
|
|
+ gui.add( params, 'tiles', 1, 6, 1 ).onChange( v => {
|
|
|
|
|
|
pathTracer.tiles.set( v, v );
|
|
pathTracer.tiles.set( v, v );
|
|
|
|
|
|
} );
|
|
} );
|
|
- gui.add( params, 'roughness', 0, 1 ).name( 'floor roughness' ).onChange( () => {
|
|
|
|
|
|
+ gui.add( params, 'roughness', 0, 1 ).name( 'floor roughness' ).onChange( v => {
|
|
|
|
|
|
- pathTracer.reset();
|
|
|
|
|
|
+ floor.material.roughness = v;
|
|
|
|
+ pathTracer.updateMaterials();
|
|
|
|
|
|
} );
|
|
} );
|
|
- gui.add( params, 'metalness', 0, 1 ).name( 'floor metalness' ).onChange( () => {
|
|
|
|
|
|
+ gui.add( params, 'metalness', 0, 1 ).name( 'floor metalness' ).onChange( v => {
|
|
|
|
|
|
- pathTracer.reset();
|
|
|
|
|
|
+ floor.material.metalness = v;
|
|
|
|
+ pathTracer.updateMaterials();
|
|
|
|
|
|
} );
|
|
} );
|
|
gui.add( params, 'download' ).name( 'download image' );
|
|
gui.add( params, 'download' ).name( 'download image' );
|
|
@@ -370,7 +336,6 @@
|
|
renderFolder.$children.appendChild( samplesEl );
|
|
renderFolder.$children.appendChild( samplesEl );
|
|
renderFolder.open();
|
|
renderFolder.open();
|
|
|
|
|
|
-
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
@@ -379,49 +344,14 @@
|
|
|
|
|
|
requestAnimationFrame( render );
|
|
requestAnimationFrame( render );
|
|
|
|
|
|
- if ( ! sceneInfo ) {
|
|
|
|
-
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
renderer.toneMapping = params.toneMapping ? THREE.ACESFilmicToneMapping : THREE.NoToneMapping;
|
|
renderer.toneMapping = params.toneMapping ? THREE.ACESFilmicToneMapping : THREE.NoToneMapping;
|
|
|
|
|
|
- if ( pathTracer.samples < 1.0 || ! params.enable ) {
|
|
|
|
-
|
|
|
|
- renderer.render( scene, camera );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
+ const samples = Math.floor( pathTracer.samples );
|
|
|
|
+ samplesEl.innerText = `samples: ${ samples }`;
|
|
|
|
|
|
- if ( params.enable && delaySamples === 0 ) {
|
|
|
|
-
|
|
|
|
- const samples = Math.floor( pathTracer.samples );
|
|
|
|
- samplesEl.innerText = `samples: ${ samples }`;
|
|
|
|
-
|
|
|
|
- floor.material.roughness = params.roughness;
|
|
|
|
- floor.material.metalness = params.metalness;
|
|
|
|
-
|
|
|
|
- pathTracer.material.materials.updateFrom( sceneInfo.materials, sceneInfo.textures );
|
|
|
|
- pathTracer.material.filterGlossyFactor = 1;
|
|
|
|
- pathTracer.material.physicalCamera.updateFrom( camera );
|
|
|
|
-
|
|
|
|
- camera.updateMatrixWorld();
|
|
|
|
-
|
|
|
|
- if ( ! params.pause || pathTracer.samples < 1 ) {
|
|
|
|
-
|
|
|
|
- pathTracer.update();
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- renderer.autoClear = false;
|
|
|
|
- fsQuad.render( renderer );
|
|
|
|
- renderer.autoClear = true;
|
|
|
|
-
|
|
|
|
- } else if ( delaySamples > 0 ) {
|
|
|
|
-
|
|
|
|
- delaySamples --;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
+ pathTracer.enablePathTracing = params.enable;
|
|
|
|
+ pathTracer.pausePathTracing = params.pause;
|
|
|
|
+ pathTracer.renderSample();
|
|
|
|
|
|
samplesEl.innerText = `samples: ${ Math.floor( pathTracer.samples ) }`;
|
|
samplesEl.innerText = `samples: ${ Math.floor( pathTracer.samples ) }`;
|
|
|
|
|