projectVector results not between -1 and 1 (three.js) - three.js

Note: code below updated with WestLangley's fix.
I've tried several examples of projecting from 3D to 2D. However, when I try to use projectVector the result is not between -1 and 1, and so when I multiply by my window's width/height I get extravagantly large numbers (much larger than my screen resolution). Hoping that my problem is something simple. I'm using three.min.js r56, and my inner window dimensions are 1366x418.
The code below yields a projected (x,y) of: (-7.874704599380493,-13.403168320655823) for the 3D point (1200,625,100). I know I still need to multiple this result by something like half my window height and width, but the resulting (x,y) in pixels is way off the screen.
<html>
<head>
<title>Test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script type="text/javascript" src="three.min.js"></script>
</head>
<body>
<canvas id='container'></canvas>
<script>
/// GLOBAL VARIABLES
var camera, scene, renderer, container, projector;
init();
function toXYCoord (object) {
var vector = projector.projectVector(object.position.clone(), camera);
return vector;
}
/// INIT
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 20000 );
scene.add(camera);
camera.position.set(0,-1500,1500);
camera.lookAt(scene.position);
camera.updateMatrixWorld(); ///////////////// THIS IS THE FIX
projector = new THREE.Projector();
// sphere
var sphere = new THREE.Mesh( new THREE.SphereGeometry( 200, 32, 16 ), new THREE.MeshBasicMaterial({ color: 0x000000 }) );
sphere.position.set(1200,625,100);
scene.add(sphere);
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer( { canvas: container, antialias:true } );
renderer.setSize( window.innerWidth, window.innerHeight );
var testVector = toXYCoord(sphere);
console.log(window.innerWidth + "x" + window.innerHeight);
console.log("Got: (" + testVector.x + "," + testVector.y + ")");
renderer.render( scene, camera );
}
</script>
</body>
</html>

The renderer does some calculations for you that are normally done in the render loop.
They are not being done in your case because you have placed your single call to render() as the last line of your script.
You need to place the following line after camera.lookAt():
camera.updateMatrixWorld();
Alternatively, you can move your
renderer.render( scene, camera );
call so it occurs before your call to your toXYCoord() function.

Related

How to look around a scene in first person in Three.JS?

I have rendered a simple cube in Three.JS to the screen, now the way I have found online is that I need to use PointerLockControls.js lock the mouse and look around the scene. I have managed to look the cursor and hide it using this, however I am unsure how I go about implementing "look around"
Here is my code so far:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="javascript/three.js"></script>
<script src="javascript/PointerLockControls.js"></script>
<script>
var scene, camera, renderer, geometry, material, cube;
var init = function(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
geometry = new THREE.BoxGeometry();
material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
document.addEventListener("mousedown", doMouseDown, false);
//const controls = new PointerLockControls( camera, document.body);
controls = new THREE.PointerLockControls( camera, document.body );
}
function doMouseDown(event){
controls.lock();
}
function render() {
renderer.render( scene, camera );
};
init();
render();
</script>
</body>
</html>
By "looking around", I assume you want to move the camera around?
This may be a start for you.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
#js3canvas { width:800px; height:600px; }
</style>
</head>
<body>
<canvas id="js3canvas"></canvas>
<script src="javascript/three.js"></script>
<script>
var scene, camera, renderer, geometry, material, cube;
var canvaselt = document.getElementById("js3canvas");
var init = function(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, canvaselt.clientWidth/canvaselt.clientHeight, 0.1, 1000 );
renderer = new THREE.WebGLRenderer( { canvas:canvaselt } );
renderer.setSize(canvaselt.clientWidth, canvaselt.clientHeight);
geometry = new THREE.BoxGeometry();
material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
canvaselt.addEventListener("mousemove", doMouseMove, false);
}
function doMouseMove(ev) { // point camera
camera.lookAt((ev.pageX-400)/100, -(ev.pageY-300)/100, 0);
}
function animate() { // need to animate, not just render once
renderer.render( scene, camera );
requestAnimationFrame( animate );
}
init();
animate();
</script>
</body>
</html>

ThreeJS full scene minimal rotation

I want to know on how does this website (http://www.cryptarismission.com/) do it's scene face to different directions when you move your mouse over.
On their home page, it seems that when you move your mouse, the scene follows smoothly in a very small amount of axis rotation.
What is the idea behind this thing using Three.js? I know that you can rotate object, scene or the camera but I am not sure what to rotate. I'm thinking of rotating the whole scene container but I don't know if that's good.
You can either change camera position and call lookAt in every loop depending on mouse position or change whole scene rotation. Here is how first approach works.
<!DOCTYPE html>
<html lang="en">
<head>
<title>ThreeJS full scene minimal rotation</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<style>
body {
margin: 0px;
padding: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas></canvas>
</body>
<script>
var camera, scene, renderer, stats, windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
mouseX = 0,
mouseY = 0;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(30, 30, 30)
scene = new THREE.Scene();
scene.add(new THREE.Mesh(
new THREE.BoxGeometry(5, 5, 5),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
));
renderer = new THREE.WebGLRenderer({ antialias: true, canvas: document.querySelector('canvas') });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
window.addEventListener('mousemove', onDocumentMouseMove, false);
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 10;
mouseY = (event.clientY - windowHalfY) / 10;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
camera.position.x += (mouseX - camera.position.x) * .05;
camera.position.y += (-mouseY - camera.position.y) * .05;
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
</script>
</html>

three.js Particles closer to camera appears larger in Orthographic Camera

It seems that with OrthographicCamera, if a particle (Points) is closer to the camera it will just appear to be larger. While, for example, BoxGeometrys with same size will always be in the same size, regardless of their distances to the camera.
See the sample below, where the green & red cubes have the same size but different distances to camera, so do the two white particles:
var container, camera, controls, scene, renderer;
init();
animate();
function init() {
var container = document.getElementById('container');
camera =
new THREE.OrthographicCamera(-window.innerWidth / 2,
window.innerWidth / 2, -window.innerHeight / 2,
window.innerHeight / 2, 1, 400);
camera.position.z = 200;
controls = new THREE.OrthographicTrackballControls(camera);
controls.addEventListener('change', render);
scene = new THREE.Scene();
var light = new THREE.DirectionalLight()
light.position.set(1, 5, 10)
scene.add(light);
var light = new THREE.DirectionalLight(0xaaaaaa)
light.position.set(-10, -1, -5)
scene.add(light);
var light = new THREE.AmbientLight(0x555555)
scene.add(light);
var pGeo = new THREE.Geometry()
var pVec1 = new THREE.Vector3
var pVec2 = new THREE.Vector3
var a = 80
pGeo.vertices.push(pVec1.fromArray([-a, -a, -a]));
pGeo.vertices.push(pVec2.fromArray([a, a, a]));
scene.add(new THREE.Points(pGeo, new THREE.PointsMaterial({
size: 80
})))
var cGeo = new THREE.BoxGeometry(80, 80, 80);
var MPG1 = new THREE.MeshPhongMaterial({
color: 0xff0000
});
var cMesh1 = new THREE.Mesh(cGeo, MPG1);
cMesh1.position.set(a, -a, -a);
cMesh1.updateMatrix();
scene.add(cMesh1);
var MPG2 = new THREE.MeshPhongMaterial({
color: 0x00ff00
});
var cMesh2 = new THREE.Mesh(cGeo, MPG2);
cMesh2.position.set(-a, a, a);
cMesh2.updateMatrix();
scene.add(cMesh2);
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
renderer.render(scene, camera)
render()
}
function animate() {
requestAnimationFrame(animate);
controls.update();
}
function render() {
renderer.render(scene, camera);
}
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrthographicTrackballControls.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Particle_Orthographic</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<div id="container"></div>
</body>
</html>
I want to know, whether there is something I missed that can make particles behave in the same way as other Geometry with OrthographicCamera.
Because I am trying to visualise some aluminum matrix, OrthographicCamera is quite necessary to demonstrate the uniform crystal structure. Also the matrix would vary from 4^3 to 100^3 in volume (and number of aluminum atoms), so the scene will sort of keep changing size, and other Geometry will make it very slow.
Thanks in advance
When using THREE.PointsMaterial, to prevent the particle size from attenuating (changing size) with distance from the camera, set
material.sizeAttenuation = false;
three.js r.72

Interact/rotate object using Three.js

I am using the basic example on three.js but am unable to interact with the object to rotate via mouse interaction. Am I missing something obvious? My desired functionality is to rotate an object (such as a box) left,right,up,down, etc. The code I am using is as follows:
<!DOCTYPE html>
<script src="scripts/three.min.js"></script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My first Three.js app</title>
<style>
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<script src="scripts/three.min.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 3, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
var render = function () {
requestAnimationFrame( render );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
render();
</script>
</body>
</html>
You are missing EventListeners, your scene doesnt know about your mouse moving.
See this simple example from mr.doob on how to spin a cube with the mouse:
http://mrdoob.github.io/three.js/examples/canvas_geometry_cube.html
Similiar to the rotation on the Y-Axis (left, right) you can add rotation on the X-Axis (up, down) too.

threejs : error when deleting a Geometry or BufferGeometry in combination with a Sprite/Ortho camera

I load some objects using the ctm binary loader in Threejs r69. This returns Mesh objects using a BufferGeometry internally.
I need to remove from the scene then delete one of these Meshes, including their material/texture/geometry. According to examples and google, I should use:
scene.remove(m_mesh);
m_mesh.geometry.dispose();
m_mesh.geometry = null;
m_mesh.material.dispose();
m_mesh.material = null;
m_mesh = null;
This removes the object from the scene, but the screen goes black for a second, and I've got a GL error :
Error: WebGL: drawElements: no VBO bound to enabled vertex attrib index 2!
Looks like the above sequence (ran in my render() operation, just before drawing the scene) did not clean everything, or at least I still have references to non existing VBOs somewhere.
I've spent quite some time debugging the problem and came to the conclusion that this happens only when using an orthographic camera with a Sprite and a Perspective camera, in 2 differents scenes.
Basically, I draw a flat background using a Sprite and a dedicated scene, then my 3D scene with Meshes. If I delete a mesh from the 3D scene, then the drawing of the flat background fails.
I can't figure out why. Looks like there's a side effect of deleting a Mesh on Sprites, even if attached to different scenes.
If I comment the background drawing, then the deletion of my mesh works perfectly.
I insert below a reproduction of the problem using the standard threejs distribution. Wait about 5 seconds and you should see some GL errors on the jaavscript console.
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - geometries</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #000;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="build/three.min.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script>
if (!Detector.webgl) Detector.addGetWebGLMessage();
var container, stats;
var camera, scene, renderer;
frame_count = 0;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.y = 400;
// I need a second camera for my 2D sprite (used as a background)
// must use another texture so that it's not destroyed when removing the first object
cameraOrtho = new THREE.OrthographicCamera(window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
1, 10);
cameraOrtho.position.z = 10;
cameraOrtho.position.y = 400;
sceneBackground = new THREE.Scene();
var map1 = THREE.ImageUtils.loadTexture('textures/disturb.jpg');
var material1 = new THREE.SpriteMaterial({
map: map1
});
var spriteBackground = new THREE.Sprite(material1);
spriteBackground.scale.set(window.innerWidth, window.innerHeight, 1);
spriteBackground.position.set(window.innerWidth / 2,
window.innerHeight / 2);
sceneBackground.add(spriteBackground);
scene = new THREE.Scene();
var light;
my_object = null;
scene.add(new THREE.AmbientLight(0x404040));
light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 1, 0);
scene.add(light);
var map = THREE.ImageUtils.loadTexture('textures/UV_Grid_Sm.jpg');
map.wrapS = map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 16;
var material = new THREE.MeshLambertMaterial({
map: map,
side: THREE.DoubleSide
});
// one object is enough to demonstrate
// can't reproduce the problem with a standard SphereGeometry
// try to convert it to a BufferGeometry
var sphereGeometry = new THREE.SphereGeometry(75, 20, 10);
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(sphereGeometry);
my_object = new THREE.Mesh(bufferGeometry, material);
my_object.position.set(-400, 0, 200);
scene.add(my_object);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.autoClear = false;
renderer.autoClearDepth = false;
container.appendChild(renderer.domElement);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild(stats.domElement);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function animate() {
requestAnimationFrame(animate);
render();
stats.update();
}
function render() {
frame_count++;
var timer = Date.now() * 0.0001;
camera.position.x = Math.cos(timer) * 800;
camera.position.z = Math.sin(timer) * 800;
camera.lookAt(scene.position);
//after a few frames I want to destroy completely the object
//means remove from the scene, remove texture, material, geometry
//note that here it's a Geometry, not a BufferGeometry
//may be different
if (frame_count > 60 * 5) {
if (my_object != null) {
console.log("destroy object buffer");
scene.remove(my_object);
my_object.material.map.dispose();
my_object.material.dispose();
my_object.geometry.dispose();
my_object = null;
}
}
for (var i = 0, l = scene.children.length; i < l; i++) {
var object = scene.children[i];
object.rotation.x = timer * 5;
object.rotation.y = timer * 2.5;
}
renderer.render(sceneBackground, cameraOrtho);
renderer.clearDepth();
renderer.render(scene, camera);
}
</script>
</body>
</html>
Any hints on how to fix this issue?
Thank you,
Pascal

Resources