Setting the camera matrix manually in ThreeJS - javascript

I'm trying to manually set the matrix of a camera in a simple three.js scene. I've tried calling the matrix.set method in combination with matrixAutoUpdate = false, but whilst the scene renders initially it doesn't change over time as I was hoping. I've also tried setting the matrix with camera.matrix = with the same result. Makes me think I'm missing something about how to get the object to 'take on' the manually set values. I've also tried applyMatrix but that seems to do something else entirely.
Any advice much appreciated - thanks!
Here's a pen of the code in action:
http://codepen.io/heyscam/pen/phflL
And here's just the JS:
var WIDTH = 640;
var HEIGHT = 360;
var VIEW_ANGLE = 31.417;
var ASPECT = WIDTH / HEIGHT;
var NEAR = 0.1;
var FAR = 10000;
var $container = $('#container');
console.log($container);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH, HEIGHT);
$container.append(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR
);
scene.add(camera);
var cube = new THREE.Mesh(
new THREE.CubeGeometry(200, 200, 200)
);
scene.add(cube);
var frame = 0;
animate();
function animate() {
camera.matrixAutoUpdate = false;
camera.matrix.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 500 + (frame * 10), 0, 0, 0, 1);
render();
frame++;
requestAnimationFrame(animate);
}
function render() {
renderer.render(scene, camera);
}

After setting the camera matrix, you need to call
camera.updateMatrixWorld( true );
What you are doing is not advisable.
three.js was not designed to be used this way. It is best not to mess with an object matrix directly -- unless you really know what you are doing, and understand the inner-workings of the library.
Instead just set the camera's quaternion (or rotation), position, and scale, and let the library update the matrix.
three.js r.60

Related

THREE.js ArrowHelper not obeying intrinsic Euler angle rotation

I would like to set intrinsic rotations to a THREE.ArrowHelper. From my understanding, THREE.js natively uses intrinsic Tait-Bryan euler angles to represent 3D rotations.
In the code below, I create a unit vector representing the x-axis, THREE.Vector3(1, 0, 0).
I then rotate it about the Y and Z axis by an arbitrary amount.
Since there was some rotation about Y and Z, the X axis of the local coordinate system (which I assume points along the red vector) has also changed.
So, when I apply a rotation about X, I don't expect the arrow to move at all (except rotate in place...but that shouldn't be visible).
Instead, I see the arrow sweeping around, as if it's rotating about some arbitrary axis, and not its local x axis.
Thanks for any assistance!
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 origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, 0xff0000);
arrow.rotation.order = 'XYZ';
arrow.rotation.y = 0.5;
arrow.rotation.z = 0.5;
scene.add(arrow);
camera.position.z = 5;
var animate = function () {
requestAnimationFrame( animate );
arrow.rotation.x += 0.01;
renderer.render( scene, camera );
};
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
The problem is that ArrowHelper is doing its own special rotation math. It creates a unit arrow facing up (+Y). It the uses custom math to setup the orientation to make that line point in the given direction in local space.
You can see this if you just create the arrow and then print the rotation
var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, color);
console.log(arrow.rotation.x, arrow.rotation.y, arrow.rotation.z);
You'll see rotation.z is already set to rotate the +Y arrow to face +X so then you go and change those rotations and the arrow is no longer based facing +X.
That means manipulating the arrow via arrow.rotation won't work as expected.
If you parent the arrow to an Object3D and then rotate that object it will work as expected (or as I expect it 😅)
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 );
scene.add(new THREE.GridHelper(10, 10));
function addArrow(x, ry, rz, color) {
var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, color);
var ob = new THREE.Object3D();
ob.position.x = x;
ob.rotation.order = 'XYZ';
ob.rotation.y = ry;
ob.rotation.z = rz;
scene.add(ob);
ob.add(arrow);
return ob;
}
addArrow(-4, 0, 0, 0xFF0000);
addArrow(-2, 0, 0.5, 0x00FF00);
addArrow( 0, 0.5, 0.5, 0xFFFF00);
const arrow = addArrow( 2, 0.5, 0.5, 0x00FFFF);
camera.position.z = 6;
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();
var animate = function () {
requestAnimationFrame( animate );
arrow.rotation.x += 0.01;
renderer.render( scene, camera );
};
animate();
body { margin: 0; }
canvas { display: block; }
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/examples/js/controls/OrbitControls.js"></script>
What I expect.
Rotations are in the local coodinate system. Rotation order 'XYZ' means, assuming you only had the arrow the full matrix calculation would be
matrix = projection *
view *
obTranslation *
obXAxisRotation *
obYAxisRotation *
obZAxisRotation *
obScale *
arrowOrientation;
In any case
var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, 0xff0000);
var ob = new THREE.Object3D();
scene.add(ob);
ob.add(arrow);
ob.rotation.order = 'XYZ';
Viewed from 0, 0, 5 this gives us an arrow pointing right.
I like to look at matrices as applied from the right to the left so looking at the formula above first scale will be applied. It's 1,1,1 so no change
Next zAxisRotation is applied. 0.5 radians is about 30 degrees so the arrow is now either pointing 30 degrees up
Next yAxisRotation is applied. The 30 degree up arrow is now pointing 30 degrees back as into the distance.
Next xAxisRotation is applyed so this funkily pointed arrow will spin around x
run it and drag on the sample above to look from above. You'll see it matches the description.
So it's up to you then. You can make a +X facing ArrowHelper and parent it to an Object3D or you can just know that an ArrowHelper actually makes a +Y arrow and then set the rotations appropriately knowing that.
To be honest, I've never used THREE.js, but I'll try to visualize my point with this answer. The imaginary or world axis is represented by the gray arrow, notice how when you toggle Y and Z axes values, it affects the red arrow but not the other gray arrow, that's what I meant by the imaginary X axis is not moving.
Your red arrow is still rotating around the X axis but not its X axis, but the world's which is why when you change the arrow's Y and Z axes it looks like it's sweeping around when in actuality it just continues to rotate around the same fixed axis it was rotating around since the beginning.
Well, I really expect I didn't make more a mess than an explanation.
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 origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, 0xff0000);
var imaginaryXAxis = new THREE.ArrowHelper(xDir, origin, length+100, 0xffffff);
arrow.rotation.order = 'XYZ';
/*arrow.rotation.y = 0.5;
arrow.rotation.z = 0.5;*/
scene.add(arrow);
scene.add(imaginaryXAxis);
camera.position.z = 2;
var animate = function () {
requestAnimationFrame( animate );
arrow.rotation.x += 0.01;
renderer.render( scene, camera );
};
animate();
const yValue = arrow.rotation.y, zValue = arrow.rotation.z;
document.querySelector('button').addEventListener('click', (e) => {
e.target.classList.toggle('affected')
if(e.target.classList.contains('affected')){
arrow.rotation.y=.5;
arrow.rotation.z=.5;
e.target.textContent = "Reset Y and Z to zero";
} else {
arrow.rotation.y=yValue;
arrow.rotation.z=zValue;
e.target.textContent = "Affect Y and Z";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<button>Affect Y and Z</button>

How do I render a 2d canvas on WebGL canvas?

I am using multiple canvas'es for animating (scaling / changing opacity) images on 2D canvas'es. Just read somewhere using webgl will increase overall FPS. I have new to WEBGL and found it quite different from 2d canvases. Can some guide me how can I render my existing 2d canvases on a main webgl canvas.
Here's a demo code, depicting a webgl canvas and virtual 2d canvas with an image drawn. How can I draw this 2d canvas with image drawn on a webgl canvas?
let webglCanvas = $('#webgl-canvas')[0];
let webglContext = backgroundCanvas.getContext('webgl');
/* setting up 2d canvas */
let virtualCanvas = document.createElement('canvas');
let virtualContext = virtualCanvas.getContext('2d');
/* setting height and width */
webglCanvas.height = 100;
webglCanvas.width = 150;
virtualCanvas.height = 100;
virtualCanvas.width = 150;
/* fetching image to draw on 2d context */
let image = new Image();
image.src = "http://ahiliahomes.saibbywebdemos.online/assets/images/brown-back.jpg";
image.onload = () => drawImageOn2D()
function drawImageon2D() {
virtualContext.save();
virtualContext.clearRect(0, 0, virtualCanvas.width, virtualCanvas.height, 0, 0);
virtualContext.drawImage(image, 0, 0, image.width, image.height, 0, 0, virtualCanvas.width, virtualCanvas.height);
virtualContext.restore();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="webgl-canvas"></canvas>
If you're not opposed to using a third party library (well, I strongly recommend it !), here's how I would do it with three.js (I'm using another image because I'm encountering CORS issues with yours).
The idea is basically to create a plane on the WebGL canvas and apply the image as a texture on it. The rest is boilerplate code to make the scene be rendered (aniamte) and visible (light):
Link to fiddle
const IMG_WIDTH = 390;
const IMG_HEIGHT = 260;
var scene = new THREE.Scene();
// these are the default PerspectiveCamera initialization settings
var camera = new THREE.PerspectiveCamera( 45, IMG_WIDTH / IMG_HEIGHT, 1, 1000 );
// you'll have to play with the camera z position to get closer
// or further to the image, so render it smaller or bigger
camera.position.z = 10;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( IMG_WIDTH, IMG_HEIGHT );
// LIGHT
var light = new THREE.PointLight( 0xffffff, 1, 0 );
light.position.set(1, 1, 100 );
scene.add(light)
var loader = new THREE.TextureLoader();
// IMAGE
var planeMaterial = new THREE.MeshLambertMaterial({
map: loader.load("https://i.imgur.com/fHyEMsl.jpg")
});
var planeGeometry = new THREE.PlaneGeometry(10, 10 * (IMG_HEIGHT/IMG_WIDTH));
var mesh = new THREE.Mesh(planeGeometry, planeMaterial);
mesh.position.set(0, 0, 0);
scene.add(mesh);
document.body.appendChild( renderer.domElement );
// here is the (stereotyped) render loop
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>

Wrapping images around sphere

I've read many blogs/questions about this and didn't find the right answer. I'm creating an earth in three.js. But every time I'm mapping my texture and bump map it doesn't show. Also there aren't any console errors. It still shows my light on the sphere so the sphere is still there. But the textures won't show. :(
var scene,
camera,
light,
renderer,
earthObject;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
var angle = 45,
aspect = WIDTH / HEIGHT,
near = 0.1,
far = 3000;
//Environment
var container = document.getElementById('container');
camera = new THREE.PerspectiveCamera(angle, aspect, near, far);
camera.position.set(0, 0, 0);
scene = new THREE.Scene();
//light
scene.add(new THREE.AmbientLight(0x333333));
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5,3,5);
scene.add(light);
var earthGeo = new THREE.SphereGeometry (30, 40, 400),
earthMat = new THREE.MeshPhongMaterial();
// diffuse map
earthMat.map = THREE.ImageUtils.loadTexture('http://i317248.iris.fhict.nl/LSTE/globe/Images/globe.jpg');
// bump map
earthMat.bumpMap = THREE.ImageUtils.loadTexture('http://i317248.iris.fhict.nl/LSTE/globe/Images/bump.jpg');
earthMat.bumpScale = 8;
var earthMesh = new THREE.Mesh(earthGeo, earthMat);
earthMesh.position.set(-100, 0, 0);
earthMesh.rotation.y=5;
scene.add(earthMesh);
camera.lookAt( earthMesh.position );
//Renderer code.
var renderer = new THREE.WebGLRenderer({antialiasing : true});
renderer.setSize(WIDTH, HEIGHT);
renderer.domElement.style.position = 'relative';
container.appendChild(renderer.domElement);
renderer.autoClear = false;
renderer.shadowMapEnabled = true;
function render () {
renderer.render(scene, camera);
}
render();
Two problems in your code:
you're not waiting for the images to load before rendering the scene
you're having Cross-domain Policy problems (https://en.wikipedia.org/wiki/Same-origin_policy) so the images are actually never loaded

Clara.io exported object not visible in Three.JS canvas - Only black square

I am using Three.JS and imported an object which was exported as a JSON from clara.io. My problem is that the object is not visible in the canvas and all I get is a black square with the same size I set in the variable (400 and 300 pixels).
Here's my code:
var WIDTH = 400,
HEIGHT = 300;
// set some camera attributes
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 1,
FAR = 100000;
var $container = $('#wrapper');
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
var camera =
new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR);
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0xcccccc, 0.002);
camera.position.z = 300;
scene.add(camera);
camera.z = 100;
renderer.setSize(WIDTH, HEIGHT);
$container.append(renderer.domElement);
var ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
var loader = new THREE.ObjectLoader();
loader.load("js/suzanne-blender.json", function (obj) {
scene.add(obj)
});
renderer.render(scene, camera);
What I expect to see is the model with the material as I exported it from clari.io. Thanks.
Here's a link to the object JSON file
I just tried the JSON by loading it into the ThreeJS editor here: http://threejs.org/editor/ (just drag and drop it.) I then added a single light and dragged the light to not be exactly at the center. I could see the Suzanne model fine in the editor. This means that the issue must be in the code you are using to visualize the model. Maybe you just need to add a point light that is off to one side to light the object properly?
Best regards,
Ben Houston

Understanding Sprite distance using Raycast in three js

I'm adding sprites in a 3d scene using three.js and I want to know distance between the camera and sprite when I click on screen. So I use a Raycater.
But if I click on the sprite, the distance property of intersection object is always "wrong" (someting like 0.3), or maybe I don't know how to read and understand the result. I thought "distance" value of intersection is the distance from camera to sprite (so, in my case something like 5).
Here is a shortened version of my code :
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 sprite = new THREE.Sprite(new THREE.SpriteMaterial({color: 0x00ff00}));
scene.add(sprite);
camera.position.z = 5;
var render = function () {
requestAnimationFrame(render);
renderer.render(scene, camera);
};
render();
window.addEventListener('mousedown', function (e) {
if (e.target == renderer.domElement) {
var vector = new THREE.Vector3((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1, 0.5);
var projector = new THREE.Projector();
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects([sprite]);
console.log(intersects[0]);
}
}, false);
You can see it in action here : http://jsfiddle.net/pWr57/
So how can I have the distance form camera to a sprite ?
three.js r66
Do this, instead
console.log( raycaster.ray.origin.distanceTo( intersects[0].point ) );
Tip: Read the source code, Raycaster.js, so you know what it is doing. It is currently returning the perpendicular distance from the sprite center to the ray.
In this case, I agree that it would be better to return the distance from the camera.
three.js r.66

Categories