three.js 3D世界中的基本概念&&第一个three.js程序

参考文章:https://discoverthreejs.com/zh/book/first-steps/first-scene/

一、3D 世界的组成

three.js世界中,主要包含这几个概念:
基本脚手架:场景、相机和渲染器。
可见对象:网格。

 

二、场景:小宇宙

场景是我们能看到的一切的载体。您可以将其视为所有 3D 对象都存在于其中的“小宇宙”。
它是一个 3D 笛卡尔坐标系。
场景的中心是点(0,0,0),也称为坐标系的原点。
每当我们创建一个新对象并将其添加到我们的场景中时,它将被放置在原点,并且每当我们移动它时,我们说的都是在这个坐标系中移动它。

import { Scene } from 'three';

const scene = new Scene();

 

三、相机:指向小宇宙的望远镜

我们上面说了场景的小宇宙是指纯数学的领域
我们肉眼要查看场景,需要将其转换为对我们人眼感觉合理的东西,这就是相机的作用
有几种方法可以将场景图形转换为人类视觉友好的格式,使用称为投影的技术。

1、透视投影PerspectiveCamera

我们使用透视投影 PerspectiveCamera 这种类型的相机是现实世界中相机的 3D 等效物。它主要用于3D场景的渲染。

创建一个PerspectiveCamera

import { PerspectiveCamera } from 'three';

const fov = 35;
const aspect = container.clientWidth / container.clientHeight;
const near = 0.1;
const far = 100;

const camera = new PerspectiveCamera(fov, aspect, near, far);

每个参数的作用:

  • 视野(fov):
  1. 类型:浮点数(Float)
  2. 描述:这是相机镜头的视角,以度(°)为单位。FOV 控制了相机视野的宽度。例如,FOV 设置为 75 度意味着相机可以看到大约75度的水*视角。通常,FOV值越大,视野越宽,但这也可能导致场景中的物体变形。
  • 宽高比或者叫纵横比(aspect ratio):
  1. 类型:浮点数(Float)
  2. 描述:这是相机视图的宽高比,通常设置为画布的宽度除以高度。这有助于保持3D对象在渲染时的正确比例
  3. 示例:window.innerWidth / window.innerHeight
  • *剪裁*面(near clipping plane)
  1. 类型:浮点数(Float)
  2. 描述:这是相机开始渲染的最小Z值。任何距离小于此值的对象都将被裁剪掉,即不会出现在渲染的图像中。通常设置为一个很小的正数(例如0.1),以确保*处的物体可以被渲染。
  3. 示例:0.1
  • 远剪裁*面(far clipping plane)
  1. 类型:浮点数(Float)
  2. 描述:这是相机停止渲染的最大Z值。任何距离大于此值的对象都将被裁剪掉。通常设置为一个很大的正数(例如1000或更大),以确保远处的物体可以被渲染。

这四个参数一起用于创建一个有边界的空间区域,我们称之为 视锥体。

当我们通过PerspectiveCamera查看场景时,截锥体内的一切都是可见的,而它外面的一切都是不可见的在上图中,Near Clipping Plane和Far Clipping Plane之间的区域是相机的视锥

创建相机后,我们都要定位相机,因为我们创建的每个对象最初都位于场景的中心,(0,0,0),我们添加到场景中的任何对象也将定位在(0,0,0), 都在彼此之上混杂在一起。

import { PerspectiveCamera } from 'three';

const fov = 35;
const aspect = container.clientWidth / container.clientHeight;
const near = 0.1;
const far = 100;

const camera = new PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 0, 10);

 

2、正交投影OrthographicCamera

正交投影 OrthographicCamera 是另外一种相机类型,正交投影意味着从相机到场景的任何物体,无论距离远*,都会以相同的比例显示。这对于渲染2D图形、制作俯视图或者需要精确度量距离的场景非常有用。
OrthographicCamera 的构造函数接受以下参数:

  1. left:相机视锥体的左边界。
  2. right:相机视锥体的右边界。
  3. top:相机视锥体的上边界。
  4. bottom:相机视锥体的下边界。
  5. near:相机视锥体的*裁剪面。
  6. far:相机视锥体的远裁剪面。

参数详解:
left, right, top, bottom:这四个参数定义了相机的视锥体(viewing frustum)的尺寸。
这些值决定了相机可以看到场景的哪一部分。例如,如果 left 是 -1,right 是 1,top 是 1,bottom 是 -1,这意味着相机可以看到一个正方形区域,其宽度和高度相等,形成一个正方形视图。
near 和 far:这两个参数定义了相机的*裁剪面和远裁剪面。在渲染时,所有在*裁剪面和远裁剪面之间的物体都会被渲染。
这两个值决定了相机可以看到的最远和最*的距离。通常,near 设置为一个很小的正值(例如0.1),而 far 设置为一个很大的值(例如10000),以确保大部分场景都被正确渲染。

 

四、渲染器

渲染器可以把通过望远镜观察到的东西 非常快的绘制到一个<canvas>中去,我们把这个过程叫做渲染,得到的图片就是一个渲染效果图。

import { WebGLRenderer } from 'three';

const renderer = new WebGLRenderer();

WebGLRenderer —— 它使用 WebGL2来渲染我们的场景 (如果可用),如果不可用则回退到WebGL V1。
场景、相机和渲染器一起为我们提供了 three.js 应用程序的基本脚手架。但是,一个都看不到,我们将介绍一种称为网格的可见对象。

 

五、网格 Mesh -- 可见对象

网格是 3D 计算机图形学中最常见的可见对象,用于显示各种 3D 对象——猫、狗、人类、树木、建筑物、花卉和山脉都可以使用网格来表示。
当然还有其他种类的可见对象,例如线条、形状、精灵和粒子等。

import { Mesh } from 'three';

const mesh = new Mesh(geometry, material);

Mesh构造函数有两个参数:几何和材质

 

1、几何体

几何体定义了网格的形状。我们将使用一种称为 BufferGeometry 的几何体。在这里,我们需要一个盒子形状,所以我们将使用 BoxBufferGeometry,它是 three.js 核心中提供的几个基本形状之一。

网格的几何定义了它的形状。如果我们创建一个盒子形状的几何体,我们的网格将被塑造成一个盒子。如果我们创建一个球形几何体,我们的网格将呈球体形状。如果我们创建一个猫形几何体,我们的网格将被塑造成一只猫。

创建1x1x1盒形几何体

import { BoxBufferGeometry } from 'three';

const length = 1;
const width = 1;
const depth = 1;

const geometry = new BoxBufferGeometry(length, width, depth);

参数说明:

  1. length(Float): 立方体的长度。
  2. width (Float): 立方体的宽度。
  3. depth (Float): 立方体的深度。
  4. widthSegments (Integer, 可选): 沿宽度方向的细分段数。默认为 1,表示没有细分。增加这个值可以创建更*滑的边缘。
  5. heightSegments (Integer, 可选): 沿高度方向的细分段数。默认为 1,表示没有细分。增加这个值可以创建更*滑的边缘。
  6. depthSegments (Integer, 可选): 沿深度方向的细分段数。默认为 1,表示没有细分。增加这个值可以创建更*滑的边缘。

 

2、材质

虽然几何体定义了形状,但材质定义了对象的表面属性,或者换句话说,定义了对象看起来是由什么制成的。几何体告诉我们网格是一个盒子、一辆汽车或一只猫,而材质告诉我们它是一个金属盒子、一辆石质汽车或一只涂成红色的猫。

MeshBasicMaterial,这是可用的最简单(也是最快)的材料类型。此材质还会忽略场景中的任何灯光,并根据材质的颜色和其他设置为网格着色(阴影),我们将在不向构造函数传递任何参数的情况下创建材质,因此我们将获得默认的白色材质。

import { MeshBasicMaterial } from 'three';

const material = new MeshBasicMaterial();

特别说明:

如果我们现在使用除MeshBasicMaterial之外的几乎任何其他材质类型,我们将无法看到任何东西,因为场景完全处于黑暗中。就像在现实世界中一样,我们通常需要光线才能看到场景中的事物MeshBasicMaterial是该规则的一个例外。

如果您看不到任何东西,请确保您已经在场景中添加了一些灯光,或者暂时将所有材质切换为MeshBasicMaterial

 

3、创建网格

有了几何体和材质,我们可以创建我们的网格,将两者都作为参数传入。

import { BoxBufferGeometry, Mesh, MeshBasicMaterial } from 'three';

const geometry = new BoxBufferGeometry(2, 2, 2);
const material = new MeshBasicMaterial();
const cube = new Mesh(geometry, material);

 

六、第一个three.js程序

<!DOCTYPE html>
<html>
<head>
    <title>第一个three.js程序</title>
    <style>
        #scene-container {
            width: 100vw;
            height: 100vh;
        }
        canvas { display: block; }
    </style>
</head>
<body>
  <div id="scene-container">
  <!-- 我们最终的 <canvas> 绘制在这里 -->
  <!-- 渲染器会自动为我们创建一个<canvas>元素,我们将把它插入到这个容器中 -->
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
  <script>
    /*
    import {
      BoxBufferGeometry,
      Color,
      Mesh,
      MeshBasicMaterial,
      PerspectiveCamera,
      Scene,
      WebGLRenderer,
    } from 'three';
    */
    
    // 获取容器元素
    const container = document.querySelector('#scene-container');
    // 场景
    const scene = new THREE.Scene();
    scene.background = new THREE.Color('skyblue');
    // 相机
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 5;
    // 渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器的大小,一般设置为容器的大小
    renderer.setSize(container.clientWidth, container.clientHeight); 
    // 设置像素比。告诉渲染器设备屏幕的像素比是多少,这是防止 HiDPI 显示器模糊所必需的 (也称为视网膜显示器)。
    renderer.setPixelRatio(window.devicePixelRatio); 
    // 渲染器将从相机的角度将我们的场景绘制到一个<canvas>元素中去。这个元素已经为我们自动创建并存储在renderer.domElement中,我们需要将它添加到页面中。
    container.append(renderer.domElement);

    // 创建立方体几何体
    const geometry = new THREE.BoxBufferGeometry(1, 1, 1, 1, 1, 1); // 宽1,高1,深1,每个方向各细分1次

    // 材质
    const material = new THREE.MeshBasicMaterial();
     
    // 网格(Mesh)
    const mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
     
    // 渲染循环
    function animate() {
      requestAnimationFrame(animate);
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.01;
      // 渲染场景: 通过这一行,我们告诉渲染器使用相机创建场景的静态图片并将该图片输出到<canvas>元素中
      renderer.render(scene, camera);
    }
    animate();
  </script>
</body>
</html>

如果我们想删除它,我们可以使用scene.remove(mesh)。 一旦网格被添加到场景中,我们称网格为场景的子节点,我们称场景为网格的父节点。

 

posted @ 2025-04-04 21:53  我用python写Bug  阅读(92)  评论(0)    收藏  举报