1
1
import {
2
2
AgXToneMapping ,
3
- PerspectiveCamera ,
4
3
Scene ,
5
- Box3 ,
6
- Mesh ,
7
- CylinderGeometry ,
8
- MeshPhysicalMaterial ,
4
+ Vector3 ,
9
5
WebGLRenderer ,
10
- EquirectangularReflectionMapping ,
11
- Color ,
12
6
} from 'three' ;
13
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js' ;
14
7
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' ;
15
8
import { 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' ;
18
9
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js' ;
19
10
import { LoaderElement } from './utils/LoaderElement.js' ;
20
11
import { getScaledSettings } from './utils/getScaledSettings.js' ;
12
+ import { MaterialOrbSceneLoader } from './utils/MaterialOrbLoader.js' ;
13
+ import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js' ;
21
14
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' ;
24
15
const 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 ' ;
26
17
27
- let pathTracer , renderer , controls , shellMaterial ;
18
+ let pathTracer , renderer , controls , material ;
28
19
let camera , database , scene ;
29
20
let loader , imgEl ;
30
21
@@ -41,6 +32,8 @@ init();
41
32
42
33
async function init ( ) {
43
34
35
+ RectAreaLightUniformsLib . init ( ) ;
36
+
44
37
loader = new LoaderElement ( ) ;
45
38
loader . attach ( document . body ) ;
46
39
@@ -49,84 +42,42 @@ async function init() {
49
42
// renderer
50
43
renderer = new WebGLRenderer ( { antialias : true } ) ;
51
44
renderer . toneMapping = AgXToneMapping ;
45
+ renderer . toneMappingExposure = 0.02 ;
52
46
document . body . appendChild ( renderer . domElement ) ;
53
47
54
48
// path tracer
55
49
pathTracer = new WebGLPathTracer ( renderer ) ;
56
50
pathTracer . multipleImportanceSampling = params . multipleImportanceSampling ;
57
51
pathTracer . tiles . set ( params . tiles , params . tiles ) ;
52
+ pathTracer . textureSize . set ( 2048 , 2048 ) ;
58
53
pathTracer . filterGlossyFactor = 0.5 ;
59
54
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
-
69
55
// scene
70
56
scene = new Scene ( ) ;
71
57
72
58
// 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 ( ) ,
76
61
fetch ( DB_URL ) . then ( res => res . json ( ) ) ,
77
62
] ) ;
78
63
79
- // background
80
- envTexture . mapping = EquirectangularReflectionMapping ;
81
- scene . background = envTexture ;
82
- scene . environment = envTexture ;
83
-
84
64
// 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 ;
118
68
119
- c . material = shellMaterial ;
69
+ // move camera to the scene
70
+ scene . attach ( camera ) ;
71
+ camera . removeFromParent ( ) ;
120
72
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 ( ) ) ;
128
76
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 ( ) ;
130
81
131
82
// database set up
132
83
database = { } ;
@@ -172,6 +123,7 @@ function onResize() {
172
123
173
124
function applyMaterialInfo ( info , material ) {
174
125
126
+ // defaults
175
127
material . color . set ( 0xffffff ) ;
176
128
material . transmission = 0.0 ;
177
129
material . attenuationDistance = Infinity ;
@@ -185,6 +137,7 @@ function applyMaterialInfo( info, material ) {
185
137
material . iridescenceIOR = 1.0 ;
186
138
material . iridescenceThicknessRange = [ 0 , 0 ] ;
187
139
140
+ // apply database values
188
141
if ( info . specularColor ) material . specularColor . setRGB ( ...info . specularColor ) ;
189
142
if ( 'metalness' in info ) material . metalness = info . metalness ;
190
143
if ( 'roughness' in info ) material . roughness = info . roughness ;
@@ -200,12 +153,23 @@ function applyMaterialInfo( info, material ) {
200
153
201
154
if ( material . transmission ) {
202
155
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 ;
205
165
206
166
} else {
207
167
208
- if ( info . color ) material . color . setRGB ( ...info . color ) ;
168
+ if ( info . color ) {
169
+
170
+ material . color . setRGB ( ...info . color ) ;
171
+
172
+ }
209
173
210
174
}
211
175
@@ -215,7 +179,7 @@ function applyMaterialInfo( info, material ) {
215
179
216
180
function onParamsChange ( ) {
217
181
218
- applyMaterialInfo ( database [ params . material ] , shellMaterial ) ;
182
+ applyMaterialInfo ( database [ params . material ] , material ) ;
219
183
220
184
pathTracer . multipleImportanceSampling = params . multipleImportanceSampling ;
221
185
pathTracer . renderScale = params . renderScale ;
0 commit comments