柔和的阴影有意外的偏移

时间:2019-03-13 09:04:33

标签: three.js shadow

我目前正在处理柔和/模糊的阴影效果,该效果投射在对象正下方的平面上(只是为了给它提供更多的深度)。光源(DirectionalLight)共享对象的中心坐标,但在Y中有一个偏移量,因此它位于上方。它指向对象的中心。

我对光的阴影参数进行了一些实验,发现减小阴影贴图的大小可以使我获得很好的柔和阴影效果,这对我来说已经足够了。例如:

light.shadow.mapSize.width = 32;
light.shadow.mapSize.height = 32;

但是,我注意到阴影有一个偏移量,使观察者可以假定光源不是直接从上方入射的。

enter image description here

我创建了this小提琴,从中创建了图像。作为阴影类型,我使用PCFSoftShadowMap。

通过这种设置,我将假定阴影效果在立方体的所有四个侧面上均等地投射,但显然不是。我还注意到,当增加阴影贴图的大小时,此“偏移”会变小,而在使用例如512或1024的大小时,这种偏移几乎不会引起注意。

此方法将是实现所需效果的简单且高效的解决方案,因此,我非常感谢对此提供的任何帮助

编辑:

如评论中所述,调整LightShadow的半径并不是令人满意的解决方案,因为阴影渐变具有硬边而不是软边。

2 个答案:

答案 0 :(得分:3)

我认为所发生的是您的阴影贴图的分辨率足够低,您看到的舍入错误。如果您切换回THREE.BasicShadowMap,我想您将看到被击中的物理光照贴图像素恰好位于您看到较大边缘的物体的一侧,并且当您移动物体时,阴影将移动逐步调整地图上像素的大小。

通常,在实践中,您想使用分辨率更高的光照贴图,并使其覆盖区域尽可能紧密地围绕场景的焦点,以从光照贴图获得最大的分辨率。然后您可以调整LightShadow的.radius以获得正确的柔和度。

答案 1 :(得分:0)

我想出的一个解决方案是使用四个光源,所有光源的位置偏移都很小,因此“阴影偏移”将来自四个不同方向(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>

enter image description here