Three.js change the pivot point of each particle - javascript

I'm trying to rotate each particle along its own axis of rotation rather than rotate them all along one point. I've looked at many similar questions on setting the pivot points, but I'm lost and confused to be honest.
My goal is to animate the particles as if they're falling from top to bottom, similar to this animation.
var camera,
scene,
renderer,
materials = [],
parameters;
var windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2;
var particles = [];
var confetti = {
maxCount: 2000, //set max confetti count
speed: 1, //set the particle animation speed
frameInterval: 30, //the confetti animation frame interval in milliseconds
waveThreshold: 3, //
start: null, //call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
stop: null, //call to stop adding confetti
toggle: null, //call to start or stop the confetti animation depending on whether it's already running
pause: null, //call to freeze confetti animation
resume: null, //call to unfreeze confetti animation
togglePause: null, //call to toggle whether the confetti animation is paused
remove: null, //call to stop the confetti animation and remove all confetti immediately
isPaused: null, //call and returns true or false depending on whether the confetti animation is paused
isRunning: null //call and returns true or false depending on whether the animation is running
};
init();
animate();
window.addEventListener('resize', onWindowResize);
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 20, 900);
camera.position.z = 100;
var geometry = new THREE.BufferGeometry();
var vertices = [];
var textureLoader = new THREE.TextureLoader();
var sprite1 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite2 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite3 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite4 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite5 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
for (var i = 0; i < confetti.maxCount; i++) {
var x = Math.random() * 2000 - 1000;
var y = Math.random() * 2000 - 1000;
var z = Math.random() * 1000 - 900;
vertices.push(x, y, z);
//particles[i].tilt = Math.random() * 10 - 10;
//particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
//particles[i].tiltAngle = Math.random() * Math.PI;
particles.push({
'tilt': Math.random() * 1 - 1,
'tiltAngle': Math.random() * Math.PI
})
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
parameters = [
[sprite2, 10],
[sprite3, 9],
[sprite1, 10],
[sprite5, 8],
[sprite4, 10]
];
for (var i = 0; i < parameters.length; i++) {
var sprite = parameters[i][0];
var size = parameters[i][1];
materials[i] = new THREE.PointsMaterial({
size: size,
map: sprite,
blending: THREE.AdditiveBlending,
depthTest: false,
transparent: true
});
var particle = new THREE.Points(geometry, materials[i]);
particle.rotation.x = Math.random() * 5;
particle.rotation.y = Math.random() * 5;
particle.rotation.z = Math.random() * 5;
scene.add(particle);
}
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor('#121212')
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
var time = Date.now() * 0.00005;
for (var i = 0; i < scene.children.length; i++) {
var object = scene.children[i];
if (object instanceof THREE.Points) {
//object.rotation.x = time * ( i < 4 ? i + 1 : ( i + 1 ) );
//object.position.x -= time * ( i < 4 ? i + 1 : -( i + 1 ) );
object.position.y -= 1;
object.position.x += particles[i]['tilt'] / 3;
object.rotation.y += .001;
//object.rotation.x += time * particles[i]['tiltAngle'];
}
}
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>

Related

ThreeJS: How to move background only in one direction?

I have a fully working model of a car mesh moving right, left, up and down using Three.JS
The input data are acceleration on the X and Z direction, we apply double integration on the acceleration to compute the displacement. So I have been able to animate the car in all directions and making the background move to keep the car in the center of the canvas. However, I only need to background to move in the right and left direction, and not for the up and down
That's my code:
<html>
<head>
<style>
canvas {
position: fixed;
top: 0;
left: 0;
}
</style>
</head>
<body>
<script src="./libs/three.min.js"></script>
<script src="./libs/OrbitControls.js"></script>
<script src="./libs/KeyboardState.js"></script>
<script src="./libs/MTLLoader.js"></script>
<script src="./libs/OBJMTLLoader.js"></script>
<script src="./data/accXaccZCOMBINEDMOTIONS.json"></script>
</body>
<script>
var data = JSON.parse(JSON.stringify(data));
var toycar;
var valid = 1;
const dispArrayX = Array.from({ length: 1 }).fill(0);
const dispArrayZ = Array.from({ length: 1 }).fill(0);
let sensorValue = 0;
var initialVelocity = 0;
var angle = 0;
var currentIndex = 0;
var initialDisplacement = 0;
var scene, renderer, camera;
var width = window.innerWidth;
var height = window.innerHeight;
var time = data[currentIndex].time
var pos = new THREE.Vector3(0, 0, 0);
init();
animate();
function init() {
var width = window.innerWidth;
var height = window.innerHeight;
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setClearColor(0x626d73, 1);
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(10, width / height, 1, 10000);
// camera.position.y = -150;
// camera.position.y = 200;
// camera.position.x = 100;
camera.lookAt(new THREE.Vector3(0, 0, 0));
var loader = new THREE.OBJMTLLoader();
loader.load('https://jyunming-chen.github.io/tutsplus/models/toycar.obj', 'https://jyunming-chen.github.io/tutsplus/models/toycar.mtl',
function (object) {
toycar = object;
toycar.rotateZ(10.8); //toycar.rotateZ(-10.99);
scene.add(toycar);
});
var gridXZ = new THREE.GridHelper(100000, 10);
gridXZ.setColors(new THREE.Color(0xff0000), new THREE.Color(0xffffff));
scene.add(gridXZ);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(350, 10, 5);
scene.add(pointLight);
var ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
}
function animate(dt) {
let time = data[currentIndex].time
dt *= 10 ** -9
time += dt
while (data[currentIndex].time < time) {
currentIndex++
if (currentIndex >= data.length) return
}
const { accX,accZ } = data[currentIndex]
var dir = new THREE.Vector3(0, 0, -1);
dir.applyAxisAngle(new THREE.Vector3(0, 0, 0), 10);
pos.add(dir);
if (toycar != undefined) {
toycar.scale.set(0.1, 0.1, 0.1);
if (currentIndex > 0) {
// compute the displacement by double integrating the acceleration for accZ (e.i. Right and left movement)
const deltaTime = ((data[currentIndex].time) / (24 * 60 * 60)) - 1; // convert time from second per day to second
const velocityInitialZ = ((data[currentIndex-3].accZ + data[currentIndex-2].accZ)/2)*deltaTime; // compute initialVelocity two step backward
const velocityCurrentZ = velocityInitialZ + ((data[currentIndex-1].accZ + data[currentIndex].accZ)/2)*deltaTime; // compute currentVelocity one step backward
const previousDispZ = dispArrayZ[0] + (deltaTime * ((velocityCurrentZ + velocityInitialZ)/2));
dispArrayZ[0] = previousDispZ;
const dispCurrentZ = previousDispZ + dispArrayZ[0];
// compute the displacement by double integrating the acceleration for accX (e.i. Up and down movement)
const velocityInitialX = ((data[currentIndex-3].accX + data[currentIndex-2].accX)/2)*deltaTime; // compute initialVelocity two step backward
const velocityCurrentX = velocityInitialX + ((data[currentIndex-1].accX + data[currentIndex].accX)/2)*deltaTime; // compute currentVelocity one step backward
const previousDispX = dispArrayX[0] + (deltaTime * ((velocityCurrentX + velocityInitialX)/2));
dispArrayX[0] = previousDispX;
const dispCurrentX = previousDispX + dispArrayX[0];
const dispCurrentXscaled = dispCurrentX/3500;
// Move the car up and down
if (dispCurrentXscaled*0.0001 < 0){
toycar.position.x = dispCurrentXscaled*0.00001;
}
else if (dispCurrentXscaled*0.0001 > 8){
toycar.position.x = dispCurrentXscaled*0.0000001;
}
else{
toycar.position.x = dispCurrentXscaled*0.0001;
}
toycar.position.y = 0;
// Move the car right and left
toycar.position.z = -(dispCurrentZ/4000)*0.0005;
// print out displacementX and displacementZ
console.log("DispX: " + (dispCurrentX*0.0000001).toFixed(5) + " DispZ: " + ((dispCurrentZ/4000)*0.0005).toFixed(5));
}
toycar.rotation.x = (angle + Math.PI);;
var relativeCameraOffset = new THREE.Vector3(-1600, 400, 0);
var cameraOffset = relativeCameraOffset.applyMatrix4(toycar.matrixWorld);
camera.position.x = cameraOffset.x*0.5;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt(toycar.position);
}
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</html>
I am using from a JSON file as input.
That's how the movement of the car looks like:
You can see that the background is following the car motion. For Up and Down I only need the car to move (not the background) and the right and left are fine because is moving so it stays within the canvas. Can you please help with that?

.points opacity / size within three.js

I'm back for question two on .points. This time wondering how to change the opacity from 0, to 1 and then back within certain pixel distances from the emitter.
var particleCount = 14,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointsMaterial({
map: new THREE.TextureLoader().load("x.png"),
blending: THREE.multiplyBlending,
flatShading: true,
size: 40,
transparent: true,
depthTest: true,
sizeAttenuation: true,
opacity: 1
});
var particleSystem;
My main confusion is that even though I've given it transparency I can't change the value within the update comp I've made for my emitter.
function update() {
//particleSystem.rotation.y += 0.01;
pCount = particleCount;
while (pCount--) {
particle = particles.vertices[pCount];
(This is where a bunch of validation is for where the points are)
particleSystem.geometry.verticesNeedUpdate = true;
particleSystem.rotation.y += (Math.random()*0.001)
}
Render loop:
renderer.setAnimationLoop(() => {
update();
composer.render(scene, camera);
});
I want to make it fade out and not appear in the scene for 20 or so pixels and then fade in. But I'm not entirely sure on how to change the opacity as particle.opacity += 0.1 won't work.
Edit: I'm also uncertain about Size as I want to do a similar thing with it but from 20 to 40, I could probably base it depending on it's Y cordinate. Anyway; I'm also uncertain how to gradually change that too.
Sorry if this is a obvious answer, duplicate question and any help I get. Any alternate methods of what I've seen is in an alternate structure that I don't understand or in array in which I don't know how to put into what I want.
(Thanks in advance)
The issue is that the opacity and the size is a property of the THREE.PointsMaterial. If the pints should have different sizes it is not sufficient to have a list of different vertices in one THREE.Points. There has to be a list of different THREE.Points with different HREE.PointsMaterials.
Create a list of THREE.Points with different materials:
var texture = new THREE.TextureLoader().load( "..." );
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
// ...
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
So in update the opacity and size can be changed individually:
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
// ....
if ( material.size < 40 )
material.size += 0.5;
if ( material.opacity < 1 )
material.opacity += 0.01;
// ....
}
}
var canvas_w = window.innerWidth, canvas_h = window.innerHeight;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, canvas_w/canvas_h, 1, 1000);
camera.position.set(0, 0, 400);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(canvas_w, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.onresize = function() {
canvas_w = window.innerWidth, canvas_h = window.innerHeight;
renderer.setSize(canvas_w, canvas_h);
camera.aspect = canvas_w/canvas_h;
camera.updateProjectionMatrix();
}
var texture = new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/circle.png");
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
var px = (Math.random() - 0.5) * 100;
var py = (Math.random() - 0.5) * 100 + 200;
var pz = (Math.random() - 0.5) * 100;
var particle = new THREE.Vector3(px, py, pz);
particle.velocity = new THREE.Vector3(0, 0, 0);
geometry.vertices.push(particle);
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
if (particle.y < -200) {
particle.x = (Math.random() - 0.5) * 100;
particle.y = (Math.random() - 0.5) * 100 + 200;
particle.z = (Math.random() - 0.5) * 100;
particle.velocity.y = 0;
material.size = 20;
material.opacity = 0;
}
particle.velocity.y -= Math.random() * .1;
particle.add(particle.velocity);
if ( material.size < 40 )
material.size += 0.25;
if ( material.opacity < 1 )
material.opacity += 0.01;
points.geometry.verticesNeedUpdate = true;
points.rotation.y += (Math.random()*0.001)
}
}
renderer.setAnimationLoop(() => {
update();
renderer.render(scene, camera);
});
body { overflow: hidden; margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>

RENDER WARNING: there is no texture bound to the unit 0

I am trying to reproduce the three.js panaorama dualfisheye example using Three.js r71.
I need to stick to r71 because eventually I will use this code on autodesk forge viewer which is based on Three.js r71.
I made some progress, but I am facing an error message in the browser javascript console saying: RENDER WARNING: there is no texture bound to the unit 0
var camera, scene, renderer;
var isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0,
distance = 500;
init();
animate();
function init() {
var container, mesh;
container = document.getElementById('container');
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
scene = new THREE.Scene();
// var geometry = new THREE.SphereBufferGeometry( 500, 60, 40 ).toNonIndexed();
var geometry = new THREE.SphereGeometry(500, 60, 40);
// invert the geometry on the x-axis so that all of the faces point inward
// geometry.scale( - 1, 1, 1 );
geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
// Remap UVs
// var normals = geometry.attributes.normal.array;
var normals = [];
geometry.faces.forEach(element => {
normals.push(element.normal)
});
var uvs = geometry.faceVertexUvs
// var uvs = geometry.attributes.uv.array;
for (var i = 0, l = normals.length / 3; i < l; i++) {
var x = normals[i * 3 + 0];
var y = normals[i * 3 + 1];
var z = normals[i * 3 + 2];
if (i < l / 2) {
var correction = (x == 0 && z == 0) ? 1 : (Math.acos(y) / Math.sqrt(x * x + z * z)) * (2 / Math.PI);
uvs[i * 2 + 0] = x * (404 / 1920) * correction + (447 / 1920);
uvs[i * 2 + 1] = z * (404 / 1080) * correction + (582 / 1080);
} else {
var correction = (x == 0 && z == 0) ? 1 : (Math.acos(- y) / Math.sqrt(x * x + z * z)) * (2 / Math.PI);
uvs[i * 2 + 0] = - x * (404 / 1920) * correction + (1460 / 1920);
uvs[i * 2 + 1] = z * (404 / 1080) * correction + (582 / 1080);
}
}
geometry.applyMatrix(new THREE.Matrix4().makeRotationZ(-Math.PI / 2))
// geometry.rotateZ( - Math.PI / 2 );
//
// var texture = new THREE.TextureLoader().load( 'ricoh_theta_s.jpg' );
var texture = new THREE.TextureLoader('https://preview.ibb.co/hZXYmz/ricoh_theta_s.jpg');
this.texture = texture;
texture.format = THREE.RGBFormat;
var material = new THREE.MeshBasicMaterial({ map: texture });
material.map.repeat = { x: 0, y: 0 }
material.map.offset = { x: 0, y: 0 };
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
document.addEventListener('wheel', onDocumentMouseWheel, false);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseDown(event) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove(event) {
if (isUserInteracting === true) {
lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon;
lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat;
}
}
function onDocumentMouseUp(event) {
isUserInteracting = false;
}
function onDocumentMouseWheel(event) {
distance += event.deltaY * 0.05;
distance = THREE.Math.clamp(distance, 400, 1000);
}
function animate() {
// requestAnimationFrame(animate);
update();
}
function update() {
if (isUserInteracting === false) {
lon += 0.1;
}
lat = Math.max(- 85, Math.min(85, lat));
phi = THREE.Math.degToRad(90 - lat);
theta = THREE.Math.degToRad(lon - 180);
camera.position.x = distance * Math.sin(phi) * Math.cos(theta);
camera.position.y = distance * Math.cos(phi);
camera.position.z = distance * Math.sin(phi) * Math.sin(theta);
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/71/three.js"></script>
<div id="container"></div>
Thank you for your time.
There are a bunch of issues with the code
The loading code is wrong for r71. It should be something like this
THREE.ImageUtils.crossOrigin = '';
var texture = THREE.ImageUtils.loadTexture('https://preview.ibb.co/hZXYmz/ricoh_theta_s.jpg');
IIRC THREE r71 didn't pre-init textures with something renderable so you need to wait for the texture to load
var texture = THREE.ImageUtils.loadTexture(
'https://preview.ibb.co/hZXYmz/ricoh_theta_s.jpg',
undefined,
animate); // call animate after texture has loaded
and removed the call to animate at the top
That will get rid of the warning but continuing on
The code sets the repeat to 0
material.map.repeat = { x: 0, y: 0 };
material.map.offset = { x: 0, y: 0 };
Setting the repeat to 0 means you'll only see the first pixel of the texture since all UVs will be multiplied by 0
It code sets the repeat and offset incorrectly.
The correct way to set repeat and offset is with set
material.map.repeat.set(1, 1);
material.map.offset.set(0, 0);
It works the other way but only by luck. The 2 settings are THREE.Vector2
objects. The code using repeat and offset could change at any time to
use methods on THREE.Vector2 or pass the repeat and offset to functions
that expect a THREE.Vector2 so it's best not to replace them
note that there is no reason to set them though. 1 1 for repeat and 0 0 for offset are the defaults.
The code only renders once
requestAnimationFrame was commented out. Textures load asynchronously
so you won't see the texture for several frames. You either need to wait
for the texture to load before rendering, render again once it has finish
loading or, render continuously so that when it happens to load it's used
The code is using a cross domain image
This isn't actually a bug, just a warning. WebGL can't use cross domain
images unless the server itself gives permission. The one the code links
to does give that permission but I wasn't sure if you're aware of that
or were just lucky. The majority of images from servers not your own
are unlikely to work.
The code's uv math is wrong
You should ask another question for that. Commenting that out I can see the texture
var camera, scene, renderer;
var isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0,
distance = 500;
init();
function init() {
var container, mesh;
container = document.getElementById('container');
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
scene = new THREE.Scene();
// var geometry = new THREE.SphereBufferGeometry( 500, 60, 40 ).toNonIndexed();
var geometry = new THREE.SphereGeometry(500, 60, 40);
// invert the geometry on the x-axis so that all of the faces point inward
// geometry.scale( - 1, 1, 1 );
geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
// Remap UVs
// var normals = geometry.attributes.normal.array;
var normals = [];
geometry.faces.forEach(element => {
normals.push(element.normal)
});
var uvs = geometry.faceVertexUvs
// var uvs = geometry.attributes.uv.array;
for (var i = 0, l = normals.length / 3; i < l; i++) {
var x = normals[i * 3 + 0];
var y = normals[i * 3 + 1];
var z = normals[i * 3 + 2];
if (i < l / 2) {
var correction = (x == 0 && z == 0) ? 1 : (Math.acos(y) / Math.sqrt(x * x + z * z)) * (2 / Math.PI);
// uvs[i * 2 + 0] = x * (404 / 1920) * correction + (447 / 1920);
// uvs[i * 2 + 1] = z * (404 / 1080) * correction + (582 / 1080);
} else {
var correction = (x == 0 && z == 0) ? 1 : (Math.acos(- y) / Math.sqrt(x * x + z * z)) * (2 / Math.PI);
// uvs[i * 2 + 0] = - x * (404 / 1920) * correction + (1460 / 1920);
// uvs[i * 2 + 1] = z * (404 / 1080) * correction + (582 / 1080);
}
}
geometry.applyMatrix(new THREE.Matrix4().makeRotationZ(-Math.PI / 2))
// geometry.rotateZ( - Math.PI / 2 );
//
THREE.ImageUtils.crossOrigin = '';
var texture = THREE.ImageUtils.loadTexture('https://preview.ibb.co/hZXYmz/ricoh_theta_s.jpg', undefined, animate);
var material = new THREE.MeshBasicMaterial({ map: texture });
material.map.repeat.set(1, 1);
material.map.offset.set(0, 0);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
document.addEventListener('wheel', onDocumentMouseWheel, false);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseDown(event) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove(event) {
if (isUserInteracting === true) {
lon = (onPointerDownPointerX - event.clientX) * 0.1 + onPointerDownLon;
lat = (onPointerDownPointerY - event.clientY) * 0.1 + onPointerDownLat;
}
}
function onDocumentMouseUp(event) {
isUserInteracting = false;
}
function onDocumentMouseWheel(event) {
distance += event.deltaY * 0.05;
distance = THREE.Math.clamp(distance, 400, 1000);
}
function animate() {
requestAnimationFrame(animate);
update();
}
function update() {
if (isUserInteracting === false) {
lon += 0.1;
}
lat = Math.max(- 85, Math.min(85, lat));
phi = THREE.Math.degToRad(90 - lat);
theta = THREE.Math.degToRad(lon - 180);
camera.position.x = distance * Math.sin(phi) * Math.cos(theta);
camera.position.y = distance * Math.cos(phi);
camera.position.z = distance * Math.sin(phi) * Math.sin(theta);
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/71/three.js"></script>
<div id="container"></div>
For those just looking for an answer to the warning
RENDER WARNING: there is no texture bound to the unit 0
It is issued by Chrome when:
The bound shader program expects a texture.
No texture is bound.
Source and further links:
https://github.com/NASAWorldWind/WebWorldWind/issues/302#issuecomment-346188472
The fix then, is to always bind a texture to the shader samplers, even when the shader won't use it.
As gman suggested in his longer answer, binding a white 1px texture when "there is no texture" is good practice because it won't consume much space or bandwidth and the shader code can use it to multiply with another colour without altering it.

Rotating an 3D object around it's y-axis in three.js

I just started exploring three.js and have been trying to adapt a project I found.
I would like to know if it would be possible to have the globe object rotate around it's y-axis with minor additions to the code or whether it has to be rewritten from the ground up.
var canvas = document.querySelector('canvas');
var width = canvas.offsetWidth,
height = canvas.offsetHeight;
var colors = [
new THREE.Color(0xac1122),
new THREE.Color(0x96789f),
new THREE.Color(0x535353)];
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
renderer.setSize(width, height);
renderer.setClearColor(0xffffff);
var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = 6;
var camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 2000);
camera.position.set(0, 0, 350);
var galaxy = new THREE.Group();
scene.add(galaxy);
// Create dots
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
var dotTexture = loader.load("img/dotTexture.png");
var dotsAmount = 3000;
var dotsGeometry = new THREE.Geometry();
var positions = new Float32Array(dotsAmount * 3);
var sizes = new Float32Array(dotsAmount);
var colorsAttribute = new Float32Array(dotsAmount * 3);
for (var i = 0; i < dotsAmount; i++) {
var vector = new THREE.Vector3();
vector.color = Math.floor(Math.random() * colors.length);
vector.theta = Math.random() * Math.PI * 2;
vector.phi =
(1 - Math.sqrt(Math.random())) *
Math.PI /
2 *
(Math.random() > 0.5 ? 1 : -1);
vector.x = Math.cos(vector.theta) * Math.cos(vector.phi);
vector.y = Math.sin(vector.phi);
vector.z = Math.sin(vector.theta) * Math.cos(vector.phi);
vector.multiplyScalar(120 + (Math.random() - 0.5) * 5);
vector.scaleX = 5;
if (Math.random() > 0.5) {
moveDot(vector, i);
}
dotsGeometry.vertices.push(vector);
vector.toArray(positions, i * 3);
colors[vector.color].toArray(colorsAttribute, i*3);
sizes[i] = 5;
}
function moveDot(vector, index) {
var tempVector = vector.clone();
tempVector.multiplyScalar((Math.random() - 0.5) * 0.2 + 1);
TweenMax.to(vector, Math.random() * 3 + 3, {
x: tempVector.x,
y: tempVector.y,
z: tempVector.z,
yoyo: true,
repeat: -1,
delay: -Math.random() * 3,
ease: Power0.easeNone,
onUpdate: function () {
attributePositions.array[index*3] = vector.x;
attributePositions.array[index*3+1] = vector.y;
attributePositions.array[index*3+2] = vector.z;
}
});
}
var bufferWrapGeom = new THREE.BufferGeometry();
var attributePositions = new THREE.BufferAttribute(positions, 3);
bufferWrapGeom.addAttribute('position', attributePositions);
var attributeSizes = new THREE.BufferAttribute(sizes, 1);
bufferWrapGeom.addAttribute('size', attributeSizes);
var attributeColors = new THREE.BufferAttribute(colorsAttribute, 3);
bufferWrapGeom.addAttribute('color', attributeColors);
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: dotTexture
}
},
vertexShader: document.getElementById("wrapVertexShader").textContent,
fragmentShader: document.getElementById("wrapFragmentShader").textContent,
transparent:true
});
var wrap = new THREE.Points(bufferWrapGeom, shaderMaterial);
scene.add(wrap);
// Create white segments
var segmentsGeom = new THREE.Geometry();
var segmentsMat = new THREE.LineBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.3,
vertexColors: THREE.VertexColors
});
for (i = dotsGeometry.vertices.length - 1; i >= 0; i--) {
vector = dotsGeometry.vertices[i];
for (var j = dotsGeometry.vertices.length - 1; j >= 0; j--) {
if (i !== j && vector.distanceTo(dotsGeometry.vertices[j]) < 12) {
segmentsGeom.vertices.push(vector);
segmentsGeom.vertices.push(dotsGeometry.vertices[j]);
segmentsGeom.colors.push(colors[vector.color]);
segmentsGeom.colors.push(colors[vector.color]);
}
}
}
var segments = new THREE.LineSegments(segmentsGeom, segmentsMat);
galaxy.add(segments);
var hovered = [];
var prevHovered = [];
function render(a) {
var i;
dotsGeometry.verticesNeedUpdate = true;
segmentsGeom.verticesNeedUpdate = true;
raycaster.setFromCamera( mouse, camera );
var intersections = raycaster.intersectObjects([wrap]);
hovered = [];
if (intersections.length) {
for(i = 0; i < intersections.length; i++) {
var index = intersections[i].index;
hovered.push(index);
if (prevHovered.indexOf(index) === -1) {
onDotHover(index);
}
}
}
for(i = 0; i < prevHovered.length; i++){
if(hovered.indexOf(prevHovered[i]) === -1){
mouseOut(prevHovered[i]);
}
}
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
renderer.render(scene, camera);
}
function onDotHover(index) {
dotsGeometry.vertices[index].tl = new TimelineMax();
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 1, {
scaleX: 10,
ease: Elastic.easeOut.config(2, 0.2),
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function mouseOut(index) {
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 0.4, {
scaleX: 5,
ease: Power2.easeOut,
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function onResize() {
canvas.style.width = '';
canvas.style.height = '';
width = canvas.offsetWidth;
height = canvas.offsetHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
var mouse = new THREE.Vector2(-100,-100);
function onMouseMove(e) {
var canvasBounding = canvas.getBoundingClientRect();
mouse.x = ((e.clientX - canvasBounding.left) / width) * 2 - 1;
mouse.y = -((e.clientY - canvasBounding.top) / height) * 2 + 1;
}
TweenMax.ticker.addEventListener("tick", render);
window.addEventListener("mousemove", onMouseMove);
var resizeTm;
window.addEventListener("resize", function(){
resizeTm = clearTimeout(resizeTm);
resizeTm = setTimeout(onResize, 200);
});
Codepen here - https://codepen.io/quickwaste/pen/PaGPdw
Thanks.
(A stretch goal would be to have the camera move in response to mouse movement)
Simply add galaxy.rotateY(0.005 * Math.PI); to render(), right before renderer.render(scene, camera) call, like this:
// pulled from the CodePen
function render(a) {
// ... omitted for brevity
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
galaxy.rotateY(0.005 * Math.PI);
renderer.render(scene, camera);
}
I used a multiplier of 0.005 to give the globe a nice, lazy spin.
The 'galaxy' object is a THREE.Group, a wrapper of sorts for collections of THREE.Object3D objects. The Object3D has all sorts of nifty functions to help rotate, translate, and transform 3D objects. The rotateY() will spin the model around its local y-axis.

Threejs Multiple Mesh animation

I am very new to threejs, and I have created a for loop to draw 400 cylinders. Everything is fine and they are called into the scene and rendered properly. When I go to animate the objects, only one of the 400 appears to rotate. How can I rotate all of the cylinders?
Code:
for( var j = 0; j < 400; j++){
abgeometry2 = new THREE.CylinderGeometry (1, 5, 8, 4);
abmesh2 = new THREE.MeshPhongMaterial({color: 0x3B170B,
wireframe: false });
mesh2 = new THREE.Mesh(abgeometry2, abmesh2);
mesh2.position.x = Math.random() * 400 - 200;
mesh2.position.y = Math.random() * 400 - 200;
mesh2.position.z = Math.random() * 400 - 200;
scene.add( mesh2 );
}
And in the animate I put : mesh2.rotation.z += 0.06;
I know I may be doing something stupid but I'm not quite familiar with threejs.
You're only applying the rotation to the last cylinder since they're all assigned to mesh2 during the loop.
Try something like this:
var numCylinders = 400;
var cylinders = [];
var geo = new THREE.CylinderGeometry (1, 5, 8, 4);
var mesh = new THREE.MeshPhongMaterial({color: 0x3B170B, wireframe: false });
for (var i = 0; i < numCylinders; i++){
var curCylinder = new THREE.Mesh(geo, mesh);
curCylinder.position.x = Math.random() * 400 - 200;
curCylinder.position.y = Math.random() * 400 - 200;
curCylinder.position.z = Math.random() * 400 - 200;
scene.add(curCylinder);
cylinders.push(curCylinder);
}
var render = function () {
requestAnimationFrame( render );
for (var i = 0; i <numCylinders; i++){
cylinders[i].rotation.z += 0.06;
}
renderer.render(scene, camera);
};
render();

Categories

Resources