Three.js海浪效果创建故障:白屏转黑方块问题排查求助
问题描述
尝试用Three.js制作海浪效果,初始运行时显示白屏,调整后变为缓慢旋转的黑方块,使用的代码如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>My first three.js app</title> <style> body { margin: 0; } </style> </head> <body> <script src="https://cdn.jsdelivr.net/npm/three@0.143/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.143/examples/js/controls/OrbitControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.143/examples/js/objects/Water.js"></script> <script> const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.set( 0, 0, 50 ); camera.lookAt( 0, 0, 0 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); const WaterGeometry = new THREE.PlaneGeometry( 45, 45 ); const texture = new THREE.TextureLoader().load( 'https://i.imgur.com/mK9cHLq.jpeg' ); const material = new THREE.MeshBasicMaterial( { map: texture, side: THREE.DoubleSide } ); const controls = new THREE.OrbitControls( camera, renderer.domElement ); const params = { color: '#ffffff', scale: 4, flowX: 1, flowY: 1 }; water = new THREE.Water( WaterGeometry, material, { color: params.color, scale: params.scale, flowDirection: new THREE.Vector2( params.flowX, params.flowY ), textureWidth: 1024, textureHeight: 1024 } ); controls.update(); scene.add( water ); function animate() { requestAnimationFrame( animate ); water.rotation.x += 0.00; water.rotation.y += 0.00; water.rotation.z += 0.01; renderer.render( scene, camera ); }; animate(); </script> </body> </html>
错误原因分析
THREE.Water构造参数错误:该类仅接受几何体和配置对象两个参数,无需传入自定义MeshBasicMaterial,额外传入的材质会破坏Water内部材质的初始化流程,导致显示异常。- 缺少波浪法线纹理:Water效果依赖法线纹理模拟波浪起伏与反射,未加载法线纹理时无法呈现海浪效果,只会显示纯色或黑色平面。
- 水面朝向错误:
PlaneGeometry默认是XY平面,相机处于Z轴位置时只能看到平面边缘,需将水面旋转至XZ平面才能正常观察。 - 动画逻辑错误:直接旋转整个water mesh无法实现波浪流动,需更新Water内部的时间uniform变量驱动动画。
修复方案
- 移除自定义material参数,正确调用
THREE.Water构造函数。 - 加载Water所需的法线纹理(可使用Three.js官方示例纹理或自行准备)。
- 将水面旋转至XZ平面(设置
water.rotation.x = -Math.PI / 2)。 - 在动画循环中更新Water的时间参数,实现波浪流动效果。
- 添加窗口大小监听,确保渲染器与相机比例随窗口变化同步更新。
修正后的代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Three.js Water Effect</title> <style> body { margin: 0; } canvas { display: block; } </style> </head> <body> <script src="https://cdn.jsdelivr.net/npm/three@0.143/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.143/examples/js/controls/OrbitControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.143/examples/js/objects/Water.js"></script> <script> const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 10, 30); camera.lookAt(0, 0, 0); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 创建水面几何体 const waterGeometry = new THREE.PlaneGeometry(100, 100); // 加载水面法线纹理,替换为你自己的纹理路径(可使用Three.js官方示例纹理) const textureLoader = new THREE.TextureLoader(); const waterNormals = textureLoader.load('path/to/your/water-normal-texture.jpg'); waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping; // 初始化Water const params = { color: '#001e0f', scale: 4, flowDirection: new THREE.Vector2(1, 1), textureWidth: 1024, textureHeight: 1024 }; const water = new THREE.Water(waterGeometry, { color: params.color, scale: params.scale, flowDirection: params.flowDirection, textureWidth: params.textureWidth, textureHeight: params.textureHeight, normalMap0: waterNormals, normalMap1: waterNormals }); // 调整水面朝向到XZ平面 water.rotation.x = -Math.PI / 2; const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; scene.add(water); // 窗口大小变化适配 window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); function animate(time) { requestAnimationFrame(animate); // 更新Water时间参数,驱动波浪流动动画 water.material.uniforms['time'].value += 1.0 / 60.0; controls.update(); renderer.render(scene, camera); } animate(); </script> </body> </html>
关键修复点说明
- Water构造调用:移除多余的material参数,改为传入包含法线纹理的配置对象,确保Water内部材质正确初始化。
- 法线纹理设置:加载法线纹理并设置重复映射,为波浪效果提供必要的视觉数据。
- 水面朝向调整:旋转水面至XZ平面,让相机可以正常观察完整水面。
- 动画逻辑修正:通过更新时间uniform变量驱动波浪流动,替代旋转整个mesh的错误方式。
- 窗口适配:添加resize事件监听,保证窗口变化时渲染器和相机参数同步更新。
内容的提问来源于stack exchange,提问作者Cambria
相关产品推荐
相关产品推荐

