11import {
22 AgXToneMapping ,
3- PerspectiveCamera ,
43 Scene ,
5- Box3 ,
6- Mesh ,
7- CylinderGeometry ,
8- MeshPhysicalMaterial ,
4+ Vector3 ,
95 WebGLRenderer ,
10- EquirectangularReflectionMapping ,
11- Color ,
126} from 'three' ;
13- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js' ;
147import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' ;
158import { WebGLPathTracer } from '../src/index.js' ;
16- import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js' ;
17- import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js' ;
189import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js' ;
1910import { LoaderElement } from './utils/LoaderElement.js' ;
2011import { getScaledSettings } from './utils/getScaledSettings.js' ;
12+ import { MaterialOrbSceneLoader } from './utils/MaterialOrbLoader.js' ;
13+ import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js' ;
2114
22- const MODEL_URL = 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/main/models/material-balls/material_ball_v2.glb' ;
23- const ENV_URL = 'https://raw.githubusercontent.com/gkjohnson/3d-demo-data/master/hdri/autoshop_01_1k.hdr' ;
2415const DB_URL = 'https://api.physicallybased.info/materials' ;
25- const CREDITS = 'Materials courtesy of "physicallybased.info"' ;
16+ const CREDITS = 'Materials courtesy of "physicallybased.info"</br>Material sphere courtesy of USD Working Group ' ;
2617
27- let pathTracer , renderer , controls , shellMaterial ;
18+ let pathTracer , renderer , controls , material ;
2819let camera , database , scene ;
2920let loader , imgEl ;
3021
@@ -41,6 +32,8 @@ init();
4132
4233async function init ( ) {
4334
35+ RectAreaLightUniformsLib . init ( ) ;
36+
4437 loader = new LoaderElement ( ) ;
4538 loader . attach ( document . body ) ;
4639
@@ -49,84 +42,42 @@ async function init() {
4942 // renderer
5043 renderer = new WebGLRenderer ( { antialias : true } ) ;
5144 renderer . toneMapping = AgXToneMapping ;
45+ renderer . toneMappingExposure = 0.02 ;
5246 document . body . appendChild ( renderer . domElement ) ;
5347
5448 // path tracer
5549 pathTracer = new WebGLPathTracer ( renderer ) ;
5650 pathTracer . multipleImportanceSampling = params . multipleImportanceSampling ;
5751 pathTracer . tiles . set ( params . tiles , params . tiles ) ;
52+ pathTracer . textureSize . set ( 2048 , 2048 ) ;
5853 pathTracer . filterGlossyFactor = 0.5 ;
5954
60- // camera
61- const aspect = window . innerWidth / window . innerHeight ;
62- camera = new PerspectiveCamera ( 75 , aspect , 0.025 , 500 ) ;
63- camera . position . set ( - 4 , 2 , 3 ) ;
64-
65- // controls
66- controls = new OrbitControls ( camera , renderer . domElement ) ;
67- controls . addEventListener ( 'change' , ( ) => pathTracer . updateCamera ( ) ) ;
68-
6955 // scene
7056 scene = new Scene ( ) ;
7157
7258 // load assets
73- const [ envTexture , gltf , dbJson ] = await Promise . all ( [
74- new RGBELoader ( ) . loadAsync ( ENV_URL ) ,
75- new GLTFLoader ( ) . setMeshoptDecoder ( MeshoptDecoder ) . loadAsync ( MODEL_URL ) ,
59+ const [ orb , dbJson ] = await Promise . all ( [
60+ new MaterialOrbSceneLoader ( ) . loadAsync ( ) ,
7661 fetch ( DB_URL ) . then ( res => res . json ( ) ) ,
7762 ] ) ;
7863
79- // background
80- envTexture . mapping = EquirectangularReflectionMapping ;
81- scene . background = envTexture ;
82- scene . environment = envTexture ;
83-
8464 // scene initialization
85- gltf . scene . scale . setScalar ( 0.01 ) ;
86- gltf . scene . updateMatrixWorld ( ) ;
87- scene . add ( gltf . scene ) ;
88-
89- const box = new Box3 ( ) ;
90- box . setFromObject ( gltf . scene ) ;
91-
92- const floor = new Mesh (
93- new CylinderGeometry ( 3 , 3 , 0.05 , 200 ) ,
94- new MeshPhysicalMaterial ( { color : 0xffffff , roughness : 0 , metalness : 0.25 } ) ,
95- ) ;
96- floor . geometry = floor . geometry . toNonIndexed ( ) ;
97- floor . geometry . clearGroups ( ) ;
98- floor . position . y = box . min . y - 0.03 ;
99- scene . add ( floor ) ;
100-
101- shellMaterial = new MeshPhysicalMaterial ( ) ;
102- const coreMaterial = new MeshPhysicalMaterial ( { color : new Color ( 0.5 , 0.5 , 0.5 ) } ) ;
103- gltf . scene . traverse ( c => {
104-
105- // the vertex normals on the material ball are off...
106- // TODO: precompute the vertex normals so they are correct on load
107- if ( c . geometry ) {
108-
109- c . geometry . computeVertexNormals ( ) ;
110-
111- }
112-
113- if ( c . name === 'Sphere_1' ) {
114-
115- c . material = coreMaterial ;
116-
117- } else {
65+ scene . add ( orb . scene ) ;
66+ camera = orb . camera ;
67+ material = orb . material ;
11868
119- c . material = shellMaterial ;
69+ // move camera to the scene
70+ scene . attach ( camera ) ;
71+ camera . removeFromParent ( ) ;
12072
121- }
122-
123- if ( c . name === 'subsphere_1' ) {
124-
125- c . material = coreMaterial ;
126-
127- }
73+ // controls
74+ controls = new OrbitControls ( camera , renderer . domElement ) ;
75+ controls . addEventListener ( 'change' , ( ) => pathTracer . updateCamera ( ) ) ;
12876
129- } ) ;
77+ // shift target
78+ const fwd = new Vector3 ( 0 , 0 , - 1 ) . transformDirection ( camera . matrixWorld ) . normalize ( ) ;
79+ controls . target . copy ( camera . position ) . addScaledVector ( fwd , 25 ) ;
80+ controls . update ( ) ;
13081
13182 // database set up
13283 database = { } ;
@@ -172,6 +123,7 @@ function onResize() {
172123
173124function applyMaterialInfo ( info , material ) {
174125
126+ // defaults
175127 material . color . set ( 0xffffff ) ;
176128 material . transmission = 0.0 ;
177129 material . attenuationDistance = Infinity ;
@@ -185,6 +137,7 @@ function applyMaterialInfo( info, material ) {
185137 material . iridescenceIOR = 1.0 ;
186138 material . iridescenceThicknessRange = [ 0 , 0 ] ;
187139
140+ // apply database values
188141 if ( info . specularColor ) material . specularColor . setRGB ( ...info . specularColor ) ;
189142 if ( 'metalness' in info ) material . metalness = info . metalness ;
190143 if ( 'roughness' in info ) material . roughness = info . roughness ;
@@ -200,12 +153,23 @@ function applyMaterialInfo( info, material ) {
200153
201154 if ( material . transmission ) {
202155
203- if ( info . color ) material . attenuationColor . setRGB ( ...info . color ) ;
204- material . attenuationDistance = 200 / info . density ;
156+ if ( info . color ) {
157+
158+ material . attenuationColor . setRGB ( ...info . color ) ;
159+
160+ }
161+
162+ // Blender uses 1 / density when exporting volume transmission which doesn't look
163+ // exactly right. But because the scene is 1000x in size we multiply by 1000 here.
164+ material . attenuationDistance = 1000 / info . density ;
205165
206166 } else {
207167
208- if ( info . color ) material . color . setRGB ( ...info . color ) ;
168+ if ( info . color ) {
169+
170+ material . color . setRGB ( ...info . color ) ;
171+
172+ }
209173
210174 }
211175
@@ -215,7 +179,7 @@ function applyMaterialInfo( info, material ) {
215179
216180function onParamsChange ( ) {
217181
218- applyMaterialInfo ( database [ params . material ] , shellMaterial ) ;
182+ applyMaterialInfo ( database [ params . material ] , material ) ;
219183
220184 pathTracer . multipleImportanceSampling = params . multipleImportanceSampling ;
221185 pathTracer . renderScale = params . renderScale ;
0 commit comments