How to set initial camera position (Three.js/VR)? - three.js

If I do not set initial position to the camera, the WEB borwser and Oculus Go browser behave differently (see images below).
const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
// camera position is Vector3(0, 0, 0)
scene.add( camera );
Pic. 1 - Initial camera position in web browser (e.g. Google Chrome)
Pic. 2 - Initial camera position in VR browser (e.g. default browser of Oculus Go)
It looks like Three.js's scene knows in which environment it runs and automatically adjust the camera position. How can I change the initial position of the camera?
Currently, I am doing somethink like this:
const cameraHolder = new Group();
cameraHolder.add(camera);
cameraHolder.position.set(0, 1, 0);
scene.add(cameraHolder);
but again, it doesn't resolve the issue with different positions in different environments.

I believe this is the behaviour of the reference space. If you use reference space type of *-floor the device knows about how tall you are. The camera y is then set to your device y position, which makes sense. As I couldn't find where to grab the device height from I just set the reference space type to local.
this.renderer.xr.setReferenceSpaceType( 'local' );
For me the scene.add(cameraHolder) was necessary, otherwise the camera wouldn't receive the position/rotation. Old HMDs had the cameras attached to a "head" model, then you would turn the head instead of the camera.

You can use
renderer.xr.addEventListener()
Example
camera = new THREE.PerspectiveCamera(35, width / height, 1, 15);
camera.position.set(0, 0.6, 3); // Set the initial Camera Position.
const cameraGroup = new THREE.Group();
cameraGroup.position.set(0, -1, 1.5); // Set the initial VR Headset Position.
//When user turn on the VR mode.
renderer.xr.addEventListener('sessionstart', function () {
scene.add(cameraGroup);
cameraGroup.add(camera);
});
//When user turn off the VR mode.
renderer.xr.addEventListener('sessionend', function () {
scene.remove(cameraGroup);
cameraGroup.remove(camera);
});

Related

THREE.js Move camera on orbit with mouse

I am trying to orbit a camera on an inclined orbit around a scene while the camera always points to the scene's origin. (Think of the camera rig installation for filming the bullet dodging scene in the first Matrix movie.)
The idea is to let the x-component of the mouse position on screen determine the position of the camera on the orbit. Hence, I created a dummy object whose position is changed in the mousemove-listener. The camera is attached to the dummy object, and the dummy object is attached to a pivot which creates the orbit inclination.
var pivot = new THREE.Object3D();
var cameraPos = new THREE.Object3D();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
cameraPos.add(camera);
pivot.rotation.y = Math.PI/4;
pivot.add(cameraPos)
I've put a minimal example together here: codepen.
The result is this erratic camera movement. Any idea WHY that happens?
I cannot figure out what I am doing wrong.
A solution
I will add that I know already how to fix it.
Step 1: Detach the camera from the dummy object cameraPos
Step 2: Set the camera position manually to the position cameraPos.
var target = new THREE.Vector3();
cameraPos.getWorldPosition(target)
camera.position.copy(target);
Outro
I really like the idea of attaching the camera to an object which follows then a prescribed path. But does attaching the camera to an object create unwanted interaction? What is the explanation for this behavior?

three.js how to allow OrbitControls to rotate around object with a static buffer geometry creating 3D spirograph effect

I am trying to create a 3d spirograph effect. In this fiddle I have a simple Points plane rotating. When you press the Enter key a transparent plane material is added in front of the camera creating a trailing effect. This is all working as intended, but I was hoping to be able to add OrbitControls to be able to rotate around the spirograph as a whole without the trailing effect created by the transparent plane being affected by the OrbitControls as well.
var fadeMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
transparent: true,
opacity: 0.01
});
var fadePlane = new THREE.PlaneBufferGeometry(10, 10);
var fadeMesh = new THREE.Mesh(fadePlane, fadeMaterial);
fadeMesh.position.z = -0.08;
fadeMesh.renderOrder = -1;
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.add(fadeMesh);
camera.position.z = 60;
The image below shows the effect from a static viewpoint (before moving the camera with OrbitControls), but as soon as I move the camera the trailing effect is also affected. Essentially I would like to be able to rotate around this without the trailing effect changing.
I think the issue is caused by the transparent plane being in front of the camera, but I'm not sure how else to create the trailing effect. Any advice on how to create this while being able to rotate around it without disturbing the trailing effect?
This is not possible with your approach. You're essentially rendering 24 points onto the canvas at a time, and the reason they stay visible on your canvas is because you're using preserveDrawingBuffer: true on your renderer. Now when you're trying to move the camera, those previous points have already been painted to the canvas but they no longer exist in 3D space. It's similar to painting with ink; once you've painted a dot, you can't re-position it.
If you want to be able to orbit the camera while maintaining the spirograph look, you'd need to re-think you approach, and not preserve the drawing buffer. I think you'd need to add 24 new points on each frame, so they always exist in 3D space. Your geometry would need to grow in quantity from 24, 48, 72, 96, 120, 144, etc...

How to make a cube to always appear in front of the XR(phone) camera?

I'm trying to make a cube mesh to be always positioned in front of the XR camera.
No matter how I move my phone camera, the cube should appear right in front of the camera showing only one side of the cube.
Firstly, I added a cube mesh to the scene in the beginning:
material = new THREE.MeshLambertMaterial({ color: 0x9797CE });
box = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), material);
box.position.set(0, 0, -3);
scene.add(box);
And then tried to draw the box in front of the XR camera:
function animate() {
let xrCamera = renderer.xr.getCamera(camera);
box.position.set(xrCamera.position.x, xrCamera.position.y, xrCamera.position.z - 3);
box.rotation.set(xrCamera.rotation.x, xrCamera.rotation.y, xrCamera.rotation.z);
renderer.render(scene, camera);
}
When I run the code, the cube appears in front of my phone camera.
But when I rotate my phone, the cube rotates itself in the same position not following the camera.
I also tried xrCamera.add(box) but it doesn't seem to work.
How can I correctly make the cube always appear still in front of the XR camera?
It's important to know that currently (r115) the transformation properties position, rotation and scale as well as the local matrix of the XR camera are not updated.
So instead of adding the box to xrCamera, add it to camera. Besides, keep in mind that WebXRManager.getCamera() is intended for internal use only and no part of the public API.
I have been solving a similar problem. I needed to get a point in front of camera using aframe API. But the challege was when the experience were on VR mode(fullscreen) and playing on movile or headset. In this context the management of the current camera is absolutely controlled by WebXR. With WebXR THREE applies headset pose to the object3D internally.
You only can use the matrixWorld of the three camera to access the camera world reference data, other properties or methods are not correct. In the case of aframe you must access to the object3D of the aframe camera entity and manage its matrixWorld. It is the only method to get correct information of the position/rotation/scale of the camera that it is move by the sensors of a movile or of a AR/VR goggles when the play is on VR/AR mode.
I use to get the in front of Camera Position With WebXR Headset Pose:
const distanceFromCamera = 250; // the depth in the screen, what ever you need
const inFrontOfCameraPosition = new AFRAME.THREE.Vector3( 0, 0, -distanceFromCamera );
const threeSceneCamera = <THREE.PerspectiveCamera>AFRAME.scenes[0].camera;
inFrontOfCameraPosition.applyMatrix4( threeSceneCamera.matrixWorld );
return { x: inFrontOfCameraPosition.x, y: inFrontOfCameraPosition.y, z: inFrontOfCameraPosition.z };

How can i achieve exact functionality like pointerlock example in threejs but move around programmatically instead of mouse

How can i achieve THREE.js pointerlock controls example functionality but move around programmatically (values from accelerometer device) instead of mouse movement?
THREE.PointerLockControls (next: Module) works this way:
Module makes a construction yawObject -> pitchObject -> camera (pitchObject is a child of yawObject and camera becomes a child of pitchObject).
You add yawObject (which you could get from Module with yourControls.getObject() function) to your scene (to keep transforms updated).
Module adds 'mousemove' event listener and updates yawObject.rotation.y and pitchObject.rotation.x when you move mouse if yourControls.enabled !== false.
Next if you are interested in actually locking cursor you could do it in your client code as in example.
And to update your camera position you could manipulate yawObject's position (yourControls.getObject().position).
So to manipulate controls without mouse you could setup Module this way:
camera.position.set(0, 0, 0);
camera.rotation.set(0, 0, 0); // THREE.PointerLockControls does this too
var myControls = new THREE.PointerLockControls(camera);
var controlsObject = myControls.getObject();
controlsObject.position.set(myEntryX, myEntryY, myEntryZ); // set starting point
controlsObject.rotation.y = myEntryYaw; // rotate yaw obj
controlsObject.children[0].rotation.x = myEntryPitch; // rotate pitch obj
scene.add(controlsObject);
and then keeping myControls.enabled = false manipulate controlsObject.position, controlsObject.rotation.y, controlsObject.children[0].rotation.x.

Get mouse clicked point's 3D coordinate in three.js

I'm new in THREE.js.
I'm trying to get 3D coordinates of point on mouse click on the object (not simple objects: Box, Sphere,..) in Canvas.
In detail, I'm working with 3D objects viewer - I have camera (THREE.PerspectiveCamera), mouse controls (rotate, zoom, move), add/remove objects (my own object, loaded using loaders for THREE.js) in scene,.. And I want to add a function, which gets 3D coordinates for clicked point in 3D.
Exactly, I want coordinates of the end point of a ray - begining from mouse click on the camera_near_window and ending to the object's point, I've clicked on..
I tried a lot of ways to do it:
Getting coordinates of point on z=0 plane -- It works fine, but it is on z=0 plane and it is not that I need, cause I have OrbitControls..
THREE.js example - clickable objects -- It uses CanvasRenderer (not WebGLRenderer) and works for a little objects (but works for my project): browser crashes when I load many objects (CanvasRenderer needs 5x more memory then WebGLRenderer).
"How to get object in WebGL 3d space from a mouse click coordinate" - I tried this one too, but raycaster.intersectObjects found nothing, intersects was an empty array (maybe it works for only simple objects like box, sphere,..).
Can anyone show me the demo code which gets 3D point coords for clicked point of clicking object in 3D, please..?
So, as I think this question is useful for someone, I'll answer it myself (I'll write my resolve):
var renderer, canvas, canvasPosition, camera, scene, rayCaster, mousePosition;
function init() {
renderer = new THREE.WebGLRenderer({ antialias: false });
canvas = renderer.domElement;
canvasPosition = $(canvas).position();
camera = new THREE.PerspectiveCamera(20, $(canvas).width() / $(canvas).height(), 0.01, 1e10);
scene = new THREE.Scene();
rayCaster = new THREE.Raycaster();
mousePosition = new THREE.Vector2();
scene.add(camera);
var myObjects = new THREE.Object3D();
// myObjects.add( your object );
// myObjects.add( your object );
// myObjects.add( your object );
myObjects.name = 'MyObj_s';
scene.add(myObjects);
};
function getClicked3DPoint(evt) {
evt.preventDefault();
mousePosition.x = ((evt.clientX - canvasPosition.left) / canvas.width) * 2 - 1;
mousePosition.y = -((evt.clientY - canvasPosition.top) / canvas.height) * 2 + 1;
rayCaster.setFromCamera(mousePosition, camera);
var intersects = rayCaster.intersectObjects(scene.getObjectByName('MyObj_s').children, true);
if (intersects.length > 0)
return intersects[0].point;
};

Resources