Three.js案例
Water

使用 Three.js 生成水面效果

在使用 Three.js 进行场景开发的过程中,如果想要模拟海洋、湖泊、泳池等水面效果,可以使用 Three.js 官方提供的 Water 类(在 three/examples/jsm/objects/Water.js 中)。Water 通过着色器和反射/折射纹理的配合,能够渲染出逼真的水面效果。

一、Water 类简介

Water 类是 Three.js 提供的一种基于 shader(着色器)实现水面效果的对象。其内部原理大致是:

  1. 创建反射与折射的渲染目标:渲染场景中特定方向的图像,生成用于实时反射/折射的贴图。
  2. 使用水面着色器:结合相应的纹理(如法线贴图)、反射贴图、折射贴图和时间因素来模拟水的动态。

这一过程能够让水面真实地反映出环境和天空,配合波浪贴图还能产生动态的波光效果。

二、基础用法

下面展示一个最基础的使用例子,包含了引入 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) 中,最常用的参数包括:

  1. waterNormals

    • 类型:THREE.Texture
    • 用于模拟水波纹理,需要一张法线贴图(常见的是类似条纹、波浪的法线纹理)。
    • 如果想要更复杂或更细腻的水面效果,可以自行寻找或制作不同的法线贴图。
  2. textureWidthtextureHeight

    • 类型:number
    • 用于指定生成反射纹理和折射纹理时的大小(分辨率)。
    • 值越大,反射和折射的效果越精细,但也会消耗更多的性能。
  3. alpha

    • 类型:number
    • 控制水面整体的透明度(0.0 ~ 1.0)。如果想让水看起来“浓”一点,就设置一个接近 1 的值;想要更透明,可以降低一些。
  4. sunDirection

    • 类型:THREE.Vector3
    • 模拟太阳或强光的方向,一般与场景中的主光源方向保持一致,以产生更真实的光照。
  5. sunColor

    • 类型:THREE.Color或 16 进制数值
    • 太阳光的颜色,影响水面的高光和反射效果。
  6. waterColor

    • 类型:THREE.Color或 16 进制数值
    • 水体的颜色,通常在深色和偏绿色、蓝色范围内。
  7. distortionScale

    • 类型:number
    • 波浪失真强度。数值越大,波纹起伏就越明显;数值小,水面更平静。
  8. fog

    • 类型:boolean
    • 是否与场景雾(Scene.fog)结合。如果场景中使用了雾效,将其设为 true 能让水面渐渐融入雾的效果。
  9. clipBias

    • 类型:number
    • 影响反射/折射裁剪面的数值,一般不需要特殊调整,默认即可。若遇到反射面“撕裂”或剔除不完整,可尝试微调。

此外,水面材质中的 time uniform 也非常重要,通过不断增加 time 的值,可以让水面波纹实现动画(这在代码示例中也有体现)。

参考链接