|
@@ -1,7 +1,7 @@
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
- <title>three.js webgl - indexed instancing (single box), interleaved buffers, dynamic updates</title>
|
|
|
+ <title>three.js webgl - indexed instancing (single box), interleaved buffers</title>
|
|
|
<meta charset="utf-8">
|
|
|
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
|
<link type="text/css" rel="stylesheet" href="main.css">
|
|
@@ -22,68 +22,29 @@
|
|
|
|
|
|
<div id="container"></div>
|
|
|
<div id="info">
|
|
|
- <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - indexed instancing (single box)<br/>interleaved buffers, dynamic updates
|
|
|
+ <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - indexed instancing (single box), interleaved buffers
|
|
|
<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
|
|
|
</div>
|
|
|
|
|
|
- <script id="vertexShader" type="x-shader/x-vertex">
|
|
|
- precision highp float;
|
|
|
-
|
|
|
- uniform mat4 modelViewMatrix;
|
|
|
- uniform mat4 projectionMatrix;
|
|
|
-
|
|
|
- attribute vec3 position;
|
|
|
- attribute vec3 offset;
|
|
|
- attribute vec2 uv;
|
|
|
- attribute vec4 orientation;
|
|
|
-
|
|
|
- varying vec2 vUv;
|
|
|
-
|
|
|
- // http://www.geeks3d.com/20141201/how-to-rotate-a-vertex-by-a-quaternion-in-glsl/
|
|
|
-
|
|
|
- vec3 applyQuaternionToVector( vec4 q, vec3 v ){
|
|
|
-
|
|
|
- return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- void main() {
|
|
|
-
|
|
|
- vec3 vPosition = applyQuaternionToVector( orientation, position );
|
|
|
-
|
|
|
- vUv = uv;
|
|
|
-
|
|
|
- gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- </script>
|
|
|
-
|
|
|
- <script id="fragmentShader" type="x-shader/x-fragment">
|
|
|
- precision highp float;
|
|
|
-
|
|
|
- uniform sampler2D map;
|
|
|
-
|
|
|
- varying vec2 vUv;
|
|
|
-
|
|
|
- void main() {
|
|
|
-
|
|
|
- gl_FragColor = texture2D(map, vUv);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- </script>
|
|
|
-
|
|
|
<script type="module">
|
|
|
import * as THREE from '../build/three.module.js';
|
|
|
|
|
|
import Stats from './jsm/libs/stats.module.js';
|
|
|
|
|
|
var container, stats;
|
|
|
+ var camera, scene, renderer, mesh;
|
|
|
+ var offsetAttribute, orientationAttribute;
|
|
|
+
|
|
|
+ var instances = 5000;
|
|
|
+ var lastTime = 0;
|
|
|
|
|
|
- var camera, scene, renderer;
|
|
|
- var orientations, instanceBuffer;
|
|
|
+ var moveQ = new THREE.Quaternion( 0.5, 0.5, 0.5, 0.0 ).normalize();
|
|
|
+ var tmpQ = new THREE.Quaternion();
|
|
|
+ var tmpM = new THREE.Matrix4();
|
|
|
+ var currentM = new THREE.Matrix4();
|
|
|
|
|
|
+ init();
|
|
|
+ animate();
|
|
|
|
|
|
function init() {
|
|
|
|
|
@@ -94,12 +55,8 @@
|
|
|
scene = new THREE.Scene();
|
|
|
scene.background = new THREE.Color( 0x101010 );
|
|
|
|
|
|
- renderer = new THREE.WebGLRenderer();
|
|
|
-
|
|
|
// geometry
|
|
|
|
|
|
- var instances = 5000;
|
|
|
-
|
|
|
var geometry = new THREE.InstancedBufferGeometry();
|
|
|
|
|
|
// per mesh data x,y,z,w,u,v,s,t for 4-element alignment
|
|
@@ -161,69 +118,63 @@
|
|
|
|
|
|
geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
|
|
|
|
|
|
- // per instance data
|
|
|
- instanceBuffer = new THREE.InstancedInterleavedBuffer( new Float32Array( instances * 8 ), 8, 1 ).setUsage( THREE.DynamicDrawUsage );
|
|
|
- var offsets = new THREE.InterleavedBufferAttribute( instanceBuffer, 3, 0 );
|
|
|
+ // material
|
|
|
|
|
|
- var vector = new THREE.Vector4();
|
|
|
- for ( var i = 0, ul = offsets.count; i < ul; i ++ ) {
|
|
|
+ var material = new THREE.MeshBasicMaterial();
|
|
|
+ material.map = new THREE.TextureLoader().load( 'textures/crate.gif' );
|
|
|
+ material.side = THREE.DoubleSide;
|
|
|
|
|
|
- var x = Math.random() * 100 - 50;
|
|
|
- var y = Math.random() * 100 - 50;
|
|
|
- var z = Math.random() * 100 - 50;
|
|
|
- vector.set( x, y, z, 0 ).normalize();
|
|
|
- // move out at least 5 units from center in current direction
|
|
|
- offsets.setXYZ( i, x + vector.x * 5, y + vector.y * 5, z + vector.z * 5 );
|
|
|
+ // per instance data
|
|
|
|
|
|
- }
|
|
|
+ var matrix = new THREE.Matrix4();
|
|
|
+ var offset = new THREE.Vector3();
|
|
|
+ var orientation = new THREE.Quaternion();
|
|
|
+ var scale = new THREE.Vector3( 1, 1, 1 );
|
|
|
+ var x, y, z, w;
|
|
|
|
|
|
- geometry.setAttribute( 'offset', offsets ); // per mesh translation
|
|
|
+ mesh = new THREE.InstancedMesh( geometry, material, instances );
|
|
|
|
|
|
- orientations = new THREE.InterleavedBufferAttribute( instanceBuffer, 4, 4 );
|
|
|
+ for ( var i = 0; i < instances; i ++ ) {
|
|
|
|
|
|
- for ( var i = 0, ul = orientations.count; i < ul; i ++ ) {
|
|
|
+ // offsets
|
|
|
|
|
|
- vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
|
|
|
- vector.normalize();
|
|
|
+ x = Math.random() * 100 - 50;
|
|
|
+ y = Math.random() * 100 - 50;
|
|
|
+ z = Math.random() * 100 - 50;
|
|
|
|
|
|
- orientations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
|
|
|
+ offset.set( x, y, z ).normalize();
|
|
|
+ offset.multiplyScalar( 5 ); // move out at least 5 units from center in current direction
|
|
|
+ offset.set( x + offset.x, y + offset.y, z + offset.z );
|
|
|
|
|
|
- }
|
|
|
+ // orientations
|
|
|
|
|
|
- geometry.setAttribute( 'orientation', orientations ); // per mesh orientation
|
|
|
+ x = Math.random() * 2 - 1;
|
|
|
+ y = Math.random() * 2 - 1;
|
|
|
+ z = Math.random() * 2 - 1;
|
|
|
+ w = Math.random() * 2 - 1;
|
|
|
|
|
|
- // material
|
|
|
- var texture = new THREE.TextureLoader().load( 'textures/crate.gif' );
|
|
|
- texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
|
|
|
+ orientation.set( x, y, z, w ).normalize();
|
|
|
|
|
|
- var material = new THREE.RawShaderMaterial( {
|
|
|
+ matrix.compose( offset, orientation, scale );
|
|
|
|
|
|
- uniforms: {
|
|
|
- map: { value: texture }
|
|
|
- },
|
|
|
- vertexShader: document.getElementById( 'vertexShader' ).textContent,
|
|
|
- fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
|
|
|
- side: THREE.DoubleSide,
|
|
|
- transparent: false
|
|
|
+ mesh.setMatrixAt( i, matrix );
|
|
|
|
|
|
- } );
|
|
|
+ }
|
|
|
|
|
|
- var mesh = new THREE.Mesh( geometry, material );
|
|
|
- mesh.frustumCulled = false;
|
|
|
scene.add( mesh );
|
|
|
|
|
|
+ renderer = new THREE.WebGLRenderer();
|
|
|
+ renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ container.appendChild( renderer.domElement );
|
|
|
|
|
|
if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
|
|
|
|
|
|
- document.getElementById( "notSupported" ).style.display = "";
|
|
|
+ document.getElementById( 'notSupported' ).style.display = '';
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
- renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
- renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
- container.appendChild( renderer.domElement );
|
|
|
-
|
|
|
stats = new Stats();
|
|
|
container.appendChild( stats.dom );
|
|
|
|
|
@@ -251,44 +202,36 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- var lastTime = 0;
|
|
|
-
|
|
|
- var moveQ = ( new THREE.Quaternion( .5, .5, .5, 0.0 ) ).normalize();
|
|
|
- var tmpQ = new THREE.Quaternion();
|
|
|
- var currentQ = new THREE.Quaternion();
|
|
|
function render() {
|
|
|
|
|
|
var time = performance.now();
|
|
|
|
|
|
-
|
|
|
- var object = scene.children[ 0 ];
|
|
|
-
|
|
|
- object.rotation.y = time * 0.00005;
|
|
|
-
|
|
|
- renderer.render( scene, camera );
|
|
|
+ mesh.rotation.y = time * 0.00005;
|
|
|
|
|
|
var delta = ( time - lastTime ) / 5000;
|
|
|
tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
|
|
|
+ tmpM.makeRotationFromQuaternion( tmpQ );
|
|
|
|
|
|
- for ( var i = 0, ul = orientations.count; i < ul; i ++ ) {
|
|
|
+ for ( var i = 0, il = instances; i < il; i ++ ) {
|
|
|
|
|
|
- var index = i * instanceBuffer.stride + orientations.offset;
|
|
|
- currentQ.set( instanceBuffer.array[ index ], instanceBuffer.array[ index + 1 ], instanceBuffer.array[ index + 2 ], instanceBuffer.array[ index + 3 ] );
|
|
|
- currentQ.multiply( tmpQ );
|
|
|
-
|
|
|
- orientations.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
|
|
|
+ mesh.getMatrixAt( i, currentM );
|
|
|
+ currentM.multiply( tmpM );
|
|
|
+ mesh.setMatrixAt( i, currentM );
|
|
|
|
|
|
}
|
|
|
- instanceBuffer.needsUpdate = true;
|
|
|
+
|
|
|
+ mesh.instanceMatrix.needsUpdate = true;
|
|
|
+
|
|
|
lastTime = time;
|
|
|
|
|
|
- }
|
|
|
+ renderer.render( scene, camera );
|
|
|
|
|
|
- init();
|
|
|
- animate();
|
|
|
+ }
|
|
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
+
|
|
|
+
|
|
|
</html>
|