Three.js 入门(一)

简介

随着浏览器功能的愈加强大,在浏览器中显示三维图像的需求逐渐增大,WebGL技术应运而生,通过提供的接口可以实现相应功能,但是需要学习底层逻辑,包括着色器语法、数学、图形学等相关知识,上手难度比较大,要学好并不简单,Three.js是基于WebGL封装的一个3D引擎,用来便捷地满足浏览器下的3D需要。

开发准备

  1. 安装Three.js:可以通过CDN的方式或者npm安装,本文安装的是0.122.0版本,如果相关api不符合,可以检查版本或者根据当前版本实现功能。 npm i --save three@0.122.0

  2. 准备HTML文件:需要一个canvas节点用于挂载。 <canvas id="webgl"></canvas>

  3. 导入Three.js

    1. 整体导入:import * as THREE from 'three'
    2. 按需加载:import { WebGLRenderer} from 'three'(导入的内容不必关心,后文会介绍)

注:本文于webpack环境下实现,具体的搭建方式与本文内容无关不多做赘述,使用CDN方式亦可

基础知识

Three.js中,主要由场景(Scene)、相机(Camera)、网格(Mesh)、渲染器(Renderer)组成

场景 Scene

可以理解为用来展示的空间,所有的图形都需要被添加到场景中才会生效。

主要使用方式:

  • 创建:new THREE.Scene()
  • 添加:scene.add()

相机 Camera

虽然构建的是一个三维图像,但是显示屏的成像结果是二维的,那如何将三维图像转化为二维图像呢?其实和拍照片是类似的。相机对准中心,调整角度和焦距,按下快门,拍摄出一张照片。Three.js中的相机也是类似的操作。

最常使用的是透视相机PerspectiveCamera,这和现实生活的视角是一致的,同种类的还有正交相机OrthograpicCamera,会用于一些比较特殊的场景,在此就不多做介绍了,感兴趣的同学可以自行搜索。

主要使用方式:

  • 创建:new THREE.PerspectiveCamera(fov, aspect, neer, far)

    • fov:相机的视角大小,可观测角度范围

    • aspect:宽高比 w/h

    • near、far:近平面和远平面的距离

      • Tips:尽量不要使用极端值,如0.00000019999999等,比较容易造成z-fighting效果,在此不做介绍
  • 更新观测点:camera.lookAt(Vector3)其中的Vector3是一个空间坐标
  • 设置宽高比: camera.aspect = width ``/ height

网格 Mesh

网格是放置三维模型的容器,添加一个网格并且设置几何体Geometry与材质Material即可创建物体。可以通过创建不同的几何体和材质实现各式各样的结构,下面介绍一个最简单的立方体创建(均使用最基础的属性创建,更多属性在此不一一列举,如果后续有用到其他属性会再做解释)。

  • 创建立方体:new THREE.BoxGeometry(1, 1, 1) 创建长宽高都为1的立方体
  • 创建材质:new THREE.MeshBasicMaterial({ color: 0xFF0000 )创建一个红色的材质
  • 创建网格:new Mesh(geometry, material)

渲染器 Renderer

物体、物机被创建并添加到场景中,但是内容不是一成不变的,何时渲染,渲染的尺寸大小,渲染的分辨率等属性都要靠渲染器来实现。

主要使用方式

  • 创建:new THREE.WebGLRenderer({ canvas }) 其中的 canvas 是在HTML中声明的node节点。
  • 设置渲染的尺寸:renderer.setSize(width, height)
  • 开始渲染:renderer.render(scene, camera)

代码实现

arduino 复制代码
import * as THREE from 'three'

/// Scene
const scene = new THREE.Scene()

/// Red cube
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
// 网格由几何体和材料组成
const mesh = new THREE.Mesh(geometry, material)

scene.add(mesh)

const sizes = { width: 800, height: 600 }
/// Camera
// 角度 + 宽高比 + 近远平面
const aspectRadio = sizes.width / sizes.height
const camera = new THREE.PerspectiveCamera(75, aspectRadio, 0.1, 100)
scene.add(camera)

/// Renderer
const canvas = document.querySelector('#webgl')
const renderer = new THREE.WebGLRenderer({ canvas })
renderer.setSize(sizes.width, sizes.height)
renderer.render(scene, camera)

实现以上代码后,可以看到如下结果:

黑色区域就是视图渲染的效果,发现有两个明显的问题:

  1. 黑色区域并没有布满屏幕,效果不好
  2. 黑色区域没有显示内容,红色立方体去哪儿了?

先来解决第一个问题,这是因为sizes800600,前文有提到它代表的是渲染的尺寸范围,应该将其改为屏幕宽高

arduino 复制代码
const sizes = { width: window.innerWidth, height: window.innerHeight }

可以看到还有边框,不要忘了设置css属性哦

css 复制代码
body {
    margin: 0;
    padding: 0;
}

html,
body {
    overflow: hidden;
}

这时候能看到屏幕全黑了,我们来解决第二个问题。

考虑一个问题,我们的成像是怎么做到的?用的相机。那新生成的相机和几何体的位置会在哪儿呢,一般来说是在原点的,而相机是向着 -z 轴方向看的,这显然是不合适的,做法也很简单,移动一下相机的位置即可。

ini 复制代码
camera.position.z = 3

此时观测角度是从正上方往下看的,可以看到出现了一个红色正方形。

如果向上移动相机呢?我们设置相机的 y 坐标为3

观测好像出问题了,问题在于相机的朝向是 -z 轴方向,相机的位置是(0, 3, 3),看向的显然不是原点,此时我们要把它指回原点,有两种做法:

  1. 直接指向原点坐标 camera.lootAt(new THREE.Vector(0, 0, 0))
  2. 指向我们创建的网格物体 camera.lootAt(mesh.position)

方向变正确了!

接下来我们让它动起来。方法很简单,只需要实现一个简易的tick方法:

scss 复制代码
const tick = () => {
    // 改变网格的旋转角度
    mesh.rotation.x += 0.01
    mesh.rotation.y += 0.01

    // 每次都必须重新进行渲染操作
    renderer.render(scene, camera)
    // 每一帧都重新调用该方法
    requestAnimationFrame(tick)
}
tick()

最终代码

arduino 复制代码
import * as THREE from 'three'

/// Scene
const scene = new THREE.Scene()

/// Red cube
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
// 网格由几何体和材料组成
const mesh = new THREE.Mesh(geometry, material)

scene.add(mesh)

const sizes = { width: window.innerWidth, height: window.innerHeight }
/// Camera
// 角度 + 宽高比 + 近远平面
const aspectRadio = sizes.width / sizes.height
const camera = new THREE.PerspectiveCamera(75, aspectRadio, 0.1, 100)
camera.position.z = 3
scene.add(camera)

/// Renderer
const canvas = document.querySelector('#webgl')
const renderer = new THREE.WebGLRenderer({ canvas })
renderer.setSize(sizes.width, sizes.height)
const tick = () => {
    mesh.rotation.x += 0.01
    mesh.rotation.y += 0.01

    renderer.render(scene, camera)
    requestAnimationFrame(tick)
}
tick()

总结

本文介绍了Three.js中的主要框架和基础内容,从零到一实现了一个最简易立方体的显示和转动,下一篇文章会介绍简单的优化和一些辅助工具,包括自带的和使用广泛的工具。

相关推荐
&白帝&44 分钟前
Vue.js 过渡 & 动画
前端·javascript
总是学不会.1 小时前
SpringBoot项目:前后端打包与部署(使用 Maven)
java·服务器·前端·后端·maven
Fanfffff7202 小时前
深入探索Vue3组合式API
前端·javascript·vue.js
光影少年2 小时前
node配置swagger
前端·javascript·node.js·swagger
昱禹2 小时前
关于CSS Grid布局
前端·javascript·css
啊QQQQQ2 小时前
HTML:相关概念以及标签
前端·html
就叫飞六吧3 小时前
vue2和vue3全面对比
前端·javascript·vue.js
Justinc.3 小时前
CSS基础-盒子模型(三)
前端·css
qq_2518364573 小时前
基于ssm vue uniapp实现的爱心小屋公益机构智慧管理系统
前端·vue.js·uni-app
._Ha!n.3 小时前
Vue基础(二)
前端·javascript·vue.js