第三章:场景和相机

7/18/2023 ReactThree.js

在本章节中,我们将深入学习如何在React+Three.js应用程序中创建Three.js场景和相机。我们将了解如何设置场景以容纳3D元素,并在React组件中控制相机的视角。此外,我们还将处理React组件大小变化,并确保相机适应新的视口尺寸。

# 1. 创建Three.js场景(Scene)和相机(Camera)

在Three.js中,场景(Scene)是3D场景中所有对象的容器。我们将把所有的几何体、光源和相机都添加到场景中,然后由Three.js引擎来渲染它们。

我们已经在前面的章节中创建了一个简单的Three.js场景,现在让我们将其封装到一个独立的ThreeScene组件中,并添加一些注释来说明代码的功能。

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

const ThreeScene = () => {
  // 使用useRef来存储Three.js场景、相机和渲染器
  const sceneRef = useRef(null);
  const scene = useRef(null);
  const camera = useRef(null);
  const renderer = useRef(null);
  const cube = useRef(null);
  const plane = useRef(null);

  useEffect(() => {
    // 在useEffect中创建Three.js场景
    scene.current = new THREE.Scene();
    camera.current = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

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

    sceneRef.current.appendChild(renderer.current.domElement);

    // 创建一个简单的红色立方体
    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    cube.current = new THREE.Mesh(geometry, material);
    scene.current.add(cube.current);

    // 创建一个绿色平面作为地面
    const planeGeometry = new THREE.PlaneGeometry(5, 5);
    const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide });
    plane.current = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.current.rotation.x = -Math.PI / 2;
    plane.current.position.y = -1;
    scene.current.add(plane.current);

    // 将相机定位到远离立方体并渲染场景
    camera.current.position.z = 5;

    // 使用requestAnimationFrame来循环渲染场景
    const animate = () => {
      requestAnimationFrame(animate);

      // 使立方体旋转
      cube.current.rotation.x += 0.01;
      cube.current.rotation.y += 0.01;

      // 使用渲染器来渲染场景
      renderer.current.render(scene.current, camera.current);
    };

    // 开始渲染循环
    animate();

    // 在组件卸载时,清理Three.js场景和渲染器
    return () => {
      scene.current.remove(cube.current);
      scene.current.remove(plane.current);
      geometry.dispose();
      material.dispose();
      planeGeometry.dispose();
      planeMaterial.dispose();
      renderer.current.dispose();
    };
  }, []);

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

export default ThreeScene;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

现在,我们已经将Three.js场景封装到了ThreeScene组件中,并在useEffect钩子中进行了创建和清理的操作。我们还在组件中使用useRef钩子来存储了场景、相机和渲染器等元素,这样我们就能在整个组件的生命周期中访问它们。

# 2. 在React中控制相机视角

Three.js相机(Camera)控制着我们在3D场景中看到的视角。通过调整相机的位置和朝向,我们可以改变我们在场景中的观察点。

在前面的章节中,我们在ThreeScene组件中创建了一个透视相机(PerspectiveCamera)。现在,让我们添加一些交互功能来控制相机视角。

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

const ThreeScene = () => {
  const sceneRef = useRef(null);
  const scene = useRef(null);
  const camera = useRef(null);
  const renderer = useRef(null);
  const cube = useRef(null);
  const plane = useRef(null);

  // 使用useState来跟踪相机的位置
  const [cameraPosition, setCameraPosition] = useState({ x: 0, y: 0, z: 5 });

  useEffect(() => {
    // ...
    // 在useEffect中创建Three.js场景和相机
    // ...

    // 使用useState中的相机位置来设置相机的位置
    camera.current.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);

    // ...

    // 使用鼠标滚轮事件来控制相机的缩放
    const handleWheel = (event) => {
      event.preventDefault();
      // 获取鼠标滚轮的delta值,并根据其方向调整相机的z位置
      setCameraPosition((prevPosition) => ({
        ...prevPosition,
        z: prevPosition.z + event.deltaY * 0.1,
      }));
    };

    // 将鼠标滚轮事件监听器添加到窗口
    window.addEventListener('wheel', handleWheel);

    // 在组件卸载时,移除事件监听器
    return () => {
      window.removeEventListener('wheel', handleWheel);
    };
  }, [cameraPosition]);

  // ...
  // 其他代码(省略)
  // ...

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

export default ThreeScene;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

在上述代码中,我们使用useState钩子来跟踪相机的位置,这样我们可以根据用户的交互来动态地更新相机的位置。

useEffect钩子中,我们使用鼠标滚轮事件来控制相机的缩放。当用户滚动鼠标滚轮时,我们根据滚动的方向调整相机的z位置,从而实现相机的缩放效果。

通过这样的交互

功能,我们可以更灵活地控制相机视角,让用户更好地观察3D场景中的元素。

# 3. 处理React组件大小变化和相机适应

在一个React应用中,组件的大小可能会因为窗口大小的变化而改变。当组件的大小变化时,我们需要相应地调整Three.js场景和相机,以确保场景适应新的视口尺寸。

让我们来添加相应的代码来处理组件大小变化和相机适应:

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

const ThreeScene = () => {
  // ...

  const handleResize = () => {
    const width = window.innerWidth;
    const height = window.innerHeight;

    // 调整渲染器和相机的尺寸
    camera.current.aspect = width / height;
    camera.current.updateProjectionMatrix();
    renderer.current.setSize(width, height);
  };

  useEffect(() => {
    // ...

    // 使用window的resize事件来监听窗口大小的变化
    window.addEventListener('resize', handleResize);

    // 在组件卸载时,移除resize事件监听器
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

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

export default ThreeScene;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

在上述代码中,我们使用handleResize函数来处理组件大小变化。在useEffect钩子中,我们添加了一个事件监听器,来监听窗口的resize事件。当窗口大小发生变化时,我们调用handleResize函数来更新渲染器和相机的尺寸,从而适应新的视口大小。

通过这样的处理,我们确保了Three.js场景和相机能够适应不同的屏幕尺寸,使我们的React+Three.js应用更具响应性和交互性。

在下一章节中,我们将学习如何在React中创建更复杂的几何体和材质,以及如何添加光源来增强场景的效果。敬请期待!

编辑时间: 7/18/2023, 10:30:56 AM