Skip to content
Open
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
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@
"webgpu_skinning_points",
"webgpu_sky",
"webgpu_sprites",
"webgpu_sprites_common_buffer",
"webgpu_storage_buffer",
"webgpu_texturegrad",
"webgpu_textures_2d-array",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
213 changes: 213 additions & 0 deletions examples/webgpu_sprites_common_buffer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<html lang="en">
<head>
<title>three.js - WebGPU - Sprites - Common buffer</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">
</head>
<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> WebGPU - Sprites - Common buffer
<div id="buffer-stats" style="
position: absolute;
top: 60px;
left: 0;
padding: 10px;
background: rgba( 0, 0, 0, 0.5 );
color: #fff;
font-family: monospace;
font-size: 12px;
line-height: 1.5;
pointer-events: none;
text-align: left;
"></div>
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.webgpu.js",
"three/tsl": "../build/three.webgpu.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';
import { texture, uv, userData, rangeFog, color, SpriteNodeMaterial, reference } from 'three/tsl';

let camera, scene, renderer, material;

let map;

let group;

let imageWidth = 1, imageHeight = 1;

const amount = 200;
const radius = 500;

const bufferStats = document.getElementById( 'buffer-stats' );

init();

function addSprite( material ) {

const x = Math.random() - 0.5;
const y = Math.random() - 0.5;
const z = Math.random() - 0.5;

const sprite = new THREE.Sprite( material );

sprite.position.set( x, y, z );
sprite.position.normalize();
sprite.position.multiplyScalar( radius );

// individual rotation per sprite
sprite.userData.rotation = 0;

group.add( sprite );

}

function init() {

const width = window.innerWidth;
const height = window.innerHeight;

camera = new THREE.PerspectiveCamera( 60, width / height, 1, 2100 );
camera.position.z = 1500;

scene = new THREE.Scene();
scene.fogNode = rangeFog( color( 0x0000ff ), 1500, 2100 );

// create sprites

const textureLoader = new THREE.TextureLoader();

map = textureLoader.load( 'textures/sprite1.png', ( map ) => {

imageWidth = map.image.width;
imageHeight = map.image.height;

} );

group = new THREE.Group();

const textureNode = texture( map );

material = new THREE.SpriteNodeMaterial();
material.colorNode = textureNode.mul( uv() ).mul( 2 ).saturate();
material.opacityNode = textureNode.a;
material.rotationNode = userData( 'rotation', 'float' ); // get value of: sprite.userData.rotation
material.transparent = true;

for ( let a = 0; a < amount; a ++ ) {

addSprite( material );

}

scene.add( group );

//

renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( render );
renderer.commonBufferSize = 1024;

document.body.appendChild( renderer.domElement );

window.addEventListener( 'resize', onWindowResize );

}

function onWindowResize() {

const width = window.innerWidth;
const height = window.innerHeight;

camera.aspect = width / height;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}

function render() {

const time = Date.now() / 1000;

for ( let i = 0, l = group.children.length; i < l; i ++ ) {

const sprite = group.children[ i ];

if ( sprite === undefined ) {

continue;


} else {

if ( Math.round( Math.random( ) * 1000 ) === 1 ) {

sprite.removeFromParent();

continue;

}

}

const scale = Math.sin( time + sprite.position.x * 0.01 ) * 0.3 + 1.0;

sprite.userData.rotation += 0.1 * ( i / l );
sprite.scale.set( scale * imageWidth, scale * imageHeight, 1.0 );

if ( Math.round( Math.random( ) * 1000 ) === 1 ) {

addSprite( material );

}

}

group.rotation.x = time * 0.05;
group.rotation.y = time * 0.075;
group.rotation.z = time * 0.1;

renderer.render( scene, camera );

const info = renderer.info;

if ( info.render.calls % 20 === 0 ) {

const stats = info.memory.common;
const lists = stats.freeLists;

let listText = '';

for ( let i = 1, l = lists.length; i < l; i ++ ) {

listText += `Blocks ${ i } - length: ${ lists[ i ]} `;

}

bufferStats.innerHTML =
`Unused bytes: ${ stats.unused }<br>` +
`Alloc: ${ stats.alloc } (reused ${ stats.reused }) Free: ${ stats.free } <br><br>` +
`Free block lists (size: ${ stats.blockSize } B)<br>` +
listText;

}

}

</script>
</body>
</html>
18 changes: 18 additions & 0 deletions src/renderers/common/Bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,24 @@ class Bindings extends DataMap {

}

delete( renderObject ) {

const bindings = renderObject.getBindings();

for ( const bindGroup of bindings ) {

for ( const binding of bindGroup.bindings ) {

if ( binding.isNodeUniformsGroup && binding._isCommon === true && ! binding.groupNode.shared ) binding.freeCommon();

}

}

super.delete( renderObject );

}

}

export default Bindings;
Loading