@@ -17,6 +17,24 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) {
17
17
precision : 1
18
18
} )
19
19
) ,
20
+ jsx ( LabelGroup , { text : 'background' } ,
21
+ jsx ( SliderInput , {
22
+ binding : new BindingTwoWay ( ) ,
23
+ link : { observer, path : 'data.scene.background' } ,
24
+ min : 0 ,
25
+ max : 50 ,
26
+ precision : 1
27
+ } )
28
+ ) ,
29
+ jsx ( LabelGroup , { text : 'emissive' } ,
30
+ jsx ( SliderInput , {
31
+ binding : new BindingTwoWay ( ) ,
32
+ link : { observer, path : 'data.scene.emissive' } ,
33
+ min : 0 ,
34
+ max : 400 ,
35
+ precision : 1
36
+ } )
37
+ ) ,
20
38
jsx ( LabelGroup , { text : 'Tonemapping' } ,
21
39
jsx ( SelectInput , {
22
40
binding : new BindingTwoWay ( ) ,
@@ -113,7 +131,8 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
113
131
114
132
const assets = {
115
133
orbit : new pc . Asset ( 'script' , 'script' , { url : scriptsPath + 'camera/orbit-camera.js' } ) ,
116
- board : new pc . Asset ( 'statue' , 'container' , { url : assetPath + 'models/chess-board.glb' } ) ,
134
+ platform : new pc . Asset ( 'statue' , 'container' , { url : assetPath + 'models/scifi-platform.glb' } ) ,
135
+ mosquito : new pc . Asset ( 'mosquito' , 'container' , { url : assetPath + 'models/MosquitoInAmber.glb' } ) ,
117
136
font : new pc . Asset ( 'font' , 'font' , { url : assetPath + 'fonts/arial.json' } ) ,
118
137
helipad : new pc . Asset ( 'helipad-env-atlas' , 'texture' , { url : assetPath + 'cubemaps/helipad-env-atlas.png' } , { type : pc . TEXTURETYPE_RGBP , mipmaps : false } )
119
138
} ;
@@ -123,7 +142,9 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
123
142
glslangUrl : glslangPath + 'glslang.js' ,
124
143
twgslUrl : twgslPath + 'twgsl.js' ,
125
144
126
- // WebGPU does not currently support antialiased depth resolve, disable it till we implement a shader resolve solution
145
+ // The scene is rendered to an antialiased texture, so we disable antialiasing on the canvas
146
+ // to avoid the additional cost. This is only used for the UI which renders on top of the
147
+ // post-processed scene, and we're typically happy with some aliasing on the UI.
127
148
antialias : false
128
149
} ;
129
150
@@ -177,7 +198,7 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
177
198
178
199
app . start ( ) ;
179
200
180
- // setup skydome
201
+ // setup skydome with low intensity
181
202
app . scene . envAtlas = assets . helipad . resource ;
182
203
app . scene . skyboxMip = 2 ;
183
204
app . scene . exposure = 0.3 ;
@@ -186,12 +207,28 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
186
207
app . scene . toneMapping = pc . TONEMAP_LINEAR ;
187
208
app . scene . gammaCorrection = pc . GAMMA_NONE ;
188
209
189
- // get the instance of the chess board and set up with render component
190
- const boardEntity = assets . board . resource . instantiateRenderEntity ( ) ;
191
- app . root . addChild ( boardEntity ) ;
210
+ // create an instance of the platform and add it to the scene
211
+ const platformEntity = assets . platform . resource . instantiateRenderEntity ( ) ;
212
+ platformEntity . setLocalScale ( 10 , 10 , 10 ) ;
213
+ app . root . addChild ( platformEntity ) ;
214
+
215
+ // get a list of emissive materials from the scene to allow their intensity to be changed
216
+ const emissiveMaterials = [ ] ;
217
+ const emissiveNames = new Set ( [ 'Light_Upper_Light-Upper_0' , 'Emissive_Cyan__0' ] ) ;
218
+ platformEntity . findComponents ( "render" ) . forEach ( ( render ) => {
219
+ if ( emissiveNames . has ( render . entity . name ) ) {
220
+ render . meshInstances . forEach ( meshInstance => emissiveMaterials . push ( meshInstance . material ) ) ;
221
+ }
222
+ } ) ;
223
+
224
+ // add an instance of the mosquito mesh
225
+ const mosquitoEntity = assets . mosquito . resource . instantiateRenderEntity ( ) ;
226
+ mosquitoEntity . setLocalScale ( 600 , 600 , 600 ) ;
227
+ mosquitoEntity . setLocalPosition ( 0 , 20 , 0 ) ;
228
+ app . root . addChild ( mosquitoEntity ) ;
192
229
193
230
// helper function to create a box primitive
194
- const createBox = ( x , y , z , sx , sy , sz , r , g , b ) => {
231
+ const createBox = ( x , y , z , r , g , b ) => {
195
232
// create material of random color
196
233
const material = new pc . StandardMaterial ( ) ;
197
234
material . diffuse = pc . Color . BLACK ;
@@ -207,41 +244,40 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
207
244
208
245
// set position and scale
209
246
primitive . setLocalPosition ( x , y , z ) ;
210
- primitive . setLocalScale ( sx , sy , sz ) ;
211
247
app . root . addChild ( primitive ) ;
212
248
213
249
return primitive ;
214
250
} ;
215
251
216
252
// create 3 emissive boxes
217
253
const boxes = [
218
- createBox ( 0 , 20 , 0 , 10 , 10 , 10 , 300 , 0 , 0 ) ,
219
- createBox ( 40 , 20 , 0 , 10 , 10 , 10 , 0 , 80 , 0 ) ,
220
- createBox ( - 40 , 20 , 0 , 15 , 15 , 15 , 80 , 80 , 20 )
254
+ createBox ( 100 , 20 , 0 , 200 , 0 , 0 ) ,
255
+ createBox ( - 50 , 20 , 100 , 0 , 80 , 0 ) ,
256
+ createBox ( 90 , 20 , - 80 , 80 , 80 , 20 )
221
257
] ;
222
258
223
259
// Create an Entity with a camera component
224
260
const cameraEntity = new pc . Entity ( ) ;
225
261
cameraEntity . addComponent ( "camera" , {
226
- clearColor : new pc . Color ( 0 , 0 , 0 ) ,
227
- farClip : 500
262
+ farClip : 500 ,
263
+ fov : 80
228
264
} ) ;
229
265
230
266
// add orbit camera script with a mouse and a touch support
231
267
cameraEntity . addComponent ( "script" ) ;
232
268
cameraEntity . script . create ( "orbitCamera" , {
233
269
attributes : {
234
270
inertiaFactor : 0.2 ,
235
- focusEntity : boxes [ 0 ] ,
236
- distanceMax : 160 ,
271
+ focusEntity : mosquitoEntity ,
272
+ distanceMax : 190 ,
237
273
frameOnStart : false
238
274
}
239
275
} ) ;
240
276
cameraEntity . script . create ( "orbitCameraInputMouse" ) ;
241
277
cameraEntity . script . create ( "orbitCameraInputTouch" ) ;
242
278
243
279
// position the camera in the world
244
- cameraEntity . setLocalPosition ( 0 , 40 , - 160 ) ;
280
+ cameraEntity . setLocalPosition ( 0 , 40 , - 220 ) ;
245
281
cameraEntity . lookAt ( 0 , 0 , 100 ) ;
246
282
app . root . addChild ( cameraEntity ) ;
247
283
@@ -255,20 +291,22 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
255
291
} ) ;
256
292
app . root . addChild ( screen ) ;
257
293
258
- // add a directional light
294
+ // add a shadow casting directional light
295
+ const lightColor = new pc . Color ( 1 , 0.7 , 0.1 ) ;
259
296
const light = new pc . Entity ( ) ;
260
297
light . addComponent ( "light" , {
261
298
type : "directional" ,
262
- color : pc . Color . WHITE ,
263
- intensity : 3 ,
264
- range : 500 ,
265
- shadowDistance : 500 ,
299
+ color : lightColor ,
300
+ intensity : 80 ,
301
+ range : 400 ,
302
+ shadowResolution : 4096 ,
303
+ shadowDistance : 400 ,
266
304
castShadows : true ,
267
305
shadowBias : 0.2 ,
268
306
normalOffsetBias : 0.05
269
307
} ) ;
270
308
app . root . addChild ( light ) ;
271
- light . setLocalEulerAngles ( 45 , 30 , 0 ) ;
309
+ light . setLocalEulerAngles ( 80 , 10 , 0 ) ;
272
310
273
311
// a helper function to add a label to the screen
274
312
const addLabel = ( name , text , x , y , layer ) => {
@@ -295,9 +333,11 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
295
333
const uiLayer = app . scene . layers . getLayerById ( pc . LAYERID_UI ) ;
296
334
addLabel ( 'TopUI' , 'Text on theUI layer after the post-processing' , 0.1 , 0.1 , uiLayer ) ;
297
335
336
+ // render passes
298
337
let scenePass ;
299
338
let composePass ;
300
339
let bloomPass ;
340
+ let colorGrabPass ;
301
341
302
342
// helper function to create a render passes for the camera
303
343
const setupRenderPasses = ( ) => {
@@ -322,18 +362,30 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
322
362
samples : 4
323
363
} ) ;
324
364
325
- // render pass that renders the scene to the render target. Render target size automatically
326
- // matches the back-buffer size with the optional scale.
365
+ // grab pass allowing us to copy the render scene into a texture and use for refraction
366
+ // the source for the copy is the texture we render the scene to
367
+ colorGrabPass = new pc . RenderPassColorGrab ( app . graphicsDevice ) ;
368
+ colorGrabPass . source = rt ;
369
+
370
+ // render pass that renders the opaque scene to the render target. Render target size
371
+ // automatically matches the back-buffer size with the optional scale. Note that the scale
372
+ // parameters allow us to render the 3d scene at lower resolution, improving performance.
327
373
scenePass = new pc . RenderPassRenderActions ( app . graphicsDevice , app . scene . layers , app . scene , app . renderer ) ;
328
374
scenePass . init ( rt , {
329
375
resizeSource : null ,
330
376
scaleX : 1 ,
331
377
scaleY : 1
332
378
} ) ;
333
379
334
- // this pass render both opaque and transparent meshes on the world layer
335
- scenePass . addLayer ( cameraEntity . camera , worldLayer , false ) ;
336
- scenePass . addLayer ( cameraEntity . camera , worldLayer , true ) ;
380
+ // this pass render opaquemeshes on the world layer
381
+ let clearRenderTarget = true ;
382
+ scenePass . addLayer ( cameraEntity . camera , worldLayer , false , clearRenderTarget ) ;
383
+
384
+ // similar pass that renders transparent meshes from the world layer to the same render target
385
+ clearRenderTarget = false ;
386
+ const scenePassTransparent = new pc . RenderPassRenderActions ( app . graphicsDevice , app . scene . layers , app . scene , app . renderer ) ;
387
+ scenePassTransparent . init ( rt ) ;
388
+ scenePassTransparent . addLayer ( cameraEntity . camera , worldLayer , true , clearRenderTarget ) ;
337
389
338
390
// create a bloom pass, which generates bloom texture based on the just rendered scene texture
339
391
bloomPass = new pcx . RenderPassBloom ( app . graphicsDevice , sceneTexture , format ) ;
@@ -349,10 +401,10 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
349
401
// final pass renders directly to the back-buffer on top of the bloomed scene, and it renders a transparent UI layer
350
402
const afterPass = new pc . RenderPassRenderActions ( app . graphicsDevice , app . scene . layers , app . scene , app . renderer ) ;
351
403
afterPass . init ( null ) ;
352
- afterPass . addLayer ( cameraEntity . camera , uiLayer , true , false ) ;
404
+ afterPass . addLayer ( cameraEntity . camera , uiLayer , true , clearRenderTarget ) ;
353
405
354
- // return these prepared render passes
355
- return [ scenePass , bloomPass , composePass , afterPass ] ;
406
+ // return these prepared render passes in the order they should be executed
407
+ return [ scenePass , colorGrabPass , scenePassTransparent , bloomPass , composePass , afterPass ] ;
356
408
} ;
357
409
358
410
// set up render passes on the camera, to use those instead of the default camera rendering
@@ -370,6 +422,16 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
370
422
composePass . toneMapping = value ;
371
423
composePass . _shaderDirty = true ;
372
424
}
425
+ if ( pathArray [ 2 ] === 'background' ) {
426
+ cameraEntity . camera . clearColor = new pc . Color ( lightColor . r * value , lightColor . g * value , lightColor . b * value ) ;
427
+ light . light . intensity = value ;
428
+ }
429
+ if ( pathArray [ 2 ] === 'emissive' ) {
430
+ emissiveMaterials . forEach ( ( material ) => {
431
+ material . emissiveIntensity = value ;
432
+ material . update ( ) ;
433
+ } ) ;
434
+ }
373
435
}
374
436
if ( pathArray [ 1 ] === 'bloom' ) {
375
437
if ( pathArray [ 2 ] === 'intensity' ) {
@@ -401,6 +463,8 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
401
463
data . set ( 'data' , {
402
464
scene : {
403
465
scale : 1.8 ,
466
+ background : 6 ,
467
+ emissive : 200 ,
404
468
tonemapping : pc . TONEMAP_ACES
405
469
} ,
406
470
bloom : {
@@ -424,9 +488,12 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
424
488
// scale the boxes
425
489
for ( let i = 0 ; i < boxes . length ; i ++ ) {
426
490
const offset = Math . PI * 2 * i / ( boxes . length ) ;
427
- const scale = 10 + Math . sin ( angle + offset ) * 7 ;
491
+ const scale = 25 + Math . sin ( angle + offset ) * 10 ;
428
492
boxes [ i ] . setLocalScale ( scale , scale , scale ) ;
429
493
}
494
+
495
+ // rotate the mosquitoEntity
496
+ mosquitoEntity . setLocalEulerAngles ( 0 , angle * 30 , 0 ) ;
430
497
} ) ;
431
498
} ) ;
432
499
return app ;
0 commit comments