3D software developer at BioDigital

(Plus a sneak peek at xeoEngine)

Lindsay Kay

Berlin WebGL Meetup, 2 July, 2015

SceneJS

Applications

and

  • WebGL-based 3D visualisation engine
  • JSON scene definition
  • Aims to abstractify 
  • Compiles to optimised internal render graph
  • Little bit the way OpenSG and nVIDIA SceniX does it
  • Started for fun on Canvas3D in 2009, now at V4.2

 

var scene = SceneJS.createScene({

  nodes:[{
      type:"material",
      color: { r: .3, g: .3, b: 1 },

      nodes:[{
          type: "rotate",
          id: "myRotate",
          y: 1.0,
          angle: 0,

          nodes: [{
              type:"geometry/teapot"
            }]
        }]
    }]
});

scene.getNode("myRotate", 
  function(myRotate) {
    var angle = 0;
    scene.on("tick", function() {
        myRotate.setAngle(angle += .5);
      });
  });

JSON-based 3D scene definition

{
  type: "fresnel",
  applyTo: "emit",
  bias: 0.3,
  power: 3.0,
  topColor: {
    r: 1.0, g: 1.0, b: 1.0
  },
  bottomColor: {
    r: 0.0, g: 0.0, b: 0.0
  },

  nodes: [
    {
      type: "texture",
      src: "crossGridColorMap.jpg",

      nodes: [
        {
          type: "geometry/torus",
          segmentsR: 60,
          segmentsT: 40
        }
      ]
    }
  ]
}

Another example: Fresnel effect

SceneJS.Types.addType("myNodes/colorizer", {
  construct: function(params){

    this._material = this.addNode({
      type: "material",
      nodes: params.nodes
    });

    if(params.color){
      this.setColor(params.color);
    }
  },

  setColor: function(color){
    this._material.setColor(color);
  },

  getColor: function(){
    return this._material.getColor();
  },

  destroy: function(){ 
    this._material.destroy();   
  }
});

Defining new scene node types

Define new node types in the plugins directory, then just include them in your scene 

{
  type: "myNodes/colorizer",
  color: { r: 1, g: 0, b: 0 },

  nodes: [
    {
       type: "geometry/teapot"
    }
  ]
}
{
  type: "postprocess/dof",
  texelSize: 0.00022,
  blurCoeff: 0.0084,
  ppm: 10000,
  autofocus: true,

  nodes: [
    {
      type: "geometry/teapot"
    }
  ]
}

Post-processing example: depth-of-field blur

  • Roll your own posteffects from render target, texture and shader nodes
{
  type: "texture",
  src: "texture-atlas.jpg",

  nodes: [
    {
      type: "translate", x: 1.5,

      nodes: [{
        type: "geometry",
        positions: [1,1,0,-1,1,0,-1,-1,0,1,-1,0],
        normals: [0,0,-1,0,0,-1,0,0,-1,0,0,-1],

        // UV coords map to left half of texture image
        uv: [1, 1, .5, 1, .5, 0, 1, 0],
        indices: [0, 1, 2, 0, 2, 3]
      }]
    },
    {
      type: "translate", x: -1.5,

      nodes: [{
        type: "geometry",
        positions: [1,1,0,-1,1,0,-1,-1,0,1,-1,0],
        normals: [0,0,-1,0,0,-1,0,0,-1,0,0,-1],

        // UV coords map to right half of texture image
        uv: [.5, 1, 0, 1, 0, 0, .5, 0],
        indices: [0, 1, 2, 0, 2, 3]
      }]
    }
  ]
}

Optimization: Texture Atlases

{
  type: "geometry",
  positions: [...],
  normals: [...],
  uv: [...],

  nodes: [{
    type: "texture",
    src: "BrickWall.jpg",

    nodes: [{
      type: "geometry",

      // Indices for first three faces
      indices: [...]
    }]
  }, {
    type: "texture",
    src: "general-zod.jpg",

    nodes: [{
      type: "geometry",

      // Indices for remaining three faces
      indices: [...]
    }]
  }]
}]
}

Optimization: Vertex sharing

{
  type: "cull/detail",
  min: [-8,-3,-5],
  max: [8,6, 5],
  sizes: [50,250,350],
  showBoundary: false,
  frustumCull: true,
  nodes: [
    {
      type: "geometry/box"
    },
    {
      type: "geometry/sphere"
    },
    {
      type: "geometry/teapot"
    }
  ]
}

Optimization: Level-of-Detail

{
  type: "physics/body",

  shape: "sphere",
  radius: 1.0,
  pos: [-5.0,-2.3,1.2],
  velocity: [0,0,0],
  mass: 1.0,
  movable: true,
  restitution: 1.0,
  friction: 0.1,

  nodes: [
    {
      type: "geometry/sphere"
    }
  ]
}

Experimental physics

Ray-picking using the depth buffer

Automatic lost WebGL context recovery

  • When context is recovered, rebuilds WebGL textures, vertex buffers etc. from scene state, without re-downloading anything 
{
  type: "clips",
  clips: [

    // Arbitrary clipping planes

    {
      x: -1.0,
      y: 0.2,
      z: 0.0,
      dist: 1.0,
      mode: "inside"
    },

    //...

  ],

  nodes: [
    {
      type: "geometry/torus"
    }
  ]
}

Cross-sections

{
  type: "texture",
  src: "earthDiffuse.jpg",
  applyTo: "color",
  nodes: [
    {
      type: "texture",
      src: "earthNormal.jpg",
      applyTo: "normals",
      nodes: [
        {
          type: "texture",
          src: "earthSpecular.png",
          applyTo: "specular",
          nodes: [
            {
              type: "texture",
              src: "earthEmit.gif",
              applyTo: "emit",
              nodes: [
                {
                  type: "geometry/sphere"
                }
              ]}
          ]}
      ]}
  ]}

Multitexturing

{
  type: "texture/procedural",
  code: [
    "uniform float time;",
    "varying vec2 surfacePosition;",
    "void main( void ) {",
    "    vec2 sp = surfacePosition;",
    "    vec2 p = sp*5.0 - vec2(10.0);",
    "    vec2 i = p;",
    "    float c = 1.0;",
    "    float inten = 0.10;",
    "    for (int n = 0; n < 10; n++) {",
    "        float t = time * (1.0 - (3.0 / float(n+1)));",
    "        i = p + vec2(cos(t - i.x) + sin(t + i.y), sin(t - i.y) + cos(t + i.x));",
    "        c += 1.0/length(vec2(p.x / (sin(i.x+t)/inten),p.y / (cos(i.y+t)/inten)));",
    "    }",
    "    c /= float(10);",
    "    c = 1.5-sqrt(c);",
    "    gl_FragColor = vec4(vec3(c*c*c*c), 999.0) + vec4(0.0, 0.3, 0.5, 1.0);",
    "}"
  ],

  nodes: [{
    type: "geometry/box"
  }]
}

Procedural textures

In the wild: BioDigital Human

  • Running Mechanics

In the wild: BIMVie.ws

What's next?

xeoEngine

  • Game objects
  • Dynamically-editable, JSON-persistable scene
  • Multiple lighting models (Phong, PBR etc.)
  • Still in development
  • API docs (what? no way!) : xeoengine.org/docs

 

Because there still aren't enough WebGL engines, right?  ;)

xeoEngine

var scene = new XEO.Scene();

var material = new XEO.PhongMaterial(scene, {
  id: "myMaterial",         
  diffuse: [ 0.6, 0.6, 0.7 ],
  specular: [ 1.0, 1.0, 1.0 ]
});

var geometry = new XEO.Geometry(scene, {
  primitive: "triangles",
  positions: [...],
  normals: [...],
  uvs: [...],
  indices: [...]
});

var camera = new XEO.Camera(scene);

var object = new XEO.GameObject(scene, {
  material: material,
  geometry: geometry,
  camera: camera
});

Component-object graph​

material.on("diffuse",
  function(value) {
    console.log(
       "material diffuse is now: " + value);
  });



material.diffuse = [0.9, 0.9, 0.6];

Updating scene state

Register change listener

Update scene state

xeoEngine

var texture = new XEO.Texture(scene, {
    src: "myTexture.jpg"
});

material.diffuseMap = texture;

Dynamically editable scenes

Eg. create a texture and add to the material

  • Designed to be editable with a graphical UI
  • Also nice for live coding

xeoEngine

var json = scene.json;


var scene2 = new XEO.Scene({ json: json });

JSON persistable

Save scene to JSON

Create scene from JSON

{
  className: "XEO.Scene",
  components: [
    {
      className: "XEO.PhongMaterial",
      id: "myMaterial",
      diffuse: [ 0.6, 0.6, 0.7 ],
      specular: [ 1.0, 1.0, 1.0 ]
    },
    {
      className: "XEO.Geometry",
      id: "c0",
      primitive: "triangles",
      positions: [...],
      normals: [...],
      uvs: [...],
      indices: [...]
    },
    {
      className: "XEO.GameObject",
      material: "myMaterial",
      geometry: "c0",
      //..
    }
  ]
}
 
    

xeoEngine

Danke!

 

  • @thsherif from BioDigital for lots of SceneJS tweaks 
  • @oletus from vNIDIA for SceneJS optimizations for Tegra/mobile
  • @xymatic for this Meetup!

 

Shouts out to

Made with Slides.com