Slide 1

Slide 1 text

Presented by Akihiro Oyamada (@yomotsu) Sep 24, 2017 WebGL Libs for
 WebApp Frameworks

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

webpack three.js pixi.js etc React Vue.js Angular etc three.js logo: https://github.com/mrdoob/three.js/issues/2789

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Import three.js plugins
 in webpack

Slide 7

Slide 7 text

• OrbitControls • EffectComposer and Passes • CSS3DRenderer • Loaders and others. Plugins for three.js

Slide 8

Slide 8 text

Plugins do not support
 es6 module import/export Problem so far.

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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 );

Slide 11

Slide 11 text

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 );

Slide 12

Slide 12 text

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 );

Slide 13

Slide 13 text

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 );

Slide 14

Slide 14 text

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 );

Slide 15

Slide 15 text

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 );

Slide 16

Slide 16 text

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 );

Slide 17

Slide 17 text

demo http://localhost:8080/

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Handle Canvas
 as a WebApp view

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Use mounted canvas,
 then bind Renderer in
 mounted or
 componentDidMount hock. Solution

Slide 22

Slide 22 text

import * as THREE from 'three'; export default { name: 'view-3d', data () { return { width: 200, height: 200 mount a canvas element

Slide 23

Slide 23 text

} }, 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 } );

Slide 24

Slide 24 text

import * as PIXI from 'pixi.js'; export default { data () { return {} }, mounted() {

Slide 25

Slide 25 text

return {} }, mounted() { this.renderer = new PIXI.WebGLRenderer( { width: 300, height: 300, view: this.$el, antialias: true, transparent: false, backgroundColor: 0xcccccc } );

Slide 26

Slide 26 text

demo http://localhost:8000/demo2-mount-three/ http://localhost:8000/demo2-mount-pixi/ http://localhost:8000/demo2-mount-chart/

Slide 27

Slide 27 text

Removing
 Canvas and GL context

Slide 28

Slide 28 text

If canvas and renderer were removed inappropriately. It causes memory leaking. Problem

Slide 29

Slide 29 text

Clear cache and unbind GL context in
 beforeDestroy or
 componentWillUnmount hock. Solution

Slide 30

Slide 30 text

tick() { if ( ! this.running ) return; requestAnimationFrame( () => this.tick() ); this.mesh.rotation.y += 0.03; this.renderer.render( this.scene, this.camera ); } Your render loop

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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 ) );

Slide 34

Slide 34 text

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 ) );

Slide 35

Slide 35 text

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 ); }

Slide 36

Slide 36 text

function disposeMaterial( material ) { if ( !! material.map ) { material.map.dispose(); material.map = undefined; } // do that for normalMap, specularMap and bumpMap too material.dispose(); material = undefined; }

Slide 37

Slide 37 text

Before unmount: • Stop render loop. • Dispose all geoms, materials and textures recursively. • Release renderer and context. • Resize canvas to 1px * 1px.

Slide 38

Slide 38 text

demo http://localhost:8000/demo3-unmount/

Slide 39

Slide 39 text

tick() { if ( ! this.running ) return; requestAnimationFrame( () => this.tick() );
 this.movieClip.rotation += 0.03; this.renderer.render( this.stage ); }

Slide 40

Slide 40 text

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; },

Slide 41

Slide 41 text

Before unmount: • Stop render loop. • Destroy the stage with deep option. • Release renderer and context. • Resize canvas to 1px * 1px.

Slide 42

Slide 42 text

FYI: May not necessary to remove the canvas. It may cause flickering. Consider to just hide it.

Slide 43

Slide 43 text

toggleShow

Slide 44

Slide 44 text

toggleShow

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Assets compressing

Slide 47

Slide 47 text

The file size of 3D objects are big. Problem

Slide 48

Slide 48 text

Compress them. Solution

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

original 16,513KB zgip 4,585KB draco 344KB

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Draco loader for three.js

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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 ); } );

Slide 56

Slide 56 text

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.

Slide 57

Slide 57 text

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.

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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 ); } );

Slide 61

Slide 61 text

demo http://yomotsu.net/blog/assets/2017-09-18-draco/demo/demo1.html

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

• Pass renderer.render() if not needed. • Store as numbers: colour, time etc… Other things

Slide 65

Slide 65 text

three.js / pixi.js and others can be
 a part of WebApp! Not only for artistic projects. Conclusion

Slide 66

Slide 66 text

For Viewer, Simulator, DataVisualization and more! Conclusion

Slide 67

Slide 67 text

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