three.js DirectionalLight and shadow cut off - javascript

As per the screenshot, shadows cast onto the THREE.PlaneGeometry(250, 380, 1, 1) below are cut off.
Steps I've taken to enable shadows
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
..
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
..
mainLight = new THREE.DirectionalLight(0xffffff, 0.5);
mainLight.position.set(50, 50, 50);
mainLight.castShadow = true;
mainLight.shadow.mapSize.width = width * window.devicePixelRatio;
mainLight.shadow.mapSize.height = width * window.devicePixelRatio;
mainLight.shadow.camera.near = 1;
mainLight.shadow.camera.far = 1000;
mainLight.shadow.camera.fov = 100;
scene.add(mainLight);
..
plane.receiveShadow = true;
..
model.castShadow = true;
model.receiveShadow = true;
I've played with different values like the shadow camera FOV and far plane values...
Is this a caveat with using DirectionalLight? I need even lighting across all of my models, as opposed to SpotLight.
I found three.js shadow cutoff but it simply suggested using a SpotLight instead and gave no explanation as to why that changes anything.
When I do use a SpotLight, I suddenly lose shadows on ground plane altogether.
--
Thanks

See the three.js documentation for DirectionalLightShadow:
This is used internally by DirectionalLights for calculating shadows.
Unlike the other shadow classes, this uses an OrthographicCamera to calculate the shadows, rather than a PerspectiveCamera. This is because light rays from a DirectionalLights are parallel.
See further DirectionalLight
A common point of confusion for directional lights is that setting the rotation has no effect. This is because three.js's DirectionalLight is the equivalent to what is often called a 'Target Direct Light' in other applications.
This means that its direction is calculated as pointing from the light's position to the target's position (as opposed to a 'Free Direct Light' that just has a rotation component).
The reason for this is to allow the light to cast shadows - the shadow camera needs a position to calculate shadows from.
This means that the area affected by the shadow is defined by the position and the camera of the light source (DirectionalLight).
Set up the camera for the mainLight and define its orthographic projection for your needs:
mainLight.shadow.camera = new THREE.OrthographicCamera( -100, 100, 100, -100, 0.5, 1000 );

Related

Artifact on 3d objects in threejs

Whenever I add a narrow 3d object like the one below to the scene, I encounter some unwanted artifacts like a repeating texture on the object's surface. It worth mentioning that everything looks fine until I switch the receive shadow property of the object to true.
to be more precise, I created a box geometry with the size of (0.35, 0.02, 0.15) then I made a MeshStandardMaterial and feed both geometry and material to a THREE.Mesh. the lightning consists of ambient light and a directional light
ideally, the object should look like this:
Here is the code for lightning, object, and material
let ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
let directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.castShadow = true;
this.directionalLight.position.set(-20, 20, 32);
scene.add(this.ambientLight);
scene.add(this.directionalLight);
let box = new THREE.BoxGeometry(0.02, 0.15,
0.35)
let material = new THREE.MeshStandardMaterial({color: 'white',
shadowSide: THREE.FrontSide, side: THREE.DoubleSide})
let mesh = new THREE.Mesh(box, material)
mesh.receiveshadow = true
mesh.castshadow = true
scene.add(mesh)
This is known as shadow acne. It happens when light hits a surface at a shallow angle. You'll probably need to make small modifications to the LightShadow.bias property. Quoting from the documentation:
Shadow map bias, how much to add or subtract from the normalized depth when deciding whether a surface is in shadow. The default is 0. Very tiny adjustments here (in the order of 0.0001) may help reduce artifacts in shadows.
Try something like: directionalLight.shadow.bias = 0.0001; and start from there, making small adjustments until the shadow acne isn't noticeable.
There's also a second parameter named LightShadow.normalBias that you could tweak.

Three.js what causes shadow acne and how to fix it

In order to let all shadows be rendered, I set shadow.camera.top / bottom / left / right to the directional light (casting shadow), but it causes shadow acne.
I try to use shadow.bias but still not right. What causes shadow acne and how to fix it?
Here is my code.
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 38, 82, 1 );
light.castShadow = true;
// light.shadow.bias = -0.001;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.camera.near = 0.1; // same as the camera
light.shadow.camera.far = 1000; // same as the camera
light.shadow.camera.top = 120;
light.shadow.camera.bottom = -120;
light.shadow.camera.left = 120;
light.shadow.camera.right = -120;
scene.add( light );
Thanks!!
Setting shadow.bias to - 0.0005 seems to remove the shadow artifacts. However, the quality of the shadows are still not good since the edges of the shadows look very blocky.
Consider setting the property renderer.shadowMap.type to THREE.PCFSoftShadowMap which will noticeable improve the shadow quality. It might also be a good idea to reduce the size of the shadow camera's frustum and only cast shadow in a certain "focus" area. Another option is to bake a high quality lighting into a light map and then apply it to the lightMap property of the city's material. You can also just increase the resolution of the shadow map to 4096 x 4096 but this will have a performance impact, especially on mobile devices.

EdgesGeometry: raycasting not accurate

I'm using EdgesGeometry on PlaneGeometry and it seems it creates a larger hitbox in mouse events. This however, isn't evident when using CircleGeometry. I have the following:
createPanel = function(width, height, widthSegments) {
var geometry = new THREE.PlaneBufferGeometry(width, height, widthSegments);
var edges = new THREE.EdgesGeometry( geometry );
var panel = new THREE.LineSegments( edges, new THREE.LineBasicMaterial({
color: 0xffffff }));
return panel;
}
var tile = createPanel(1.45, .6, 1);
Now I'm using a library called RayInput which does all the raycasting for me but imagine I'm just using a normal raycaster for mouse events. Without the edges and using just the plane, the boundaries of collision is accurate.
After adding EdgesGeometry, the vertical hitbox seems to has increased dramatically thus, the object is detected being clicked when I'm not even clicking on it. The horizontal hitbox seems to have increased only slightly. I've never used EdgesGeometry before so anyone have a clue what is going on?
Thanks in advance.
If you are raycasting against THREE.Line or THREE.LineSegments, you should set the Line.threshold parameter to a value appropriate to the scale of your scene:
raycaster.params.Line.threshold = 0.1; // default is 1
three.js r.114

Partial Equirectangular Panorama Three.js

I've got full equirectangular images working well with Three.js:
scene = new THREE.Scene();
geometry = new THREE.SphereBufferGeometry( 500, 60, 40 );
geometry.scale(-1, 1, 1);
material = new THREE.MeshBasicMaterial({ map: texture });
mesh = new THREE.Mesh(geometry, material);
mesh.rotation.y = Math.PI;
scene.add( mesh );
But my images actually only contain 180x180 degrees (half the sphere) so I'm trying to get a square texture partially applied on the spherical mesh without stretching the image across the entire sphere. I figure it has something to do with the texture.offset.xyz parameters, but I haven't been successful. While I can continue to pad my images to conform to 2x1 Equirectangular standards, I'd rather cut this step out of my processing workflow.
Below you'll find both the full equirectangular image and the square one I'm trying to get working. Does anyone have any clues on how to accomplish this? Thanks!
SphereBufferGeometry has more optional parameters:
SphereBufferGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
radius — sphere radius. Default is 50.
widthSegments — number of horizontal segments. Minimum value is 3, and the default is 8.
heightSegments — number of vertical segments. Minimum value is 2, and the default is 6.
phiStart — specify horizontal starting angle. Default is 0.
phiLength — specify horizontal sweep angle size. Default is Math.PI * 2.
thetaStart — specify vertical starting angle. Default is 0.
thetaLength — specify vertical sweep angle size. Default is Math.PI.
you can use phiStart, phiLength, thetaStart and thetaLength to define partial sphere
so to do an half sphere you can try something like:
geometry = new THREE.SphereBufferGeometry( 500, 60, 40, 0, Math.PI, 0, Math.PI );
reference http://threejs.org/docs/#Reference/Extras.Geometries/SphereBufferGeometry
The error is not in source code, it's in texture images: they are both wrong.
A 180 degrees fisheye like this:
reprojected into equirectangular will look like this:
Your textures looks like a mix of 360x180 equirectangular and 270° fisheye, wihich looks like this (with wrong labels/numbers, as I used same 180 FOV fisheye to create it):

Three.js Child of camera isn't visible

I'm trying to attach an object to the camera so that it can be used more more or less as a GUI element.
My camera is defined as follows:
camera = new THREE.PerspectiveCamera( 45, windowWidth / windowHeight, 1, 2000 );
camera.position.z = 100;
In my init(), I define the object to be added:
obj = new THREE.Mesh(new THREE.CubeGeometry(5, 5, 5, 1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xFFFFFF } ));
obj.position.set( 0, 0, -50);
camera.add(obj);
But, the block does not show up. I have tried adding this object to scene, and it is visible. I added a loop to animate() that will slide the object's z position between (-50, 50), but I can't see it.
I tried using camera.lookAt(obj) and logging the world position of obj (obj position + camera position), and they behave as expected. World position seems to be what I'd expect, and camera.lookAt flips the camera when the z position crosses 0.
I apologize for not providing more clear example code, but I will do my best to cooperate with anyone trying to help me. Thanks!
Did you add the camera to the scene?
scene.add( camera );
The camera does not usually have to be added to the scene, but in this case, the object is a child of the camera, so you must.
three.js r.58

Categories

Resources