Three.js BoxGeometry角落阴影锯齿状漏光问题求助
Three.js DirectionalLight角落锯齿状漏光问题修复
问题描述
使用BoxGeometry构建结构并添加环绕旋转的DirectionalLight后,角落出现锯齿状漏光。已尝试调整shadow.mapSize、模糊半径、renderer.shadowMap.type等参数,问题未解决;替换为导入的开放盒模型后,问题依然存在。

相关代码
JavaScript
const gui = new lil.GUI(); // SCENE const scene = new THREE.Scene(); const renderer = new THREE.WebGLRenderer({ antialias: true }); const settings = { shadowMapType: "basic", physicallyCorrectLights: false, shadowMapSize: 2056, shadowRadius: 5 } // CAMERA const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); // RENDERER renderer.setSize(window.innerWidth, window.innerHeight); renderer.physicallyCorrectLights = settings.physicallyCorrectLights; renderer.shadowMap.type = settings.shadowMapType; renderer.shadowMap.enabled = true; // CONTROLS const controls = new THREE.OrbitControls(camera, renderer.domElement); // RESIZE HAMDLER function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } window.addEventListener('resize', onWindowResize); // INIT CAMERA camera.position.z = 35; camera.position.x = 7.5; camera.position.y = 13; camera.lookAt(7.5, 0, -20) // INIT HEMISPHERE LIGHT scene.add(new THREE.AmbientLight( 0xffffff, 0.5 )); // SCENE scene.background = new THREE.Color(0xffffff); // FLOOR const plane = new THREE.Mesh(new THREE.PlaneGeometry(500, 500, 32), new THREE.MeshPhongMaterial({ color: 0xfab74b})); plane.rotation.x = - Math.PI / 2 plane.receiveShadow = true scene.add(plane); const geometry = new THREE.BoxGeometry( 1, 15, 7 ); const material = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} ); const cube = new THREE.Mesh( geometry, material ); cube.position.y = 7.5; cube.position.z = -4.5; cube.receiveShadow = true cube.castShadow = true scene.add( cube ); const geometry2 = new THREE.BoxGeometry( 1, 15, 7 ); const material2 = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} ); const cube2 = new THREE.Mesh( geometry2, material2); cube2.position.y = 7.5; cube2.position.z = -4.5; cube2.position.x = 16; cube2.receiveShadow = true cube2.castShadow = true scene.add( cube2 ); //back wall const geometry3 = new THREE.BoxGeometry( 1, 15, 15 ); const material3 = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} ); const cube3 = new THREE.Mesh( geometry3, material3); cube3.position.y = 7.5; cube3.position.x = 8; cube3.position.z = -7.5 cube3.rotateY(Math.PI / 2) cube3.receiveShadow = true cube3.castShadow = true scene.add( cube3 ); //top const geometry4 = new THREE.BoxGeometry( 1, 7, 17 ); const material4 = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} ); const cube4 = new THREE.Mesh( geometry4, material4); cube4.position.y = 15.5; cube4.position.x = 8; cube4.position.z = -4.5 cube4.rotateY(Math.PI / 2) cube4.rotateZ(Math.PI / 2) cube4.receiveShadow = true cube4.castShadow = true scene.add( cube4 ); // DIRECTIONAL LIGHT const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.x += 20 directionalLight.position.y += 20 directionalLight.position.z += 20 directionalLight.castShadow = true directionalLight.shadow.mapSize.width = settings.shadowMapSize; directionalLight.shadow.mapSize.height = settings.shadowMapSize; directionalLight.shadow.radius = settings.shadowRadius // directionalLight.shadow.blurSamples = 500 const d = 25; directionalLight.shadow.camera.near = 0.5 directionalLight.shadow.camera.far = 60 directionalLight.shadow.camera.left = - d; directionalLight.shadow.camera.right = d; directionalLight.shadow.camera.top = d; directionalLight.shadow.camera.bottom = - d; scene.add(directionalLight); scene.add( new THREE.CameraHelper( directionalLight.shadow.camera ) ); // ANIMATE function animate() { // TARGET const time = Date.now() * 0.0005; directionalLight.position.x = Math.sin(time * 0.7) * 20; directionalLight.position.z = Math.cos(time * 0.7) * 20; renderer.render(scene, camera); requestAnimationFrame(animate); } document.body.appendChild(renderer.domElement); animate(); gui.add( settings, 'shadowMapType', ["basic", "pcf","pcfsoft", "vsm"]) .onChange( value => { switch (value) { case 'basic': renderer.shadowMap.type = THREE.BasicShadowMap; break; case 'pcf': renderer.shadowMap.type = THREE.PCFShadowMap; break; case 'pcfsoft': renderer.shadowMap.type = THREE.PCFSoftShadowMap; break; case 'vsm': renderer.shadowMap.type = THREE.VSMShadowMap; break; default: console.log('Invalid shadow map type'); } console.log(renderer.shadowMap.type) } ); gui.add(settings, "physicallyCorrectLights") .onChange( value => { renderer.physicallyCorrectLights = settings.physicallyCorrectLights }) gui.add( settings, 'shadowMapSize', 0, 12000, 200 ) .onChange( value => { directionalLight.shadow.map.dispose(); // important directionalLight.shadow.mapSize = new THREE.Vector2(value,value) }) gui.add( settings, 'shadowRadius', 0, 100, 1 ) .onChange( value => { directionalLight.shadow.radius = value; directionalLight.shadow.updateMatrices(directionalLight); })
CSS
* { margin: 0; padding: 0; } html, body { overflow: hidden; } .webgl { position: fixed; top: 0; left: 0; outline: none; }
HTML
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/js/controls/OrbitControls.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three-buffer-geometry-utils@1.0.0/index.min.js"></script> <script src="https://unpkg.com/lil-gui@0.16.1/dist/lil-gui.umd.js"></script> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Open Box</title> </head> <body> <canvas class="webgl"></canvas> </body> </html>
解决方案
1. 缩小阴影相机视锥体范围
当前阴影相机范围过大,导致目标区域的阴影纹理精度不足。修改阴影相机参数,仅包裹需要计算阴影的区域:
// 替换原阴影相机配置 directionalLight.shadow.camera.near = 1; directionalLight.shadow.camera.far = 40; directionalLight.shadow.camera.left = -10; directionalLight.shadow.camera.right = 26; directionalLight.shadow.camera.top = 20; directionalLight.shadow.camera.bottom = -5; // 更新相机投影矩阵 directionalLight.shadow.camera.updateProjectionMatrix();
2. 添加阴影偏移参数
通过阴影偏移解决表面精度误差导致的漏光:
directionalLight.shadow.bias = -0.0001; directionalLight.shadow.normalBias = 0.01;
bias:调整深度偏移,负值可减少角落漏光normalBias:基于表面法线的偏移,针对性修复边缘/角落的精度问题
3. 优化阴影采样类型
使用PCFSoftShadowMap提升阴影边缘精度与柔和度:
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
4. 合并相邻几何体(可选)
多个BoxGeometry拼接处易出现精度误差,合并为单个几何体可消除接缝处的阴影问题:
const mergedGeometry = THREE.BufferGeometryUtils.mergeBufferGeometries([ geometry, geometry2, geometry3, geometry4 ]); const mergedMesh = new THREE.Mesh(mergedGeometry, material); mergedMesh.position.y = 7.5; mergedMesh.receiveShadow = true; mergedMesh.castShadow = true; // 移除原多个cube,添加合并后的网格 scene.remove(cube, cube2, cube3, cube4); scene.add(mergedMesh);
内容的提问来源于stack exchange,提问作者Ashbury
相关产品推荐
相关产品推荐

