Why is my SpotLight casting a perfect circle? - three.js

I am having a hard time figuring out why my three.js SpotLight is behaving the way it is. I am trying to make a flashlight for a game. I am adding the SpotLight to the scene like this:
flashLight = new THREE.SpotLight( 0xffffff, 1 );
scene.add( flashLight );
And then updating the flashlight to move in sync with the player like this:
flashLight.position.copy(player.body.position);
flashLight.target.position.set(flashLight.position.x + player.lookDirection.x,
flashLight.position.y + player.lookDirection.y,
flashLight.position.z + player.lookDirection.z);
flashLight.target.updateMatrixWorld();
For some reason, the flashlight casts a perfect circle on the surfaces that it shines on: Image
The expected behaviour is demonstrated here
Thanks!

In the demo, you can see it takes the camera position but offsets it a little
flashLight.position.copy(camera.position);
flashLight.position.x += 2;
flashLight.position.y -= 3;
flashLight.position.z -= 1;
I feel you are seeing a perfect circle, because you're viewing the beam from the same angle as its source.

Related

Threejs: Shadow appearing as box shape

I set up a simple glb viewer with three.js. The model casts and accepts shadows. The problem is that dark boxes appear once I set a spotLight. I'm not sure what the problem is.
I uploaded the project here: https://github.com/maxibenner/threejsviewer
Configuring proper shadows can sometimes be difficult.
var spotLight = new THREE.SpotLight(0xffa95c, 2)
spotLight.castShadow = true
spotLight.position.set(2,2,-2)
spotLight.angle = Math.PI * 0.1;
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 4;
spotLight.shadow.bias = - 0.002;
spotLight.shadow.mapSize.set( 1024, 1024 );
The idea is to move the spot light closer to the model and tighten the shadow camera's frustum as good as possible. The bias configuration is necessary to avoid self-shadowing artifacts. A bigger shadow map size (default is 512x512) sharpens the shadows.
As mentioned in the comment, adding an instance of CameraHelper to your scene is very helpful when optimizing shadows:
scene.add( new THREE.CameraHelper( spotLight.shadow.camera ) );

How can I rotate a background or skybox in three.js?

I need to be able to rotate the background by a specific number of degrees on the x, y, or z axes. How can I do this?
There's no rotation axis property for scene.background.
If I create a box, I can only see the images on the outside, not the inside, and it moves relative to the camera. It needs to never move, even if my distance and scale are both maxed out (like 1e+16). Bad things happen when the render far plane is too high.
Also, I would prefer not to have to pre-mirror all skybox textures if possible.
The three.js fundamentals page says nothing about this.
Other posts are 5+ years old, probably before built-in support for backgrounds.
If I set material.depthWrite = false that just makes something appear behind everything, but that doesn't keep it from moving, etc.
Sorry I have very limited knowledge of three.js, as this is not my area of expertise.
I modified/updated the code from this post Rotating a skybox in Three.js and it's working right now with only minor perspective effects (the background starts to move a tiny bit if i zoom out all the way, which is a massive zoom level)...
the background cube will be scaled to skyLength.
skyHyp is used to set the maximum viewing distance.
pxmICRF = Postive X, mirrored, International Celestial Reference Frame. it's a jpg file in base64.
this variable name describes the image, NOT where it will go in three.js CGI coordinates.
ICRF to THREE.js CGI mapping:
+x ICRF === +x CGI
-x ICRF === -x CGI
+y ICRF === -z CGI
-y ICRF === +z CGI
+z ICRF === +y CGI
-z ICRF === -y CGI
The conversion is a 90 degree rotation on the x axis.
all you need to know is the array order of left, right, top, bottom, center, back. "center" means that top and bottom need to be rotated to match above and below center.
let skyLength = 1e7;
let skyHyp = Math.sqrt(skyLength**2 + skyLength**2) + 1;
camera = new THREE.PerspectiveCamera(
25,
window.innerWidth / window.innerHeight,
0.001,
skyHyp
);
// LEF RIG TOP BOT CEN BAK
// pxmCGI nxmCGI pymCGI nymCGI pzCGI nzCGI
let walls = [pxmICRF, nxmICRF, pzmICRF, nzmICRF, nymICRF, pymICRF];
let materialArray = [];
for (let index = 0; index < 6; index++) {
let texture = new THREE.TextureLoader().load(walls[index]);
materialArray.push(new THREE.MeshBasicMaterial({
map: texture,
side: THREE.BackSide
}));
}
let geometry = new THREE.CubeGeometry(1, 1, 1);
skybox = new THREE.Mesh(geometry, materialArray);
skybox.scale.set(skyLength, skyLength, skyLength);
skybox.rotation.x = - 23.44 * Math.PI / 180;
scene.add(skybox);
now i can rotate the skybox as-needed..
i will note that using .rotation.x in some cases is unpredictable. when i need to be serious about rotating something, i use rotateOnAxis() and rotateOnWorldAxis().

Center mesh position for text-geometry

I'm struggling with the positioning of some aframe text geometry and am wondering if I'm going about this the wrong way 😅
I'm finding that when the box renders, the center point is at the minimum point of all the axises (bottom-left-close). This means the text expands more to the top-right-far than I would expect. This is different from aframe geometry entitites where the center point is at the very center of all axises.
Sorry if the above phrasing is confusing, I'm still not sure how to best describe things in a 3d space 😆
What I'm thinking I need to do is calculate the bounding box after the element has loaded and change the position to the center. I've based that approach on the answer here AFRAME text-geometry component rotation from center?.
Does that seem like the right direction? If so, I'm currently trying to do this through an aframe component
aframe.registerComponent('center-all', {
update() {
// Need to wait for the element to be loaded
setTimeout(() => {
const mesh = this.el.getObject3D('mesh');
const bbox = new THREE.Box3().setFromObject(this.el.object3D);
const offsetX = (bbox.min.x - bbox.max.x) / 2;
const offsetY = (bbox.min.y - bbox.max.y) / 2;
const offsetZ = (bbox.min.z - bbox.max.z) / 2;
mesh.position.set(offsetX, offsetY, offsetZ);
}, 0);
}
});
This code illustrates the problem I'm seeing
This code shows my attempted solution
This code (with the translation hard coded) is more like what I would like
TextGeometry and TextBufferGeometry are both subclasses of the respective geometry classes, and so both have the boundingBox property. You just need to compute it, then get its center point:
textGeo.computeBoundingBox();
const center = textGeo.boundingBox.getCenter(new Vector3());
Then center will accurately reflect the center of the geometry, in local space. If you need it in global space, you will need to apply the matrix of the mesh that contains textGeo to the center vector, e.g.
textMesh.updateMatrixWorld();
center.applyMatrix4(textMesh.matrixWorld);

Nearby culling in Three.js despite camera not being near face

I've run into an issue after switching to a logarithmic depth buffer in Three.js. Everything runs nicely except for nearby culling of the ground as described in the following photos:
As you can see, the camera is elevated above the ground significantly. The character box that is shown is about 2 units above the ground, and my camera is set up as such:
var WIDTH = window.innerWidth
, HEIGHT = window.innerHeight;
var VIEW_ANGLE = 70
, ASPECT = WIDTH / HEIGHT
, NEAR = 1e-6
, FAR = 9000;
var aspect = WIDTH / HEIGHT;
var camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
camera.rotation.order = 'YXZ';
So my NEAR parameter is nowhere near 2, the distance between the camera and the ground. You can see in the second image that I even move up the camera with my PointerLockControls and still run into the issue.
Can anyone diagnose my issue?
I also tested my issue by seeing if this bug occurred with a static camera as well. It does.
Additionally, this problem only happens with the logarithmic depth buffer, as it doesn't happen with the default depth buffer.
I have my camera as a child to a controls object, which is defined as follows:
controls = new THREE.PointerLockControls(camera);
controls.getObject().position.set(strtx, 50, strtz);
scene.add(controls.getObject());
camera.position.z += 2;
camera.position.y += .1;
Here's the relevant code for PointerLockControls:
var pitchObject, yawObject;
var v = new THREE.Vector3(0, 0, -1);
THREE.PointerLockControls = function(camera){
var scope = this;
camera.rotation.set(0, 0, 0);
pitchObject = new THREE.Object3D();
pitchObject.rotation.x -= 0.3;
pitchObject.add(camera);
yawObject = new THREE.Object3D();
yawObject.position.y = 10;
yawObject.add(pitchObject);
var PI_2 = Math.PI / 2;
var onMouseMove = function(event){
if (scope.enabled === false) return;
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
yawObject.rotation.y -= movementX * 0.002;
pitchObject.rotation.x -= movementY * 0.002;
pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
};
this.dispose = function() {
document.removeEventListener( 'mousemove', onMouseMove, false );
};
document.addEventListener( 'mousemove', onMouseMove, false );
this.enabled = false;
this.getObject = function () {
return yawObject;
};
this.getDirection = function() {
// assumes the camera itself is not rotated
var rotation = new THREE.Euler(0, 0, 0, "YXZ");
var direction = new THREE.Vector3(0, 0, -1);
return function() {
rotation.set(pitchObject.rotation.x, yawObject.rotation.y, 0);
v.copy(direction).applyEuler(rotation);
return v;
};
}();
};
You'll also notice that it's only the ground that is being culled, not other objects
Edit:
I've whipped up an isolated environment that shows the larger issue. In the first image, I have a flat PlaneBufferGeometry that has 400 segments for both width and height, defined by var g = new THREE.PlaneBufferGeometry(380, 380, 400, 400);. Even getting very close to the surface, no clipping is present:
However, if I provide only 1 segment, var g = new THREE.PlaneBufferGeometry(380, 380, 1, 1);, the clipping is present
I'm not sure if this intended in Three.js/WebGL, but it seems that I'll need to do something to work around it.
I don't think this is a bug, I think this is a feature of how the depthbuffer in the different settings works. Look at this example. On the right, the depthbuffer can't make up its mind between the letters in "microscopic" and the sphere. This is because it has lower precision at very small scales and starts doing rounding that oscilates between one object and another, and favoring draw order over z-depth.
It's always a tradeoff. If you want to forgo this issue, you can try raising the scale of your scene overall, so that the 'near' of the camera will never be so close to something that it can round it off - so just work in a number range that won't be rounded in the exponential model of the logarithmic z-buffer.
Also another question - how is the blue defined, because maybe what you're seeing is not clipping from being too close, but confusion between whether blue or the ground is closer. If it's just a blue box encompassing everything, you could try making it bigger and more distant from the ground.
EDIT:
Okay, this looks like it should work. so I would start looking for edge cases. What can you do to change the scene so that it does work? What can you do to make other things start breaking?
try moving the landscape far down/ far up (does the issue persist when looking up instead of down at it, does it persist even when it's unquestionably far away?)
try rotating the landscape
try changing the camera FOV
try changing the camera far plane
try changing the camera near plane from 1e-x notation to .000001, .0001,.01,.1, etc. see what effect it has.
console.log the camera object in your render function, and make sure that the fov, near, far etc, is as you set on setup and that it's not being overwritten and reset to default. check what it prints out in chrome's developer tools, you can browse the whole object, check position, parent name, all that stuff.
basically i don't see a blatant mistake, so I would guess it's something hard to spot, or it's working exactly as it should. Figure out what you can do to improve the effect/ make it worse, and that will clarify a direction to go.
A good rule of thumb for debugging is to try and just take things to an extreme, without trying to fix it, or keep the code true to its purpose, and just see in what way it breaks further/changes. report back when you find something.

Auto rotate camera change direction when limit

How to make camera auto rotate right and up, if it reach the limit so it rotate right and down like this demo (http://carvisualizer.plus360degrees.com/threejs/)
If you want to get a smooth up-and-down with easing, you can use a sine wave. The core part of the animation looks like this:
angle += speed;
camera.position.y = centerY + (Math.sin(angle) * waveHeight);
I made a a JSFiddle you can check out. You can play with the centerY, speed, and waveHeight properties to get the feel you want.
The example also has a quick-and-dirty way to spin around an object.
you can just add some conditions in render function..
if(camera.rotation.x <= 100)
camera.rotation.x +=0.01;
else if(camera.rotation.x > 100)
camera.rotation.x -= 0.01;
have a try in your code..

Resources