Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified examples/screenshots/webgl_instancing_dynamic.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/textures/edge3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
184 changes: 132 additions & 52 deletions examples/webgl_instancing_dynamic.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - instancing - dynamic<br/>
Based on <a href="https://web.archive.org/web/20210101053442/http://oos.moxiecode.com/js_webgl/cubescape/" target="_blank" rel="noopener">Cubescape</a>
by <a href="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/oosmoxiecode" target="_blank" rel="noopener">oosmoxiecode</a>
</div>

<script type="importmap">
{
"imports": {
Expand All @@ -20,64 +26,119 @@

import * as THREE from 'three';

import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { Timer } from 'three/addons/misc/Timer.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
import TWEEN from 'three/addons/libs/tween.module.js';

let camera, scene, renderer, timer, mesh;

let camera, scene, renderer, stats;
const amount = 100;

let mesh;
const amount = parseInt( window.location.search.slice( 1 ) ) || 10;
const count = Math.pow( amount, 3 );
const count = Math.pow( amount, 2 );
const dummy = new THREE.Object3D();

const seeds = [];
const baseColors = [];

const color = new THREE.Color();
const colors = [ new THREE.Color( 0x00ffff ), new THREE.Color( 0xffff00 ), new THREE.Color( 0xff00ff ) ];
const animation = { t: 0 };
let currentColorIndex = 0;
let nextColorIndex = 1;

const maxDistance = 75;
const cameraTarget = new THREE.Vector3();

init();

function init() {

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.NeutralToneMapping;
renderer.setAnimationLoop( animate );
document.body.appendChild( renderer.domElement );

camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( amount * 0.9, amount * 0.9, amount * 0.9 );
camera.position.set( 10, 10, 10 );
camera.lookAt( 0, 0, 0 );

const pmremGenerator = new THREE.PMREMGenerator( renderer );

scene = new THREE.Scene();
scene.background = new THREE.Color( 0xadd8e6 );
scene.environment = pmremGenerator.fromScene( new RoomEnvironment(), 0.04 ).texture;

const loader = new THREE.BufferGeometryLoader();
loader.load( 'models/json/suzanne_buffergeometry.json', function ( geometry ) {
timer = new Timer();
timer.connect( document );

geometry.computeVertexNormals();
geometry.scale( 0.5, 0.5, 0.5 );
const loader = new THREE.TextureLoader();
const texture = loader.load( 'textures/edge3.jpg' );
texture.colorSpace = THREE.SRGBColorSpace;

const material = new THREE.MeshNormalMaterial();
// check overdraw
// let material = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.1, transparent: true } );
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial( { map: texture } );

mesh = new THREE.InstancedMesh( geometry, material, count );
mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
scene.add( mesh );
mesh = new THREE.InstancedMesh( geometry, material, count );
mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
scene.add( mesh );

//
let i = 0;
const offset = ( amount - 1 ) / 2;

const gui = new GUI();
gui.add( mesh, 'count', 0, count );
for ( let x = 0; x < amount; x ++ ) {

} );
for ( let z = 0; z < amount; z ++ ) {

//
dummy.position.set( offset - x, 0, offset - z );
dummy.scale.set( 1, 2, 1 );

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
document.body.appendChild( renderer.domElement );
dummy.updateMatrix();

//
color.setHSL( 1, 0.5 + ( Math.random() * 0.5 ), 0.5 + ( Math.random() * 0.5 ) );
baseColors.push( color.getHex() );

mesh.setMatrixAt( i, dummy.matrix );
mesh.setColorAt( i, color.multiply( colors[ 0 ] ) );

i ++;

seeds.push( Math.random() );

stats = new Stats();
document.body.appendChild( stats.dom );
}

}

//

window.addEventListener( 'resize', onWindowResize );

setInterval( startTween, 3000 );

}

function startTween() {

// tween for animating color transition

new TWEEN.Tween( animation )
.to( {
t: 1
}, 2000 )
.easing( TWEEN.Easing.Sinusoidal.In )
.onComplete( () => {

animation.t = 0;

currentColorIndex = nextColorIndex;
nextColorIndex ++;

if ( nextColorIndex >= colors.length ) nextColorIndex = 0;

} )
.start();

}

function onWindowResize() {
Expand All @@ -93,49 +154,68 @@

function animate() {

render();
timer.update();

stats.update();
const time = timer.getElapsed();

}
TWEEN.update();

// animate camera

camera.position.x = Math.sin( time / 4 ) * 10;
camera.position.z = Math.cos( time / 4 ) * 10;
camera.position.y = 8 + Math.cos( time / 2 ) * 2;

cameraTarget.x = Math.sin( time / 4 ) * - 8;
cameraTarget.z = Math.cos( time / 2 ) * - 8;

camera.lookAt( cameraTarget );
Comment on lines +163 to +172
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please stop the unnecessary spinning? Keep the camera still. The demo will look very nice without it.

Copy link
Collaborator Author

@Mugen87 Mugen87 May 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, the animation makes the demo more exciting. Besides, the previous version of the demo also had an animation. Instead of rotating the entire instanced mesh, the camera is now animated. I would prefer to keep the status quo. Not just because it looks better but also to honor the style of the example's reference from oosmoxiecode.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it that is a "No."

The demo is nauseating to me, so I will have to avoid it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unfortunate. I do understand your point though and also from a pure functional point of view it's indeed better if examples are static since that makes it easier to compare different versions across releases and spot visual differences.

However, #30695 (comment) outlines why we need more visual appealing demos. And depending on the scene, motion can be a good tool to make 3D scenes more exciting. I feel that's true for webgl_instancing_dynamic.


function render() {
camera.up.x = Math.sin( time / 400 );

if ( mesh ) {
// animate instance positions and colors

const time = Date.now() * 0.001;
for ( let i = 0; i < mesh.count; i ++ ) {

mesh.rotation.x = Math.sin( time / 4 );
mesh.rotation.y = Math.sin( time / 2 );
mesh.getMatrixAt( i, dummy.matrix );
dummy.matrix.decompose( dummy.position, dummy.quaternion, dummy.scale );

let i = 0;
const offset = ( amount - 1 ) / 2;
dummy.position.y = Math.abs( Math.sin( ( time + seeds[ i ] ) * 2 + seeds[ i ] ) );

for ( let x = 0; x < amount; x ++ ) {
dummy.updateMatrix();

for ( let y = 0; y < amount; y ++ ) {
mesh.setMatrixAt( i, dummy.matrix );

for ( let z = 0; z < amount; z ++ ) {
// colors

dummy.position.set( offset - x, offset - y, offset - z );
dummy.rotation.y = ( Math.sin( x / 4 + time ) + Math.sin( y / 4 + time ) + Math.sin( z / 4 + time ) );
dummy.rotation.z = dummy.rotation.y * 2;
if ( animation.t > 0 ) {

dummy.updateMatrix();
const currentColor = colors[ currentColorIndex ];
const nextColor = colors[ nextColorIndex ];

const f = dummy.position.length() / maxDistance;

mesh.setMatrixAt( i ++, dummy.matrix );
if ( f <= animation.t ) {

}
color.set( baseColors[ i ] ).multiply( nextColor );

} else {

color.set( baseColors[ i ] ).multiply( currentColor );

}

}
mesh.setColorAt( i, color );

mesh.instanceMatrix.needsUpdate = true;
mesh.computeBoundingSphere();
}

}

mesh.instanceMatrix.needsUpdate = true;
if ( animation.t > 0 ) mesh.instanceColor.needsUpdate = true;

mesh.computeBoundingSphere();

renderer.render( scene, camera );

}
Expand Down