Three.js 入门——核心概念和坐标系理解

Three.js 是什么?

一个封装了 WebGL 的库,简化 WebGL 的使用

WebGL vs OpenGL

OpenGL 主要被认为是一种 API(应用程序编程接口),它为我们提供了大量可用于操作图形和图像的函数,主要用 C语言编写的。

然而,OpenGL 本身并不是一个 API,而是一个规范,规定了每个函数如何执行和输出,至于具体实现,是由各个厂商的开发者根据自己的硬件特性开发出相应的 API。市场上,OpenGL 大都是显卡、GPU 、浏览器厂商来实现的,例如 Google。

WebGL 是一种用于在Web浏览器中展示 3D 图形的技术,是基于 OpenGL ES 2.0 的 Javascript API。也就是,通过浏览器提供的接口,我们可以使用底层的 OpenGL 库。

OpenGL ES 是 OpenGL 为了满足嵌入式设备需求而开发的一个特殊版本,是它的子集。但和 OpenGL 还是有差别,并不是只取其中一部分。

基础的 Three.js 应用结构

要写一个 Three.js 应用常用用到的四个核心概念,包括渲染器(Renderer)、场景(Scene)、相机(Camera)、网格(Mesh)。
在这里插入图片描述

渲染器 Renderer

将在指定相机角度下看到的 scene 画面绘制到浏览器上,也就是将摄像机的三维场景渲染成一个二维场景。

如果涉及到动态的场景,例如旋转,需要创建循环渲染 requestAnimationFrame,它会以每秒 60 次的频率来绘制场景

var renderer = new THREE.WebGLRenderer({ antialias: true });
// 将渲染器的像素比设置为设备像素比
renderer.setPixelRatio(window.devicePixelRatio);
// 将渲染器的大小设置为窗口的内部宽度(window.innerWidth)和高度(window.innerHeight)
//  如果为 1/2 ,视图将缩小一半
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置动画循环
document.body.appendChild(renderer.domElement);

function renderFunc() {
  requestAnimationFrame(renderFunc); // 循环渲染
  renderer.render(scene, camera);
}

renderFunc()

场景 Scene

场景是一个容器,可以将所有我们想渲染的物体都装在里面

 var scene = new THREE.Scene();
 scene.add(cube) // 把cube装入场景里,初始位置为 (0,0,0)

相机 Camera

在浏览器里渲染的景象就是从相机视角拍出来的。
相机的位置、视角不同,拍到的风景也不同,只能拍到相机投影范围内的景物
相机的类型有两种,透视摄像机(PerspectiveCamera)、正投影相机(OrthographicCamera)

透视摄像机 PerspectiveCamera

近大远小,距离相机近的地方大,距离相机远的地方小
在这里插入图片描述

var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

PerspectiveCamera 的参数说明,如下图所示:

  • fov:视场。可以理解为睁眼时,下眼皮到上眼皮睁开的角度。假如是 90 度,那么两个眼皮距离中间的角度分别是 45 度。假如是 180 度,那么物体会变小,因为在整个可视区的占比变小了。
  • aspect:长宽比。整个窗口的长宽比,一般是 window.innerWidth / window.innerHeight
  • near:近面。距离相机多近的距离开始渲染场景。
  • far:远面。定义相机可以看多远。

近面到远面的范围,为相机可以看到的范围,出了这个范围,则不可见。

在这里插入图片描述

正投影相机 OrthographicCamera

正投影相机的所有对象渲染的尺寸都一样。因此它不关心使用什么样的长宽比和视角。
在这里插入图片描述

var camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far)

OrthographicCamera 的参数说明,如下图所示:

  • left:左边界。可视范围的左平面,超出这个范围的不会被渲染。
  • right:右边界。可被渲染的最右面。
  • top:上边界。可被渲染的最上面。
  • bottom:下边界。可被渲染的最下面。
  • near:近面。距离相机的位置,从该位置开始渲染
  • far:远面。基于相机的位置,一直渲染到场景中的这一点

只有在范围 left、right、top、bottom、near、far 范围内的可见。超出该范围不可见。

在这里插入图片描述

网格 Mesh

可以理解为用一种特定的材质(Material)来绘制的一个特定的几何体(Geometry)

var cubeGeometry = new THREE.BoxGeometry(4, 4, 4); // 绘制立方体
var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true}); // 绘制材质
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial); // 用该材质绘制后的立方体
scene.add(cube); // 添加到场景中

坐标系转换

坐标如下所示
在这里插入图片描述
可通过创建坐标系辅助对象,来展示坐标系。红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.

var coneAxesHelper = new THREE.AxesHelper(size); // size 为轴的线段长度

右手坐标系

Three.js 使用的是右手坐标系,x 轴朝右,y 轴朝上,z 轴朝向自己。

在这里插入图片描述

围绕轴旋转

拇指指向轴正方向,四指弯曲的方向为旋转正方向。
在这里插入图片描述

如下图所示,将 rotation 的 y 值,从 0 度改为 50 度,物体沿着 y 轴逆时针旋转

在这里插入图片描述
物体旋转 50 度:
在这里插入图片描述

editor 编辑器

使用 Three.js 源码 的 editor编辑器,可以让我们更直观的理解 Three.js 中的物体的坐标系位置。

将项目跑起来,点击到对应的目录即可。

由此可看出,相机被添加到场景中时的初始位置是 (0,0,0)
在这里插入图片描述
将相机沿着 Z 轴负方向移动,如下图展示的负值。对比上下图,由此可知,相机朝向 Z 轴的负方向
在这里插入图片描述

一个简单的 Three.js 应用

HTML 代码:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
		</style>
	</head>
	<body>
		<script type="module" src="/main.js"></script>
	</body>
</html>

Javascript 代码:

import * as THREE from 'three';

const scene = new THREE.Scene(); // 场景
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); // 透视摄像机

const renderer = new THREE.WebGLRenderer(); // 渲染器
renderer.setSize( window.innerWidth, window.innerHeight ); // 设置渲染器的大小
document.body.appendChild( renderer.domElement ); // 将渲染器元素添加到 HTML 文档中

const geometry = new THREE.BoxGeometry( 1, 1, 1 ); // 创建一个立方体几何
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); // 创建材质
const cube = new THREE.Mesh( geometry, material ); // 用材质绘制后的立方体
scene.add( cube ); // 添加到场景中

camera.position.z = 5; // 设置相机的位置,因为相机朝向 Z 轴负方向。往后推,这样才能看得到全局

function animate() {
	requestAnimationFrame( animate ); // 循环渲染

	cube.rotation.x += 0.01; // 立方体沿 x 轴旋转
	cube.rotation.y += 0.01; // 立方体沿 y 轴旋转

	renderer.render( scene, camera ); // 渲染
}

animate(); 

换成优雅的类封装

把所有的代码放在同一个页面里,会越堆越多,阅读体验不好。因此可按照功能拆分成函数。进一步可放到类里调用。

以下代码仅提供思路。

// 调用
 game = new Game("canvas.webgl");
 game.start();
 
// 封装
export class Game {
  width!: number; // 视图宽度
  height!: number; // 视图高度
  aspect!: number; // 视图宽高比
  ...
  constructor(selector: string) {
 	if (Game.instance) {
      return Game.instance;
    }
    Game.instance = this;
    this.selector = selector;

	this.initConfig();
    this.initTime();
    this.initResource();
    this.initContainer();
    this.initScene();
    this.initCamera();
    this.initRenderer();
    ...
  }

  initConfig() {
    this.width = window.innerWidth;
    this.height = window.innerHeight;
    this.aspect = this.width / this.height;
  }

  initScene() {
    this.gameScene = new GameScene();
  }

  initCamera() {
    this.gameCamera = new GameCamera();
  }
  ...
}

参考资料,也为不错的学习资料。

《Three.js 开发指南》
Three.js 专栏
Three.js-docs
Three.js-manual

  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值