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):
- 类型:浮点数(Float)
- 描述:这是相机镜头的视角,以度(°)为单位。FOV 控制了相机视野的宽度。例如,FOV 设置为 75 度意味着相机可以看到大约75度的水*视角。通常,FOV值越大,视野越宽,但这也可能导致场景中的物体变形。
- 宽高比或者叫纵横比(aspect ratio):
- 类型:浮点数(Float)
- 描述:这是相机视图的宽高比,通常设置为画布的宽度除以高度。这有助于保持3D对象在渲染时的正确比例。
- 示例:window.innerWidth / window.innerHeight
- *剪裁*面(near clipping plane):
- 类型:浮点数(Float)
- 描述:这是相机开始渲染的最小Z值。任何距离小于此值的对象都将被裁剪掉,即不会出现在渲染的图像中。通常设置为一个很小的正数(例如0.1),以确保*处的物体可以被渲染。
- 示例:0.1
- 远剪裁*面(far clipping plane):
- 类型:浮点数(Float)
- 描述:这是相机停止渲染的最大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 的构造函数接受以下参数:
- left:相机视锥体的左边界。
- right:相机视锥体的右边界。
- top:相机视锥体的上边界。
- bottom:相机视锥体的下边界。
- near:相机视锥体的*裁剪面。
- 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);
参数说明:
- length(Float): 立方体的长度。
- width (Float): 立方体的宽度。
- depth (Float): 立方体的深度。
- widthSegments (Integer, 可选): 沿宽度方向的细分段数。默认为 1,表示没有细分。增加这个值可以创建更*滑的边缘。
- heightSegments (Integer, 可选): 沿高度方向的细分段数。默认为 1,表示没有细分。增加这个值可以创建更*滑的边缘。
- 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)。 一旦网格被添加到场景中,我们称网格为场景的子节点,我们称场景为网格的父节点。