我正在研究一种柔和/模糊的阴影效果,它直接在我的物体下面的平面上投射(只是为了给它一些更深的深度)。光源(DirectionalLight)共享对象的中心坐标,但偏移量为Y,因此它直接位于上方。它指向物体的中心。
我用光的阴影参数进行了一些实验,发现降低阴影贴图的大小给了我很好的柔和阴影效果,这对我来说已经足够了。例如:
light.shadow.mapSize.width = 32;
light.shadow.mapSize.height = 32;
但是,我注意到阴影有一个偏移,让观察者认为光源不是直接从上面来的:
我创建了this小提琴,我从中创建了图像。作为阴影类型我使用PCFSoftShadowMap。
通过这种设置,我会假设阴影效果在立方体的所有四个侧面上同等地投射,但显然不是。我还注意到,当增加阴影贴图大小时,此“偏移”会变小,而在使用512或1024等大小时几乎不可察觉。
这种方法对于期望的效果来说是一种简单且高效的解决方案,所以我非常感谢您对此的任何帮助
编辑:
正如评论中所述,调整LightShadow的半径并不是一个令人满意的解决方案,因为阴影渐变具有硬边而不是软边。
我认为发生的事情是你的阴影贴图分辨率足够低,你会看到舍入错误。如果你切换回THREE.BasicShadowMap,我想你会看到被击中的物理光照贴图像素恰好位于你看到较大边缘的物体一侧,当你移动物体时,阴影会移动逐步调整地图上像素的大小。
通常在实践中,您希望使用更高分辨率的光照贴图,并尽可能将其覆盖区域保持在场景焦点周围,以便从光照贴图中获得最高分辨率。然后你可以调整LightShadow的.radius来获得正确的柔软度。
我提出的一个解决方案是使用四个光源,所有光源都具有非常轻微的位置偏移,因此“阴影偏移”将来自四个不同的方向(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>