返回

有趣的!3D效果让二维图片动起来了,全是Three.js的功劳!

前端

用 Three.js 和 React 让二维图片栩栩如生

作为技术达人和图形爱好者,探索将二维图片转化为三维视觉世界的可能性让我着迷不已。很幸运,Three.js 为我们提供了这个机会。在这个博文中,我将分享我的创作过程和技巧,帮助你使用 Three.js 和 React 技术栈将二维图片变成令人惊叹的 3D 效果。

准备工作

在你开始创作之前,需要准备一些基本工具和资源:

  • Three.js 库: 从 Three.js 官网下载最新版本并将其添加到你的项目中。
  • React: 使用 React 作为前端框架来构建用户界面。
  • 图片资源: 选择你想要转换的二维图片,建议使用轮廓清晰、形状简单的图片。
  • 3D 建模软件: 如果你需要对图片进行建模,可以使用 Blender 或 Maya 等 3D 建模软件。

创建基本场景

首先,在你的 React 项目中创建一个新的组件并导入 Three.js 库。在组件中定义一个场景(scene)、一个相机(camera)和一个渲染器(renderer)。设置相机的视角和位置,并将渲染器附加到 DOM 元素。

import { useEffect, useRef } from 'react';
import * as THREE from 'three';

const Scene = () => {
  const mountRef = useRef(null);

  useEffect(() => {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 2;

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);

    mountRef.current.appendChild(renderer.domElement);

    const animate = () => {
      requestAnimationFrame(animate);

      renderer.render(scene, camera);
    };

    animate();

    return () => {
      mountRef.current.removeChild(renderer.domElement);
    };
  }, []);

  return <div ref={mountRef} />;
};

导入图片并转换为 3D 模型

现在,让我们导入图片并将其转换为 3D 模型。使用 Three.js 中的 TextureLoader 类加载图片,创建一个平面几何体作为图片的载体,将图片纹理应用到平面几何体上,最后将平面几何体添加到场景中。

import { useEffect, useRef } from 'react';
import * as THREE from 'three';

const Image3D = ({ image }) => {
  const mountRef = useRef(null);

  useEffect(() => {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 2;

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);

    mountRef.current.appendChild(renderer.domElement);

    const textureLoader = new THREE.TextureLoader();
    const texture = textureLoader.load(image);

    const geometry = new THREE.PlaneGeometry(1, 1);
    const material = new THREE.MeshBasicMaterial({ map: texture });
    const mesh = new THREE.Mesh(geometry, material);

    scene.add(mesh);

    const animate = () => {
      requestAnimationFrame(animate);

      renderer.render(scene, camera);
    };

    animate();

    return () => {
      mountRef.current.removeChild(renderer.domElement);
    };
  }, []);

  return <div ref={mountRef} />;
};

添加灯光和阴影

为了让图片更加逼真,我们需要添加灯光和阴影。使用 Three.js 中的 AmbientLight 类或 DirectionalLight 类添加灯光,使用 ShadowMaterial 类或 PointLight 类添加阴影。

import { useEffect, useRef } from 'react';
import * as THREE from 'three';

const Image3D = ({ image }) => {
  const mountRef = useRef(null);

  useEffect(() => {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 2;

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);

    mountRef.current.appendChild(renderer.domElement);

    const textureLoader = new THREE.TextureLoader();
    const texture = textureLoader.load(image);

    const geometry = new THREE.PlaneGeometry(1, 1);
    const material = new THREE.MeshBasicMaterial({ map: texture });
    const mesh = new THREE.Mesh(geometry, material);

    scene.add(mesh);

    const ambientLight = new THREE.AmbientLight(0x404040, 1);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
    directionalLight.position.set(10, 10, 10);
    scene.add(ambientLight);
    scene.add(directionalLight);

    const animate = () => {
      requestAnimationFrame(animate);

      renderer.render(scene, camera);
    };

    animate();

    return () => {
      mountRef.current.removeChild(renderer.domElement);
    };
  }, []);

  return <div ref={mountRef} />;
};

动画效果

最后,为了让图片动起来,我们需要添加动画效果。使用 TweenMax 库或 Anime.js 库实现动画。你可以让图片旋转、平移或缩放,以创造出不同的视觉效果。

import { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import Anime from 'animejs';

const Image3D = ({ image }) => {
  const mountRef = useRef(null);

  useEffect(() => {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 2;

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);

    mountRef.current.appendChild(renderer.domElement);

    const textureLoader = new THREE.TextureLoader();
    const texture = textureLoader.load(image);

    const geometry = new THREE.PlaneGeometry(1, 1);
    const material = new THREE.MeshBasicMaterial({ map: texture });
    const mesh = new THREE.Mesh(geometry, material);

    scene.add(mesh);

    const ambientLight = new THREE.AmbientLight(0x404040, 1);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
    directionalLight.position.set(10, 10, 10);
    scene.add(ambientLight);
    scene.add(directionalLight);

    const [isRotating, setIsRotating] = useState(false);

    useEffect(() => {
      if (isRotating) {
        Anime({
          targets: mesh.rotation,
          y: 2 * Math.PI,
          duration: 2000,
          easing: 'linear',
          loop: true,
        });
      } else {
        Anime.remove(mesh.rotation);
      }
    }, [isRotating]);

    const animate = () => {
      requestAnimationFrame(animate);

      renderer.render(scene, camera);
    };

    animate();

    return () => {
      mountRef.current.removeChild(renderer.domElement);
      Anime.remove(mesh.rotation);
    };
  }, []);

  return <div ref={mountRef} />;
};

常见问题解答

1. 为什么我的图片没有显示?

  • 确保你正确加载了图片并将其应用到了平面几何体上。

2. 为什么我的图片没有阴影?

  • 确保你添加了灯光和阴影到场景中。

3. 如何让我的图片旋转?

  • 使用 TweenMax 或 Anime.js 库添加动画,让图片的旋转属性随时间变化。

4. 如何让我的图片平移?

  • 与旋转类似,使用动画库让图片的平移属性随时间变化。

5. 我可以使用 Three.js 转换任何类型的图片吗?

  • 是的,你可以转换任何类型的图片,但具有清晰轮廓和简单形状的图片效果最佳。