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.
Related
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.
I'm working on a solar system project that involves planets and a rocketship orbiting around a sun. There is currently a main light source with the code being as follows
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.PointLight(0xffffff);
spotLight.position.set(-200, 50, 150);
spotLight.castShadow = true;
scene.add(spotLight);
I have created a sun object and tried giving it an emissive property so that it can shine light at other planets too. Although it seems to make the planet glow in a sense, it's more of just the object lighting up and not having shading rather than actually becoming a light source. The code for the sun object is as follows
function createSunMesh(geom) {
var loader = new THREE.TextureLoader();
var planetSunTexture = loader.load("../assets/textures/planets/sun.jpg");
//var normalSunTexture = loader.load("../assets/textures/planets/moonbump.jpg");
var planetSunMaterial = new THREE.MeshLambertMaterial({map: planetSunTexture, emissive: 0xac3d25});
//bumpMap: normalMoonTexture
// create a multimaterial
var planetSunMesh = THREE.SceneUtils.createMultiMaterialObject(geom, [planetSunMaterial]);
planetSunMesh.visible = false;
return planetSunMesh;
}
So my question is, is there a way to make the emissive property of the light stronger / strong enough to impact the other objects? I have tried using both Lambert / Phong materials but they seem to have the same effect as one another.
I ended up repositioning the main spotlight so it was within my sun object, which gave it the same effect that I was looking for. Also, I increased the intensity and reduced the decay to give the light a more realistic effect
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 );
First time using three.js and I'm doing a very simple particle animation in which I'm mapping 4 different textures. So far everything is working as desired except that I can't figure out how to rotate particles so that they're rendered with a random orientation (upside down, sideways, etc.) Any help would be appreciated!
You can see my progress so far here: http://development.shapes.divshot.io/particles.html
And here is the relevant code:
sprite1 = THREE.ImageUtils.loadTexture( "sprite1.png" );
sprite2 = THREE.ImageUtils.loadTexture( "sprite2.png" );
sprite3 = THREE.ImageUtils.loadTexture( "sprite3.png" );
sprite4 = THREE.ImageUtils.loadTexture( "sprite4.png" );
parameters = [ sprite1, sprite2, sprite3, sprite4];
for ( i = 0; i < parameters.length; i ++ ) {
sprite = parameters[i];
materials[i] = new THREE.PointCloudMaterial( { size: 45, map: sprite, depthTest: false, transparent : true} );
particles = new THREE.PointCloud( geometry, materials[i] );
particles.rotation.x = Math.random() * 60;
particles.rotation.y = Math.random() * 60;
particles.rotation.z = Math.random() * 60;
scene.add( particles );
}
Using three.js r71
AFAIK the three.js PointCloud/PointCloudMaterial particle system uses gl.POINTS to draw the points. Which means it has several limitations.
You can't rotate the points.
You can rotate the UV coordinates in your fragment shader if you write a custom shader but that won't help if your image fills the point because rotating a square texture inside a square will clip the corners as it rotates.
You can't make points larger than the max point side of the GPU/Driver you're on.
WebGL only requires the max size = 1.0 which means there are GPUs/Drivers that only support 1 pixel large points.
Checking webglstats.com it looks like the number of GPUs/Drivers that only support 1 pixel large points has gotten smaller. There's still about 5% of machines that only support points 63 pixels and smaller which should only be an issue if you're flying through a point field.
You can only have square points.
You can't have rectangular point if you wanted something long and thin like a spark for example.
One solution is to make your own particle system that uses quads and can rotate their vertices as well as scale them in multiple directions. This example runs entirely on the GPU. Unfortunately it is not three.js based.
I have a camera that moves in a few different ways in the scene. The camera should rotate around a target position. In my case, this is a point on a mesh that the user has targeted. Because the camera usually doesn't require moving relative to this point, I was not able to use the pivot idea here: https://github.com/mrdoob/three.js/issues/1830. My current solution uses the following code:
var rotationY = new THREE.Matrix4();
var rotationX = new THREE.Matrix4();
var translation = new THREE.Matrix4();
var translationInverse = new THREE.Matrix4();
var matrix = new THREE.Matrix4();
function rotateCameraAroundObject(dx, dy, target) {
// collect up and right vectors from camera perspective
camComponents.up = rotateVectorForObject(new THREE.Vector3(0,1,0), camera.matrixWorld);
camComponents.right = rotateVectorForObject(new THREE.Vector3(1,0,0), camera.matrixWorld);
matrix.identity();
rotationX.makeRotationAxis(camComponents.right, -dx);
rotationY.makeRotationAxis(camComponents.up, -dy);
translation.makeTranslation(
target.position.x - camera.position.x,
target.position.y - camera.position.y,
target.position.z - camera.position.z);
translationInverse.getInverse(translation);
matrix.multiply(translation).multiply(rotationY).multiply(rotationX).multiply(translationInverse);
camera.applyMatrix(matrix);
camera.lookAt(target.position);
}
The issue is that we do not want to use lookAt, because of the reorientation. We want to be able to remove that line.
If we use the code above without lookAt, we rotate around the point but we do not look at the point. My understanding is that my method should rotate the camera's view as much as the camera itself is rotate, but instead the camera is rotated only a small amount. Could anyone help me understand what's wrong?
EDIT: Cleaned up the original post and code to hopefully clarify my question.
My thinking is that I can translate to the origin (my target position), rotate my desired amount, and then translate back to the beginning position. Because of the rotation, I expect to be in a new position looking at the origin.
In fact, I'm testing it now without the translation matrices being used, so the matrix multiplication line is:
matrix.multiply(rotationY).multiply(rotationX);
and it seems to be behaving the same. Thanks for all the help so far!
ONE MORE THING! A part of the problem is that when the camera behaves badly close to the north or south poles. I am looking for a 'free roaming' sort of feel.
Put the following in your render loop:
camera.position.x = target.position.x + radius * Math.cos( constant * elapsedTime );
camera.position.z = target.position.z + radius * Math.sin( constant * elapsedTime );
camera.lookAt( target.position );
renderer.render( scene, camera );
Alternatively, you can use THREE.OrbitControls or THREE.TrackballControls. See the three.js examples.
The Gimbal lock that you are referring to (reorientation) is because of the use of Euler angles in the default implementation of the camera lookat. If you set
camera.useQuaternion = true;
before your call to lookat, then euler angles will not be used. Would this solve your problem ?