Threejs入门(一)场景、相机、渲染器

487 阅读7分钟

image.png

代码高亮样式:atelier-seaside-light
书籍:DISCOVER three.js

创建一个JS项目

为了方便学习,我使用vite直接创建了一个本地项目 npm init vite
threejs使用的版本是0.162.0

在根文件添加项目内容

根文件 index.html

<!-- 根节点 -->
<div id="app" style="width: 700px;height: 700px;"></div>
<!-- 程序入口文件 -->
<script type="module" src="src/container.js"></script>

threejs应用程序的基本组件

image.png
在正式开始写代码之前,我们先了解一下threejs的空间构成
  1. 首先是场景相机渲染器,他们是构成程序的脚手架
  2. 接下来是HTML<canvas>元素
  3. 最后是要有一个可见的对象,例如网格(也就是我们要展示的模型)
场景

所谓场景是我们能看到的一切的载体,我们后面创建的相机、模型、灯光等都需要放到场景中,书中是将他比作小宇宙,所有的3D对象都存在其中,下面是如何创建一个场景

import {Scene} from 'three'
const scene = new Scene()

场景空间中使用的是3D笛卡尔坐标系,中心是点(0,0,0),也就是原点,我们往场景中添加的物体,默认会放置在原点位置,移动物体也是在场景中进行移动

当我们将对象添加到场景中时,它会被放到场景图中,这是一个树形结构,场景位于顶端

image.png
相机

相机就类似我们的眼睛,只有放置了相机我们才可以看到场景的物体(前提还要有灯光,后面再讲)
书中的介绍是:如果我们要查看场景,我们需要打开一个进入到这个领域的窗口,并将其转换为对我们人眼感觉合理的东西,这就是相机,使用的技术称为投影,对于我们来说最重要的投影类型是透视投影 首先我们要创建一个相机

import {PerspectiveCamera} from 'three'
const app = document.querySelector('#app')

const fov = 35; // 视野
const aspect = app.clientWidth / app.clientHeight; // 长宽比
const near = 0.1; // 近截面
const far = 100; // 远截面

const camera = new PerspectiveCamera(fov, aspect, near, far)
  • fov 视野:相机的视野有多宽,以度为单位;视野定义了 平截头体 扩展的角度,小视场会产生窄截椎体,而宽视场会产生宽截椎体
  • aspect 纵横比:场景的宽度与高度比率;它将 平截头体 与场景容器元素相匹配,当我们将其设置为容器的 宽/高 时,我们确保可以将类似矩形的平截头体完美的扩展到容器中,如果弄错这个值,场景会看起来很模糊
  • near 近剪裁平面:任何比这更靠近相机的东西都是不可见的
  • far 远剪裁平面:任何比这更远离相机的东西都是不可见的

上面的这四个参数一起创建了一个有边界的空间区域,称之为视椎体,只有处在这个空间内的物体才是可见的

image.png image.png
渲染器

如果将场景比作一个小宇宙,相机是指向宇宙的望远镜,那渲染器就是一个艺术家,它通过望远镜观察并将他们看到的东西 非常快 的渲染到<canvas>中去这个过程叫做渲染

我们使用WebGLRenderer进行场景的渲染;渲染器不会绘制场景中不在平截头体内的任何对象。如果一个物体部分在平截头体内部,部分在外部,则外部部分会被切掉(剪掉)

import {WebGLRenderer} form 'three'
const renderer = new WebGLRenderer()

场景、相机和渲染器一起为我们提供了threejs应用程序的基本脚手架,但是,这些我们一个也看不到😂😂😂(这是书中原话,什么也看不到),下面开始介绍一种称为网格的可见对象

网格

网格是3D计算机图形学中最常见可见对象
在创建一个网格之前,我们还需要先了解几何材质,他们是创建网格的必要参数

几何体

几何体定义了网格的形状 我们使用BoxGeometry来创建几何体,它是threejs核心中提供的几个基本形状之一

注意:书中提到的BoxBufferGeometry构造函数已经弃用,目前使用的是BoxGeometry

  • 创建一个几何体
import {BoxGeometry} from 'three'

// 创建一个2*2*2的几何体
const width = 2; // X轴上面的宽度
const height = 2; // Y轴上面的高度
const depth = 2; // Z轴上面的深度

const geometry = new BoxGeometry(length, width, depth)

构造函数BoxGeometry最多需要六个参数,这里我们只提供了三个,另外三个是

  • widthSegments (可选)宽度的分段数,默认值是 1
  • widthSegments (可选)宽度的分段数,默认值是 1
  • widthSegments (可选)宽度的分段数,默认值是 1

有兴趣的小伙伴可以去深入了解下,threejs 提供的很多其他的几何体也会用到类似的参数

材质

几何体定义了形状,而材质定义了网格表面的外观,这里我们将使用MeshBasicMaterial,这是可用的最简单的材质,更重要的是不需要在场景中添加灯光,如果不传任何参数,默认创建白色材质

import {MeshBasicMaterial} from 'three'
const material = new MeshBasicMaterial()

创建一个最基本的threejs程序

现在我们开始利用上面介绍的内容,开始创建一个简单的程序 需要引入的类(后面不再重复引入了)

import {
  BoxGeometry,
  Color,
  Mesh,
  MeshBasicMaterial,
  PerspectiveCamera,
  Scene,
  WebGLRenderer,
} from 'three'
const app = document.querySelector('#app')
  1. 创建场景,并且给场景添加背景颜色
const scene = new Scene()
// 给场景设置背景色(可以像在css一样使用颜色)
scene.background = new Color('skyblue')
  1. 创建相机,并且设置相机位置

注: 我们创建的每个对象最初都位于(0,0,0)位置,相机也是一样,后面添加在场景的任何对象也会被定位在(0,0,0),所以我们要更改相机的位置,以便于我们可以看到场景中的内容

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

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

// 更改相机的位置
camera.position.set(0,0,10) // set(x,y,z)

// 或者单独设置位置,和上面的效果是一样的
camera.position.x = 0
camera.position.y = 0
camera.position.z = 10
  1. 创建一个可见对象

注:如果使用 MeshBasicMaterial 之外的材质类型,则需要灯光才可以看的到物体,否则无法看到任何东西

// 几何体
const geometry = new BoxGeometry(2,2,2)
// 材质
const material = new MeshBasicMaterial()
// 创建网格,并将几何、材质传入
const mesh = new Mesh(geometry, material)
  1. 将网格添加到场景中
scene.add(mesh)
  1. 创建渲染器

它负责将场景绘制(渲染)到<canvas>元素中

const renderer = new WebGLRenderer()
// 设置渲染器大小
renderer.setSize(app.clientWidth, app.clientHeight)

设置像素比(DPR)这是为了防止HiDPI显示模糊所必须的(也称视网膜显示器)

renderer.setPixelRatio(window.devicePixelRatio)
  1. <canvas>元素添加到我们的页面

canvas元素已经被自动创建并且存储在renderer.domElement中,但是在我们看到它之前我们需要将它添加到页面中

app.append(renderer.domElement)
  1. 最后一步,渲染场景
renderer.render(scene, camera)

在这里我们告诉渲染器使用相机创建场景的静态图片并将该图片输出到<canvas>元素中。如果一切正确,将看到蓝色背景下的白色立方体(准确点说是白色正方形😂,因为是正对着我们的,所以只能看见一个面),很难看出是立方体😂,后面会逐步解决这个问题,下面这张图是渲染效果

image.png
  1. 完整代码
import {
  BoxGeometry,
  Color,
  Mesh,
  MeshBasicMaterial,
  PerspectiveCamera,
  Scene,
  WebGLRenderer,
} from 'three'

const app = document.querySelector('#app')

// 创建场景
const scene = new Scene();
scene.background = new Color('skyblue');

// 创建一个相机
const fov = 35; // 角度
const aspect = app.clientWidth / app.clientHeight; // 长宽比
const near = 0.1; // 近截面
const far = 1000; // 远截面

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

// 创建几何和材质
const geometry = new BoxGeometry(2, 2, 2);
const material = new MeshBasicMaterial()

const mesh = new Mesh(geometry, material) // 网格

scene.add(mesh)

const renderer = new WebGLRenderer();

renderer.setSize(app.clientWidth, app.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio)

app.append(renderer.domElement)

renderer.render(scene, camera)

总结

有人会觉得学了那么久才写个立方体,而且还是个长得像正方形的立方体,不要急,一切总要一步一步的来嘛

这上面的内容基本都是文章顶部推荐的 DISCOVER three.js 书中内容,我只是做了一些筛选,如果想看更详细的讲解可以直接去看 DISCOVER three.js 这本书,因为threejs迭代的很快,书中一些创建几何体的方法有的已经弃用了,大家再看的时候还是要注意点

最后,我也是一个刚刚入坑的小白,刚刚开始学Threejs,写的不对的地方还望各位大佬指正(轻喷哈),一起进步哈😁😁😁,如果有好的学习资料和学习途径也欢迎推荐一波哈