@@ -17,6 +17,24 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) {
1717 precision : 1
1818 } )
1919 ) ,
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+ ) ,
2038 jsx ( LabelGroup , { text : 'Tonemapping' } ,
2139 jsx ( SelectInput , {
2240 binding : new BindingTwoWay ( ) ,
@@ -113,7 +131,8 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
113131
114132 const assets = {
115133 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' } ) ,
117136 font : new pc . Asset ( 'font' , 'font' , { url : assetPath + 'fonts/arial.json' } ) ,
118137 helipad : new pc . Asset ( 'helipad-env-atlas' , 'texture' , { url : assetPath + 'cubemaps/helipad-env-atlas.png' } , { type : pc . TEXTURETYPE_RGBP , mipmaps : false } )
119138 } ;
@@ -123,7 +142,9 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
123142 glslangUrl : glslangPath + 'glslang.js' ,
124143 twgslUrl : twgslPath + 'twgsl.js' ,
125144
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.
127148 antialias : false
128149 } ;
129150
@@ -177,7 +198,7 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
177198
178199 app . start ( ) ;
179200
180- // setup skydome
201+ // setup skydome with low intensity
181202 app . scene . envAtlas = assets . helipad . resource ;
182203 app . scene . skyboxMip = 2 ;
183204 app . scene . exposure = 0.3 ;
@@ -186,12 +207,28 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
186207 app . scene . toneMapping = pc . TONEMAP_LINEAR ;
187208 app . scene . gammaCorrection = pc . GAMMA_NONE ;
188209
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 ) ;
192229
193230 // 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 ) => {
195232 // create material of random color
196233 const material = new pc . StandardMaterial ( ) ;
197234 material . diffuse = pc . Color . BLACK ;
@@ -207,41 +244,40 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
207244
208245 // set position and scale
209246 primitive . setLocalPosition ( x , y , z ) ;
210- primitive . setLocalScale ( sx , sy , sz ) ;
211247 app . root . addChild ( primitive ) ;
212248
213249 return primitive ;
214250 } ;
215251
216252 // create 3 emissive boxes
217253 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 )
221257 ] ;
222258
223259 // Create an Entity with a camera component
224260 const cameraEntity = new pc . Entity ( ) ;
225261 cameraEntity . addComponent ( "camera" , {
226- clearColor : new pc . Color ( 0 , 0 , 0 ) ,
227- farClip : 500
262+ farClip : 500 ,
263+ fov : 80
228264 } ) ;
229265
230266 // add orbit camera script with a mouse and a touch support
231267 cameraEntity . addComponent ( "script" ) ;
232268 cameraEntity . script . create ( "orbitCamera" , {
233269 attributes : {
234270 inertiaFactor : 0.2 ,
235- focusEntity : boxes [ 0 ] ,
236- distanceMax : 160 ,
271+ focusEntity : mosquitoEntity ,
272+ distanceMax : 190 ,
237273 frameOnStart : false
238274 }
239275 } ) ;
240276 cameraEntity . script . create ( "orbitCameraInputMouse" ) ;
241277 cameraEntity . script . create ( "orbitCameraInputTouch" ) ;
242278
243279 // position the camera in the world
244- cameraEntity . setLocalPosition ( 0 , 40 , - 160 ) ;
280+ cameraEntity . setLocalPosition ( 0 , 40 , - 220 ) ;
245281 cameraEntity . lookAt ( 0 , 0 , 100 ) ;
246282 app . root . addChild ( cameraEntity ) ;
247283
@@ -255,20 +291,22 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
255291 } ) ;
256292 app . root . addChild ( screen ) ;
257293
258- // add a directional light
294+ // add a shadow casting directional light
295+ const lightColor = new pc . Color ( 1 , 0.7 , 0.1 ) ;
259296 const light = new pc . Entity ( ) ;
260297 light . addComponent ( "light" , {
261298 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 ,
266304 castShadows : true ,
267305 shadowBias : 0.2 ,
268306 normalOffsetBias : 0.05
269307 } ) ;
270308 app . root . addChild ( light ) ;
271- light . setLocalEulerAngles ( 45 , 30 , 0 ) ;
309+ light . setLocalEulerAngles ( 80 , 10 , 0 ) ;
272310
273311 // a helper function to add a label to the screen
274312 const addLabel = ( name , text , x , y , layer ) => {
@@ -295,9 +333,11 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
295333 const uiLayer = app . scene . layers . getLayerById ( pc . LAYERID_UI ) ;
296334 addLabel ( 'TopUI' , 'Text on theUI layer after the post-processing' , 0.1 , 0.1 , uiLayer ) ;
297335
336+ // render passes
298337 let scenePass ;
299338 let composePass ;
300339 let bloomPass ;
340+ let colorGrabPass ;
301341
302342 // helper function to create a render passes for the camera
303343 const setupRenderPasses = ( ) => {
@@ -322,18 +362,30 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
322362 samples : 4
323363 } ) ;
324364
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.
327373 scenePass = new pc . RenderPassRenderActions ( app . graphicsDevice , app . scene . layers , app . scene , app . renderer ) ;
328374 scenePass . init ( rt , {
329375 resizeSource : null ,
330376 scaleX : 1 ,
331377 scaleY : 1
332378 } ) ;
333379
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 ) ;
337389
338390 // create a bloom pass, which generates bloom texture based on the just rendered scene texture
339391 bloomPass = new pcx . RenderPassBloom ( app . graphicsDevice , sceneTexture , format ) ;
@@ -349,10 +401,10 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
349401 // final pass renders directly to the back-buffer on top of the bloomed scene, and it renders a transparent UI layer
350402 const afterPass = new pc . RenderPassRenderActions ( app . graphicsDevice , app . scene . layers , app . scene , app . renderer ) ;
351403 afterPass . init ( null ) ;
352- afterPass . addLayer ( cameraEntity . camera , uiLayer , true , false ) ;
404+ afterPass . addLayer ( cameraEntity . camera , uiLayer , true , clearRenderTarget ) ;
353405
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 ] ;
356408 } ;
357409
358410 // 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,
370422 composePass . toneMapping = value ;
371423 composePass . _shaderDirty = true ;
372424 }
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+ }
373435 }
374436 if ( pathArray [ 1 ] === 'bloom' ) {
375437 if ( pathArray [ 2 ] === 'intensity' ) {
@@ -401,6 +463,8 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
401463 data . set ( 'data' , {
402464 scene : {
403465 scale : 1.8 ,
466+ background : 6 ,
467+ emissive : 200 ,
404468 tonemapping : pc . TONEMAP_ACES
405469 } ,
406470 bloom : {
@@ -424,9 +488,12 @@ async function example({ canvas, deviceType, assetPath, glslangPath, twgslPath,
424488 // scale the boxes
425489 for ( let i = 0 ; i < boxes . length ; i ++ ) {
426490 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 ;
428492 boxes [ i ] . setLocalScale ( scale , scale , scale ) ;
429493 }
494+
495+ // rotate the mosquitoEntity
496+ mosquitoEntity . setLocalEulerAngles ( 0 , angle * 30 , 0 ) ;
430497 } ) ;
431498 } ) ;
432499 return app ;
0 commit comments