Orbiting around the origin using a device's orientation - javascript

I am trying to replicate the functionality of Google's Cardboard Demo "Exhibit" with three.js. I took the starting example straight from the Chrome Experiments web page and just dropped in code to draw a simple triangular pyramid in the init method:
function init() {
renderer = new THREE.WebGLRenderer();
element = renderer.domElement;
container = document.getElementById('example');
container.appendChild(element);
effect = new THREE.StereoEffect(renderer);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(90, 1, 0.001, 700);
camera.position.set(0, 0, 50);
scene.add(camera);
controls = new THREE.OrbitControls(camera, element);
controls.rotateUp(Math.PI / 4);
controls.noZoom = true;
controls.noPan = true;
var geometry = new THREE.CylinderGeometry( 0, 10, 30, 4, 1 );
var material = new THREE.MeshPhongMaterial( { color:0xffffff, shading: THREE.FlatShading } );
var mesh = new THREE.Mesh( geometry, material );
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
function setOrientationControls(e) {
if (!e.alpha) {
return;
}
controls = new THREE.DeviceOrientationControls(camera);
controls.connect();
controls.update();
element.addEventListener('click', fullscreen, false);
window.removeEventListener('deviceorientation', setOrientationControls, true);
}
window.addEventListener('deviceorientation', setOrientationControls, true);
window.addEventListener('resize', resize, false);
setTimeout(resize, 1);
}
The OrbitControls method on desktop works perfectly: by dragging with the mouse, the screen orbits around the pyramid. On mobile using DeviceOrientationControls however, this effect is entirely lost and instead the camera moves freely at (0, 0, 0). I tried doing as a previous question suggested and replacing the camera with scene such that:
controls = new THREE.DeviceOrientationControls(scene);
however this does not work at all and nothing moves when the device is rotated. What do I need to change to replicate OrbitControls behavior with the motion captured by DeviceOrientationControls?

To create a deviceorientation orbit controler, like you see on this demo, http://novak.us/labs/UmDemo/; It involves modifying the existing OrbitControls.js.
The file changes can be seen in this commit on github: https://github.com/snovak/three.js/commit/f6542ab3d95b1c746ab4d39ab5d3253720830dd3
I've been meaning to do a pull request for months. Just haven't gotten around to it. Needs a bunch of clean up.
You can download my modified OrbitControls.js here (I haven't merged in months either, results may vary): https://raw.githubusercontent.com/snovak/three.js/master/examples/js/controls/OrbitControls.js
Below is how you would implement the modified OrbitControls in your own scripts:
this.controls = new THREE.OrbitControls( camera, document.getElementById('screen') ) ;
controls.tiltEnabled = true ; // default is false. You need to turn this on to control with the gyro sensor.
controls.minPolarAngle = Math.PI * 0.4; // radians
controls.maxPolarAngle = Math.PI * 0.6; // radians
controls.noZoom = true ;
// How far you can rotate on the horizontal axis, upper and lower limits.
// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
controls.minAzimuthAngle = - Math.PI * 0.1; // radians
controls.maxAzimuthAngle = Math.PI * 0.1; // radians
this.UMLogo = scene.children[1];
controls.target = UMLogo.position;
I hope that gets you where you want to be! :-)

To use the DeviceOrientationControls You must call controls.update() during your animation loop or the control will not update it's position based on device info.

Related

Three.js - how to create mirror image for the entire scene

so I'm building an AR headset that uses stereo rendering to create 3d images.
The only problem is that when the image reflects off the headset's reflectors it creates a flipped (mirror) image.
I need to take account for that in my Three.js code so I was wondering if there are 2 options:
Flip every 3d object in the scene
Or flip the camera to simulate an optically inverted (mirror image) camera
I'll post the basics of my code here:
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();
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
const stereo = new THREE.StereoCamera();
function render() {
//stereo rendering code
camera.updateWorldMatrix();
stereo.update(camera);
const size = new THREE.Vector2();
renderer.getSize(size);
renderer.setScissorTest(true);
renderer.setScissor(0, 0, size.width / 2, size.height);
renderer.setViewport(0, 0, size.width / 2, size.height);
renderer.render(scene, stereo.cameraL);
renderer.setScissor(size.width / 2, 0, size.width / 2, size.height);
renderer.setViewport(size.width / 2, 0, size.width / 2, size.height);
renderer.render(scene, stereo.cameraR);
renderer.setScissorTest(false);
}
requestAnimationFrame(render);
I've also attached some images to explain what my goal is:
It might be easy to just flip the cube using a matrix but what if dozens of more 3d objects get added to the scene?
Ideally, I'd like it if the entire camera could be flipped to simulate a mirrored image for the entire scene with respect to the stereo camera (if possible).
I would appreciate any help. It's also worth noting that I've tried manually flipping the display through Window's display settings but that did not have the outcome I was looking for.
Could you just use CSS to flip the element you're using to render the scene?
<style>
canvas {
transform: scaleX(-1);
}
</style>

How to set vertical angle of spherebuffergeometry? Why Orbit controls don't work properly when mesh is rotated?

I am having issues trying to set the default view of a panoramic image inside spherebuffergeometry. I have a mesh that makes use of this geometry and the material is an equirectangle panorama image. During runtime, the default view of the image is somewhere towards the right. I want the initial angle to be at the bottom of the camera. I tried changing the Phi angle and theta angle parameters of spherebuffergeometry. While I am able to move the default horizontal angle by changing Phi angle, the panoramic image looks weird when I change the theta angle.
I took the first two snapshots when phi angle was 0 and 100 respectively and theta angle was 0. I took the last snapshot when phi angle was 100 and theta angle was 1.
Any help would be appreciated.
Thanks
sphereBufferGeometry = new THREE.SphereGeometry(100,100,100,0,Math.PI*2,0,Math.PI); sphereBufferGeometry.scale( 1, 1, -1 );
var material = new THREE.MeshBasicMaterial( {
map: new THREE.TextureLoader().load([image] )
});
mesh = new THREE.Mesh( sphereBufferGeometry, material );
I have tried this as well.
init();
var scene,renderer,camera;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
var light = new THREE.PointLight(0xffffff, 0.8);
var light2 = new THREE.DirectionalLight( 0xffddcc, 1 );
light.position.set( 1, 0.75, 0.5 );
scene.add( light2 );
var sphereBufferGeometry = new THREE.SphereGeometry(100, 100, 100, 0, Math.PI * 2, 0, Math.PI);
sphereBufferGeometry.scale(1, 1, -1);
var material = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('https://threejs.org/examples/textures/2294472375_24a3b8ef46_o.jpg')
});
var mesh = new THREE.Mesh(sphereBufferGeometry, material);
mesh.rotation.x=1.6;
scene.add(camera);
scene.add(light);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.set(0, 0, 0.001);
orbitControls.update();
var container = document.getElementById('container');
var canvas = renderer.domElement;
canvas.setAttribute("class", "frame");
container.appendChild(canvas);
document.body.appendChild(container);
animate();
}
function animate()
{
requestAnimationFrame(animate);
renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
. As you can see, I've rotated the mesh so that the default view is looking at the floor.. if we click and drag the mouse upwards such that the window in the view is horizontal and then click and drag left or right, orbit controls don't seem to work properly in that it doesn't give a proper panoramic experience.. I would like to know how can this be fixed

Camera position for endless game in ThreeJs

I'm studying Three.js and I'm tryng to do my first game: and endless game.
I have read this article and the purpose is to do something very similar.
The protagonist (the hero) is a blue ball that rolls towards the "infinity" and must avoid some obstacles that gradually arise in front of him. The user can avoid these obstacles by guiding the ball to the left or right and jumping (the idea is to use the keyboard and in particular the left/right arrow keys and the space bar to jump).
Here is my idea:
I want to follow the idea of the article but not to copy the code (I want to understand it).
This is what I've done so far:
let sceneWidth = window.innerWidth;
let sceneHeight = window.innerHeight;
let canvas;
let camera;
let scene;
let renderer;
let dom;
let sun;
let hero;
let ground;
let clock;
let spotLight;
let ambientLight;
init();
function init() {
createScene();
showHelpers();
update();
}
/**
* Set up scene.
*/
function createScene() {
clock = new THREE.Clock();
clock.start();
scene = new THREE.Scene();
window.scene = scene;
camera = new THREE.PerspectiveCamera(60, sceneWidth / sceneHeight, 0.1, 1000);
camera.position.set(0, 0, 0);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor(0x333f47, 1);
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
renderer.setSize(sceneWidth, sceneHeight);
canvas = renderer.domElement;
document.body.appendChild(canvas);
// const orbitControls = new THREE.OrbitControls(camera, canvas);
addGround();
addHero();
addLight();
camera.position.set(0, -1, 0.6);
camera.lookAt(new THREE.Vector3(0, 0, 0));
window.addEventListener("resize", onWindowResize, false);
}
/**
* Show helper.
*/
function showHelpers() {
const axesHelper = new THREE.AxesHelper(5);
// scene.add(axesHelper);
const spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);
}
/**
* Add ground to scene.
*/
function addGround() {
const geometry = new THREE.PlaneGeometry(1, 4);
const material = new THREE.MeshLambertMaterial({
color: 0xcccccc,
side: THREE.DoubleSide
});
ground = new THREE.Mesh(geometry, material);
ground.position.set(0, 1, 0);
ground.receiveShadow = true;
scene.add(ground);
}
/**
* Add hero to scene.
*/
function addHero() {
var geometry = new THREE.SphereGeometry(0.03, 32, 32);
var material = new THREE.MeshLambertMaterial({
color: 0x3875d8,
side: THREE.DoubleSide
});
hero = new THREE.Mesh(geometry, material);
hero.receiveShadow = true;
hero.castShadow = true;
scene.add(hero);
hero.position.set(0, -0.62, 0.03);
}
/**
* Add light to scene.
*/
function addLight() {
// spot light
spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(2, 30, 0);
spotLight.angle = degToRad(10);
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 4000;
spotLight.shadow.camera.fov = 45;
scene.add(spotLight);
// ambient light
ambientLight = new THREE.AmbientLight(0x303030, 2);
scene.add(ambientLight);
}
/**
* Call game loop.
*/
function update() {
render();
requestAnimationFrame(update);
}
/**
* Render the scene.
*/
function render() {
renderer.render(scene, camera);
}
/**
* On window resize, render again the scene.
*/
function onWindowResize() {
sceneHeight = window.innerHeight;
sceneWidth = window.innerWidth;
renderer.setSize(sceneWidth, sceneHeight);
camera.aspect = sceneWidth / sceneHeight;
camera.updateProjectionMatrix();
}
/**
* Degree to radiants
*/
function degToRad(degree) {
return degree * (Math.PI / 180);
}
<script src="https://threejs.org/build/three.min.js"></script>
(JSFiddle)
I'm having several problems, the first is the position of objects and the camera.
I would like to be able to position the plane so that the minor side is positioned at the beginning of the screen (the entire plane must therefore be visible, there must not be a hidden part).
I would like the ball to be positioned horizontally in the middle and vertically almost at the beginning of the floor (in short, as shown in the figure) and with the shadow projected onto the plane. Each object must have the shadow projected onto the plane.
I'm using a spotlight and Lambert materials so the shade should be there, but there is not. Why?
I don't even understand how to position objects.
I understood that the point (0, 0, 0) is the center of the screen.
I would like the ground to be at y=0 and all the other objects are positioned above as if they were resting.
My code works but I don't know if there are better ways to handle object placement.
I would also simplify my life by assigning to sphere radius 1 and not 0.03 and then making the scene "smaller" moving the camera away as zoom-out (I think this is the trick).
So, I need help setting the scene correctly.
That is my first application in ThreeJs so any advice is welcome!
EDIT 1
I changed camera.lookAt(new THREE.Vector3(0, 0, 0)); to camera.lookAt(new THREE.Vector3(0, 0, -5)); and I added spotLight.lookAt(new THREE.Vector3(0, 0, -5));.
This is the result:
Not exactly what I want...
You're right in placing your plane and sphere at 0 on the y-axis. The problem you're having is that you're telling the camera to look straight at (0, 0, 0) when you do
camera.lookAt(0, 0, 0);
so you'll get the ball perfectly centered. What you should do is tell the camera to look a little bit ahead of the sphere. You'll have to tweak the value, but something like this should do the trick:
camera.lookAt(0, 0, -5);
Additionally, your spotlight is pointing straight ahead. When you place it at (2, 30, 0), its effects get lost. You need to point it to where you want:
spotLight.lookAt(0, 0, -5);

Ceiling Lights Effect Using THREE.JS

I don't find any good example to do that so any help would be very useful :)
I want achieve this type of light in ceiling of my house object in Three.js
rectLight = new THREE.RectAreaLight( 0xffffff, 500, 10, 10 );
rectLight.position.set( 5, 5, 0 );
rectLightHelper = new THREE.RectAreaLightHelper( rectLight );
scene.add( rectLightHelper );
I have Tried all the light type for example pointLight , DirectionalLight , SpotLight and in last i found this ReactAreaLight but still i don't achieve this type of light in my three.js scene.
You need to apply postprocessing. Try Effect Composer and BloomPass
//For adding additional effect an the top of rendering
function postprocessing() {
renderScene = new THREE.RenderPass(scene, camera);
effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight);
var copyShader = new THREE.ShaderPass(THREE.CopyShader);
copyShader.renderToScreen = true;
bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.5, 0.1, 0.85); //( resolution, strenght, radius, threshold));
composer = new THREE.EffectComposer(renderer);
composer.setSize(window.innerWidth, window.innerHeight);
composer.addPass(renderScene);
composer.addPass(effectFXAA);
composer.addPass(bloomPass);
composer.addPass(copyShader);
}
// start the render loop
function render() {
renderer.clear();
composer.render();
renderer.render(scene, camera);
composer.render();
}

How to make 3D objects jump smoothly apart from each other upon click? Three.JS

I'm very new to Three.JS and 3D web dev in general what I'm trying to do is mimic this action: https://www.youtube.com/watch?v=HWSTxPc8npk&feature=youtu.be&t=7s Essentially this is a set of 3D planes and upon click the whole stack reacts and gives space around the one that's clicked.
For now, my base case is 3 planes and figuring first out if I can click the the middle one, how do I get the others to jump back smoothly as if they were pushed rather than instant appear and disappear as they do now on the click of a button.
The long term goal is to have a separate button for every plane so that on click, the selected plane will have padding around it and the rest of the planes in stack move accordingly.
I've looked into Tween.js, and CSS3D but pretty overwhelmed as a newbie. Any tutorials or tips would be greatly appreciated!
// Our Javascript will go here.
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize(){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
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.PlaneGeometry( 3, 3, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.y = -.7;
var material2 = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
var material3 = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var plane2 = new THREE.Mesh(geometry, material2 );
plane2.rotation.y = -.7;
plane2.position.x = 1;
var plane3 = new THREE.Mesh(geometry, material3);
plane3.rotation.y = -.7;
plane3.position.x = -1;
scene.add( plane, plane2, plane3 );
camera.position.z = 5;
function render() {
requestAnimationFrame( render );
// cube.rotation.x += 0.1;
// cube.rotation.y += 0.1;
renderer.render( scene, camera );
}
render();
function clickFirst() {
TWEEN.removeAll();
var tween = new TWEEN.Tween(plane3.position).to({x: -2}, 1000).start();
tween.easing(TWEEN.Easing.Elastic.InOut);
render();
}
</script>
<button onclick="clickFirst();" style="background-color: white; z-index: 9999;">Click me</button>
First, you need to locate the 2 planes.
Second, you need to make the planes clickable:
https://threejs.org/examples/#webgl_interactive_cubes
https://github.com/josdirksen/learning-threejs/blob/master/chapter-09/02-selecting-objects.html
Third, you should use Tween.js for the transition.
after picking the right plane, make a tween for the other planes with a tween, all to move on the same Axis:
example:
createjs.Tween.get(plane3.position.z).to(
plane3.position.z + 100
, 1000, createjs.Ease.cubicOut)
If you will add some code here after starting to implement i would be able to help more.

Categories

Resources