How to change rotation direction in three.js - three.js

I'm new to three.js and having trouble finding documentation on how to change rotation direction. The current rotation is set so the sphere looks like it's spinning towards the viewer on a screen but I want it to rotate in a clockwise direction, so it looks like it's spinning from a side view if a viewer is looking at it.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, 500 / 400, 1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(500, 400);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.SphereGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry);
scene.add(cube);
camera.position.z = 10;
var render = function () {
requestAnimationFrame(render);
cube.rotation.x -= 0.10;
cube.rotation.y += 0.00;
renderer.render(scene, camera);
};
render();
http://jsfiddle.net/SF9tX/1968/

If I understand your question correctly, then rotation around different directions (or axis') can be achieved by incrementing different components of the objects rotation vector.
So for example, if you want to rotate the cube around the y-axis (ie to create a "spinning globe" effect) you can increment the .y component of the .rotation vector, as follows:
var render = function () {
requestAnimationFrame(render);
// This was casuing rotation around about the x-axis
// cube.rotation.x -= 0.10;
// Add this to cause rotation around the z-axis,
cube.rotation.z += 0.10;
renderer.render(scene, camera);
};

Ah I figured it out. It's as simple as replacing the cube.rotation.x to cube.rotation.z, so:
camera.position.z = 10;
var render = function () {
requestAnimationFrame(render);
cube.rotation.z -= 0.10;
cube.rotation.y += 0.00;
renderer.render(scene, camera);
};

Related

Three.js - How to slowly change the camera's orientation untill it reaches a specific vector

I'm using Three.js to develop a player for 360° pictures, and I need some advice.
I have created a few cliquable meshs inside the scene. Currently, when the user clicks on a mesh, the camera's orientation is brutally changed to the mesh's direction. (this done by calling THREE.Camera.lookat()).
What I want is that when the users clicks, the camera transitions smoothly from it's target vector to the mesh's direction. I would like that the camera takes about 1 second to go from its current vector to the mesh's direction.
I have seen that tween is a library with which we can animate the scene, but I didn't really understand how it works.
Do you know what I could use to implement this animation ?
If tween can help me, can you explain how tween comes into play with three.js, or can you link some githubs or else ?
Thank you for feedbacks.
Just an extension of the manthrax's idea with Tween.js
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var sphere = new THREE.Mesh(new THREE.SphereGeometry(10, 32, 24), new THREE.MeshBasicMaterial({
color: "yellow",
wireframe: true
}));
scene.add(sphere);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var startRotation = new THREE.Quaternion();
var targetRotation = new THREE.Quaternion();
window.addEventListener("mousedown", onMouseDown, false);
function onMouseDown(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
let newPosition = raycaster.ray.at(10);
setPoint(newPosition);
// manthrax's idea + Tween.js
startRotation.copy(camera.quaternion);
camera.lookAt(newPosition);
camera.updateMatrixWorld();
targetRotation = camera.quaternion.clone();
camera.quaternion.copy(startRotation);
new TWEEN.Tween(camera.quaternion).to(targetRotation, 1000).easing(TWEEN.Easing.Bounce.Out).delay(250).start();
// one of benefits of using Tween.js is easings
// you can find many of them here
// https://sole.github.io/tween.js/examples/03_graphs.html
}
function setPoint(position) {
let point = new THREE.Mesh(new THREE.SphereGeometry(0.125, 4, 2), new THREE.MeshBasicMaterial({
color: "red",
wireframe: true
}));
point.position.copy(position);
scene.add(point);
}
render()
function render() {
requestAnimationFrame(render);
TWEEN.update(); // don't forget to put this line into the animation loop, when you use Tween.js
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.min.js"></script>
Something like:
var targetRotation,startTime,transitionDuration;
var startRotation = camera.quaternion.clone();
function smoothTransition(newTarget){
startRotation.copy(camera.quaternion);
camera.lookAt(newTarget);
camera.updateMatrixWorld();
targetRotation = camera.rotation.clone();
startTime = performance.now();
transitionDuration = 1000;
}
In animate:
if(startRotation){
var playTime = (performance.now()-startTime)/transitionDuration;
if(playTime>1)playTime = 1;
Quaternion.slerp(startRotation,targetRotation,camera.rotation,playTime);
camera.updateMatrixWorld();
}

three js drag and drop Uncaught TypeError

I have been trying to implement the drag and drop functionality found here...
http://www.smartjava.org/tjscb/07-animations-physics/07.08-drag-n-drop-object-around-scene.html
Whenever I customise it slightly and use it in my project I get the following..
"Uncaught TypeError: Cannot read property 'point' of undefined"
whenever I try to drag a cube. The rotation isn't occurring so it must be recognising that I'm trying to drag an object and it relates to this line of code..
"selectedObject.position.copy(intersects[0].point.sub(offset))"
I assumed since I am new to all of this that I had messed up, so I copied all of the code from the link above into a new page (so should be identical) and ran it and I get the same thing (everything else works good)
Im probably missing something really stupid, I have searched for this and looked at other examples on how to achieve this, but since I was working my way through a book which explained everything I thought I would stick with this, and also it would be a good learning experience to figure out why its not working. If anyone could point me in the right direction I would appreciate it
<!DOCTYPE html>
<html>
<head>
<title>07.08 - Drag and drop object around scene</title>
<script type="text/javascript" src="js/threejs/three.min.js"></script>
<script type="text/javascript" src ="js/threejs/OrbitControls.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
<script>
// global variables
var renderer;
var scene;
var camera;
var cube;
var control;
var orbit;
// used for drag and drop
var plane;
var selectedObject;
var offset = new THREE.Vector3();
var objects = [];
// based on http://mrdoob.github.io/three.js/examples/webgl_interactive_draggablecubes.html
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render, sets the background color and the size
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xffffff, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
plane = new THREE.Mesh(new THREE.PlaneGeometry(2000, 2000, 18, 18), new THREE.MeshBasicMaterial({
color: 0x00ff00,
opacity: 0.25,
transparent: true
}));
plane.visible = false;
scene.add(plane);
var dirLight = new THREE.DirectionalLight();
dirLight.position.set(25, 23, 15);
scene.add(dirLight);
var dirLight2 = new THREE.DirectionalLight();
dirLight2.position.set(-25, 23, 15);
scene.add(dirLight2);
for (var i = 0; i < 200; i++) {
// create a cube and add to scene
var cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
var cubeMaterial = new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff});
cubeMaterial.transparent = true;
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
objects.push(cube);
cube.scale.x = Math.random() + 0.5 * 2;
cube.scale.y = Math.random() + 0.5 * 2;
cube.scale.z = Math.random() + 0.5 * 2;
cube.position.x = Math.random() * 50 - 25;
cube.position.y = Math.random() * 50 - 25;
cube.position.z = Math.random() * 50 - 25;
cube.rotation.x = Math.random() * Math.PI * 2;
cube.rotation.y = Math.random() * Math.PI * 2;
cube.rotation.z = Math.random() * Math.PI * 2;
scene.add(cube);
}
// position and point the camera to the center of the scene
camera.position.x = 35;
camera.position.y = 35;
camera.position.z = 53;
camera.lookAt(scene.position);
// add some controls so we can rotate
orbit = new THREE.OrbitControls(camera);
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
// call the render function
render();
}
function render() {
renderer.render(scene, camera);
orbit.update();
requestAnimationFrame(render);
}
document.onmousemove = function (event) {
// make sure we don't access anything else
event.preventDefault();
// get the mouse positions
var mouse_x = ( event.clientX / window.innerWidth ) * 2 - 1;
var mouse_y = -( event.clientY / window.innerHeight ) * 2 + 1;
// get the 3D position and create a raycaster
var vector = new THREE.Vector3(mouse_x, mouse_y, 0.5);
vector.unproject(camera);
var raycaster = new THREE.Raycaster(camera.position,
vector.sub(camera.position).normalize());
// first check if we've already selected an object by clicking
if (selectedObject) {
// check the position where the plane is intersected
var intersects = raycaster.intersectObject(plane);
// reposition the selectedobject based on the intersection with the plane
selectedObject.position.copy(intersects[0].point.sub(offset));
} else {
// if we haven't selected an object, we check if we might need
// to reposition our plane. We need to do this here, since
// we need to have this position before the onmousedown
// to calculate the offset.
var intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
// now reposition the plane to the selected objects position
plane.position.copy(intersects[0].object.position);
// and align with the camera.
plane.lookAt(camera.position);
}
}
};
document.onmousedown = function (event) {
// get the mouse positions
var mouse_x = ( event.clientX / window.innerWidth ) * 2 - 1;
var mouse_y = -( event.clientY / window.innerHeight ) * 2 + 1;
// use the projector to check for intersections. First thing to do is unproject
// the vector.
var vector = new THREE.Vector3(mouse_x, mouse_y, 0.5);
// we do this by using the unproject function which converts the 2D mouse
// position to a 3D vector.
vector.unproject(camera);
// now we cast a ray using this vector and see what is hit.
var raycaster = new THREE.Raycaster(camera.position,
vector.sub(camera.position).normalize());
// intersects contains an array of objects that might have been hit
var intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
orbit.enabled = false;
// the first one is the object we'll be moving around
selectedObject = intersects[0].object;
// and calculate the offset
var intersects = raycaster.intersectObject(plane);
offset.copy(intersects[0].point).sub(plane.position);
}
};
document.onmouseup = function (event) {
orbit.enabled = true;
selectedObject = null;
}
// calls the init function when the window is done loading.
window.onload = init;
</script>
</head>
<body>
</body>
</html>
"Uncaught TypeError: Cannot read property 'point' of undefined"
"selectedObject.position.copy(intersects[0].point.sub(offset))"
This means, intersects[0] is undefined which means the array intersects has no element (length = 0). You are using raycasting and it isn't working properly.
You should share your modified code so that we can check what is going wrong in your raycasting.
Update: I think your three.js version is greater than 71 while three.js version of this website is 71 or less. In the 72th version, there is an update in the raycaster -
Ignore invisible objects. (#mrdoob, #tschw)
So, the problem is here -
var intersects = raycaster.intersectObject(plane);
Since the plane is invisible, the intersectObject is returning empty array.
Workaround: I found a workaround. You can remove the following line -
plane.visible = false;
You can hide the material of the plane instead in the following way -
plane = new THREE.Mesh(new THREE.PlaneGeometry(2000, 2000, 18, 18), new THREE.MeshBasicMaterial({
color: 0xffff00,
opacity: 0.50,
transparent: true,
visible: false
}));
In this way, the raycaster will work properly and the plane will be invisible as well.

Three.js Point cloud with transparency using a shader material disappears when adding non transparent cube

The behavior happens only on firefox. (I use the developper edition).
I have some point clouds which need to use a shader with transparency activated.
When I add a CubeGeometry to the scene without transparency it makes the point cloud disappear.
I also noted that using a point cloud with a PointMaterial works as intended, but in my program I need to use shaders.
If you use shaderMaterial on the cube in this part of the code:
mesh = new THREE.Mesh(geometry, material);
//mesh = new THREE.Mesh(geometry, shaderMaterial);
The cloud appears correctly as well, but of course I need a non transparent cube with some other material than the shader of the cloud.
I'm using three.js r74
Thank you for your help!
var $ = document.querySelector.bind(document);
var camera, scene, renderer, geometry, material, mesh;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
var pointMaterial = new THREE.PointsMaterial();
var vShader = $('#vertexshader');
var fShader = $('#fragmentshader');
var shaderMaterial =
new THREE.ShaderMaterial({
vertexShader: vShader.text,
fragmentShader: fShader.text
});
shaderMaterial.transparent = true;
shaderMaterial.vertexColors = THREE.VertexColors;
shaderMaterial.depthWrite = true;
geometry = new THREE.Geometry();
particleCount = 20000;
for (i = 0; i < particleCount; i++) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2000 - 1000;
vertex.y = Math.random() * 2000 - 1000;
vertex.z = Math.random() * 2000 - 1000;
geometry.vertices.push(vertex);
}
parameters = [
[
[1, 1, 0.5], 5
],
[
[0.95, 1, 0.5], 4
],
[
[0.90, 1, 0.5], 3
],
[
[0.85, 1, 0.5], 2
],
[
[0.80, 1, 0.5], 1
]
];
parameterCount = parameters.length;
for (i = 0; i < parameterCount; i++) {
color = parameters[i][0];
size = parameters[i][1];
//If we use pointMaterial instead of ShaderMaterial the cloud is visible
particles = new THREE.Points(geometry, shaderMaterial);
particles.sizeAttenuation = true;
particles.sortParticles = true;
particles.colorsNeedUpdate = true;
particles.scale.set(1, 1, 1);
particles.rotation.x = Math.random() * 6;
particles.rotation.y = Math.random() * 6;
particles.rotation.z = Math.random() * 6;
scene.add(particles);
}
geometry = new THREE.CubeGeometry(200, 200, 200);
//POINT CLOUD DISAPPEARS WHEN USING NON TRANSPARENT MATERIAL
material = new THREE.MeshBasicMaterial({color: 0x00ff00});
mesh = new THREE.Mesh(geometry, material);
//mesh = new THREE.Mesh(geometry, shaderMaterial);
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
<script type="x-shader/x-vertex" id="vertexshader">
void main()
{
gl_PointSize = 5.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
precision highp float;
void main()
{
gl_FragColor = vec4(1.0,0.0,1.0,1.0);
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.js"></script>
It's not clear what effect you're trying to achieve
Do you want to see the points inside to cube?
Your shader is returning opaque purple
gl_FragColor = vec4(1.0,0.0,1.0,1.0);
So your particles will not be transparent regardless of the transparent setting on the material.
Your cube is Non-Transparent so of course the points inside the cube disappear. That's the definition of non-transparent.
Setting the cube to transparent won't fix the issue either. Dealing with transparency is hard. You generally need to draw things front to back. To do that three.js needs every object to be able to be drawn separately so it can first draw all the particles behind the cube, then the back of the cube, then the particles inside the cube, then the front of the cube, then the particles in front of the cube.
To do that requires you split the cube into 6 planes and put every particle in it's own scene object.
There are ways to fake it. Turning off depthTest can sometimes be used as a substitute but it won't be totally correct.

Using three.js raycaster as the developers recommend?

My code works for picking objects and turning them a different color, but what is the technique to get them back to the original color after my mouse crosses the cube? Here is my code. As it stands, when I bring my mouse over the cube, it turns red.
(document).ready(function () {
window.addEventListener('mousemove', onMouseMove, false);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
//start a scene
var scene = new THREE.Scene();
//add a cube
var cube = new THREE.Mesh(new THREE.CubeGeometry(3, 3, 3), new THREE.MeshBasicMaterial({ color:0x454545 }));
cube.position.set(0, 0, 0);
scene.add(cube);
//get a renderer
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xffffff);
renderer.setSize(window.innerWidth, window.innerHeight);
$('#GameArea').append(renderer.domElement);
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(10, 10, 10);
camera.lookAt(0,0,0);
var controls = new THREE.TrackballControls(camera);
var light = new THREE.SpotLight(0xeeeeee);
light.position.set(-10, 5, 40);
scene.add(light);
render();
function onMouseMove(event) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
for (var i = 0; i < intersects.length; i++) {
intersects[i].object.material.color.set(0xff0000);
}
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
//calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
});

Identifying overlap region of two geometries in a canvas using THREE.js

I need to identify the overlapping region of two geometries in a canvas and show the overlapping region with different color/texture.
sample code: http://jsfiddle.net/v4B3d/1/
var camera, scene, renderer, geometry, material, mesh,mesh2;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
geometry = new THREE.CubeGeometry(100, 100, 100);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
mesh2 = new THREE.Mesh(geometry, material);
scene.add(mesh);
scene.add(mesh2);
mesh.position.y = -30;
mesh2.position.y = 40;
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
mesh2.rotation.x += 0.01;
mesh2.rotation.y += 0.02;
renderer.render(scene, camera);
}
Please let me know how to achieve this.
Thanks in advance.
You probably want to make a third geometry from the overlapping region. You can then render that however you want.
This can be achieved using Constructive Solid Geometry (CSG) boolean operations (namely, intersection). If you only want to render the overlapping part, you can render only the intersection result.
There is a Three.js wrapper for CSG.js library, that should make it easy. See http://learningthreejs.com/blog/2011/12/10/constructive-solid-geometry-with-csg-js/ , http://www.chandlerprall.com/2011/12/constructive-solid-geometry-with-three-js/ and http://en.wikipedia.org/wiki/Constructive_solid_geometry for more information.

Resources