In my latest project, I developed a small three.js application that showcases the movement of multiple circles from the bottom to the top of the canvas:
let renderer, scene, light, circles, camera;
initialize();
animate();
function initialize() {
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
light = new THREE.AmbientLight();
scene.add(light);
circles = new THREE.Group();
scene.add(circles);
camera = new THREE.PerspectiveCamera(45, renderer.domElement.clientWidth / renderer.domElement.clientHeight, 1);
camera.position.z = circles.position.z + 500;
}
function animate() {
// Update each circle.
Array.from(circles.children).forEach(circle => {
if (circle.position.y < visibleBox(circle.position.z).max.y) {
circle.position.y += 4;
} else {
circles.remove(circle);
}
});
// Create a new circle.
let circle = new THREE.Mesh();
circle.geometry = new THREE.CircleGeometry(30, 30);
circle.material = new THREE.MeshToonMaterial({ color: randomColor(), transparent: true, opacity: 0.5 });
circle.position.z = _.random(camera.position.z - camera.far, camera.position.z - (camera.far / 10));
circle.position.x = _.random(visibleBox(circle.position.z).min.x, visibleBox(circle.position.z).max.x);
circle.position.y = visibleBox(circle.position.z).min.y;
circles.add(circle);
// Render the scene.
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function visibleBox(z) {
return new THREE.Box2(
new THREE.Vector2(-1000, -1000),
new THREE.Vector2(1000, 1000)
);
}
function randomColor() {
return `#${ _.sampleSize("abcdef0123456789", 6).join("")}`;
}
body {
width: 100%;
height: 100%;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js">
</script>
The usage of visibleBox(z)
function is essential in determining the creation and removal of each circle. As of now, the return value for this function is hardcoded, but I intend to automate it so that it calculates the size of the visible rectangle within the camera's frustum at a particular depth, z
.
To elaborate, my goal is for every circle to appear precisely at the base of the camera frustum (the lower edge of the red rectangle depicted in the image above) and vanish as soon as it reaches the frustum's top (the upper edge of the red rectangle).
Hence, how would I go about computing this rectangle?