Auto rotate animation on Three.js - three.js

I'm using Three.js in order to render a 3D element.
I work with mousemove in order to rotate the scene with the movement of the mouse.
I'm looking to add an animation that slightly rotates the scene automatically.
It essentially would emulate the rotation done via mouse movement but automatically so the object doesn't appear static when loading.
Does someone know a trivial way to achieve that?
Thanks in advance,
$(function() {
var logoSrc = './assets/vector_gltf/scene.gltf';
var renderer,
scene,
camera,
holder = document.getElementById('holder');
canvas = document.getElementById('canvasLogo');
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true,
});
renderer.setSize($(holder).width(), $(holder).height());
holder.appendChild(renderer.domElement);
renderer.setClearColor( 0x000000, 0 );
renderer.setPixelRatio(window.devicePixelRatio);
// renderer.setSize(window.innerWidth, window.innerHeight);
//on resize
window.addEventListener('resize', () => {
// Update camera
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
// Update renderer
// renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setSize($(holder).width(), $(holder).height());
holder.appendChild(renderer.domElement);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000 );
scene = new THREE.Scene();
var light = new THREE.AmbientLight(0x0000ff, 1);
scene.add(light);
var light2 = new THREE.PointLight(0xff0000, 1);
scene.add(light2);
var loader = new THREE.GLTFLoader();
loader.load(logoSrc, handle_load);
var s;var mesh;
function handle_load(gltf) {
s = gltf.scene;
mesh = s.children[0].children[0].children[0];
// s.children[0].material = new THREE.MeshStandardMaterial({
// normalMap : logoSrc + 'textures/StingrayPBS1SG_normal.png',
// emissiveMap : logoSrc + 'textures/StingrayPBS1SG_emissive.png',
// metalnessMap : logoSrc + 'textures/StingrayPBS1SG_metallicRoughness.png',
// map : logoSrc + 'textures/StingrayPBS1SG_baseColor.png'
// });
scene.add(s);
s.position.y = -0.2;
s.position.z = -2;//15
}
$(".intro").on("mousemove", function(e){
mesh.rotation.set(-0.003 * (e.pageY - window.innerHeight / 20),0, -0.02 * (e.pageX - (window.innerWidth / 2) - 3.14));
//.set(0.0018 * (e.pageY - 291.45) - 1.45,0, 0.02 * (e.pageX - (window.innerWidth / 2) - 3.14))
});
//anim
const clock = new THREE.Clock();
const loop = () =>
{
const elapsedTime = clock.getElapsedTime();//delta time
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(loop);
}
loop();
});

This is what you want: http://threejs.org/examples/misc_controls_orbit.html
Include the orbit controls (after you have downloaded them):
<script src="js/controls/OrbitControls.js"></script>
Setup the variable:
var controls;
Attach the controls to the camera and add a listener:
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
and in your animate function update the controls:
controls.update();
controls.autoRotate();
That last line (autoRotate) is really what you want for the rotation, but everything else is setting up your controls.

You can use a function called animate() that is making your 3D element move automatically and combining it to a render() method.
function animate() {
requestAnimationFrame( animate );
group.rotation += 0.005;
render();
}
function render() {
renderer.render( scene, camera );
}

Related

Adding DeviceOrientationControls to a GLTF GLB object

I am using Three.JS and have gotten a .glb file to display beautifully with some mouse movement.
I'm now trying to add DeviceOrientationControls to my GLtf/GLB scene, so I can move around the scene when moving the mobile phone around, however I can't quite work it out.
No matter what I try I always get one error or another.
I have tried adapting the code from this example: https://threejs.org/examples/misc_controls_deviceorientation.html
This is my current code without any DeviceOrientationControls code added:
HTML
<!-- three.min.js r87 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<!-- GLTFLoader.js -->
<script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js#r92/examples/js/loaders/GLTFLoader.js"></script>
<!-- Orientation Controls -->
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/DeviceOrientationControls.js"></script>
<div id="overlay">
<div>
<button id="startButton">Start Demo</button>
<p>Using device orientation might require a user interaction.</p>
</div>
</div>
<div class="single-object">
<div id="container"></div>
JS
window.addEventListener("load", loadGltf, false);
window.addEventListener("resize", onWindowResize, false);
const progressBar = document.querySelector("progress");
const gltfStore = {};
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// Attach to container
container = document.getElementById( 'container' );
//Setup camera
const camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight,0.1, 1000 );
camera.position.set(0, 0, 0);
camera.lookAt(0, 0, 5);
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
mouseX = 0,
mouseY = 0;
//Re-establish camera view on window resize
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//Lighting
const lightFill = new THREE.PointLight(0xffffff, 1, 500);
lightFill.position.set(0, 0, 5);
scene.add(lightFill);
const lightKey = new THREE.PointLight(0xffffff, 1, 500);
lightKey.position.set(20, 0, 20);
scene.add(lightKey);
const loader = new THREE.GLTFLoader();
// Device Orientation
// Load the GLB file
function loadGltf() {
loader.load(
"<?php echo get_template_directory_uri();?>/objects/SM_LookDev_TextureTest_FromFBX.glb",
function(gltf) {
scene.add(gltf.scene);
mesh = gltf.scene;
gltf.scene.children[0];
gltfStore.scene = gltf.scene;
// Set listener
document.addEventListener("mousemove", onMouseMove);
}
);
container.appendChild(renderer.domElement);
// Mouse movement
function onMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 10;
mouseY = (event.clientY - windowHalfY) / 30;
}
function animate() {
requestAnimationFrame(animate);
// Adjust mouse and scene position
camera.position.x += (mouseX - camera.position.x) * .0005;
camera.position.y += (-mouseY - camera.position.y) * .0003;
camera.lookAt(0,0,10);
renderer.render(scene, camera);
}
animate();
}
I have gathered from the examples that I need to add these lines (I think):
controls = new DeviceOrientationControls( camera );
controls.update();
and the below for the start button...
var startButton = document.getElementById( 'startButton' );
startButton.addEventListener( 'click', function () {
init();
animate();
}, false );
EDIT
Below is an example of where I have tried to add the lines, but have not worked successfully.
window.addEventListener("load", loadGltf, false);
window.addEventListener("resize", onWindowResize, false);
const progressBar = document.querySelector("progress");
// Add Start Button
var startButton = document.getElementById( 'startButton' );
startButton.addEventListener( 'click', function () {
init();
animate();
}, false );
const gltfStore = {};
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// Attach to container
container = document.getElementById( 'container' );
//Setup camera
const camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight,0.1, 1000 );
camera.position.set(0, 0, 0);
camera.lookAt(0, 0, 5);
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
mouseX = 0,
mouseY = 0;
//Re-establish camera view on window resize
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//Lighting
const lightFill = new THREE.PointLight(0xffffff, 1, 500);
lightFill.position.set(0, 0, 5);
scene.add(lightFill);
const lightKey = new THREE.PointLight(0xffffff, 1, 500);
lightKey.position.set(20, 0, 20);
scene.add(lightKey);
const loader = new THREE.GLTFLoader();
// Added Device Orientation Controls
controls = new DeviceOrientationControls( camera );
// Load the GLB file
function loadGltf() {
loader.load(
"<?php echo get_template_directory_uri();?>/objects/SM_LookDev_TextureTest_FromFBX.glb",
function(gltf) {
scene.add(gltf.scene);
mesh = gltf.scene;
gltf.scene.children[0];
gltfStore.scene = gltf.scene;
// Set listener
document.addEventListener("mousemove", onMouseMove);
}
);
container.appendChild(renderer.domElement);
// Mouse movement
function onMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 10;
mouseY = (event.clientY - windowHalfY) / 30;
}
function animate() {
requestAnimationFrame(animate);
// Adjust mouse and scene position
camera.position.x += (mouseX - camera.position.x) * .0005;
camera.position.y += (-mouseY - camera.position.y) * .0003;
camera.lookAt(0,0,10);
// Add Controls Update
controls.update();
renderer.render(scene, camera);
}
animate();
}
When adding the lines like this, I get these 2 error messages:
Uncaught TypeError: Cannot read property 'addEventListener' of null
and
Uncaught ReferenceError: Cannot access 'loader' before initialization at loadGltf
So, I am unsure how to actually add these lines into my specific code to get it to work?
Any assistance would be appreciated
It's hard to tell exactly the reason why it's not working, especially since your question includes so much extraneous code. You should try to ask your questions with a minimal reproducible example.
First, you're importing your <scripts> from varying sources, and varying versions. The Three.js and GLTFLoader you're loading is r92, but the DeviceOrientationControls script doesn't specify a version, so it's loading the latest at r112, which might no longer be compatible with ThreeJS r92.
Secondly, you're using:
controls = new DeviceOrientationControls( camera );, when it should be:
controls = new THREE.DeviceOrientationControls( camera );
Finally, make sure you organize your code, and call your functions in a sequential manner. Is loadGltf() being called before you initiate const loader = new THREE.GLTFLoader()? Does startButton exist, and is it defined before you call startButton.addEventListener();?
Here is a minimal example implementing the orientation controls: Notice that both scripts are being imported from the same source with r92 to ensure compatibility.
window.addEventListener("resize", resize);
const renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")});
const camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
const controls = new THREE.DeviceOrientationControls( camera );
// Make a scene with geometry
const scene = new THREE.Scene();
const geometry = new THREE.DodecahedronBufferGeometry(100,1);
const material = new THREE.MeshBasicMaterial({
color: 0xff9900,
wireframe: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
function resize() {
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
function animate(time) {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
resize();
animate(0);
body { margin: 0; }
<canvas></canvas>
<!-- three.min.js r92 -->
<script src="https://cdn.jsdelivr.net/npm/three#0.92/build/three.min.js"></script>
<!-- Orientation Controls r92 -->
<script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js#r92/examples/js/controls/DeviceOrientationControls.js"></script>
Note:
Apple disabled gyroscope access by default since iOS 12.2, so you'll have to enable it in settings when testing. See here for a discussion on the matter.

How to change the camera position for multiple views in Three JS

I've tried to change the camera view (say: Front View, Left View, Right View, Back View, Bottom View and Top View ) in button click. I have achieved by changing the camera position for each view where I have a concern that what if camera position or the mesh position get changes. I have given the mesh position to the camera position in the initial View and the mesh disappeared from the scene.
So is there any alternate to achieve this without hard coding the camera position.
Kindly, help me out with the issue.
Here's the fiddle https://jsfiddle.net/8L10qkzt/1/ and my piece of code
var camera, scene, renderer;
var views;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 1;
scene = new THREE.Scene();
var geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
var material = new THREE.MeshNormalMaterial();
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
setTimeout(() => {
controls.enableDamping = false;
controls.reset();
}, 5000);
document.querySelector('#frontView').addEventListener('click', () => {
console.log("frontview");
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 1;
scene.add(mesh);
controls.update();
render();
});
document.querySelector('#sideView').addEventListener('click', () => {
console.log("Side View");
camera.position.x = 1;
camera.position.y = 1;
camera.position.z = 1;
scene.add(mesh);
controls.update();
render();
});
document.querySelector('#backView').addEventListener('click', () => {
console.log("Back View");
});
}
function render() {
for (var ii = 0; ii < views; ++ii) {
var view = views[ii];
var camera = view.camera;
view.updateCamera(camera, scene, mouseX, mouseY);
var left = Math.floor(windowWidth * view.left);
var top = Math.floor(windowHeight * view.top);
var width = Math.floor(windowWidth * view.width);
var height = Math.floor(windowHeight * view.height);
renderer.setViewport(left, top, width, height);
renderer.setScissor(left, top, width, height);
renderer.setScissorTest(true);
renderer.setClearColor(view.background);
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.render(scene, camera);
}
}
function animate() {
render();
requestAnimationFrame(animate);
renderer.render(scene, camera);
}

Bringing back the object to the starting position

Here I'm trying to bring back the object to the starting position in the button click. I tried with some fixing the position of the object with the static position where I have no idea where it works or not. I have searched for solutions where they say to move the camera to FOV direction axis(say calculating the x, y, z).
In detail, if I pan or rotate aa object from initial position to different position, in button click I have to undo it to the the start position.
Here's the https://jsfiddle.net/Ajay_Venkatesh/thpb8csv/1/
'use strict';
var camera, scene, renderer;
var cube, cube_geometry, cube_material;
var controls;
init();
render();
function init() {
scene = new THREE.Scene();
// renderer
renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 12;
// controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', render);
controls.enableZoom = false;
// mesh - cube
cube_geometry = new THREE.CubeGeometry(5, 5, 5);
for (var i = 0; i < cube_geometry.faces.length; i += 2) {
var color = Math.random() * 0xffffff;
cube_geometry.faces[i].color.setHex(color);
cube_geometry.faces[i + 1].color.setHex(color);
}
cube_material = new THREE.MeshLambertMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors
});
cube = new THREE.Mesh(cube_geometry, cube_material);
scene.add(cube);
// Lights
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(1, 1, 1);
scene.add(light);
var light = new THREE.DirectionalLight(0x002288);
light.position.set(-1, -1, -1);
scene.add(light);
var light = new THREE.AmbientLight(0x222222);
scene.add(light);
// events
window.addEventListener('resize', onWindowResize, false);
}
function render() {
renderer.render(scene, camera);
}
function onWindowResize(event) {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}

touch controls: repeat action until touchend

I am trying to add touch controls to a three.js scene. I want to move the camera in whatever direction the user touches. It works great using the keyboard because you can press and hold the button and the camera moves continuously. But when I try the same thing using touchstart, you have to keep tapping the screen over and over to move, you can't just hold your finger down like on a keyboard or mouse.
I looked at touchmove, but if you just tap and hold without moving, there are no new touches.
Is there something similar to holding down the keyboard or mousekey using touch events?
There is no builtin callback for a touch event which fires repeatedly like the keyboard. You can, however, simply track the start and end of the touch and then call the move method at a set interval.
First, subscribe to the correct events and set a bool to track the state:
var isTouching = false;
window.addEventListener("touchstart", () => isTouching = true);
window.addEventListener("touchend", () => isTouching = false);
In Three.js you will most likely already have a render loop (e.g. a function called "animate"). Check the state variable at every iteration and apply the movement each time. You may need to also factor in deltaTime (the duration of the last frame), to make movement framerate independent.
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
if (isTouching) {
console.log("move camera");
}
renderer.render(scene, camera);
}
Here is a snippet which shows the basic approach. Click and hold in the left or right half of the output window to move the camera.
var camera, scene, renderer, mesh, material, clock;
init();
animate();
var isTouching = false;
var mousePositionX;
window.addEventListener("mousedown", (e) => {
isTouching = true;
mousePositionX = e.clientX;
});
window.addEventListener("mouseup", (e) => isTouching = false);
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
clock = new THREE.Clock();
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
scene = new THREE.Scene();
material = new THREE.MeshPhongMaterial();
var geometry = new THREE.BoxGeometry(200, 200, 200);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var light = new THREE.AmbientLight(0x404040);
scene.add(light);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
let deltaTime = clock.getDelta();
if (isTouching) {
let speed = 200; // px per second
let movement = speed * deltaTime;
if (mousePositionX > window.innerWidth / 2) {
camera.translateX(-movement);
} else {
camera.translateX(movement);
}
}
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
padding: 0;
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js"></script>

Loading External Shaders Using THREE.JS

I am trying to make a THREE.JS scene with GLSL shaders applied, but am having trouble figuring out how to make it load my shaders. The scene appears to work, but the shaders don't do what they're supposed to. I'm using a shader loader function I found that uses pure THREE.JS instead of AJAX or Jquery.
Here is the main javascript for my scene.
var container,
renderer,
scene,
camera,
light,
mesh,
controls,
start = Date.now(),
fov = 30;
window.addEventListener( 'load', function() {
container = document.getElementById( "container" );
if(!Detector.webgl) {
Detector.addGetWebGLMessage(container);
return;
}
//get the width and height
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
//sphere params
var radius = 20,
segments = 4,
rotation = 6;
scene = new THREE.Scene();
// ASPECT RATIO
camera = new THREE.PerspectiveCamera(fov, WIDTH / HEIGHT, 1, 10000);
camera.position.z = 100;
scene.add(new THREE.AmbientLight(0x333333));
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 3, 5);
scene.add(light);
;
ShaderLoader('./shaders/vertex.vert', './shaders/fragment.frag')
material = new THREE.ShaderMaterial( {
uniforms: {
tExplosion: {
type: "t",
value: THREE.ImageUtils.loadTexture( 'images/explosion.png' )
},
time: { //float initialized to 0
type: "f",
value: 0.0
}
},
vertexShader: ShaderLoader.vertex_text,
fragmentShader: ShaderLoader.fragment_text
} );
mesh = new THREE.Mesh(
new THREE.IcosahedronGeometry( radius, segments ),
material
);
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setSize( WIDTH, HEIGHT );
renderer.setPixelRatio( window.devicePixelRatio );
//update renderer size, aspect ratio, and projectionmatrix, on resize
window.addEventListener('resize', function() {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
controls = new THREE.TrackballControls( camera );
container.appendChild( renderer.domElement );
render();
} );
function render() {
material.uniforms[ 'time' ].value = .00025 * ( Date.now() - start );
controls.update();
requestAnimationFrame( render );
renderer.render(scene, camera);
}
// This is a basic asyncronous shader loader for THREE.js.
function ShaderLoader(vertex_url, fragment_url, onLoad, onProgress, onError) {
var vertex_loader = new THREE.XHRLoader(THREE.DefaultLoadingManager);
vertex_loader.setResponseType('text');
vertex_loader.load(vertex_url, function (vertex_text) {
var fragment_loader = new THREE.XHRLoader(THREE.DefaultLoadingManager);
fragment_loader.setResponseType('text');
fragment_loader.load(fragment_url, function (fragment_text) {
onLoad(vertex_text, fragment_text);
});
}, onProgress, onError);
}
But when my scene loads, it is just a red sphere with no lighting or applied shaders... What am I doing wrong? I'm new to all of this so it is probably something easily noticeable for someone more experienced, but I have searched and searched and been experimenting and can't figure it out.
Thanks!
Try to add material.needsUpdate = true after you load your shader.
The snippet show error of the WebGL detector.
Are you loading that library?
https://github.com/mrdoob/three.js/blob/master/examples/js/Detector.js

Resources