I will like to put each of the spheres in different canvas so that I could manipulate their positions easily with CSS.
Then hover in each h2 to show display them one by one
I have been advised to refactor the code with ES6 Classes for constructing every canvas with the sphere inside.
how can I acchive this?
https://jsfiddle.net/zhampu/q5j42pu9/9/
const v = new THREE.Vector3()
const sceneElements = []
const spheres = []
// Renderer
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#c'),
antialias: true,
alpha: true
})
renderer.setClearColor(0x000000, 0)
renderer.setPixelRatio(window.devicePixelRatio)
// renderer.setSize(window.innerWidth, window.innerHeight)
// Scene & Camera
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
// const camera = new THREE.PerspectiveCamera(70, 2, 1, 1000);
camera.position.z = 10
// Data Diagram
document.querySelectorAll('[data-diagram]').forEach((elem) => {
const sceneTexture = elem.dataset.diagram
const eachTexture = new THREE.TextureLoader().load(sceneTexture)
sceneElements.push(eachTexture)
return sceneElements
})
function makeBlob(sceneElements) {
for (var i = 0; i < sceneElements.length; i++) {
const sphere_geometry = new THREE.SphereBufferGeometry(1, 32, 16).toNonIndexed()
let material = new THREE.MeshBasicMaterial({
map: sceneElements[i]
})
const sphere = new THREE.Mesh(sphere_geometry, material)
const positionOfPiece = document.querySelectorAll('[data-diagram]')
sphere.position.x = 3 * i -3
// sphere.position.y = positionOfPiece.top
scene.add(sphere)
spheres.push(sphere)
}
}
makeBlob(sceneElements)
function animate() {
requestAnimationFrame(animate)
let time = performance.now() * 0.0005
resizeCanvasToDisplaySize()
for (let i = 0; i < spheres.length; i++) {
var sphere = spheres[i]
let k = 1
var positionAttribute = sphere.geometry.getAttribute('position')
for (let j = 0; j < positionAttribute.count; j++) {
v.fromBufferAttribute(positionAttribute, j)
v.normalize().multiplyScalar(1 + 0.3 * noise.perlin3(v.x * k + time, v.y * k, v.z * k))
positionAttribute.setXYZ(j, v.x, v.y, v.z)
}
positionAttribute.needsUpdate = true
sphere.rotation.y += 0.003
}
renderer.render(scene, camera)
}
animate()
function resizeCanvasToDisplaySize() {
const canvas = renderer.domElement
const width = canvas.clientWidth
const height = canvas.clientHeight
if (canvas.width !== width || canvas.height !== height) {
// you must pass false here or three.js sadly fights the browser
renderer.setSize(width, height, false)
camera.aspect = width / height
camera.updateProjectionMatrix()
// set render target sizes here
}
}
#c {
position: absolute;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
display: block;
z-index: -1;
}
*[data-diagram] {
display: inline-block;
width: 5em;
height: 3em;
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script src="https://www.fariskassim.com/stage/rebel9/teaf/blob/v4/js/perlin.js"></script>
<canvas id="c"></canvas>
<a class="pieces-list" href="" target="_self">
<h2>Publications</h2>
<span data-diagram="https://i.picsum.photos/id/1002/600/300.jpg"></span>
</a>
<a class="pieces-list" href="" target="_self">
<h2>Fashion</h2>
<span data-diagram="https://i.picsum.photos/id/1002/200/300.jpg"></span>
</a>
Related
I'm learning three.js's frustum by modifying the example in Scene Graph below.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
Settings:
A sphere at the origin with radius = 1.
A camera positioned at y = 50, looking towards the origin, with the UP direction as (0,0,1).
Its frustum has fov = 40, aspect = 2, near = 0.1 and far = 1000.
I changed two parameters:
near: 0.1 → 48
far: 1000 → 52.
I'm expecting the sphere being contained inside the frustum, but why is the sphere's top part truncated?
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 48;
const far = 52;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
Thanks to WestLangley's comment, I've found out the answer: the property Object3D.scale is a 3D vector that scales the object locally. Mesh is a subclass of Object3D. In my modified example above, the "sphere" (a hexagon in fact) is scaled to 5×. I commented out this line, so that there's no scaling (default factor 1×), and I observed that the "sphere" is contained inside the frustum.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 10;
const aspect = 2; // the canvas default
const near = 49;
const far = 51;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
//sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
To check my understanding, I reduced the distance between near and the center of the "sphere" by 0.3, and I observed a hole as expected.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 10;
const aspect = 2; // the canvas default
const near = 49.3;
const far = 50.7;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
//sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
Remark: I'm answering my own question in order to clear it from the unanswered queue.
I've been trying to modify this demo. Currently the particles are placed in a text geometry, my end goal here is opposite. I'd like to:
Replace the text geometries with a box, cone and sphere.
Replace the particles with numbers.
So basically, have the numbers placed inside one of the shapes and animate the particles to form the next shape with a delay.
First, I tried changing the shapes, leaving the particles and everything else as is. However, for some reason the particles aren't showing up. I'm not sure why the particles aren't rendered? Console logs no errors.
Also, what would be the fastest way to have numbers as particles? It'd be great if someone could point me in the right direction. Thank you!
Below is the modified code:
// Options
const shapes = [{
"geoCode": new THREE.ConeGeometry(25, 50, 30),
"color": 0x11659C,
},
{
"geoCode": new THREE.SphereGeometry(25, 33, 33),
"color": 0x8F3985,
},
{
"geoCode": new THREE.BoxGeometry(50, 50, 50),
"color": 0x029894,
}
],
triggers = document.getElementsByTagName('span'),
particleCount = 1000,
particleSize = .3,
defaultAnimationSpeed = 1,
morphAnimationSpeed = 18,
color = '#FFFFFF';
// Renderer
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Ensure Full Screen on Resize
function fullScreen() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', fullScreen, false)
// Scene
const scene = new THREE.Scene();
// Camera and position
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.y = -45;
camera.position.z = 45;
// Lighting
const light = new THREE.AmbientLight(0xFFFFFF, 1);
scene.add(light);
// Particle Vars
const particles = new THREE.Geometry();
const pMaterial = new THREE.PointsMaterial({
size: particleSize,
});
// Texts
const loader = new THREE.FontLoader();
const typeface = 'https://dl.dropboxusercontent.com/s/bkqic142ik0zjed/swiss_black_cond.json?';
//CONTINUE
loader.load(typeface, (font) => {
Array.from(shapes).forEach((shape, idx) => {
shapes[idx].geometry = shapes[idx].geoCode;
const material = new THREE.MeshLambertMaterial({
color: shapes[idx].color,
opacity: .5,
transparent: true,
wireframe: true
});
const mesh = new THREE.Mesh(shapes[idx].geometry, material);
//THREE.GeometryUtils.center(shapes[idx].geometry)
scene.add(mesh);
shapes[idx].particles = new THREE.Geometry();
shapes[idx].points = THREE.GeometryUtils.randomPointsInGeometry(shapes[idx].geometry, particleCount);
createVertices(shapes[idx].particles, shapes[idx].points)
enableTrigger(shape, idx, triggers[idx]);
});
});
// Particles
for (var i = 0; i < shapes; i++) {
const vertex = new THREE.Vector3();
vertex.x = 0;
vertex.y = 0;
vertex.z = 0;
particles.vertices.push(vertex);
}
function createVertices(emptyArray, points) {
for (var p = 0; p < particleCount; p++) {
const vertex = new THREE.Vector3();
vertex.x = points[p]['x'];
vertex.y = points[p]['y'];
vertex.z = points[p]['z'];
emptyArray.vertices.push(vertex);
}
}
function enableTrigger(trigger, idx, el) {
el.setAttribute('data-disabled', false);
el.addEventListener('click', () => {
morphTo(shapes[idx].particles, el.dataset.color);
})
if (idx == 0) {
morphTo(shapes[idx].particles, el.dataset.color);
}
}
const particleSystem = new THREE.Points(
particles,
pMaterial
);
particleSystem.sortParticles = true;
// Add the particles to the scene
scene.add(particleSystem);
// Animate
const normalSpeed = (defaultAnimationSpeed / 100),
fullSpeed = (morphAnimationSpeed / 100)
let animationVars = {
speed: normalSpeed,
color: color,
rotation: 100
}
function animate() {
particleSystem.rotation.y += animationVars.speed;
particles.verticesNeedUpdate = true;
camera.position.z = animationVars.rotation;
camera.position.y = animationVars.rotation;
camera.lookAt(scene.position);
particleSystem.material.color = new THREE.Color(animationVars.color);
window.requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
function morphTo(newParticles, color = '#FFFFFF') {
TweenMax.to(animationVars, 2, {
ease: Linear.easeNone,
color: color
});
particleSystem.material.color.setHex(color);
for (const i = 0; i < particles.vertices.length; i++) {
TweenMax.to(particles.vertices[i], 2, {
ease: Elastic.easeOut.config(0.1, .3),
x: newParticles.vertices[i].x,
y: newParticles.vertices[i].y,
z: newParticles.vertices[i].z
})
}
}
body {
font-family: 'Titillium Web', sans-serif;
margin: 0;
overflow: hidden;
}
.triggers {
bottom: 20px;
color: white;
left: 50%;
position: absolute;
text-align: center;
transform: translateX(-50%);
width: 100%;
z-index: 10;
}
.triggers span {
cursor: pointer;
display: inline-block;
font-size: 14px;
margin: 0 20px;
padding: 2px 4px;
transition: opacity 0.5s, color 0.5s;
}
.triggers span[data-disabled="true"] {
opacity: 0.3;
pointer-events: none;
}
.triggers span:hover {
color: red;
}
<div class="triggers">
<span data-disabled="true" data-color="#3D8CD0">CLICK</span>
<span data-disabled="true" data-color="#D32A7B">TO</span>
<span data-disabled="true" data-color="#2AD37A">SWITCH</span>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/605067/GeometryUtils.js" type="text/javascript"></script>
EDIT: Particles are now rendered, the updated code is here --> Fiddle
Apologies, I was actually able to fix the particles not rendering. I was iterating over the shapes instead of the particleCount.
// Particles
for (var i = 0; i < shapes; i++) {
^^^^^^
let vertex = new THREE.Vector3();
vertex.x = 0;
vertex.y = 0;
vertex.z = 0;
particles.vertices.push(vertex);
}
Below is the updated code that loads the particles.
// Options
const shapes = [{
"geoCode": new THREE.ConeGeometry(25, 50, 30),
"color": 0x11659C,
},
{
"geoCode": new THREE.SphereGeometry(25, 33, 33),
"color": 0x8F3985,
},
{
"geoCode": new THREE.BoxGeometry(50, 50, 50),
"color": 0x029894,
}
],
triggers = document.getElementsByTagName('span'),
particleCount = 1000,
particleSize = 3,
defaultAnimationSpeed = 1,
morphAnimationSpeed = 18,
color = '#FFFFFF';
// Renderer
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Ensure Full Screen on Resize
function fullScreen() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', fullScreen, false)
// Scene
const scene = new THREE.Scene();
// Camera and position
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.y = -45;
camera.position.z = 45;
// Lighting
const light = new THREE.AmbientLight(0xFFFFFF, 1);
scene.add(light);
// Particle Vars
const particles = new THREE.Geometry();
const pMaterial = new THREE.PointsMaterial({
size: particleSize,
});
// Texts
const loader = new THREE.FontLoader();
const typeface = 'https://dl.dropboxusercontent.com/s/bkqic142ik0zjed/swiss_black_cond.json?';
//CONTINUE
loader.load(typeface, (font) => {
Array.from(shapes).forEach((shape, idx) => {
shapes[idx].geometry = shapes[idx].geoCode;
const material = new THREE.MeshLambertMaterial({
color: shapes[idx].color,
opacity: .5,
transparent: true,
wireframe: true
});
const mesh = new THREE.Mesh(shapes[idx].geometry, material);
//THREE.GeometryUtils.center(shapes[idx].geometry)
scene.add(mesh);
shapes[idx].particles = new THREE.Geometry();
shapes[idx].points = THREE.GeometryUtils.randomPointsInGeometry(shapes[idx].geometry, particleCount);
createVertices(shapes[idx].particles, shapes[idx].points)
enableTrigger(shape, idx, triggers[idx]);
});
});
// Particles
for (var i = 0; i < particleCount; i++) {
const vertex = new THREE.Vector3();
vertex.x = 0;
vertex.y = 0;
vertex.z = 0;
particles.vertices.push(vertex);
}
function createVertices(emptyArray, points) {
for (var p = 0; p < particleCount; p++) {
const vertex = new THREE.Vector3();
vertex.x = points[p]['x'];
vertex.y = points[p]['y'];
vertex.z = points[p]['z'];
emptyArray.vertices.push(vertex);
}
}
function enableTrigger(trigger, idx, el) {
el.setAttribute('data-disabled', false);
el.addEventListener('click', () => {
morphTo(shapes[idx].particles, el.dataset.color);
})
if (idx == 0) {
morphTo(shapes[idx].particles, el.dataset.color);
}
}
let particleSystem = new THREE.Points(
particles,
pMaterial
);
particleSystem.sortParticles = true;
// Add the particles to the scene
scene.add(particleSystem);
// Animate
const normalSpeed = (defaultAnimationSpeed / 100),
fullSpeed = (morphAnimationSpeed / 100)
let animationVars = {
speed: normalSpeed,
color: color,
rotation: 100
}
function animate() {
particleSystem.rotation.y += animationVars.speed;
particles.verticesNeedUpdate = true;
camera.position.z = animationVars.rotation;
camera.position.y = animationVars.rotation;
camera.lookAt(scene.position);
particleSystem.material.color = new THREE.Color(animationVars.color);
window.requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
function morphTo(newParticles, color = '#FFFFFF') {
TweenMax.to(animationVars, 2, {
ease: Linear.easeNone,
color: color
});
particleSystem.material.color.setHex(color);
for (let i = 0; i < particles.vertices.length; i++) {
TweenMax.to(particles.vertices[i], 2, {
ease: Elastic.easeOut.config(0.1, .3),
x: newParticles.vertices[i].x,
y: newParticles.vertices[i].y,
z: newParticles.vertices[i].z
})
}
}
body {
font-family: 'Titillium Web', sans-serif;
margin: 0;
overflow: hidden;
}
.triggers {
bottom: 20px;
color: white;
left: 50%;
position: absolute;
text-align: center;
transform: translateX(-50%);
width: 100%;
z-index: 10;
}
.triggers span {
cursor: pointer;
display: inline-block;
font-size: 14px;
margin: 0 20px;
padding: 2px 4px;
transition: opacity 0.5s, color 0.5s;
}
.triggers span[data-disabled="true"] {
opacity: 0.3;
pointer-events: none;
}
.triggers span:hover {
color: red;
}
<div class="triggers">
<span data-disabled="true" data-color="#3D8CD0">CLICK</span>
<span data-disabled="true" data-color="#D32A7B">TO</span>
<span data-disabled="true" data-color="#2AD37A">SWITCH</span>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/605067/GeometryUtils.js" type="text/javascript"></script>
Note: This post only has a solution for particles not rendering. I will accept the post that answers how I should go about replacing the particles with a number.
I want to create a grid of geometries (ROWS * COLUMNS) and wanna rotate each individual mesh around it's own center.
I generate the grid through two for loops, translate the single elements to there correct position and push them into an array to use them again in the animation function. In the animation function I iterate again over all the elements and rotate every single one.
the problem is that even though I address each element individually and I have translated each element to the right place, each element still revolves around the center of the page and not its own center (see the screenshot)
Here is my current attempt:
const main = () => {
const ROWS = 4;
const COLUMNS = 4;
const ITEMS = [];
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(1, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#e5e5e5');
camera.position.z = 500;
// GEOMETRY
for (let x = 0; x < COLUMNS; x++) {
for (let y = 0; y < ROWS; y++) {
const geometry = new THREE.BoxGeometry(1, 1, 0.5);
const edges = new THREE.EdgesGeometry(geometry);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x1940EB });
const edgesMesh = new THREE.LineSegments(edges, edgesMaterial);
ITEMS.push(edgesMesh);
edges.translate(x*1.5, y*1.5, 0);
scene.add(edgesMesh);
}
}
scene.position.set(-COLUMNS/2, -ROWS/2, 0);
const animation = () => {
requestAnimationFrame(animation);
ITEMS.map(item => {
item.rotation.x += 0.01;
item.rotation.y += 0.01;
})
camera.updateMatrixWorld();
renderer.render(scene, camera);
}
const onWindowResize = () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
}
animation();
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
};
window.addEventListener('load', main, false);
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<canvas id="canvas"></canvas>
Can someone explain to me why the elements individually addressed in the animation function still do not revolve around themselves?
Thanks for your help
Because the code is moving the geometry itself so that it's center is no longer in the middle of the box.
change
edges.translate(x*1.5, y*1.5, 0);
to
edgesMesh.position.set(x*1.5, y*1.5, 0);
This will move the Object3D in the scenegraph instead of the vertices of the geometry data.
const main = () => {
const ROWS = 4;
const COLUMNS = 4;
const ITEMS = [];
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(1, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#e5e5e5');
camera.position.z = 500;
// GEOMETRY
for (let x = 0; x < COLUMNS; x++) {
for (let y = 0; y < ROWS; y++) {
const geometry = new THREE.BoxGeometry(1, 1, 0.5);
const edges = new THREE.EdgesGeometry(geometry);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x1940EB });
const edgesMesh = new THREE.LineSegments(edges, edgesMaterial);
ITEMS.push(edgesMesh);
edgesMesh.position.set(x*1.5, y*1.5, 0);
scene.add(edgesMesh);
}
}
scene.position.set(-COLUMNS/2, -ROWS/2, 0);
const animation = () => {
requestAnimationFrame(animation);
ITEMS.map(item => {
item.rotation.x += 0.01;
item.rotation.y += 0.01;
})
camera.updateMatrixWorld();
renderer.render(scene, camera);
}
const onWindowResize = () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
}
animation();
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
};
window.addEventListener('load', main, false);
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<canvas id="canvas"></canvas>
You might find this article useful
I have a three js Mesh on Click of a button the mesh need to be focus based on the button
for example on click of view top camera need to focus from the top of the mesh
in three.js is there any inbuilt method to focus a mesh or how to calculate top left front of a mesh?
var scene, camera, renderer, controls;
var ROTATE = true;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, 450 / 450, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.userPan = false;
controls.userPanSpeed = 0.0;
controls.maxDistance = 5000.0;
controls.maxPolarAngle = Math.PI * 0.495;
// controls.rotateUp(Math.PI * 0.1);
// controls.autoRotate = ROTATE; //true:自動回転する,false:自動回転しない
controls.autoRotateSpeed = 4.0; //自動回転する時の速度
renderer.setSize(465, 465);
renderer.shadowMapEnabled = true;
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 150;
camera.lookAt(scene.position);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(100, 100, -100);
scene.add(spotLight);
var light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(100, 100, 100).normalize();
scene.add(light);
var light2 = new THREE.DirectionalLight(0xffffff);
light2.position.set(-100, -100, -100).normalize();
scene.add(light2);
document.body.appendChild(renderer.domElement);
var parser = new vox.Parser();
// yuusha.vox
parser.parse("https://rawcdn.githack.com/siouxcitizen/3DModel/b2d06e45f6b64c3e342b724a4ec5a0c427a4ce0a/vox/hiyoko.vox").then(function(voxelData) {
var builder = new vox.MeshBuilder(voxelData, {
voxelSize: 7.0,
vertexColor: true,
optimizeFaces: false,
});
var v0 = builder.createMesh();
v0.position.y -= 50;
scene.add(v0);
});
render();
}
function render() {
controls.update();
requestAnimationFrame(render);
renderer.render(scene, camera);
}
init();
function topView(){
alert("topView view")
}
function sideView(){
alert("sideview view")
}
function frontiew(){
alert("front view")
}
* {
margin: 0;
padding: 0;
border: 0;
overflow: hidden;
}
body {
background: #000;
font: 30px sans-serif;
}
button{
padding:5px 10px
}
<!-- three.min.js r87 -->
<button onClick={topView()}>top view</button>
<button onClick={frontiew()}>front view</button>
<button onClick={sideView()}>side view</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
<!-- vox.js v1.0.1 -->
<script src="https://cdn.jsdelivr.net/gh/daishihmr/vox.js#1.0.1/build/vox.min.js"></script>
<!-- Old OrbitControls.js -->
<script src="https://codepen.io/siouxcitizen/pen/XWWXZZN.js"></script>
the above is demo code.
I try to forcefully update camera position it won't work
You can get axis aligned bbox with new THREE.Box3().setFromObject(mesh)
and next - fit camera to this box.
Here is my code (typescript) to do it (works well with PerspetiveCamera and OrbitControls).
fitCameraTo(boundingBox: THREE.Box3) {
const camera = this._camera;
const objPosition = boundingBox.getCenter(new THREE.Vector3());
const objSize = boundingBox.getSize(new THREE.Vector3());
boundingBox.min.y = 0;
boundingBox.max.y = 0;
const boundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere());
let dim = boundingSphere.radius * 2;
if (dim < camera.near) {
dim = camera.near;
}
const direction = THREE.Object3D.DefaultUp.clone(); // view direction
// object angular size
const fov = THREE.Math.degToRad(camera.fov);
let distance = dim / (2.0 * Math.tan(fov / 2.0));
if (camera.aspect <= 1) {
distance = distance / camera.aspect;
}
if (distance < camera.near) {
distance = objSize.y;
}
if (distance < camera.near) {
distance = camera.near;
}
camera.position.copy(objPosition.clone().add(direction.multiplyScalar(distance)));
if (this.this._orbitControls) {
this._orbitControls.target.copy(objPosition);
this._orbitControls.rotateLeft(Math.PI);
} else {
camera.lookAt(objPosition);
}
camera.updateProjectionMatrix();
}
Im developing an web-app that is supposed to create a 3d model of a gravel pile
from points measured with a laser instrument with three.js. The only problem is the
creating a hull that supposed to be the surface of the pile that includes all points. I already made a pointswarm model with the points and tried to make a hull around it with ConvexBufferGeometry but not all points get included in the hull when using ConvexBufferGeometry. Can someone please point me in the right direction?
You can try to combine Three.js with Delaunator library for triangulation of your points to make a surface:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
camera.position.setScalar(150);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
var canvas = renderer.domElement;
document.body.appendChild(canvas);
var controls = new THREE.OrbitControls(camera, canvas);
var light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.setScalar(100);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
var size = { x: 200, y: 200 };
var pointsCount = 1000;
var points3d = [];
for (let i = 0; i < pointsCount; i++) {
let x = THREE.Math.randFloatSpread(size.x);
let z = THREE.Math.randFloatSpread(size.y);
let y = noise.perlin2(x / size.x * 5, z / size.y * 5) * 50;
points3d.push(new THREE.Vector3(x, y, z));
}
var geom = new THREE.BufferGeometry().setFromPoints(points3d);
var cloud = new THREE.Points(
geom,
new THREE.PointsMaterial({ color: 0x99ccff, size: 2 })
);
scene.add(cloud);
// triangulate by [x, z]
var indexDelaunay = Delaunator.from(
points3d.map(v => {
return [v.x, v.z];
})
);
var meshIndex = []; // delaunay index => three.js index
for (let i = 0; i < indexDelaunay.triangles.length; i++){
meshIndex.push(indexDelaunay.triangles[i]);
}
geom.setIndex(meshIndex); // add three.js index to the existing geometry
geom.computeVertexNormals();
var mesh = new THREE.Mesh(
geom, // re-use the existing geometry
new THREE.MeshLambertMaterial({ color: "purple", wireframe: true })
);
scene.add(mesh);
var gui = new dat.GUI();
gui.add(mesh.material, "wireframe");
render();
function resize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resize(renderer)) {
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
html, body {
height: 100%;
margin: 0;
overflow: hidden;
font-family: Verdana;
}
canvas {
width: 100%;
height: 100%;
display; block;
}
#info{
position: absolute;
margin-left: 10px;
}
a{
color: yellow;
text-decoration: none;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/examples/js/controls/OrbitControls.js"></script>
<!-- https://github.com/mapbox/delaunator -->
<script src="https://unpkg.com/delaunator#3.0.2/delaunator.js"></script>
<script src="https://josephg.github.io/noisejs/perlin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.min.js"></script>
<div id="info">
Delaunator<br>(triangulation)
</div>