OrbitControls: Set default camera zoom - three.js

I'm using the OrbitControls script on my site. By default it is zoomed a little too far in.
I was wondering what the best way to manually set, or set the default camera zoom is?

Set the camera position to a comfortable distance before creating the OrbitControls:
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000)
camera.position.z = -10
// Now initialize camera controls
const controls = new THREE.OrbitControls(camera, renderer.domElement)
controls.rotateSpeed = 2.0
controls.zoomSpeed = 5
controls.panSpeed = 2
controls.enableZoom = true
// ...

Related

ThreeJs Raycaster not changing color of the touched boxes

//SCENE and CAMERA
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 0, 10);
//GEOMETRIES
const boxGeo=new THREE.BoxGeometry(2,2)
const boxMat=new THREE.MeshStandardMaterial()
const box1=new THREE.Mesh(boxGeo,boxMat)
const box2=new THREE.Mesh(boxGeo,boxMat)
const box3=new THREE.Mesh(boxGeo,boxMat)
scene.add(box1)
scene.add(box2)
scene.add(box3)
box1.position.set(-7,0,0)
box3.position.set(7,0,0)
let objectsToTest=[box1,box2,box3]
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#333333')
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
let clock=new THREE.Clock()
let tick = () => {
controls.update();
let elapsedTime=clock.getElapsedTime()
const raycaster = new THREE.Raycaster();
const rayOrigin = new THREE.Vector3(-8, 0, 0);
const rayDirection = new THREE.Vector3(10, 0, 0);
rayDirection.normalize();
raycaster.set(rayOrigin, rayDirection);
let intersects = raycaster.intersectObjects(objectsToTest);
for (let obj of objectsToTest){
obj.material.color.set('blue')
}
for (let obj of intersects) {
obj.object.material.color.set('pink')
}
box1.position.y = (Math.sin(elapsedTime/2)) * 5;
box2.position.y = (Math.sin(elapsedTime/4)) * 5;
box3.position.y = (Math.sin(-elapsedTime/3)) * 5;
renderer.render(scene, camera);
window.requestAnimationFrame(tick);
};
tick();
Initially they're all pink which I get is cuz they all start at the center.
Then all 3 of them turn pink even if one of them touches the ray, even if the rest of the boxes are far away from the ray.
How can I make only the one that is touching the ray pink?
Please ignore the below text
(Initially they're all pink which I get is cuz they all start at the center.
Then all 3 of them turn pink even if one of them touches the ray, even if the rest of the boxes are far away from the ray.
How can I make only the one that is touching the ray pink?)
You first initialize the box material with a color(a default one).
Then you make 3 boxes with the same material.
When you change the color of the material all 3 boxes change color because they all use the same material instance.
if you initialize your boxes like this it should work:
const box1=new THREE.Mesh(boxGeo,new THREE.MeshStandardMaterial())
Because then every box gets their own instance of material and you can change them independently from each other.

three.js: Casting SpotLight Shadows

all.
I'm working with three.js 127, following the documentation to add a SpotLight. However, shadows won't get shown on the scene. Below, you can find my scene. I already set the plane to receive shadow as well as other elements enabled to cast shadows yet no shadows are rendered on the scene.
const canvas = document.getElementById('webgl-output')
const init = () => {
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000,
)
const renderer = new THREE.WebGLRenderer({
canvas,
})
renderer.setClearColor(new THREE.Color(0x000000))
renderer.setSize(window.innerWidth, window.innerHeight)
const planeGeometry = new THREE.PlaneGeometry(60, 20)
const planeMaterial = new THREE.MeshLambertMaterial({
color: 0xaaaaaa,
})
const plane = new THREE.Mesh(planeGeometry, planeMaterial)
plane.receiveShadow = true
plane.rotation.x = -0.5 * Math.PI
plane.position.x = 15
plane.position.y = 0
plane.position.z = 0
scene.add(plane)
// Creating a cube
const cubeGeometry = new THREE.BoxGeometry(4, 4, 4)
const cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000,
})
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
cube.castShadow = true
cube.position.x = -4
cube.position.y = 3
cube.position.z = 0
scene.add(cube)
// Creating a sphere
const sphereGeometry = new THREE.SphereGeometry(4, 20, 20)
const sphereMaterial = new THREE.MeshLambertMaterial({
color: 0x7777ff,
})
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
sphere.castShadow = true
sphere.position.x = 20
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)
// Position and point the camera to the center of the scene
camera.position.x = -30
camera.position.y = 12
camera.position.z = 30
camera.lookAt(scene.position)
// Add spotlight for the shadows
const spotLight = new THREE.SpotLight(0xffffff)
spotLight.position.set(8, 40, 80)
spotLight.castShadow = true
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
spotLight.shadow.camera.near = 8
spotLight.shadow.camera.far = 80
spotLight.shadow.camera.fov = 16
scene.add(spotLight)
renderer.render(scene, camera)
}
init()
Am I missing something to get the shadow rendered?
I noticed a few things from your code that may have caused this issue:
The biggest thing is that you didn't enable the shadowMap on your renderer, you can do this like so :
renderer.shadowMap.enabled = true;
This part doesn't seem to be appearing on the documentation of SpotLight which you linked, more information about this can be found on the documentation of SpotLightShadow.
After I tried this on a JS sandbox, I still didn't see the shadow, but it seems like the spotLight was simply too far away, it was set to 80 at it's 'z' position. I made the value slightly smaller (40) and it seemed to do the trick. What helped me figure it out is by using a helper and OrbitControls which makes it a little easier to debug:
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three#0.127/examples/jsm/controls/OrbitControls.js';
...
spotLight.position.set(8, 40, 40); // Third parameter (z) with value of 80 was too far away
controls = new OrbitControls(camera, renderer.domElement);
helper = new THREE.PointLightHelper(pointLight);
scene.add(helper);
Here is a JSFiddle of the fixed version: https://jsfiddle.net/tombugolya/hx61L5vp/17/

Soft shadow has an unintended offset

I'm currently working on a soft / blurred shadow effect that is casted on a plane directly under my object (just for giving it some more depth). The light source (DirectionalLight) shares the center coordinates of the object but with an offset in Y, so that it's straight above. It is pointing down to the center of the object.
I experimented a little bit with the shadow parameters of the light and found out that lowering the shadow map size gives me quite a nice soft shadow effect which would be sufficient for me. For example:
light.shadow.mapSize.width = 32;
light.shadow.mapSize.height = 32;
However, i noticed that there is an offset to the shadow which lets the observer assume that the light source is not coming directly from above:
I created this fiddle from which i created the image. As shadow type i use the PCFSoftShadowMap.
With this setup I would assume that the shadow effect is equally casted on all four sides of the cube, but it's obviously not. I also noticed that this 'offset' gets smaller when increasing the shadow map size and is barely noticable when using for example sizes like 512 or 1024.
This method would be an easy and performant solution for the desired effect, so I really appreciate any help on this
EDIT:
As stated out in the comments, tweaking the radius of the LightShadow isn't a satisfiying solution because the shadow gradient has hard edges instead of soft ones.
I think what is happening is that your shadowmap is low enough resolution, that you're seeing rounding error. If you switch back to THREE.BasicShadowMap, I think you will see that the physical lightmap pixels being hit happen to lie on the side of the object that you're seeing the larger edge, and as you move the object, the shadow will move in steps the size of the pixels on the map.
Generally in practice, you want to use a higher res lightmap, and keep its coverage area as tight around the focal point of your scene as possible to give you the most resolution from the lightmap. Then you can tweak the .radius of of the LightShadow to get the right softness.
One solution i came up with is using four light sources, all with a very slight positional offset, so that the 'shadow-offset' would come from four different directions (http://jsfiddle.net/683049eb/):
// a basic three.js scene
var container, renderer, scene, camera, controls, light, light2, light3, light4, cubeCenter, cube;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xccccff);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container = document.createElement('div');
document.body.appendChild(container);
container.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 200, 800);
camera.lookAt(scene.position);
// (camera) controls
// mouse controls: left button to rotate,
// mouse wheel to zoom, right button to pan
controls = new THREE.OrbitControls(camera, renderer.domElement);
var size = 100;
// ambient light
var ambient = new THREE.AmbientLight(0xffffff, 0.333);
scene.add(ambient);
// mesh
var cubeGeometry = new THREE.BoxGeometry(size, size, size);
var cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000
});
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.y = size / 2.0;
cube.castShadow = true;
cube.receiveShadow = false;
scene.add(cube);
// Get bounding box center
var boundingBox = new THREE.Box3().setFromObject(cube);
cubeCenter = new THREE.Vector3();
boundingBox.getCenter(cubeCenter);
var position1 = new THREE.Vector3(0, size * 2, 0.0000001);
createDirectionalLight(scene, 0.15, position1, size, cubeCenter);
var position2 = new THREE.Vector3(0, size * 2, -0.0000001);
createDirectionalLight(scene, 0.15, position2, size, cubeCenter);
var position3 = new THREE.Vector3(0.0000001, size * 2, 0);
createDirectionalLight(scene, 0.15, position3, size, cubeCenter);
var position4 = new THREE.Vector3(-0.0000001, size * 2, 0);
createDirectionalLight(scene, 0.15, position4, size, cubeCenter);
// shadow plane
var planeGeometry = new THREE.PlaneGeometry(500, 500, 100, 100);
var planeMaterial = new THREE.MeshLambertMaterial({
// opacity: 0.6,
color: 0x65bf32,
side: THREE.FrontSide
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
// events
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize(event) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function createDirectionalLight(scene, intensity, position, cameraSize, targetPosition) {
var light = new THREE.DirectionalLight(0xffffff, intensity);
light.position.set(position.x, position.y, position.z);
light.target.position.set(targetPosition.x, targetPosition.y, targetPosition.z);
light.target.updateMatrixWorld(true);
light.castShadow = true;
scene.add(light);
light.shadow.mapSize.width = 32;
light.shadow.mapSize.height = 32;
light.shadow.camera.left = -cameraSize;
light.shadow.camera.right = cameraSize;
light.shadow.camera.bottom = -cameraSize;
light.shadow.camera.top = cameraSize;
light.shadow.camera.near = 1.0;
light.shadow.camera.far = cameraSize * 3;
light.shadow.bias = 0.0001;
scene.add(new THREE.CameraHelper(light.shadow.camera));
}
<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>

Three.VRControls initial rotation

I've been doing through this Unable to change camera position when using VRControls and Three.js - VRControls integration - How to move in the scene? but it's not quite doing what I need.
I have a VR video app and I've just switched to VRControls with the WebVR polyfill from something old and custom. This is working well, however I'm really struggling to set the initial camera angle.
I.e. I want the camera to start pointing at a particular angle, and then rotate with the controls - however the controls always override this angle.
I've tried adding the camera to a Dolly Group or Persepective Camera, and it seems like I can move the camera but not set the initial viewing angle.
Here is how the camera is set up
container = document.getElementById('container');
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1024);
scene = new THREE.Scene();
target = new THREE.Vector3();
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
var vrEffect = new THREE.VREffect(renderer);
vrEffect.setSize(window.innerWidth, window.innerHeight);
var params = {
hideButton: false, // Default: false.
isUndistorted: false // Default: false.
};
manager = new WebVRManager(renderer, vrEffect, params);
dolly = new THREE.PerspectiveCamera();
dolly.add( camera );
//scene.add( dolly );
controls = new THREE.VRControls(camera);// (I've tried using Dolly here)
controls.standing = true;
And I've tried various ways to rotate the camera, dolly or scene
camera.position.y = currentScene.pan * Math.PI/180;
//controls.resetPose();
//dolly.position.x = currentScene.tilt * Math.PI/180;
//camera.updateProjectionMatrix();
I can rotate the mesh, but then all the objects inside the mesh are in the wrong place.. I could try moving them but this seems like the wrong approach to point the camera to the place I want it..
The camera was getting re-added to the scene at a later time, overriding the dolly. It's working now.

Isometric cube, size does not match up with tilesize 128x128

I've been trying for bigger parts of the night to make a export code that quickly will let me texture cubes and export them to a game i'm making, but for some reason I can't make my cube to cover the entire 128x128 width and height that I want it to have.
I have the following code:
function init(){
if( Detector.webgl ){
renderer = new THREE.WebGLRenderer({
antialias : false, // to get smoother output
preserveDrawingBuffer : true // to allow screenshot
});
renderer.setClearColorHex( 0xBBBBBB, 1 );
// uncomment if webgl is required
//}else{
// Detector.addGetWebGLMessage();
// return true;
}else{
renderer = new THREE.CanvasRenderer();
}
renderer.setSize(128,128);
document.getElementById('container').appendChild(renderer.domElement);
// add Stats.js - https://github.com/mrdoob/stats.js
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
document.body.appendChild( stats.domElement );
var zoom = 1.0;
// create a scene
scene = new THREE.Scene();
// put a camera in the scene
camera = new THREE.OrthographicCamera(WIDTH / -zoom, HEIGHT / zoom, WIDTH / zoom, HEIGHT / -zoom, -2000, 1000);
//camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set(0.45,0.45,0.45);
camera.lookAt(scene.position);
//camera.position.set(0, 0, 5);
scene.add(camera);
// create a camera contol
//cameraControls = new THREEx.DragPanControls(camera)
// transparently support window resize
THREEx.WindowResize.bind(renderer, camera);
// allow 'p' to make screenshot
THREEx.Screenshot.bindKey(renderer);
// allow 'f' to go fullscreen where this feature is supported
//if( THREEx.FullScreen.available() ){
// THREEx.FullScreen.bindKey();
// document.getElementById('inlineDoc').innerHTML += "- <i>f</i> for fullscreen";
//}
// here you add your objects
// - you will most likely replace this part by your own
//var geometry = new THREE.TorusGeometry( 1, 0.42 );
var cubeSize = 128;
var geometry = new THREE.CubeGeometry( cubeSize, cubeSize, cubeSize );
var material = new THREE.MeshNormalMaterial();
mesh= new THREE.Mesh( geometry, material );
mesh.rotation.x = 0;
mesh.rotation.y = 0;
mesh.rotation.z = 0;
scene.add( mesh );
}
I've been trying out different "zooms" but it still ends up either too big or too small.
The point with all this is to end up with a code that can generate something like this:
https://dl.dropboxusercontent.com/u/5256694/cube_ex.png
What am I doing wrong?
Kind Regards
Hiam
Instead of thinking about the parameters of THREE.OrthographicCamera as "zoom" levels, you should think of them as coordinates of boundary planes for what the camera is able to see.
Also see the answer to Three.js - Orthographic camera for more details about using orthographic cameras in Three.js

Resources