three.js ~ MultiMaterial Impostor Example

2016-14-03

by Ludovic Bailly

HTML

<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script id="shaderCyl-vs" type="x-shader/x-vertex">
// Inherits from Phong Shading in ThreeJS, allows compatibility with chunks
#define PHONG

// Normalize diagonal matrix (1)
const mat4 D = mat4(
    vec4(1.0, 0.0, 0.0, 0.0),
    vec4(0.0, 1.0, 0.0, 0.0),
    vec4(0.0, 0.0, 0.0, 0.0),
    vec4(0.0, 0.0, 0.0, -1.0)
);

const mat4 t1 = mat4(
    vec4(1.0, 0.0, 0.0, 0.0),
    vec4(0.0, 1.0, 0.0, 0.0),
    vec4(0.0, 0.0, 0.0, 0.0),
    vec4(0.0, 0.0, 1.0, 1.0)
);

const mat4 t2 = mat4(
    vec4(1.0, 0.0, 0.0, 0.0),
    vec4(0.0, 1.0, 0.0, 0.0),
    vec4(0.0, 0.0, 0.0, 0.0),
    vec4(0.0, 0.0, -1.0, 1.0)
);

// Create transpose method (not built-in OpenGL ES Shading Language)
highp mat4 transpose(in highp mat4 inMatrix) {
    highp vec4 i0 = inMatrix[0];
    highp vec4 i1 = inMatrix[1];
    highp vec4 i2 = inMatrix[2];
    highp vec4 i3 = inMatrix[3];

    highp mat4 outMatrix = mat4(
        vec4(i0.x, i1.x, i2.x, i3.x),
        vec4(i0.y, i1.y, i2.y, i3.y),
        vec4(i0.z, i1.z, i2.z, i3.z),
        vec4(i0.w, i1.w, i2.w, i3.w)
    );

    return outMatrix;
}

#include <common>
#include <color_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>

attribute float radius;
attribute vec3 dir;

uniform vec4 viewport;
uniform mat4 modelViewMatrixInverse;
uniform float devicePixelRatio;

varying mat4 MTInverse;
varying mat4 VPInverse;
varying vec4 centernormclip;

// Near, far, width and height
varying float n, f, w, h;

// Compatibility with threejs chunks
varying vec3 vViewPosition;
varying vec3 vViewDirection;
varying float vLength;

vec4 ComputeBoundsFromPMT(mat4 PMT) {
    // Getting coefficients
    vec4 r1 = PMT[0];
    vec4 r2 = PMT[1];
    vec4 r4 = PMT[3];

    // Intermediate computations
    float r4TDr4 = dot(r4, D * r4);
    float r1TDr4 = dot(r1, D * r4);
    float r1TDr1 = dot(r1, D...

JavaScript

var camera, scene, light, renderer, geometry, material, mesh1, mesh2, gl;
var directionalLight;

var WIDTH = window.innerWidth - 20;
var HEIGHT = window.innerHeight - 20;

var controls;
var impostorMaterialCyl;
var impostorMaterialSphere;

createImpostorMaterial();
init();
animate();

function init() {
    scene = new THREE.Scene();

		var axisHelper = new THREE.AxisHelper( 1 );
		scene.add( axisHelper );

    camera = new THREE.PerspectiveCamera(50, WIDTH / HEIGHT, 5, 100);
    camera.position.z = 10;
    camera.position.y = 0;
    
    var ambient = new THREE.AmbientLight( 0x404040 );
    scene.add(ambient);
    
    directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
		directionalLight.position.set( 0, 15, 15 );
		scene.add( directionalLight );
    
    scene.add(camera);
    
    // controls
    controls = new THREE.OrbitControls( camera );

		var geometry = createBufferGeometry();
    geometry.addGroup(0, 2, 0);
    geometry.addGroup(2, 2, 1);
    var materials = new THREE.MultiMaterial([impostorMaterialCyl, impostorMaterialSphere]);
    mesh1 = new THREE.Points(geometry, materials);
    scene.add(mesh1);
    
    renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true});
    renderer.setSize(WIDTH, HEIGHT);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.autoClear = false;
    renderer.sortObjects = true;

    document.body.appendChild(renderer.domElement);
}
    
var previousTime;

function animate() {
  	requestAnimationFrame(animate);
    
    var now = Date.now();
    if (isNaN(previousTime)) {
    	previousTime = Date.now();
    }
    var time = (now - previousTime) * 0.0005;
    previousTime = now;
    
    var x = directionalLight.position.x;
    var z = directionalLight.position.z;
  	directionalLight.position.x = x * Math.cos(time) - z * Math.sin(time);
  	directionalLight.position.z = z * Math.cos(time) + x * Math.sin(time);
    
    render();
}

function render() {
    // Clear...