|
@@ -26,11 +26,6 @@
|
|
|
<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
|
|
|
</div>
|
|
|
|
|
|
- <script src="../build/three.js"></script>
|
|
|
-
|
|
|
- <script src="js/WebGL.js"></script>
|
|
|
- <script src="js/libs/stats.min.js"></script>
|
|
|
-
|
|
|
<script id="vertexShader" type="x-shader/x-vertex">
|
|
|
precision highp float;
|
|
|
|
|
@@ -79,225 +74,237 @@
|
|
|
|
|
|
</script>
|
|
|
|
|
|
- <script>
|
|
|
-
|
|
|
- if ( WEBGL.isWebGLAvailable() === false ) {
|
|
|
-
|
|
|
- document.body.appendChild( WEBGL.getWebGLErrorMessage() );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- var container, stats;
|
|
|
-
|
|
|
- var camera, scene, renderer;
|
|
|
- var orientations, instanceBuffer;
|
|
|
-
|
|
|
-
|
|
|
- function init() {
|
|
|
-
|
|
|
- container = document.getElementById( 'container' );
|
|
|
-
|
|
|
- camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
|
|
|
-
|
|
|
- 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
|
|
|
- // only use x,y,z and u,v; but x, y, z, nx, ny, nz, u, v would be a good layout
|
|
|
- var vertexBuffer = new THREE.InterleavedBuffer( new Float32Array( [
|
|
|
- // Front
|
|
|
- - 1, 1, 1, 0, 0, 0, 0, 0,
|
|
|
- 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
- - 1, - 1, 1, 0, 0, 1, 0, 0,
|
|
|
- 1, - 1, 1, 0, 1, 1, 0, 0,
|
|
|
- // Back
|
|
|
- 1, 1, - 1, 0, 1, 0, 0, 0,
|
|
|
- - 1, 1, - 1, 0, 0, 0, 0, 0,
|
|
|
- 1, - 1, - 1, 0, 1, 1, 0, 0,
|
|
|
- - 1, - 1, - 1, 0, 0, 1, 0, 0,
|
|
|
- // Left
|
|
|
- - 1, 1, - 1, 0, 1, 1, 0, 0,
|
|
|
- - 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
- - 1, - 1, - 1, 0, 0, 1, 0, 0,
|
|
|
- - 1, - 1, 1, 0, 0, 0, 0, 0,
|
|
|
- // Right
|
|
|
- 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
- 1, 1, - 1, 0, 1, 1, 0, 0,
|
|
|
- 1, - 1, 1, 0, 0, 0, 0, 0,
|
|
|
- 1, - 1, - 1, 0, 0, 1, 0, 0,
|
|
|
- // Top
|
|
|
- - 1, 1, 1, 0, 0, 0, 0, 0,
|
|
|
- 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
- - 1, 1, - 1, 0, 0, 1, 0, 0,
|
|
|
- 1, 1, - 1, 0, 1, 1, 0, 0,
|
|
|
- // Bottom
|
|
|
- 1, - 1, 1, 0, 1, 0, 0, 0,
|
|
|
- - 1, - 1, 1, 0, 0, 0, 0, 0,
|
|
|
- 1, - 1, - 1, 0, 1, 1, 0, 0,
|
|
|
- - 1, - 1, - 1, 0, 0, 1, 0, 0
|
|
|
- ] ), 8 );
|
|
|
-
|
|
|
- // Use vertexBuffer, starting at offset 0, 3 items in position attribute
|
|
|
- var positions = new THREE.InterleavedBufferAttribute( vertexBuffer, 3, 0 );
|
|
|
- geometry.addAttribute( 'position', positions );
|
|
|
- // Use vertexBuffer, starting at offset 4, 2 items in uv attribute
|
|
|
- var uvs = new THREE.InterleavedBufferAttribute( vertexBuffer, 2, 4 );
|
|
|
- geometry.addAttribute( 'uv', uvs );
|
|
|
-
|
|
|
- var indices = new Uint16Array( [
|
|
|
- 0, 1, 2,
|
|
|
- 2, 1, 3,
|
|
|
- 4, 5, 6,
|
|
|
- 6, 5, 7,
|
|
|
- 8, 9, 10,
|
|
|
- 10, 9, 11,
|
|
|
- 12, 13, 14,
|
|
|
- 14, 13, 15,
|
|
|
- 16, 17, 18,
|
|
|
- 18, 17, 19,
|
|
|
- 20, 21, 22,
|
|
|
- 22, 21, 23
|
|
|
- ] );
|
|
|
-
|
|
|
- geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
|
|
|
-
|
|
|
- // per instance data
|
|
|
- instanceBuffer = new THREE.InstancedInterleavedBuffer( new Float32Array( instances * 8 ), 8, 1 ).setDynamic( true );
|
|
|
- var offsets = new THREE.InterleavedBufferAttribute( instanceBuffer, 3, 0 );
|
|
|
-
|
|
|
- var vector = new THREE.Vector4();
|
|
|
- for ( var i = 0, ul = offsets.count; i < ul; i ++ ) {
|
|
|
-
|
|
|
- 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 );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- geometry.addAttribute( 'offset', offsets ); // per mesh translation
|
|
|
-
|
|
|
- orientations = new THREE.InterleavedBufferAttribute( instanceBuffer, 4, 4 );
|
|
|
-
|
|
|
- for ( var i = 0, ul = orientations.count; i < ul; i ++ ) {
|
|
|
-
|
|
|
- vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
|
|
|
- vector.normalize();
|
|
|
-
|
|
|
- orientations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
|
|
|
+ <script type="module">
|
|
|
+ import {
|
|
|
+ BufferAttribute,
|
|
|
+ Color,
|
|
|
+ DoubleSide,
|
|
|
+ InstancedBufferGeometry,
|
|
|
+ InstancedInterleavedBuffer,
|
|
|
+ InterleavedBuffer,
|
|
|
+ InterleavedBufferAttribute,
|
|
|
+ Mesh,
|
|
|
+ PerspectiveCamera,
|
|
|
+ Quaternion,
|
|
|
+ Scene,
|
|
|
+ RawShaderMaterial,
|
|
|
+ TextureLoader,
|
|
|
+ Vector4,
|
|
|
+ WebGLRenderer
|
|
|
+ } from "../build/three.module.js";
|
|
|
+
|
|
|
+ import Stats from './jsm/libs/stats.module.js';
|
|
|
+
|
|
|
+ var container, stats;
|
|
|
+
|
|
|
+ var camera, scene, renderer;
|
|
|
+ var orientations, instanceBuffer;
|
|
|
+
|
|
|
+
|
|
|
+ function init() {
|
|
|
+
|
|
|
+ container = document.getElementById( 'container' );
|
|
|
+
|
|
|
+ camera = new PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
|
|
|
+
|
|
|
+ scene = new Scene();
|
|
|
+ scene.background = new Color( 0x101010 );
|
|
|
+
|
|
|
+ renderer = new WebGLRenderer();
|
|
|
+
|
|
|
+ // geometry
|
|
|
+
|
|
|
+ var instances = 5000;
|
|
|
+
|
|
|
+ var geometry = new InstancedBufferGeometry();
|
|
|
+
|
|
|
+ // per mesh data x,y,z,w,u,v,s,t for 4-element alignment
|
|
|
+ // only use x,y,z and u,v; but x, y, z, nx, ny, nz, u, v would be a good layout
|
|
|
+ var vertexBuffer = new InterleavedBuffer( new Float32Array( [
|
|
|
+ // Front
|
|
|
+ - 1, 1, 1, 0, 0, 0, 0, 0,
|
|
|
+ 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
+ - 1, - 1, 1, 0, 0, 1, 0, 0,
|
|
|
+ 1, - 1, 1, 0, 1, 1, 0, 0,
|
|
|
+ // Back
|
|
|
+ 1, 1, - 1, 0, 1, 0, 0, 0,
|
|
|
+ - 1, 1, - 1, 0, 0, 0, 0, 0,
|
|
|
+ 1, - 1, - 1, 0, 1, 1, 0, 0,
|
|
|
+ - 1, - 1, - 1, 0, 0, 1, 0, 0,
|
|
|
+ // Left
|
|
|
+ - 1, 1, - 1, 0, 1, 1, 0, 0,
|
|
|
+ - 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
+ - 1, - 1, - 1, 0, 0, 1, 0, 0,
|
|
|
+ - 1, - 1, 1, 0, 0, 0, 0, 0,
|
|
|
+ // Right
|
|
|
+ 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
+ 1, 1, - 1, 0, 1, 1, 0, 0,
|
|
|
+ 1, - 1, 1, 0, 0, 0, 0, 0,
|
|
|
+ 1, - 1, - 1, 0, 0, 1, 0, 0,
|
|
|
+ // Top
|
|
|
+ - 1, 1, 1, 0, 0, 0, 0, 0,
|
|
|
+ 1, 1, 1, 0, 1, 0, 0, 0,
|
|
|
+ - 1, 1, - 1, 0, 0, 1, 0, 0,
|
|
|
+ 1, 1, - 1, 0, 1, 1, 0, 0,
|
|
|
+ // Bottom
|
|
|
+ 1, - 1, 1, 0, 1, 0, 0, 0,
|
|
|
+ - 1, - 1, 1, 0, 0, 0, 0, 0,
|
|
|
+ 1, - 1, - 1, 0, 1, 1, 0, 0,
|
|
|
+ - 1, - 1, - 1, 0, 0, 1, 0, 0
|
|
|
+ ] ), 8 );
|
|
|
+
|
|
|
+ // Use vertexBuffer, starting at offset 0, 3 items in position attribute
|
|
|
+ var positions = new InterleavedBufferAttribute( vertexBuffer, 3, 0 );
|
|
|
+ geometry.addAttribute( 'position', positions );
|
|
|
+ // Use vertexBuffer, starting at offset 4, 2 items in uv attribute
|
|
|
+ var uvs = new InterleavedBufferAttribute( vertexBuffer, 2, 4 );
|
|
|
+ geometry.addAttribute( 'uv', uvs );
|
|
|
+
|
|
|
+ var indices = new Uint16Array( [
|
|
|
+ 0, 1, 2,
|
|
|
+ 2, 1, 3,
|
|
|
+ 4, 5, 6,
|
|
|
+ 6, 5, 7,
|
|
|
+ 8, 9, 10,
|
|
|
+ 10, 9, 11,
|
|
|
+ 12, 13, 14,
|
|
|
+ 14, 13, 15,
|
|
|
+ 16, 17, 18,
|
|
|
+ 18, 17, 19,
|
|
|
+ 20, 21, 22,
|
|
|
+ 22, 21, 23
|
|
|
+ ] );
|
|
|
+
|
|
|
+ geometry.setIndex( new BufferAttribute( indices, 1 ) );
|
|
|
+
|
|
|
+ // per instance data
|
|
|
+ instanceBuffer = new InstancedInterleavedBuffer( new Float32Array( instances * 8 ), 8, 1 ).setDynamic( true );
|
|
|
+ var offsets = new InterleavedBufferAttribute( instanceBuffer, 3, 0 );
|
|
|
+
|
|
|
+ var vector = new Vector4();
|
|
|
+ for ( var i = 0, ul = offsets.count; i < ul; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- geometry.addAttribute( 'orientation', orientations ); // per mesh orientation
|
|
|
+ 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 );
|
|
|
|
|
|
- // material
|
|
|
- var texture = new THREE.TextureLoader().load( 'textures/crate.gif' );
|
|
|
- texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
|
|
|
+ }
|
|
|
|
|
|
- var material = new THREE.RawShaderMaterial( {
|
|
|
+ geometry.addAttribute( 'offset', offsets ); // per mesh translation
|
|
|
|
|
|
- uniforms: {
|
|
|
- map: { value: texture }
|
|
|
- },
|
|
|
- vertexShader: document.getElementById( 'vertexShader' ).textContent,
|
|
|
- fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
|
|
|
- side: THREE.DoubleSide,
|
|
|
- transparent: false
|
|
|
+ orientations = new InterleavedBufferAttribute( instanceBuffer, 4, 4 );
|
|
|
|
|
|
- } );
|
|
|
+ for ( var i = 0, ul = orientations.count; i < ul; i ++ ) {
|
|
|
|
|
|
- var mesh = new THREE.Mesh( geometry, material );
|
|
|
- mesh.frustumCulled = false;
|
|
|
- scene.add( mesh );
|
|
|
+ vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
|
|
|
+ vector.normalize();
|
|
|
+
|
|
|
+ orientations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ geometry.addAttribute( 'orientation', orientations ); // per mesh orientation
|
|
|
+
|
|
|
+ // material
|
|
|
+ var texture = new TextureLoader().load( 'textures/crate.gif' );
|
|
|
+ texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
|
|
|
+
|
|
|
+ var material = new RawShaderMaterial( {
|
|
|
+
|
|
|
+ uniforms: {
|
|
|
+ map: { value: texture }
|
|
|
+ },
|
|
|
+ vertexShader: document.getElementById( 'vertexShader' ).textContent,
|
|
|
+ fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
|
|
|
+ side: DoubleSide,
|
|
|
+ transparent: false
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ var mesh = new Mesh( geometry, material );
|
|
|
+ mesh.frustumCulled = false;
|
|
|
+ scene.add( mesh );
|
|
|
+
|
|
|
+
|
|
|
+ if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
|
|
|
+
|
|
|
+ document.getElementById( "notSupported" ).style.display = "";
|
|
|
+ return;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
+ renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ container.appendChild( renderer.domElement );
|
|
|
|
|
|
- if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
|
|
|
+ stats = new Stats();
|
|
|
+ container.appendChild( stats.dom );
|
|
|
|
|
|
- document.getElementById( "notSupported" ).style.display = "";
|
|
|
- return;
|
|
|
+ window.addEventListener( 'resize', onWindowResize, false );
|
|
|
|
|
|
}
|
|
|
|
|
|
- renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
- renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
- container.appendChild( renderer.domElement );
|
|
|
-
|
|
|
- stats = new Stats();
|
|
|
- container.appendChild( stats.dom );
|
|
|
-
|
|
|
- window.addEventListener( 'resize', onWindowResize, false );
|
|
|
-
|
|
|
- }
|
|
|
+ function onWindowResize() {
|
|
|
|
|
|
- function onWindowResize() {
|
|
|
+ camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ camera.updateProjectionMatrix();
|
|
|
|
|
|
- camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
- camera.updateProjectionMatrix();
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
|
|
|
- renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ //
|
|
|
|
|
|
- //
|
|
|
+ function animate() {
|
|
|
|
|
|
- function animate() {
|
|
|
+ requestAnimationFrame( animate );
|
|
|
|
|
|
- requestAnimationFrame( animate );
|
|
|
+ render();
|
|
|
+ stats.update();
|
|
|
|
|
|
- render();
|
|
|
- stats.update();
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ var lastTime = 0;
|
|
|
|
|
|
- var lastTime = 0;
|
|
|
+ var moveQ = ( new Quaternion( .5, .5, .5, 0.0 ) ).normalize();
|
|
|
+ var tmpQ = new Quaternion();
|
|
|
+ var currentQ = new Quaternion();
|
|
|
+ function render() {
|
|
|
|
|
|
- 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 time = performance.now();
|
|
|
|
|
|
+ var object = scene.children[ 0 ];
|
|
|
|
|
|
- var object = scene.children[ 0 ];
|
|
|
+ object.rotation.y = time * 0.00005;
|
|
|
|
|
|
- object.rotation.y = time * 0.00005;
|
|
|
+ renderer.render( scene, camera );
|
|
|
|
|
|
- renderer.render( scene, camera );
|
|
|
+ var delta = ( time - lastTime ) / 5000;
|
|
|
+ tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
|
|
|
|
|
|
- var delta = ( time - lastTime ) / 5000;
|
|
|
- tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
|
|
|
+ for ( var i = 0, ul = orientations.count; i < ul; i ++ ) {
|
|
|
|
|
|
- for ( var i = 0, ul = orientations.count; i < ul; 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 );
|
|
|
|
|
|
- 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 );
|
|
|
|
|
|
- orientations.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
|
|
|
+ }
|
|
|
+ instanceBuffer.needsUpdate = true;
|
|
|
+ lastTime = time;
|
|
|
|
|
|
}
|
|
|
- instanceBuffer.needsUpdate = true;
|
|
|
- lastTime = time;
|
|
|
|
|
|
- }
|
|
|
+ init();
|
|
|
+ animate();
|
|
|
|
|
|
- init();
|
|
|
- animate();
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
-
|
|
|
-
|
|
|
</html>
|