As the title indicates,
using the traditional method to destroy the material and geometry objects does not properly remove them, and an error will be logged in the console:
[Buffer (unlabeled)] used in submit while destroyed.
While calling [Queue].Submit([[CommandBuffer from CommandEncoder “renderContext_0”]])
Reproduction Steps:
- Use the
webgpu_spritesexample. - Bind the group to the window,
window.group = group. - Destroy any Sprite object.
- Observe the view and console.
The complete code is as follows:
import * as THREE from 'three';
import { texture, uv, userData, fog, rangeFogFactor, color } from 'three/tsl';
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2(Infinity, Infinity);
let camera, scene, renderer;
let map;
let group;
let imageWidth = 1, imageHeight = 1;
init();
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 = fog(color(0x0000ff), rangeFogFactor(1500, 2100));
// create sprites
const amount = 200;
const radius = 500;
const textureLoader = new THREE.TextureLoader();
map = textureLoader.load('textures/sprite1.png', (map) => {
imageWidth = map.image.width;
imageHeight = map.image.height;
});
group = new THREE.Group();
window.group = group;
const textureNode = texture(map);
const 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
for (let a = 0; a < amount; a++) {
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);
}
scene.add(group);
//
renderer = new THREE.WebGPURenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(render);
document.body.appendChild(renderer.domElement);
//
document.addEventListener('click', onPointerMove);
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 onPointerMove(event) {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObject(group);
if (intersects.length > 0) {
console.log('intersects: ', intersects)
intersects.forEach(s => {
s.object.position.set(100, 100, 100)
// if (s.object.material) {
// s.object.material.color = new THREE.Color(0xff0000)
// s.object.material.needsUpdate = true;
// }
})
}
}
function render() {
const time = 1000;
// for (let i = 0, l = group.children.length; i < l; i++) {
// const sprite = group.children[i];
// // const scale = Math.sin( time + sprite.position.x * 0.01 ) * 0.3 + 1.0;
// const scale = 1
// // sprite.userData.rotation += 0.1 * ( i / l );
// sprite.scale.set(scale * imageWidth, scale * imageHeight, 1.0);
// }
// group.rotation.x = time * 0.5;
// group.rotation.y = time * 0.75;
// group.rotation.z = time * 1.0;
renderer.render(scene, camera);
}
After successful rendering, execute the following in the console:
group.children.forEach(item => {
item.material.dispose();
item.geometry.dispose();
})