使用 Three.js 生成水面效果
在使用 Three.js 进行场景开发的过程中,如果想要模拟海洋、湖泊、泳池等水面效果,可以使用 Three.js 官方提供的 Water
类(在 three/examples/jsm/objects/Water.js
中)。Water
通过着色器和反射/折射纹理的配合,能够渲染出逼真的水面效果。
一、Water
类简介
Water
类是 Three.js 提供的一种基于 shader(着色器)实现水面效果的对象。其内部原理大致是:
- 创建反射与折射的渲染目标:渲染场景中特定方向的图像,生成用于实时反射/折射的贴图。
- 使用水面着色器:结合相应的纹理(如法线贴图)、反射贴图、折射贴图和时间因素来模拟水的动态。
这一过程能够让水面真实地反映出环境和天空,配合波浪贴图还能产生动态的波光效果。
二、基础用法
下面展示一个最基础的使用例子,包含了引入 Water
类、创建水面平面和在场景中使用的步骤。
1. 安装或引入 Three.js
如果你使用 npm 包管理,可以在项目中通过命令
npm install three
将 Three.js 安装到本地,然后在代码中通过 ES6 的方式引入。
2. 引入 Water
类
Water
类位于 Three.js 的示例库中,因此需要在源码或项目中引用其路径:
import * as THREE from "three";
import { Water } from "three/examples/jsm/objects/Water.js";
注意:有些版本中,
Water2
类也存在(同样在three/examples/jsm/objects/Water2.js
),它是另一种水面实现方式。但通常最常用的还是Water
。
3. 准备几何体与材质选项
要创建水面,需要一个可供渲染的几何体(通常是 THREE.PlaneGeometry
)。然后使用 Water
类时,需要提供一系列配置项,如贴图、波动速度、颜色、尺寸等。
示例代码如下:
// 1. 创建一个场景
const scene = new THREE.Scene();
// 2. 创建相机
const camera = new THREE.PerspectiveCamera(
75, // 视野(FOV)
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近截面
1000 // 远截面
);
camera.position.set(0, 10, 30);
// 3. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 4. 添加环境光或太阳光
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 50, 50);
scene.add(light);
// 5. 加载水面法线贴图(此处仅作演示,需替换为你自己的纹理路径)
const textureLoader = new THREE.TextureLoader();
const waterNormals = textureLoader.load("textures/waternormals.jpg", function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping; // 让法线贴图可以重复
});
// 6. 创建水面
const waterGeometry = new THREE.PlaneGeometry(100, 100);
// 7. 使用 Water 类
const water = new Water(waterGeometry, {
textureWidth: 512, // 生成的反射/折射纹理宽度
textureHeight: 512, // 生成的反射/折射纹理高度
waterNormals: waterNormals, // 法线贴图
alpha: 1.0, // 水面的透明度
sunDirection: light.position.clone().normalize(), // 模拟光线方向
sunColor: 0xffffff, // “太阳光”颜色
waterColor: 0x001e0f, // 水体颜色
distortionScale: 3.7, // 失真程度,决定波纹起伏的大小
fog: scene.fog !== undefined, // 是否结合场景雾效
});
water.rotation.x = -Math.PI / 2; // 让水面水平
scene.add(water);
// 8. 动画循环
function animate() {
requestAnimationFrame(animate);
// 让水面波纹动起来,内部水面材质会根据 time 进行偏移
water.material.uniforms["time"].value += 0.01;
renderer.render(scene, camera);
}
animate();
如上代码,通过 Water
类即可快速实现一个带有反射、折射与波动效果的水面。你可以根据需要,调整参数来获得不同风格的水面。
三、主要配置项说明
在 new Water(geometry, options)
中,最常用的参数包括:
-
waterNormals
- 类型:
THREE.Texture
- 用于模拟水波纹理,需要一张法线贴图(常见的是类似条纹、波浪的法线纹理)。
- 如果想要更复杂或更细腻的水面效果,可以自行寻找或制作不同的法线贴图。
- 类型:
-
textureWidth
、textureHeight
- 类型:
number
- 用于指定生成反射纹理和折射纹理时的大小(分辨率)。
- 值越大,反射和折射的效果越精细,但也会消耗更多的性能。
- 类型:
-
alpha
- 类型:
number
- 控制水面整体的透明度(0.0 ~ 1.0)。如果想让水看起来“浓”一点,就设置一个接近 1 的值;想要更透明,可以降低一些。
- 类型:
-
sunDirection
- 类型:
THREE.Vector3
- 模拟太阳或强光的方向,一般与场景中的主光源方向保持一致,以产生更真实的光照。
- 类型:
-
sunColor
- 类型:
THREE.Color
或 16 进制数值 - 太阳光的颜色,影响水面的高光和反射效果。
- 类型:
-
waterColor
- 类型:
THREE.Color
或 16 进制数值 - 水体的颜色,通常在深色和偏绿色、蓝色范围内。
- 类型:
-
distortionScale
- 类型:
number
- 波浪失真强度。数值越大,波纹起伏就越明显;数值小,水面更平静。
- 类型:
-
fog
- 类型:
boolean
- 是否与场景雾(Scene.fog)结合。如果场景中使用了雾效,将其设为
true
能让水面渐渐融入雾的效果。
- 类型:
-
clipBias
- 类型:
number
- 影响反射/折射裁剪面的数值,一般不需要特殊调整,默认即可。若遇到反射面“撕裂”或剔除不完整,可尝试微调。
- 类型:
此外,水面材质中的 time
uniform 也非常重要,通过不断增加 time
的值,可以让水面波纹实现动画(这在代码示例中也有体现)。
参考链接