I'm rotating a PerspectiveCamera with orbitControls around an object. Now I want to move the camera closer to the object but keep the actual rotation.
My coordinate to set the distance to the objects are output as [0,0,100].
But my CameraPosition is [-500, 96, 1772]. How can I keep the "rotation" but move the camera closer to the object to a distance of 100.
Do I need to use getAzimuthalAngle() or getPolarAngle()?
For example, like this, if you object is not in the center of a scene:
camera.position.sub(obj.position).setLength(100).add(obj.position);
If the object is in the center:
camera.position.setLength(100);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(25, 0, 100);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(100, 100));
var obj = new THREE.Mesh(new THREE.CubeGeometry(10, 10, 10), new THREE.MeshBasicMaterial({
color: "red",
wireframe: true
}));
obj.position.set(25, 0, 0);
scene.add(obj);
controls.target.copy(obj.position);
controls.update();
stepCloser.addEventListener("click", onClick, false);
function onClick() {
camera.position.sub(obj.position).setLength(100).add(obj.position);
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<button id="stepCloser" style="position: absolute;">
100 units to the object
</button>
this will work. I was trying to load the GLTF model through this code snippet.
loader.load('./demo-models/model-1/scene.gltf', function (gltf) {
gltf.position.x -= 60
scene.add(gltf.scene);
});
But that didn't work out. So after debugging for a while, I realized I could play around with some parameters of the Perspective Camera. Well initially the first argument was 75 units which made my object way too far from the screen, I had to zoom in until I could get a clear view. Some code tweaks, it works the best when I set it to 1.
var camera = new THREE.PerspectiveCamera(1, window.innerWidth / window.innerHeight, 0.1, 1000);
Related
I am trying to rotate the model. However, I have no idea why my model is not rotating because there is no error shown on the developer tools. Therefore I can not identify what causes the error. I might be missing something in my code but I'm not sure where about. Please, if anyone has any solution on this, thank you for your advice.
let scene, camera, renderer, controls, car;
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 5000);
camera.rotation.y = 45 / 180 * Math.PI;
camera.position.x = 40;
camera.position.y = 0;
camera.position.z = 40;
hlight = new THREE.AmbientLight(0x404040, 0.001);
scene.add(hlight);
directionalLight = new THREE.DirectionalLight(0x333333, 10);
directionalLight.position.set(1, 1, 0);
directionalLight.castShadow = true;
scene.add(directionalLight);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener('change');
let loader = new THREE.GLTFLoader();
loader.load('./5/3D_model.gltf', function (gltf) {
car = gltf.scene.children[0];
car.scale.set(10, 10, 10);
scene.add(gltf.scene);
renderer.render(scene, camera);
animate();
});
}
function animate() {
if (car) car.rotation.y += 0.3;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
init();
animate();
There are a couple of issues in your code:
You are setting an undefined event listener to the change event of OrbitControls. Please remove this line.
You start the animation loop twice. Remove one call of animate();.
Rotating the asset 0.3 per animation step is quite a lot. The model might rotate so fast that you don't see it. Use 0.01 for now.
The intensity of the directional light is too high. Just use the default value for now. Besides, setting castShadow to true has no effect with your current scene setup.
I am new to ThreeJS and still exploring the library.
I have a very basic example of drawing a white dot, with a simple translation force.
let dotGeometry = new THREE.Geometry();
let dotMaterial = new THREE.PointsMaterial({
size: 10,
color: 0xffffff,
});
dotGeometry.vertices.push(new THREE.Vector3(1, 0, -1));
let dot = new THREE.Points(dotGeometry, dotMaterial);
scene.add(dot);
function animate() {
requestAnimationFrame(animate);
dot.position.x += 0.01;
renderer.render(scene, camera);
}
animate();
I was wondering why the translation is pretty laggy even with a single point rendered on screen.
Is there a better way to achieve smoother transformations, am I doing something wrong?
Many thanks in advance.
Andrea
Actually you should always honor the time delta of a single animation step when transforming objects. In this way, a value like 0.1 gets a better semantic. It's 0.1 world units per second. It also makes the animation more smooth and independent of the framerate. Try this code:
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 5;
const scene = new THREE.Scene();
const clock = new THREE.Clock();
const dotGeometry = new THREE.Geometry();
const dotMaterial = new THREE.PointsMaterial({
size: 10,
color: 0xffffff,
});
dotGeometry.vertices.push(new THREE.Vector3(1, 0, -1));
const dot = new THREE.Points(dotGeometry, dotMaterial);
scene.add(dot);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
dot.position.x += 0.1 * delta;
renderer.render(scene, camera);
}
animate();
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.123/build/three.js"></script>
I'm using ArrowHelpers to model and display forces on an object. The length of the vector represents the magnitude of the force.
But: modifying the length of my arrowhelper does not seem to have any visible effect. When viewing the scene, all of my ArrowHelpers appear to have the length that was assigned to them during construction. The direction of the vector is properly updated. What am I doing wrong? Here's my code:
// construction:
ARROW_GRAVITY = new THREE.ArrowHelper( dir, origin, length, 0xDD3377 );
SCENE.add( ARROW_GRAVITY );
// updating scene:
ARROW_GRAVITY.position.copy(BULLET.position);
var dirGrav = new THREE.Vector3( 0, -1, 0 );
dirGrav.normalize(); // not necessary in this case, I know
ARROW_GRAVITY.setDirection(dirGrav);
var gravForce = PROJ_MASS * 9.8;
ARROW_GRAVITY.length = gravForce;
ARROW_GRAVITY.updateMatrix();
console.log(ARROW_GRAVITY.length); // works as expected (returns gravForce)
See the documentation for how to change the length of ArrowHelper.
arrowHelper.setLength( length );
Note: there are additional optional arguments.
three.js r.93
In addition to WestLangley's answer, .setLength() with parameters works as expected:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(2, 3, 5).setLength(4);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x101000);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var dir = new THREE.Vector3(0, 1, 0);
var arrow = new THREE.ArrowHelper(dir, scene.position, 2);
scene.add(arrow);
scene.add(new THREE.GridHelper(4, 4));
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
arrow.setLength(Math.cos(time) * 0.5 + 1.5, 0.5, 0.1);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
I'm using a THREE.Path to create a Circular path and then using a TubeGeometry to use that Path to basically create a circle with transparent fill and a stroke whose thickness I can control. My question is, how can I scale up the Circular path at runtime?
Accessing the vertices through mesh.vertices results in a very weird result, because I'm accessing the Tube's Geometry, not the Path. If I change the path, then I have to create a brand new TubeGeometry and update the Tube with the new geometry by doing mesh.geometry = newTubeGeometry, and that just doesn't work at all. Scaling the Tube itself increases it's radius as well, so that's not the right solution either.
Any ideas?
Thanks
Just changing the geometry property does not work, you have to create a new instance of THREE.Mesh. You should also call geometry.dispose() on your old geometry because the renderer internally caches the corresponding WebGLBuffer objects. More information here:
https://discourse.threejs.org/t/changing-the-geometry-of-a-mesh/306
Just an example of how you can do that trick with THREE.TorusGeometry():
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 5, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var geometry = new THREE.TorusGeometry(5, 0.5, 8, 32);
geometry.rotateX(Math.PI * -0.5);
geometry.vertices.forEach(v => {
v.initPosition = new THREE.Vector3().copy(v);
});
var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: "red",
wireframe: true
}));
scene.add(mesh);
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
geometry.vertices.forEach(v => {
v // take a vertex
.setY(0) // make it scalable in two dimensions (x, z)
.normalize() // make a normal from it
.multiplyScalar(Math.sin(time) * 2) // set the distance
.add(v.initPosition); // add the initial position of the vertex
});
geometry.verticesNeedUpdate = true;
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://threejs.org/examples/js/controls/OrbitControls.js"></script>
First image is what I want. (I even want the opposite diagonal too, but I will derive that accordingly.) Second image is a normal GridHelper.
Would this be possible by adding another grid helper but rotated along the y-axis by 45 degrees?
I cannot seem to find anything in the official docs, so I am assuming that there is an alternate approach. Any pointers on how this could be done?
My code for the GridHelper:
grid = new THREE.GridHelper(80, 15, 0x000000, 0x000000);
grid.position.y = -0.2;
scene.add(grid);
I am thinking that a possible solution may lie in EdgesGeometry. I am still unsure about how to proceed with this, since:
EdgesGeometry( geometry, thresholdAngle )
Where geometry is any geometry object.
Does the GridHelper count as a geometry object? Here are the official docs.
EDIT:
Here is a visual example of my current situation: codepen example.
Using of THREE.PlaneBufferGeometry() and a material with wireframe: true will give you the desired result.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 50, 25);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var gridGeometry = new THREE.PlaneBufferGeometry(80, 80, 15, 15);
gridGeometry.rotateY(Math.PI);
gridGeometry.rotateX(-Math.PI * .5);
var gridMaterial = new THREE.MeshBasicMaterial({
color: "black",
wireframe: true
});
var gridWithDiagonals = new THREE.Mesh(gridGeometry, gridMaterial);
scene.add(gridWithDiagonals);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>