WebGL Libs for
 WebApp Frameworks

3c557c6103a4addc52f7b76ffd0a0f27?s=47 yomotsu
September 24, 2017

WebGL Libs for
 WebApp Frameworks

3c557c6103a4addc52f7b76ffd0a0f27?s=128

yomotsu

September 24, 2017
Tweet

Transcript

  1. Presented by Akihiro Oyamada (@yomotsu) Sep 24, 2017 WebGL Libs

    for
 WebApp Frameworks
  2. Frontend Engineer at PixelGrid, Inc. Akihiro Oyamada @yomotsu https://github.com/mrdoob/three.js/graphs/contributors

  3. https://twitter.com/aniplex_plus/status/779261285783392256 http://yomotsu.net/blog/assets/2016-12-25-xmas/ Made with +

  4. webpack three.js pixi.js etc React Vue.js Angular etc three.js logo:

    https://github.com/mrdoob/three.js/issues/2789
  5. Tips for development with frameworks + WebGL libs: 1. Import

    three.js plugins in webpack. 2. Handle Canvas and Renderer as a WebApp view. 3. Remove Canvas and GL context appropriately. 4. Assets compressing. Agenda
  6. Import three.js plugins
 in webpack

  7. • OrbitControls • EffectComposer and Passes • CSS3DRenderer • Loaders

    and others. Plugins for three.js
  8. Plugins do not support
 es6 module import/export Problem so far.

  9. webpack imports-loader Solution https://github.com/mrdoob/three.js/issues/9562#issuecomment-307092696

  10. import * as THREE from 'three'; THREE.OrbitControls = require( 'imports-loader?THREE!

    exports-loader?THREE.OrbitControls!../node_modules/ three/examples/js/controls/OrbitControls.js' ); // snip const controls = new THREE.OrbitControls( camera );
  11. import * as THREE from 'three'; THREE.OrbitControls = require( 'imports-loader?THREE!

    exports-loader?THREE.OrbitControls!../node_modules/ three/examples/js/controls/OrbitControls.js' ); // snip const controls = new THREE.OrbitControls( camera );
  12. import * as THREE from 'three'; THREE.OrbitControls = require( 'imports-loader?THREE!

    exports-loader?THREE.OrbitControls!../node_modules/ three/examples/js/controls/OrbitControls.js' ); // snip const controls = new THREE.OrbitControls( camera );
  13. import * as THREE from 'three'; THREE.OrbitControls = require( 'imports-loader?THREE!

    exports-loader?THREE.OrbitControls!../node_modules/ three/examples/js/controls/OrbitControls.js' ); // snip const controls = new THREE.OrbitControls( camera );
  14. import * as THREE from 'three'; THREE.OrbitControls = require( 'imports-loader?THREE!

    exports-loader?THREE.OrbitControls!../node_modules/ three/examples/js/controls/OrbitControls.js' ); // snip const controls = new THREE.OrbitControls( camera );
  15. import * as THREE from 'three'; THREE.OrbitControls = require( 'imports-loader?THREE!

    exports-loader?THREE.OrbitControls!../node_modules/ three/examples/js/controls/OrbitControls.js' ); // snip const controls = new THREE.OrbitControls( camera );
  16. import * as THREE from 'three'; THREE.OrbitControls = require( 'imports-loader?THREE!

    exports-loader?THREE.OrbitControls!../node_modules/ three/examples/js/controls/OrbitControls.js' ); // snip const controls = new THREE.OrbitControls( camera );
  17. demo http://localhost:8080/

  18. Warnings will be ignored in the production build. http://localhost:8000/demo1-webpack/

  19. Handle Canvas
 as a WebApp view

  20. Canvas element has to be handled
 by the WebApp framework.

    Problem
  21. Use mounted canvas,
 then bind Renderer in
 mounted or
 componentDidMount

    hock. Solution
  22. <template> <canvas></canvas> </template> <script> import * as THREE from 'three';

    export default { name: 'view-3d', data () { return { width: 200, height: 200 mount a canvas element
  23. } }, mounted() { const width = this.width; const height

    = this.height; const loader = new THREE.JSONLoader(); // (snip) this.renderer = new THREE.WebGLRenderer( { canvas: this.$el, // <-- bind to mouted canvas antialias: true, stencil: false } );
  24. <template> <canvas></canvas> </template> <script> import * as PIXI from 'pixi.js';

    export default { data () { return {} }, mounted() {
  25. return {} }, mounted() { this.renderer = new PIXI.WebGLRenderer( {

    width: 300, height: 300, view: this.$el, antialias: true, transparent: false, backgroundColor: 0xcccccc } );
  26. demo http://localhost:8000/demo2-mount-three/ http://localhost:8000/demo2-mount-pixi/ http://localhost:8000/demo2-mount-chart/

  27. Removing
 Canvas and GL context

  28. If canvas and renderer were removed inappropriately. It causes memory

    leaking. Problem
  29. Clear cache and unbind GL context in
 beforeDestroy or
 componentWillUnmount

    hock. Solution
  30. tick() { if ( ! this.running ) return; requestAnimationFrame( ()

    => this.tick() ); this.mesh.rotation.y += 0.03; this.renderer.render( this.scene, this.camera ); } Your render loop
  31. beforeDestroy() { this.running = false; while ( this.scene.children.length > 0

    ) { const object = this.scene.children[ this.scene.children.length - 1 ]; deepDispose( object ); this.scene.remove( object ); } this.renderer.dispose(); this.renderer.forceContextLoss(); this.renderer.context = undefined; this.renderer.domElement = undefined; // until next garbage collection this.$el.width = 1; this.$el.height = 1; }, Dispose all objects
  32. beforeDestroy() { this.running = false; while ( this.scene.children.length > 0

    ) { const object = this.scene.children[ this.scene.children.length - 1 ]; deepDispose( object ); this.scene.remove( object ); } this.renderer.dispose(); this.renderer.forceContextLoss(); this.renderer.context = undefined; this.renderer.domElement = undefined; // until next garbage collection this.$el.width = 1; this.$el.height = 1; }, Children of scene consist of • Meshes • Lights • Object3d
  33. function deepDispose( object3D ) { object3D.traverse( object3D => dispose( object3D

    ) ); } function dispose ( object3D ) { if ( !! object3D.geometry ) { object3D.geometry.dispose(); object3D.geometry = undefined; } if ( !! object3D.material && object3D.material instanceof Array ) { object3D.material.forEach( material => disposeMaterial( material ) );
  34. function deepDispose( object3D ) { object3D.traverse( object3D => dispose( object3D

    ) ); } function dispose ( object3D ) { if ( !! object3D.geometry ) { object3D.geometry.dispose(); object3D.geometry = undefined; } if ( !! object3D.material && object3D.material instanceof Array ) { object3D.material.forEach( material => disposeMaterial( material ) );
  35. function dispose ( object3D ) { if ( !! object3D.geometry

    ) { object3D.geometry.dispose(); object3D.geometry = undefined; } if ( !! object3D.material && object3D.material instanceof Array ) { object3D.material.forEach( material => disposeMaterial( material ) ); } else if ( !! object3D.material ) { disposeMaterial( material ); }
  36. function disposeMaterial( material ) { if ( !! material.map )

    { material.map.dispose(); material.map = undefined; } // do that for normalMap, specularMap and bumpMap too material.dispose(); material = undefined; }
  37. Before unmount: • Stop render loop. • Dispose all geoms,

    materials and textures recursively. • Release renderer and context. • Resize canvas to 1px * 1px.
  38. demo http://localhost:8000/demo3-unmount/

  39. tick() { if ( ! this.running ) return; requestAnimationFrame( ()

    => this.tick() );
 this.movieClip.rotation += 0.03; this.renderer.render( this.stage ); }
  40. beforeDestroy() { this.running = false; const destroyChildren = true; this.stage.destroy(

    destroyChildren ); this.renderer.destroy(); this.renderer.view = undefined; // until next garbage collection this.$el.width = 1; this.$el.height = 1; },
  41. Before unmount: • Stop render loop. • Destroy the stage

    with deep option. • Release renderer and context. • Resize canvas to 1px * 1px.
  42. FYI: May not necessary to remove the canvas. It may

    cause flickering. Consider to just hide it.
  43. <template> <button type="button" @click="toggleShow”> toggleShow </button> <my-canvas-component v-if="show"></my-canvas-component> </template>

  44. <template> <button type="button" @click="toggleShow”> toggleShow </button> <my-canvas-component v-show="show"></my-canvas-component> </template>

  45. demo http://localhost:8000/demo3-show-toggle/

  46. Assets compressing

  47. The file size of 3D objects are big. Problem

  48. Compress them. Solution

  49. None
  50. original 16,513KB zgip 4,585KB draco 344KB

  51. • gZip and Zip reduce 70%. • Draco reduces 98%.

  52. Draco loader for three.js

  53. Prepare draco libs https://github.com/mrdoob/three.js/tree/dev/examples/js/loaders/draco

  54. Load only DRACOLoader.js into your JS. Others will be loaded

    by Loader.
  55. const DRACO_PATH = './libs/'; const DRACO_TYPE = !! window.WebAssembly ?

    'wasm' : 'js'; const dracoLoader = new THREE.DRACOLoader( DRACO_PATH, { type: DRACO_TYPE } ); dracoLoader.load( './model/rameses.drc', ( geometry ) => { const material = new THREE.MeshStandardMaterial( { map: texture } ); const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); } );
  56. const DRACO_PATH = './libs/'; const DRACO_TYPE = !! window.WebAssembly ?

    'wasm' : 'js'; const dracoLoader = new THREE.DRACOLoader( DRACO_PATH, { type: DRACO_TYPE } ); dracoLoader.load( './model/rameses.drc', ( geometry ) => { const material = new THREE.MeshStandardMaterial( { map: texture } ); const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); The decoder will be loaded from external file.
 You need set the path to it.
  57. const DRACO_PATH = './libs/'; const DRACO_TYPE = !! window.WebAssembly ?

    'wasm' : 'js'; const dracoLoader = new THREE.DRACOLoader( DRACO_PATH, { type: DRACO_TYPE } ); dracoLoader.load( './model/rameses.drc', ( geometry ) => { const material = new THREE.MeshStandardMaterial( { map: texture } ); const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); The recorder is available in both Wasm and JS. Use preferred version.
  58. const DRACO_PATH = './libs/'; const DRACO_TYPE = !! window.WebAssembly ?

    'wasm' : 'js'; const dracoLoader = new THREE.DRACOLoader( DRACO_PATH, { type: DRACO_TYPE } ); dracoLoader.load( './model/rameses.drc', ( geometry ) => { const material = new THREE.MeshStandardMaterial( { map: texture } ); const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); Make a loader instance for three.js
  59. const DRACO_PATH = './libs/'; const DRACO_TYPE = !! window.WebAssembly ?

    'wasm' : 'js'; const dracoLoader = new THREE.DRACOLoader( DRACO_PATH, { type: DRACO_TYPE } ); dracoLoader.load( './model/rameses.drc', ( geometry ) => { const material = new THREE.MeshStandardMaterial( { map: texture } ); const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); } ); Load a drc file
  60. const DRACO_PATH = './libs/'; const DRACO_TYPE = !! window.WebAssembly ?

    'wasm' : 'js'; const dracoLoader = new THREE.DRACOLoader( DRACO_PATH, { type: DRACO_TYPE } ); dracoLoader.load( './model/rameses.drc', ( geometry ) => { const material = new THREE.MeshStandardMaterial( { map: texture } ); const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); } );
  61. demo http://yomotsu.net/blog/assets/2017-09-18-draco/demo/demo1.html

  62. For more info, read my blog post:
 http://yomotsu.net/blog/2017/09/18/draco.html

  63. Unzip JS lib is available on NPM as well:
 https://www.npmjs.com/package/zip-loader

  64. • Pass renderer.render() if not needed. • Store as numbers:

    colour, time etc… Other things
  65. three.js / pixi.js and others can be
 a part of

    WebApp! Not only for artistic projects. Conclusion
  66. For Viewer, Simulator, DataVisualization and more! Conclusion

  67. gl.finish(); @yomotsu All demos can be found: https://www.dropbox.com/sh/hx2zgyh38i1n4z3/AACdSlmGcI3OOZ9Hv_wMU1kua?dl=0