Mesh suddenly disappears in three.js. Clipping? - javascript

Scenario:
In my scene I implemented a vertex shader that positions a plane mesh on the xz-axis at the position of the camera.
So if the camera moves, the plane mesh moves with it. This leads to the visual effect that, while moving the camera, the plane mesh seems to stay fixed in place. This seems to work correctly.
Problem:
If I move the camera (and therefore the plane mesh) to a certain extend, the mesh suddenly disappears.
I realized that there seems to be a relationship between the disappearance and the size of the plane, i.e. the larger the plane, the more I can move the camera before the plane mesh disappears.
Also, in my test scene the plane mesh only disappears when moving on the negative x-axis, positive x-axis or negative z-axis. It does NOT disappear when moving on the positive z-axis.
I assume it has something to do with some kind of clipping, but may be wrong. Recomputing the bounding box of the plane mesh had no effect.
Any ideas?
Cheers
Fiddle:
I created a fiddle that shows the problem: http://jsfiddle.net/p8wZ6/10/
In the fiddle I added an additional box mesh to better visualize that the camera actually moves.
- To change the axis the camera moves on (negative z-axis by default) (un-)comment the appropriate code line in the tick method.
- To change the size of the plane change the size value in the createPlane method.
Sourcecode Shader:
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
vec4 pos = vec4( position, 1.0 );
vec4 wPos = modelMatrix * pos;
wPos.x += cameraPosition.x;
wPos.z += cameraPosition.z;
// standard
// vec4 pPos = projectionMatrix * modelViewMatrix * pos;
// keep fixed
vec4 pPos = projectionMatrix * viewMatrix * wPos;
gl_Position = pPos;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
void main() {
gl_FragColor.rgb = vec3(0.7, 0.7, 0.7);
gl_FragColor.a = 1.0;
}
</script>
Sourcecode JS:
var scene;
var camera;
var light;
var renderer;
var controls;
var onTick;
var planeMesh;
var boxMesh;
var heightmap;
var clock;
function createPlane(){
// disappearance seems related to size of geometry.
// the larger the longer it takes until disappearance.
var size = 20;
var geom = new THREE.PlaneGeometry(size, size, 20, 20);
return geom;
}
function createBox(){
var geom = new THREE.CubeGeometry(2, 2, 4);
return geom;
}
function createMesh(){
// plane
var geom = createPlane();
var shaderMaterial = new THREE.ShaderMaterial({
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.DoubleSide,
wireframe: true
});
planeMesh = new THREE.Mesh(geom, shaderMaterial);
var axis = new THREE.AxisHelper(4);
planeMesh.rotation.x = -90 * (Math.PI / 180);
planeMesh.add(axis);
scene.add(planeMesh);
// box
geom = createBox();
var material = new THREE.MeshBasicMaterial( {
color: 0xff00ff,
});
boxMesh = new THREE.Mesh(geom, material);
boxMesh.position.x = 5;
boxMesh.position.z = -15;
axis = new THREE.AxisHelper(4);
boxMesh.add(axis);
scene.add(boxMesh);
}
function startRendering(){
onTick();
};
function onTick(){
// move camera
// causes disappearance
// neg. z
camera.position.z -= .1;
// pos. x
// camera.position.x += .1;
// neg. x
// camera.position.x -= .1;
// causes no disappearance
// pos. z
// camera.position.z += .1;
requestAnimationFrame(onTick);
//controls.update(clock.getDelta());
renderer.render(scene, camera);
}
function init(){
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xffffff, 1 );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
scene.add(new THREE.AxisHelper(4));
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1, 0);
light = new THREE.DirectionalLight(0xffffff, 1);
light.shadowCameraVisible = true;
light.position.set(0, 0, 100);
scene.add(light);
//clock = new THREE.Clock();
//controls = new THREE.FirstPersonControls(camera);
//controls.movementSpeed = 20;
//controls.lookSpeed = .1;
}
init();
createMesh();
startRendering();

You have a fundamental misunderstanding.
You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU.
The camera's frustum calculation knows nothing about the vertex displacements in the vertex shader.
As a work-around, you can set
planeMesh.frustumCulled = false;
A better solution is to just add the plane as a child of the camera, and omit vertex displacements.
planeMesh.position.set( 0, -1, 0 );
camera.add( planeMesh );
scene.add( camera );
You must add the camera to the scene graph it you use the second approach.
three.js r.65

When you define your camera in r73 the last two parameters allow you to specify your camera's near and far z clipping distance.
Taken from this link: http://threejs.org/docs/#Manual/Introduction/Creating_a_scene
var camera =
new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
The third parameter of Three.PerspectiveCamera defines the camera's near clipping distance and the fourth parameter defines the camera's far clipping distance.

Related

THREE.JS update the fov value of a perspective camera and keep the same camera distance

I would like to modify the FOV value of my camera during an animation.
Unfortunately as soon as I implement the FOV value, I can observe my scene becoming smaller.
So I've been wondering, what's the mathematic link between FOV value and the distance position of a perspective camera ?
The idea is to have the same scene (same size by modifying the camera position) while the FOV value is changing.
Thank you very much.
EDIT 1 :
Here's a snippet that illustrates my issue : When I implement the FOV value of my camera (from 4 to 45), the distance between my square and my camera changes. How can I prevent it ?
let W = window.innerWidth, H = window.innerHeight;
let renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( W, H );
document.body.appendChild( renderer.domElement );
let camera = new THREE.PerspectiveCamera( 4, W/H, 1, 100 );
let scene = new THREE.Scene();
camera.position.set(0,0,14);
camera.lookAt(0,0,0);
let geo = new THREE.BoxGeometry(0.5, 0.5, 0.5);
let mat = new THREE.MeshNormalMaterial();
let mesh = new THREE.Mesh(geo, mat);
mesh.rotation.set(0.2,0.4,-0.1);
scene.add(mesh);
renderer.render(scene, camera);
let progress = {};
progress.fov = 4;
TweenMax.to(progress, 2,{
fov:45,
onUpdate:function(){
camera.lookAt(0,0,0);
camera.updateProjectionMatrix();
camera.fov = progress.fov;
renderer.render(scene, camera);
},
repeat:-1,
ease:Power3.easeInOut
});
body{margin:0;padding:0;overflow:hidden;background: #666;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
The perspective projection describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport.
This means an object which is projected on the viewport becomes smaller, by its depth.
The relation between the projected area in view space and the Z coordinate of the view space is linear. It depends on the field of view angle and the aspect ratio.
See also THREE.js PerspectiveCamera focalLength off by a factor of two, inconsistent with FOV.
The relation between Z-distance and size for a filed of view fov_y, at perspective projection is:
depht_s = Math.tan(fov_y/2.0 * Math.PI/180.0) * 2.0;
The projected size of the object on the viewport depends on the filed of view and the depth. This cause that a 3 dimensional object never can "look" the same, when filed of view changes. You can define a plane in a certain distance (depth) and find a new distance, so that the projection of the object doesn't change in size, for exactly that depth. Of course the projected size of the object "before" and "behind" this certain distance will (at least slightly) change. See also How to switch between Perspective and Orthographic cameras keeping size of desired object respectively Transpose z-position from perspective to orthographic camera in three.js .
See the illustration. While the top point of the cube can't be seen from the camera when the object is near to the camera, it can be seen when it is far away:
The initial filed of view is 4.0. So the ration between Z-distance and size is:
let init_depht_s = Math.tan(4.0/2.0 * Math.PI/180.0) * 2.0;
When the filed of view is animated, then the current ration between Z-distance and size is:
let current_depht_s = Math.tan(progress.fov/2.0 * Math.PI/180.0) * 2.0;
Now you've to define a distance, which has to "keep size". The initial distance to the center of the cube is 14.0, so I'll choose this for the reference distance. This distance has to be scaled by the ration init_depht_s / current_depht_s, then the projection of the cube (exactly at this distance) keeps its size:
camera.position.set(0, 0, 14 * init_depht_s / current_depht_s);
See the example, which is based on your original code (I've change the near plane to 0.1, else the cube would be clipped, because final distance is below 1.0):
let W = window.innerWidth, H = window.innerHeight;
let renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( W, H );
document.body.appendChild( renderer.domElement );
let camera = new THREE.PerspectiveCamera( 4, W/H, 0.1, 100 );
let scene = new THREE.Scene();
camera.position.set(0,0,14);
camera.lookAt(0,0,0);
let geo = new THREE.BoxGeometry(0.5, 0.5, 0.5);
let mat = new THREE.MeshNormalMaterial();
let mesh = new THREE.Mesh(geo, mat);
mesh.rotation.set(0.2,0.4,-0.1);
scene.add(mesh);
renderer.render(scene, camera);
let progress = {};
progress.fov = 4;
TweenMax.to(progress, 2,{
fov:45,
onUpdate:function(){
let init_depht_s = Math.tan(4.0/2.0 * Math.PI/180.0) * 2.0;
let current_depht_s = Math.tan(progress.fov/2.0 * Math.PI/180.0) * 2.0;
camera.position.set(0, 0, 14 * init_depht_s / current_depht_s);
camera.lookAt(0,0,0);
camera.updateProjectionMatrix();
camera.fov = progress.fov;
renderer.render(scene, camera);
},
repeat:-1,
ease:Power3.easeInOut
});
body{margin:0;padding:0;overflow:hidden;background: #666;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>

Three.js: Raycast Has an Empty Array of Intersects

I am using this example for my WebGL panorama cube: https://threejs.org/examples/?q=pano#webgl_panorama_equirectangular
I want to know what cube user clicks on and I discovered I can use Raycaster for this. According to docs I added the following function:
function onMouseDown( event ) {
event.preventDefault();
var mouseVector = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1,
1 );
//projector.unprojectVector( mouseVector, camera );
mouseVector.unproject( camera );
var raycaster = new THREE.Raycaster( camera.position, mouseVector.sub( camera.position ).normalize() );
// create an array containing all objects in the scene with which the ray intersects
var intersects = raycaster.intersectObjects( scene.children );
console.log(intersects);
if (intersects.length>0){
console.log("Intersected object:", intersects.length);
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}
// ...
But intersects is always empty. My scene is defined as
scene = new THREE.Scene();
and has skyBox added:
var skyBox = new THREE.Mesh( new THREE.CubeGeometry( 1, 1, 1 ), materials );
skyBox.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, - 1 ) );
scene.add( skyBox );
I've seen similar posts related to this issue but could not figure out how to apply to this example. Any directions are appreciated.
Try adding this to your material definition:
var materials = new THREE.SomeMaterial({
/* other settings */,
side: THREE.DoubleSide
});
Raycaster won't intersect back-faces unless the side property is set to THREE.BackSide or THREE.DoubleSide. Even though your scaling technically inverts the face direction, the vertex order stays the same, which is what's important to Raycaster.
Some further explanation
The snippet below is showing how a ray projected from a camera at the center of a skybox inverted by a -Z scale might look.
The box itself looks weird because it has been -Z scaled, and the normals no longer match the material. But that's here nor there.
The green arrow represents the original ray. The red arrow represents what will happen to that ray inside the Mesh.raycast function, which will apply the inverse of the object's world matrix to the ray, but not to the object's geometry. This is a whole different problem.
The point I'm making is that within Mesh.raycast, it does not affect the vertex/index order, so when it checks the triangles of the mesh, they are still in their original order. For a standard BoxGeometry/BoxBufferGeometry, this means the faces all face outward from the geometric origin.
This means the rays (regardless of how the transformation matrix affects them) are still trying to intersect the back-face of those triangles, which will not work unless the material is set to THREE.DoubleSide. (It can also be set to THREE.BackSide, but the -Z scale will ruin that.)
Clicking either of the raycast buttons will produce 0 intersects if the -Z scaled box is not set to THREE.DoubleSide (default). Click the "Set THREE.DoubleSide" button and try it again--it will now intersect.
var renderer, scene, camera, controls, stats;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000,
ray1, ray2, mesh;
function populateScene(){
var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10),
cubeMat = new THREE.MeshPhongMaterial({ color: "red", transparent: true, opacity: 0.5 });
mesh = new THREE.Mesh(cubeGeo, cubeMat);
mesh.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, -1 ) );
mesh.updateMatrixWorld(true);
scene.add(mesh);
var dir = new THREE.Vector3(0.5, 0.5, 1);
dir.normalize();
ray1 = new THREE.Ray(new THREE.Vector3(), dir);
var arrow1 = new THREE.ArrowHelper(ray1.direction, ray1.origin, 20, 0x00ff00);
scene.add(arrow1);
var inverseMatrix = new THREE.Matrix4();
inverseMatrix.getInverse(mesh.matrixWorld);
ray2 = ray1.clone();
ray2.applyMatrix4(inverseMatrix);
var arrow2 = new THREE.ArrowHelper(ray2.direction, ray2.origin, 20, 0xff0000);
scene.add(arrow2);
}
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateScene();
animate();
var rayCaster = new THREE.Raycaster();
document.getElementById("greenCast").addEventListener("click", function(){
rayCaster.ray.copy(ray1);
alert(rayCaster.intersectObject(mesh).length + " intersections!");
});
document.getElementById("redCast").addEventListener("click", function(){
rayCaster.ray.copy(ray2);
alert(rayCaster.intersectObject(mesh).length + " intersections!");
});
document.getElementById("setSide").addEventListener("click", function(){
mesh.material.side = THREE.DoubleSide;
mesh.material.needsUpdate = true;
});
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function () {
function addScript(url, callback) {
callback = callback || function () { };
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function () {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function () {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function () {
threeReady();
})
})
})
})();
body{
text-align: center;
}
<input id="greenCast" type="button" value="Cast Green">
<input id="redCast" type="button" value="Cast Red">
<input id="setSide" type="button" value="Set THREE.DoubleSide">
You might actually want to use an easier process to determine your ray from the camera:
THREE.Raycaster.prototype.setFromCamera( Vector2, Camera );
Simply define your mouse coordinates as you do in a Vector2, then pass the elements to the Raycaster and let it do its thing. It hides the complexity of intersecting the frustrum with the ray from the camera, and should solve your problem.
(Also, the raycaster does indeed only intersect faces that face the ray directly, but since your SkyBox has been inverted its geometries faces are pointing to the inside of the box, so they should intersect if the camera is inside the box. Another possibility is that your box is further away than the raycasters default far value.)
function onMouseDown( event ) {
event.preventDefault();
var mouseVector = new THREE.Vector2(
event.clientX / window.innerWidth * 2 - 1,
-event.clientY / window.innerHeight * 2 + 1
);
var raycaster = new THREE.Raycaster;
raycaster.setFromCamera( mouseVector, camera );
var intersects = raycaster.intersectObjects( scene.children );
console.log(intersects);
if( intersects.length > 0 ){
console.log( "Intersected object:", intersects[ 0 ] );
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}
}

How to calculate angle between two planes?

I have two planes, how can I calculate angle between them? Is it also possible to calculate angle between two Object3D points like in case of planes?
Heres an example fiddle: https://jsfiddle.net/rsu842v8/1/
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(25, 25, 12);
var material = new THREE.MeshBasicMaterial({
color: 0x00fff0,
side: THREE.DoubleSide
});
window.plane1 = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), material);
scene.add(plane1);
plane1.position.set(0.3, 1, -2);
plane1.rotation.set(Math.PI / 3, Math.PI / 2, 1);
window.plane2 = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.MeshBasicMaterial({
color: 0x0fff00,
side: THREE.DoubleSide
}));
scene.add(plane2);
// setup rest
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight)
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x20252f);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
animate();
// TODO: What is the angle between plane1 and plane2?
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r82/three.js"></script>
<script src="https://yume.human-interactive.org/examples/buffer-geometry/OrbitControls.js"></script>
You want to find the angle between two three.js plane meshes.
Unrotated, a THREE.PlaneGeometry is oriented to face the positive z-axis. So the plane's normal points in the direction of the positive z-axis.
So, create a ( 0, 0, 1 ) vector, and apply the same rotation to it as is applied to the plane mesh.
Note that plane.quaternion is automatically updated when you set plane.rotation, so you can use the quaternion in the calculation -- like so:
var vec1 = new THREE.Vector3( 0, 0, 1 ); // create once and reuse
var vec2 = new THREE.Vector3( 0, 0, 1 );
vec1.applyQuaternion( plane1.quaternion );
vec2.applyQuaternion( plane2.quaternion );
var angle = vec1.angleTo( vec2 ); // radians
The problem is a bit more complicated if the planes are children of other rotated objects.
Of course, you can use angleTo() to find the angle between any two vectors.
three.js r.86
I would suggest somehow calculating the normal vectors for each plane you are rendering. Once you have these two vectors - let's say n1 and n2 - it is easy to calculate the angle between the planes with the dot product.
If you aren't familiar with the dot product, dot(n1,n2) where n1 = (x1,y1,z1) and n2 = (x2,y2,z2) would be equal to x1*x2 + y1*y2 + z1*z2. There is another simple identity that says dot(n1,n2) = |v1||v2|cos(a) where || indicates the magnitude of a vector - i.e. |v| = sqrt(x*x + y*y + z*z) if v = (x,y,z) - and a is the angle between the normals which is the angle between the planes. Here is a link to a Mathematics Stack Exchange answer.
In short a = arccos(dot(n1,n2) / |n1||n2|).
If you are interested in learning more about how planes are defined and what the normal vector represents try looking at this.
If you know n1 and n2 are unit vectors then the equation simplifies further to a = arccos(dot(n1,n2)).

Three.js Rotate sphere to arbitrary position

Using Three.js I have a sphere (globe) and several sprites attached to volcano points. I can rotate (spin) the globe and the sprites stay in their positions because they're added as a group to the sphere.
Now I want to be able to spin the globe to an arbitrary position using a button. How can I do this? For example if the point I want to spin to is at the back of the globe, how can I rotate the globe so it's in the front?
This code is essentially what I have right now. A main mesh which I add sprite to.
<html>
<head></head>
<body>
<script src="three.min.js"></script>
<script>
var scene, camera, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: false } );
geometry = new THREE.SphereGeometry( 159, 32, 32 );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
var map = THREE.ImageUtils.loadTexture( "sprite1.png" );
var material2 = new THREE.SpriteMaterial( { map:map, color:0x00ff00 } );
var sprite1 = new THREE.Sprite( material2 );
sprite1.position.set(100,100,100);
sprite1.scale.set(40,40,40);
mesh.add(sprite1);
var sprite2 = new THREE.Sprite( material2);
sprite2.position.set(-100,-100,-100);
sprite2.scale.set(30,30,30);
mesh.add(sprite2);
var sprite3 = new THREE.Sprite(material2);
sprite3.position.set(100,-100,100);
sprite3.scale.set(20,20,20);
mesh.add(sprite3);
renderer = new THREE.WebGLRenderer({alpha:true});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.y += 0.01;
renderer.render( scene, camera );
}
</script>
</body>
</html>
Diagram example
This would be my approach:
// as sprite is a child of mesh get world position
var spritePos = new THREE.Vector3().setFromMatrixPosition(sprite.matrixWorld);
// get the vectors for calculating angle
var cv3 = new THREE.Vector3().subVectors(camera.position, mesh.position);
var sv3 = new THREE.Vector3().subVectors(spritePos, mesh.position);
// we only want to rotate around y-axis, so only the angle in x-z-plane is relevant
var cv2 = new THREE.Vector2(cv3.x, cv3.z);
var sv2 = new THREE.Vector2(sv3.x, sv3.z);
// normalize Vectors
cv2.normalize();
sv2.normalize();
// dot product
var dot = cv2.dot(sv2);
// angle to between sprite and camera in radians
// cosinus is from 1 to -1, so we need to normalize and invert it and multiply it with PI to get proper angle
var angle = (1 - (dot + 1) / 2) * Math.PI;
// is sprite left or right from camera?
if(spritePos.x < 0)
mesh.rotation += angle;
else
mesh.rotation -= angle;
Now, I made a Plunker.
It seems a bit inaccurate as it always rotates a bit left or right to the very front position. Maybe it's due to the cosinus near to some specific angles.
Also keep in mind that the determination whether the sprite is left or right from the camera is a bit more difficult if camera or mesh is somewhere else in the scene.
Explanation after dot product:
The dot product gives the angle of two vectors as cosinus. So we get a value between -1 and 1. e.g. cos(0) = 1 cos(PI/2) = 0 cos(PI) = -1 So at the moment is 0° = 1 and 180° = -1.
We want to get the angle in radians to rotate the mesh in position. So first we normalize it (dot + 1) / 2, so 0° = 1 and 180° = 0.
Then invert it (0° = 0, 180° = 1) and multiply with PI (0° = 0, 180° = PI).
Now, we have the angle to rotate, but we don't know if need to rotate to the left or to the right, that's why I check if the sprite is left or right from camera.
I don't know if it's explanation enough or if it's comprehensable at all?

Trying to aim the particles to camera while rotating and alert message on intersecting in three js?

I'm trying to create event handler on the particles, with alert message on a sphere, aiming always on the camera.
Something similar to this demo ( and making it to work on IE 9+ )
here is my code..
http://jsfiddle.net/praveenv29/cVnKV/11/
var renderer, projector;
var mouseX, mouseY, stats, container;
var objects = [];
var INTERSECTED;
var camera, scene, renderer, material, mesh, cont;
var w1 = 960;
var h1 = 700;
var halfWidth = w1 / 2;
var halfHeigth = h1 / 2;
function init() {
cont = document.createElement('div');
cont.id = "cont";
document.body.appendChild(cont);
camera = new THREE.PerspectiveCamera(75, w1 / h1, 1, 10000);
camera.position.set(90, 90, -200);
scene = new THREE.Scene();
scene.add(camera);
controls = new THREE.OrbitControls(camera);
controls = new THREE.TrackballControls(camera, cont);
controls.rotateSpeed = 0.8;
controls.zoomSpeed = 1.2;
controls.panSpeed = 2.5;
controls.noZoom = true;
controls.noPan = true;
controls.staticMoving = false;
controls.target.set(0, 0, 0);
controls.keys = [95, 90, 84];
renderer = new THREE.CanvasRenderer();
material = new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: true
});
renderer.setSize(w1, h1);
cont.appendChild(renderer.domElement);
generateGeometry();
var light = new THREE.PointLight(0xffffff);
light.position.set(10, 0, 0);
scene.add(light);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
controls.update();
renderer.render(scene, camera);
}
function generateGeometry() {
var axis = new THREE.AxisHelper();
scene.add(axis);
for (var i = 0; i < 20; i++) {
var gloom = new THREE.ImageUtils.loadTexture('map_pin.png');
materialr = new THREE.MeshBasicMaterial({
map: gloom,
overdraw: true,
side: THREE.DoubleSide
});
var geometry = new THREE.PlaneGeometry(15, 15, 2, 2);
var cube = new THREE.Mesh(geometry, materialr);
cube.position.x = Math.random() * 2 - 1;
cube.position.y = Math.random() * 2 - 1;
cube.position.z = Math.random() * 2 - 1;
cube.position.normalize();
cube.position.multiplyScalar(125);
cube.rotation.x = cube.position.x / Math.PI; //57.38
cube.rotation.y = 360 / Math.PI * 2;
objects.push(cube);
scene.add(cube);
}
//earth
var texture = THREE.ImageUtils.loadTexture('world.jpg');
var materials = new THREE.MeshBasicMaterial({
map: texture,
overdraw: true
});
var cone = new THREE.SphereGeometry(120, 35, 35);
var coneMesh = new THREE.Mesh(cone, material);
coneMesh.position.y = 0;
coneMesh.rotation.set(0, 0, 0);
scene.add(coneMesh);
}
init();
animate();
It is pretty unclear what you are looking for; your demo link seems unrelated...
Are you trying to make cubes appear camera normal (always facing the camera)? If so, you'll need logic to re-orient them to re-face the camera anytime the user moves the camera view, as I see you are also setting up the TrackballControls, which actually move the camera, not the scene. This means a user can change the camera view of your scene, and items you want facing the camera need to be re-orientated. That re-orientation logic needs to be placed inside your render() function.
BTW, to get an object to always face the camera:
Define it such that when not rotated, it is facing the direction you
want;
Place the object into your scene via any method you want,
including whatever hierarchical rotations or translations you want to use to get them positioned where you want; (Note, they may not be facing
where you want at this point, but that is okay at this step);
Request from three.js that it calculate the local to world space
transformations for your scene. After that, each object's world
transformation matrix contains the concatenated rotations, and
translations that transform each object from local space to world
space.
Go into each object's local-to-world transform matrix and
replace the rotation 3x3 matrix components with the identity
transformation { [1 0 0] [0 1 0] [0 0 1] }. This effectively wipes
out the rotations in world space, making all the objects you do this
to always face the camera.

Categories