ThreeJS rotate around axis - javascript

I want to rotate this cube around the light blue axis. I works If I change the rotation around THREE.Vector3(0,0,0) instead of THREE.Vector3(0.4,0,0.9)
I don't know why the cubes shape changes and why it gets smaller with more iterations
An fiddle showing this problem (please ignore the crappy implementation. I just changed a old one)
So this is how I do the rotation:
function rotate(deg) {
_initTranslation = new THREE.Vector3();
_initRotation = new THREE.Quaternion();
_initScale = new THREE.Vector3();
rotateMatrix = new THREE.Matrix4();
cube.matrix.decompose(_initTranslation, _initRotation, _initScale);
cube.matrix = rotateMatrix.compose(_initTranslation, new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0.4,1,0.9), THREE.Math.degToRad(deg)), _initScale);
cube.matrixAutoUpdate = false;
cube.matrixWorldNeedsUpdate = true;
}
Maybe someone knows what I did wrong.
var renderer, scene, camera, controls;
var geometry, material, line, vertices, last, _initTranslation, _initRotation, initScale, rotateMatrix;
var deg = 0;
init();
animate();
function init() {
document.body.style.cssText = 'margin: 0; overflow: hidden;' ;
renderer = new THREE.WebGLRenderer( { alpha: 1, antialias: true, clearColor: 0xffffff } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 5, 5, 5 );
controls = new THREE.OrbitControls( camera, renderer.domElement );
geometry2 = new THREE.BoxGeometry( .5, .5, .5 );
material2 = new THREE.MeshNormalMaterial();
cube = new THREE.Mesh( geometry2, material2 );
scene.add( cube );
material = new THREE.LineBasicMaterial({ color: 0x0077ff });
geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( 0, 0, 0) );
line = new THREE.Line( geometry, material )
scene.add( line );
var sphereAxis = new THREE.AxesHelper(20);
scene.add(sphereAxis);
addStep();
cube.lookAt(new THREE.Vector3(0.4,0,0.9));
}
function addStep() {
vertices = geometry.vertices;
last = vertices[ vertices.length - 1 ];
vertices.push(
new THREE.Vector3(0.4,0,0.9)
);
geometry = new THREE.Geometry();
geometry.vertices = vertices;
scene.remove( line );
line = new THREE.Line( geometry, material )
scene.add( line );
}
function animate() {
rotate(deg)
deg += 5
requestAnimationFrame( animate );
renderer.render(scene, camera);
controls.update();
}
function rotate(deg) {
_initTranslation = new THREE.Vector3();
_initRotation = new THREE.Quaternion();
_initScale = new THREE.Vector3();
rotateMatrix = new THREE.Matrix4();
cube.matrix.decompose(_initTranslation, _initRotation, _initScale);
cube.matrix = rotateMatrix.compose(_initTranslation, new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0.4,0,0.9), THREE.Math.degToRad(deg)), _initScale);
cube.matrixAutoUpdate = false;
cube.matrixWorldNeedsUpdate = true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

The vector component of the Quaternion has to be (normalize.). The length of a normalized vector (Unit vector) is 1.0.
In your case the length of the vector component (THREE.Vector3(0.4, 0, 0.9)) is less than 1.0:
sqrt(0.9*0.9 + 0.0*0.0 + 0.4*0.4) = sqrt(0.81 + 0.16) = sqrt(0.97) = 0.9409
This causes that the cube scales sown by time. This can be verified by logging the scaling component (console.log(_initScale)).
If you would use a vector component with a length greater than 1.0 (e.g. THREE.Vector3(0.5, 0, 0.9), then the cube will scale up.
Normalize the axis of the Quaternion, to solve the issue:
let axis = new THREE.Vector3(0.4, 0, 0.9);
let q = new THREE.Quaternion().setFromAxisAngle(axis.normalize(), THREE.Math.degToRad(deg));
cube.matrix = rotateMatrix.compose(_initTranslation, q, _initScale);
If you want that one side of the cube is aligned to the axis, in that way, that the axis is normal to the side, then this is something completely different.
You've to do 2 rotations. First rotate the cube (e.g.) continuously around the x-axis, then turn the x-axis to the target axis (0.4, 0, 0.9). Use .setFromAxisAngle` to initialize a quaternion which rotates the x-axis to the target axis:
let x_axis = new THREE.Vector3(1, 0, 0);
let axis = new THREE.Vector3(0.4, 0, 0.9);
let q_align = new THREE.Quaternion().setFromUnitVectors(x_axis, axis.normalize());
let q_rotate = new THREE.Quaternion().setFromAxisAngle(x_axis, THREE.Math.degToRad(deg));
let q_final = q_align.clone().multiply(q_rotate);
cube.matrix = rotateMatrix.compose(_initTranslation, q, _initScale);
See the example, which compares the 2 different behavior:
var renderer, scene, camera, controls;
var geometry, material, line, vertices, last, _initTranslation, _initRotation, initScale, rotateMatrix;
var deg = 0;
init();
animate();
function init() {
document.body.style.cssText = 'margin: 0; overflow: hidden;' ;
renderer = new THREE.WebGLRenderer( { alpha: 1, antialias: true, clearColor: 0xffffff } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 1, 3, 3 );
controls = new THREE.OrbitControls( camera, renderer.domElement );
geometry2 = new THREE.BoxGeometry( .5, .5, .5 );
material2 = new THREE.MeshNormalMaterial();
let shift = 0.5
cube = new THREE.Mesh( geometry2, material2 );
cube.matrix.makeTranslation(shift, 0, 0);
scene.add( cube );
cube2 = new THREE.Mesh( geometry2, material2 );
cube2.matrix.makeTranslation(-shift, 0, 0);
scene.add( cube2 );
material = new THREE.LineBasicMaterial({ color: 0x0077ff });
geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3(-0.4, 0, -0.9), new THREE.Vector3(0.4, 0, 0.9) );
line = new THREE.Line( geometry, material )
line.position.set(shift, 0, 0);
scene.add( line );
line2 = new THREE.Line( geometry, material )
line2.position.set(-shift, 0, 0);
scene.add( line2 );
var sphereAxis = new THREE.AxesHelper(20);
scene.add(sphereAxis);
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
}
function animate() {
rotate(deg)
deg += 5
requestAnimationFrame( animate );
renderer.render(scene, camera);
controls.update();
}
function rotate(deg) {
_initTranslation = new THREE.Vector3();
_initRotation = new THREE.Quaternion();
_initScale = new THREE.Vector3();
let x_axis = new THREE.Vector3(1, 0, 0);
let axis = new THREE.Vector3(0.4, 0, 0.9);
// cube
cube.matrix.decompose(_initTranslation, _initRotation, _initScale);
let q_align = new THREE.Quaternion().setFromUnitVectors(x_axis, axis.normalize());
let q_rotate = new THREE.Quaternion().setFromAxisAngle(x_axis, THREE.Math.degToRad(deg));
let q_final = q_align.clone().multiply(q_rotate);
cube.matrix.compose(_initTranslation, q_final, _initScale);
cube.matrixAutoUpdate = false;
cube.matrixWorldNeedsUpdate = true;
// cube2
cube2.matrix.decompose(_initTranslation, _initRotation, _initScale);
q = new THREE.Quaternion().setFromAxisAngle(axis.normalize(), THREE.Math.degToRad(deg));
cube2.matrix.compose(_initTranslation, q, _initScale);
cube2.matrixAutoUpdate = false;
cube2.matrixWorldNeedsUpdate = true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Related

spotLight not attached to camera properly

I'm still struggling with getting spotLight to stick to the camera. I can see the light but looks like it stays at one place (?).
See the video for reference
//Camera
camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 1, 0 );
//spotLight attached to camera
spotlight = new THREE.SpotLight( 0xffffff, 55 );
spotlight.angle = 0.20*(Math.PI / 3);
spotlight.penumbra = 0.1;
spotlight.decay = 2;
spotlight.distance = 200;
camera.add( spotlight);
camera.add( spotlight.target );
spotlight.target.position.set( 0, 0, 1 );
spotlight.target=camera;
spotlight.position.copy( camera.position );
controls = new PointerLockControls( camera, document.body );
//adding first person camera from PointerLockControls
scene.add( controls.getObject() );
I also tried grouping camera and spotlight:
const group = new THREE.Group();
group.add(camera);
group.add(spotlight);
spotlight.target=camera;
spotlight.position.copy( camera.position );
controls = new PointerLockControls( group, document.body );
but that did not work either. What should I change? What's missing here?
//edit this is what my current code looks like
import * as THREE from "../node_modules/three/build/three.module.js";
import { GUI } from './jsm/libs/dat.gui.module.js';
let renderer, scene, camera, gui;
let spotlight, lightHelper;
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set(0,0,1);
const boxgeometry = new THREE.BoxGeometry( 25, 25, 25 );
const boxmaterial = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const cube = new THREE.Mesh( boxgeometry, boxmaterial );
scene.add( cube );
cube.position.set(-20,0,1);
const ambient = new THREE.AmbientLight( 0xffffff, 0.2 );
scene.add(ambient);
**scene.add(camera);
spotlight = new THREE.SpotLight(0xffffff, 55, 80, 0.8*Math.PI);
camera.add(spotlight);
camera.add(spotlight.target);**
let material = new THREE.MeshPhongMaterial( { color: 0x808080, dithering: true } );
let geometry = new THREE.PlaneGeometry( 2000, 2000 );
let floor= new THREE.Mesh( geometry, material );
floor.position.set( 0, - 1, 0 );
floor.rotation.x = - Math.PI * 0.5;
floor.receiveShadow = true;
scene.add(floor);
render();
window.addEventListener( 'resize', onWindowResize );
}
function animate()
{
requestAnimationFrame( animate );
camera.rotation.y+=0.01;
renderer.render( scene, camera );
}
animate();
The setup should look like so:
let camera, scene, renderer;
init();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 1;
scene = new THREE.Scene();
scene.add(camera);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambientLight);
const spotLight = new THREE.SpotLight(0xffffff, 0.6, 0, Math.PI * 0.05);
camera.add(spotLight);
const geometry = new THREE.PlaneGeometry();
const material = new THREE.MeshPhongMaterial();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animation);
document.body.appendChild(renderer.domElement);
}
function animation(time) {
const t = time * 0.001;
camera.position.x = Math.sin(t) * 0.25;
camera.position.y = Math.cos(t) * 0.25;
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.134.0/build/three.min.js"></script>
It's important to add the spot light as a child to the camera and the camera itself to the scene.

Need help in figuring out why my 3d model is not being rendered in the browser

I am new to three.js and 3D model rendering and have been playing around with this sample code that i found on the internet. My aim here is to be able to render my 3D model using the chrome browser.
The default code is :
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( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
var animate = function () {
requestAnimationFrame( animate );
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
};
animate();
When i go to my browser and type in http://localhost:8080/ i get a cube rotating
Now i try modifying the code to
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 200;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
var keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30, 100%, 75%)'), 1.0);
keyLight.position.set(-100, 0, 100);
var fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240, 100%, 75%)'), 0.75);
fillLight.position.set(100, 0, 100);
var backLight = new THREE.DirectionalLight(0xffffff, 1.0);
backLight.position.set(100, 0, -100).normalize();
scene.add(keyLight);
scene.add(fillLight);
scene.add(backLight);
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setTexturePath('/examples/3d-obj-loader/assets/');
mtlLoader.setPath('/examples/3d-obj-loader/assets/');
mtlLoader.load('r2-d2.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('/examples/3d-obj-loader/assets/');
objLoader.load('r2-d2.obj', function (object) {
scene.add(object);
object.position.y -= 60;
});
});
var animate = function () {
requestAnimationFrame( animate );
controls.update();
renderer.render(scene, camera);
};
animate();
This should give me an image of r2-d2 model being rendered but i see a blank screen
I would really appreciate help in understanding what i am missing.

Low scales models aren’t drawn in THREE.js

I draw a sphere using this code
function sphere(cena,x,y,z,radius,colorr)
{
var sphereGeometry = new THREE.SphereGeometry( radius, 10, 10);
var materiall = new THREE.MeshBasicMaterial( { color: colorr , side: THREE.DoubleSide} );
var cir = new THREE.Mesh( sphereGeometry, materiall )
cir.position.x=x;
cir.position.y=y;
cir.position.z=z;
cena.add( cir );
return cir ;
}
Now I am drawing a sphere with radius 10^(-6)
var um=Math.pow(10,-6);
sphere(scene,0,0,0,um*3.5,'green')
Nothing is drawn. The same is for um*30. Only from um*300 the sphere appears. Camera near is set 0.000001
I need that scales, what to do?
Lines, by the way are drawn well
It depends on how you position your camera. Try it with this code:
var camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.000001, 1 );
scene = new THREE.Scene();
var um = Math.pow( 10, - 6 );
camera.position.z = um * 10;
var mesh = sphere( scene, 0, 0, 0, um * 3.5 , 'green' );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
function sphere(cena,x,y,z,radius,colorr) {
var sphereGeometry = new THREE.SphereBufferGeometry( radius, 10, 10);
var materiall = new THREE.MeshBasicMaterial( { color: colorr , side: THREE.DoubleSide} );
var cir = new THREE.Mesh( sphereGeometry, materiall );
cir.position.x=x;
cir.position.y=y;
cir.position.z=z;
cena.add( cir );
return cir;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.114/build/three.js"></script>

Strange overlapping rendering in THREE.js (and some shadow issues)

This is my second foray into the world of THREE.js, the first being a short one. I just set up this scene to try some things out. It's a flat box mesh with three box meshes sitting on it. For some reason, the boxes on top are being rendered behind certain polygons of the ground mesh. Anyone have any idea why this is? Does it have something to do with the scale of the scene, or some setting I'm missing? Thanks.
While I have you, does anyone know why the meshes aren't casting directional light shadows onto the ground mesh?
Fiddle: http://jsfiddle.net/k8E43/1/
var scene, camera, renderer;
var material;
var player, ground, mesh1, mesh2;
var directionalLight, spotLight, ambientLight;
init();
animate();
function init() {
renderer = new THREE.CanvasRenderer();
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
renderer.setSize( window.innerWidth, window.innerHeight );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.x = 0;
camera.position.y = 450;
camera.position.z = 750;
camera.lookAt( new THREE.Vector3( 0, 150, 0 ) );
material = new THREE.MeshLambertMaterial( { color: 0xffffff } );
// Meshes
player = new THREE.Mesh( new THREE.BoxGeometry( 70, 160, 70 ), material );
player.position.y = 80;
player.castShadow = true;
player.receiveShadow = true;
scene.add( player );
ground = new THREE.Mesh( new THREE.BoxGeometry( 1000, 10, 600 ), material );
ground.position.y = -5;
ground.receiveShadow = true;
scene.add( ground );
mesh1 = new THREE.Mesh( new THREE.BoxGeometry( 100, 40, 100 ), material );
mesh1.position.x = -150;
mesh1.position.y = 20;
mesh1.position.z = 100;
mesh1.castShadow = true;
mesh1.receiveShadow = true;
scene.add( mesh1 );
mesh2 = new THREE.Mesh( new THREE.BoxGeometry( 100, 200, 100 ), material );
mesh2.position.x = 150;
mesh2.position.y = 100;
mesh2.position.z = -100;
mesh2.castShadow = true;
mesh2.receiveShadow = true;
scene.add( mesh2 );
// Lights
directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.x = -1000;
directionalLight.position.y = 500;
directionalLight.position.z = -1000;
directionalLight.target.position.x = 1000;
directionalLight.target.position.y = -500;
directionalLight.target.position.z = 1000;
directionalLight.castShadow = true;
scene.add( directionalLight );
ambientLight = new THREE.AmbientLight( 0x404040 );
scene.add( ambientLight );
document.body.appendChild( renderer.domElement );
}
var lightIncreasing = false;
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}

Create a unique sphere geometry from a sphere and a cylinder Three.js

I'm trying to create a bead like object in Three.js, essentially a sphere with a cylinder through it. I can create the two independently, but I'm wondering how to match the height of the sphere and the cylinder and how to merge / intersect them, so that the result will be one geometry.
Any ideas?
Thanks!
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 );
//material
var material = new THREE.MeshNormalMaterial( {
wireframe: true
} );
//sphere
var sphere = new THREE.SphereGeometry(2,20,20);
var sphereMesh = new THREE.Mesh( sphere, material );
scene.add( sphereMesh );
//cyl
var cylinder = new THREE.CylinderGeometry(0.5, 0.5, 2, 32 );
var cylinderMesh = new THREE.Mesh( cylinder, material );
scene.add( cylinderMesh );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 5;
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
http://jsfiddle.net/RqU2v/
#gaitat, totally awesome, thanks.
here's the solution with ThreeCSG:
//sphere
var sphere = new THREE.SphereGeometry(2,20,20);
var sphereMesh = new THREE.Mesh( sphere, material );
var sphereBSP = new ThreeBSP( sphereMesh );
//cyl
var cylinder = new THREE.CylinderGeometry(0.5, 0.5, 5, 32 );
var cylinderMesh = new THREE.Mesh( cylinder, material );
var cylinderBSP = new ThreeBSP( cylinderMesh );
//result
var subtract_bsp = sphereBSP.subtract( cylinderBSP );
var result = subtract_bsp.toMesh( material );
result.geometry.computeVertexNormals();
scene.add( result );

Categories

Resources