Centering pivot point in three.js with OrbitControls autorotate - three.js

I'm loading a .glb model into three.js, and while I have it rotating automatically using OrbitControls, I'm not able to see how to change the pivot point so the rotating model is centered.
I've seen a lot of questions on setting boxes or pivot points with rotation, but not with OrbitControls and autorotate. Is there a way for me to center the imported model using autorotate as per my code below?
var scene = new THREE.Scene();
// Load Camera Perspective
var camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 1, 8000 );
camera.position.set( 200, 100, 0 );
// Load a Renderer
var renderer = new THREE.WebGLRenderer({ alpha: false });
renderer.setClearColor( 0xC5C5C3 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Load the Orbitcontroller
var controls = new THREE.OrbitControls( camera, renderer.domElement );
camera.position.set( 60, 20, 100 );
controls.update();
controls.autoRotate = true;
controls.minDistance = 700;
controls.maxDistance = 2000;
//controls.update();
// Load Light
var ambientLight = new THREE.AmbientLight( 0xcccccc );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 0, 1, 1 ).normalize();
scene.add( directionalLight );
// glTf 2.0 Loader
var loader = new THREE.GLTFLoader();
loader.load( 'BTR.glb', function ( gltf ) {
var object = gltf.scene;
gltf.scene.scale.set( 1, 1, 1 );
gltf.scene.position.x = 0; //Position (x = right+ left-)
gltf.scene.position.y = 0; //Position (y = up+, down-)
gltf.scene.position.z = 0; //Position (z = front +, back-)
scene.add( gltf.scene );
});
function animate() {
// required if controls.enableDamping or controls.autoRotate are set to true
controls.update();
render();
requestAnimationFrame( animate );
}
function render() {
renderer.render( scene, camera );
}
render();
animate();

I think this issue can be solved by setting Controls.target (the focus point) to the center point of your glTF asset. You should be able to do this like so:
var aabb = new THREE.Box3().setFromObject( gltf.scene );
aabb.getCenter( controls.target );
controls.update();
three.js R107

Correct way to set target.
var aabb = new THREE.Box3().setFromObject( gltf.scene );
controls.target.set(aabb.getCenter());
controls.update();
it should take (aab.getCenter()), as it returns a vector3 with 3 axis values. But I found this didn't work for me, so I used the following
let aabb = new THREE.Box3().setFromObject( gltf.scene );
let aabbc = aabb.getCenter()
controls.target.set(aabbc.x, aabbc.y, aabbc.z);
controls.update();
just separating into 3 values, if you ever get stuck just console.log(whateveryourstuckwith) and read through the methods and variables and stuff, really helped me understand Three.js more

Related

Render second scene to texture not working

I'm trying to learn something new in three.js. My goal is to be able to use what a second camera sees in a separate scene as a texture for the main scene.
Or alternatively to be able to use what a second camera sees in the main scene as a texture. But i only see a black screen. I posted my code for it here. I hope someone recognizes where my mistake is, because I just can't figure it out.
In 3 steps:
texture = second camera view
material use texture
apply material ordinary to a mesh
see below
var camera, controls, scene, renderer, container, aspect;
function main() {
init();
animate();
}
function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container = document.getElementById('container');
renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild( renderer.domElement );
aspect = container.clientWidth / container.clientHeight;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x000000 );
camera = new THREE.PerspectiveCamera( 60, container.clientWidth / container.clientHeight, 1, 1000000 );
camera.position.set(0, 0, 200);
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = true;
controls.enabled = true;
controls.target.set(0, 0, 0);
//-----End three basic setups-----
var tex = generateTexture(renderer);
var plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(100.0, 100.0),
new THREE.MeshBasicMaterial({
color: 0x00caff,
map: tex,
side: THREE.DoubleSide,
})
);
scene.add(plane);
}//-------End init----------
function animate() {
requestAnimationFrame( animate );
render();
}//-------End animate----------
function render() {
camera.updateMatrixWorld();
camera.updateProjectionMatrix();
renderer.render(scene, camera);
}//-------End render----------
function generateTexture(renderer) {
var resolution = 2000;
var textureScene = new THREE.Scene();
textureScene.background = new THREE.Color(0x404040);
var renderTarget = new THREE.WebGLRenderTarget(resolution, resolution, {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat});
var textureCamera = new THREE.PerspectiveCamera(60, aspect, 0.1, 100000.0);
textureCamera.position.set(0, 0, 200);
textureCamera.lookAt(0, 0, 0);
var geometry = new THREE.SphereGeometry( 60, 32, 16 );
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
var sphere = new THREE.Mesh( geometry, material);
textureScene.add( sphere );
//---changes---
renderer.setRenderTarget( renderTarget );
renderer.clear();
renderer.render( textureScene, textureCamera );
renderer.setRenderTarget(null);
return renderTarget.texture;
//-------------
//---now it works fine---:)
}//----- End generateTexture ------
Are you copying this approach from a tutorial? What version of three.js are you using? I'm asking because you're using renderer.render(scene, camera, target, true); but the docs state that .render() only accepts two arguments, so passing a renderTarget doesn't do anything.
I recommend you copy the approach in this demo, you can see the source code by clicking on the < > icon. The essential part is as follows:
// Render first scene into texture
renderer.setRenderTarget( renderTarget );
renderer.clear();
renderer.render( textureScene, textureCamera );
// Render full scene to canvas
renderer.setRenderTarget( null );
renderer.clear();
renderer.render( scene, camera );

Three.js - Camera collision with scene

This is my first question in StackOverflow, but I've been browsing it for some years now, so I kindly ask you to bear with me. :)
I've been experimenting with Three.js to create a 3D world, and everything looked fine until I needed to control the camera. Since I'm using this lib to avoid having to do matricial calculations myself I found and added TrackballControls to my code aswell. It worked fine but then my camera could pass through the 3D shapes, and also below terrain. Unfortunately, although the movement is exactly what I needed, it didn't serve the purpose of allowing camera to respect collision.
My scene is simply the ground (thin BoxGeometry) and a cube (normal-sized BoxGeometry), and a rotating sphere that shares directionalLight position for a "sun light" effect. Some people here suggested adding Physijs to the code and simulate() physics within the scene, and adding a BoxMesh to the camera to make the physics apply to it aswell, but it simply didn't work (scene turned blank).
My working code so far (without Physijs) is:
window.onload = function() {
var renderer, scene, camera, ground, box, sphere, ambient_light, sun_light, controls;
var angle = 0;
var clock = new THREE.Clock();
init();
render();
function init(){
// Create renderer and add it to the page
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xffffff );
renderer.shadowMapEnabled = true;
document.body.appendChild( renderer.domElement );
// Create a scene to hold our awesome 3D world
scene = new THREE.Scene();
/*** 3D WORLD ***/
// Objects
ground = new THREE.Mesh(
new THREE.BoxGeometry(50, 1, 50),
new THREE.MeshLambertMaterial({ color: 0x33CC33 }),
0 // mass
);
ground.receiveShadow = true;
scene.add( ground );
box = new THREE.Mesh(
new THREE.BoxGeometry( 10, 10, 10 ),
new THREE.MeshLambertMaterial({ color: 0xDD3344 })
);
box.position.y = 5;
box.castShadow = true;
scene.add( box );
sphere = new THREE.Mesh(
new THREE.SphereGeometry( 3, 32, 32 ),
new THREE.MeshBasicMaterial({ color: 0xFFBB00 })
);
sphere.position.set( 1, 15.5, 5 );
scene.add( sphere );
// Light
ambient_light = new THREE.AmbientLight( 0x333333 );
ambient_light.mass = 0;
scene.add( ambient_light );
sun_light = new THREE.DirectionalLight( 0xBBBBBB );
sun_light.position.set( 1, 15.5, 5 );
sun_light.castShadow = true;
sun_light.shadowCameraNear = 1;
sun_light.shadowCameraFar = 100;
sun_light.shadowCameraLeft = -50;
sun_light.shadowCameraRight = 50;
sun_light.shadowCameraTop = -50;
sun_light.shadowCameraBottom = 50;
sun_light.shadowBias = -.01;
scene.add( sun_light );
// Create a camera
camera = new THREE.PerspectiveCamera(
45, // FOV
window.innerWidth / window.innerHeight, // Aspect Ratio
1, // Near plane
1000 // Far plane
);
camera.position.set( 30, 30, 30 ); // Position camera
camera.lookAt( box.position ); // Look at the scene origin
scene.add(camera);
// After swapping THREE.Mesh to Physijs.BoxMesh, this is where I'd attach a BoxMesh to the camera
window.addEventListener( 'resize', onWindowResize, false );
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 4.0;
controls.panSpeed = 0.3;
controls.staticMoving = true; // No sliding after-effects
}
function render() {
// use requestAnimationFrame to create a render loop
angle += .007;
var oscillateZ = Math.sin(angle * (Math.PI*4));
var oscillateX = -Math.cos(angle * (Math.PI*4));
//console.log(oscillateZ);
sphere.position.setZ( sphere.position.z + oscillateZ );
sphere.position.setX( sphere.position.x + oscillateX );
sun_light.position.setZ( sun_light.position.z + oscillateZ );
sun_light.position.setX( sun_light.position.x + oscillateX );
requestAnimationFrame( render );
controls.update();
renderer.render( scene, camera );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
};
Can you guys enlighten me? Thank you for your time!
#Edit
Physijs attempt

three.js shadow map shows on back side issue. Is there a work around?

I have an issue with shadow map showing on the back side of an object, I have been fighting with that for almost two days now, thinking I was surely doing something wrong.
Then I wrote this jsFiddle : http://jsfiddle.net/gui2one/ec1snfee/
code below :
// shadow map issue
var light ,mesh, renderer, scene, camera, controls;
var clock = new THREE.Clock({autoStart:true});
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.shadowMapEnabled = true;
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( -58, 10, 29 );
// controls
controls = new THREE.OrbitControls( camera );
// ambient
scene.add( new THREE.AmbientLight( 0x222222 ) );
// light
light = new THREE.SpotLight();
light.position.set( 20, 0, 0 );
//light.target.position.set(1000,0,0);
light.castShadow = true;
light.shadowBias = -0.001;
light.shadowCameraNear = 1;
light.shadowCameraFar = 100;
light.shadowMapWidth = 2048;
light.shadowMapHeight = 2048;
light.shadowCameraVisible = true;
scene.add( light );
// axes
scene.add( new THREE.AxisHelper( 5 ) );
var materialTest = new THREE.MeshPhongMaterial();
var planetTestGeo = new THREE.SphereGeometry(5);
var planetTest = new THREE.Mesh(planetTestGeo, materialTest);
planetTest.castShadow = true;
planetTest.receiveShadow = true;
scene.add(planetTest);
planetTest.position.set(-30,0,0);
var moonTestGeo = new THREE.SphereGeometry(1);
var moonTest = new THREE.Mesh(moonTestGeo, materialTest);
moonTest.castShadow = true;
moonTest.receiveShadow = true;
scene.add(moonTest);
moonTest.position.set(planetTest.position.x+10 ,planetTest.position.y-0.5 ,planetTest.position.z);
camera.lookAt(moonTest.position);
}
function animate() {
requestAnimationFrame( animate );
//controls.update();
renderer.render( scene, camera );
}
and then I noticed this question ( more than 2 years ago )
THREE.JS Shadow on opposite side of light.
Is there a work around that now ?
I am trying to make an animated solar system. And I can't use different objects , or different materials to solve this problem, because planets and moons evidently always rotate on themselves and around each other.
Easy fix:
light.shadowBias = 0.001;
instead of
light.shadowBias = -0.001;
I've seen examples on the web using negative bias. I'm not sure if that is correct behaviour.
Good luck!
UPDATE
You will see some shadow acne on the light terminal on the sphere. This appears to be an issue with three.js shadow mapping methods and is due for an update AFAIK.

Threejs r55 sprites doesn't display

I try to display a sprite with the r55 of three js version but there is nothing on the screen. Does anyone can help me finding the mistake please ? I'm not loading things locally to prevent some error. I don't understand why all 3d object work fine and not the texture or the sprites.
Thanks to all
var startTime = Date.now();
var container = document.getElementById('container');
var camera, scene, renderer;
var ambientlight,directionallight;
var texturenuage, obj1;
init();
animate();
function init() {
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 30, 901 / 727, 1, 1000 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 1000;
ambientlight = new THREE.AmbientLight(0x220066);
scene.add( ambientlight );
directionallight = new THREE.DirectionalLight( 0xff88aa );
directionallight.position.set( 40, 50, 100 ).normalize();
scene.add( directionallight );
texturenuage = new THREE.ImageUtils.loadTexture('nuage1.png');
obj1 = new THREE.Sprite( new THREE.SpriteMaterial( { color: 0xff0000, map: texturenuage, alphaTest: 0.5 } ) )
sprite.scale.set( 381, 84, 1.0 )
sprite.position.set( 100, 100, 0 );
sprite.position.normalize();
scene.add( obj1 );
renderer = new THREE.WebGLRenderer();
renderer.sortObjects = false;
renderer.setSize( 901, 727 );
container.appendChild( renderer.domElement );
}
function animate() {
render();
requestAnimationFrame( animate );
}
function render() {
renderer.render(scene, camera);
}
You probably can't see anything because the textures can't be loaded if ran locally.
Use some of the suggestions in three's wiki page on running a local server and you might start seeing textures on the screen ;-)
https://github.com/mrdoob/three.js/wiki/How-to-run-things-locally

Why lighting isn't working in Three.js?

I don't understand why the lighting does not work in my code. I downloaded a simple OBJ. file to test the OBJLoader but the model isn't affected. Before I edited the lighting more, at least the Ambient Lighting would work. Maybe the OBJ. model needs a texture?
var container, stats;
var camera, scene, renderer, controls;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 2.5;
scene.add( camera );
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 2.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.0;
controls.noZoom = false;
controls.noPan = true;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
var ambient = new THREE.AmbientLight( 0x020202 );
scene.add( ambient );
directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 1, 1, 0.5 ).normalize();
scene.add( directionalLight );
pointLight = new THREE.PointLight( 0xffaa00 );
pointLight.position.set( 0, 0, 0 );
scene.add( pointLight );
sphere = new THREE.SphereGeometry( 100, 16, 8 );
lightMesh = new THREE.Mesh( sphere, new THREE.MeshBasicMaterial( { color: 0xffaa00 } ) );
lightMesh.scale.set( 0.05, 0.05, 0.05 );
lightMesh.position = pointLight.position;
scene.add( lightMesh );
var loader = new THREE.OBJLoader();
loader.load( "originalMeanModel.obj", function ( object ) {
scene.add( object );
} );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
MeshBasicMaterial in THREE.js is like a toon shader (good for silouhette, shadow drawing or wireframe) and and is not affected by lights.
Try MeshLambertMaterial or MeshPhongMaterial
I had similar problems when using the Three.js exporter for Blender, everything appeared dark even with diffuse colors set in the original blender model and an ambient light added to the scene in the Three.js code. It turns out the fix was to edit part of the converted model file, there was a line to the effect of:
"colorAmbient" : [0, 0, 0]
which I manually changed to
"colorAmbient" : [0.75, 0.75, 0.75]
everywhere it appeared, and that fixed the problem. I bring this up because my best guess is that you are experiencing a problem similar to this. Without seeing the *.obj file it is difficult to diagnose the problem exactly, but perhaps in your model settings you could try changing the ambient color value rather than, say, the diffuse color value, which is what we normally think of when assigning color to a model.
Maybe this will help you if you are experiencing the same problem like me a few days ago, if you have no normals in your obj that's definitely somewhere to look at.
You can try to start with a MeshBasicMaterial as well just to check the vertices/ faces are ok: new THREE.MeshBasicMaterial({ color: 0x999999, wireframe: true, transparent: true, opacity: 0.85 } )
Also, as mr doob said, please consider sharing the obj you're loading.

Resources