返回

трехмерная графика three.js: как создать эффект распространения ряби с помощью трех.js + курсор плавает

前端

创建涟漪效果和交互式浮动指针的非凡三维水景

引言

使用 Three.js 创建逼真的三维可视化效果是一项激动人心的挑战。本文将指导您完成一个引人入胜的项目,在这个项目中,我们将利用 Three.js 的强大功能来创建一个栩栩如生的涟漪效果,并添加一个可以在水面自由移动的交互式浮动指针。

构建三维场景

第一步是创建一个场景,容纳我们的水景。我们将使用 Three.js 的 Scene 类来创建这个场景。

const scene = new THREE.Scene();

接下来,我们需要一个摄像机来查看场景。我们将使用 PerspectiveCamera 类创建一个透视摄像机。

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

创建水波纹效果

要创建涟漪效果,我们需要编写一个着色器。着色器是一种特殊类型的程序,可用于处理图形的像素。我们将创建一个片段着色器来计算每个像素的涟漪效果。

// 片段着色器
const fragmentShader = `
  uniform float time;
  uniform vec2 resolution;

  varying vec2 vUv;

  void main() {
    vec2 uv = vUv;

    // 计算涟漪中心
    vec2 center = vec2(0.5, 0.5);

    // 计算到涟漪中心的距离
    float distance = length(uv - center);

    // 计算涟漪半径
    float radius = 0.2;

    // 计算涟漪振幅
    float amplitude = 0.1;

    // 计算涟漪波长
    float wavelength = 0.1;

    // 计算涟漪速度
    float speed = 1.0;

    // 计算涟漪相位
    float phase = time * speed;

    // 计算涟漪位移
    float displacement = amplitude * sin((distance - phase) / wavelength);

    // 移动 UV 坐标
    uv += vec2(displacement, 0.0);

    // 采样纹理
    vec3 color = texture2D(map, uv).rgb;

    // 输出颜色
    gl_FragColor = vec4(color, 1.0);
  }
`;

使用此着色器,我们将创建一个材料并将其应用到我们的水面几何体上。

const material = new THREE.ShaderMaterial({
  fragmentShader: fragmentShader,
  uniforms: {
    time: { value: 0.0 },
    resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
  },
});

添加交互式浮动指针

为了增强互动性,我们将添加一个浮动指针,它可以在水面自由移动。我们将创建一个球体几何体并将其作为指针。

const geometry3 = new THREE.SphereGeometry(0.1, 32, 32);
const material3 = new THREE.MeshBasicMaterial({ color: 0xffffff });
const cursor = new THREE.Mesh(geometry3, material3);

实现动画和交互性

要使场景栩栩如生,我们需要对其进行动画处理。我们将使用 requestAnimationFrame() 来不断更新场景。

function animate() {
  requestAnimationFrame(animate);

  // 更新时间统一变量
  material.uniforms.time.value += 0.01;

  // 渲染场景
  renderer.render(scene, camera);
}

为了使指针具有交互性,我们需要在鼠标移动时更新其位置。

function onMouseMove(event) {
  // 计算光标位置
  const x = (event.clientX / window.innerWidth) * 2 - 1;
  const y = -(event.clientY / window.innerHeight) * 2 + 1;

  // 更新光标位置
  cursor.position.x = x;
  cursor.position.y = y;
}

完成代码示例

以下是完整的代码示例:

<html>
  <head>
    
    <script src="https://unpkg.com/three@0.124.0/build/three.min.js"></script>
  </head>
  <body>
    <script>
      // 场景
      const scene = new THREE.Scene();

      // 相机
      const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

      // 涟漪着色器
      const fragmentShader = `
        uniform float time;
        uniform vec2 resolution;

        varying vec2 vUv;

        void main() {
          vec2 uv = vUv;

          // 涟漪中心
          vec2 center = vec2(0.5, 0.5);

          // 到涟漪中心的距离
          float distance = length(uv - center);

          // 涟漪半径
          float radius = 0.2;

          // 涟漪振幅
          float amplitude = 0.1;

          // 涟漪波长
          float wavelength = 0.1;

          // 涟漪速度
          float speed = 1.0;

          // 涟漪相位
          float phase = time * speed;

          // 涟漪位移
          float displacement = amplitude * sin((distance - phase) / wavelength);

          // 移动 UV 坐标
          uv += vec2(displacement, 0.0);

          // 采样纹理
          vec3 color = texture2D(map, uv).rgb;

          // 输出颜色
          gl_FragColor = vec4(color, 1.0);
        }
      `;

      // 涟漪材质
      const material = new THREE.ShaderMaterial({
        fragmentShader: fragmentShader,
        uniforms: {
          time: { value: 0.0 },
          resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
        },
      });

      // 交互式指针
      const geometry3 = new THREE.SphereGeometry(0.1, 32, 32);
      const material3 = new THREE.MeshBasicMaterial({ color: 0xffffff });
      const cursor = new THREE.Mesh(geometry3, material3);

      // 动画
      function animate() {
        requestAnimationFrame(animate);

        // 更新时间统一变量
        material.uniforms.time.value += 0.01;

        // 渲染场景
        renderer.render(scene, camera);
      }

      // 交互性
      function onMouseMove(event) {
        // 计算光标位置
        const x = (event.clientX / window.innerWidth) * 2 - 1;
        const y = -(event.clientY / window.innerHeight) * 2 + 1;

        // 更新光标位置
        cursor.position.x = x;
        cursor.position.y = y;
      }

      // 渲染器
      const renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      // 添加到场景
      scene.add(cursor);

      // 事件监听器
      window.addEventListener('mousemove', onMouseMove);

      // 开始动画
      animate();
    </script>
  </body>
</html>

常见问题解答

1. 如何调整涟漪效果的强度和波长?
通过修改涟漪材料中“振幅”和“波长”统一变量的值,可以调整涟漪效果的强度和波长。

2. 如何添加纹理以使水表面更逼真?
可以通过将纹理图像作为贴图应用到水面几何体来添加纹理。

3. 如何让指针对水面的互动更真实?
可以通过添加物理引擎(如 Cannon.js)来模拟水面的物理特性,使指针与水面的互动更加逼真。

4. 如何优化性能以提高帧速率?
可以通过降低纹理分辨率、使用更简单的几何体和减少场景中的光源数量来优化性能。

5. 可以使用 Three.js 创建其他哪些水效果?
使用 Three.js 可以创建各种水效果,包括波浪、漩涡和水下失真。