threeJS学习笔记

渲染的基本要素

场景

使用const scene = new THREE.Scene();来创建一个场景,用于展示我们创建的物体或引入的建模

摄像机

在现实生活中,我们人眼就好比一个摄像机,离一个物体越近,看到的物体就显得越大,反之亦然;当我们把摄像机向左移动,就相当于物体向右发生了移动,反之亦然。

threejs中最基本的相机是透视摄像机PerspectiveCamera,实现方式为

const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
scene.add(camera)

第一个参数是Field of View,代表视场角度,单位是。我们人的眼睛就是一台摄像机,我们在正前方画一条垂直于地面直线,那么必定存在两个点使得点到人眼形成的角度之和为75度,以这个75度角发散出去能看到的范围就是可观测到的范围,如下图
在这里插入图片描述
第二个参数是宽高比,从上图可以看到,看出去的视角范围实际上是一个视锥,底部都是矩形的视觉区域,而每个矩形的宽高比必定为指定的宽高比。

网格(mesh)

mesh就可以理解为要在场景中展示的东西了,要生成一个mesh,需要一个几何体(Geometry)(或自己的3D模型),加上该几何体的材质Material)
在这里插入图片描述

在这里插入图片描述

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

渲染器(renderer)

当我们有了要观察的物体后,就可以把他们渲染到场景上,用摄像机去观察了

renderer通常使用WebGLRenderer来创建,可以在创建WebGLRenderer时指定画布dom元素

const canvas = document.querySelector(".my-canvas")!;
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(sizes.width, sizes.height);

也可以不传该参数,它会默认创建一个canvas元素,我们只需要把他添加到页面指定位置即可,如

const renderer = new THREE.WebGLRenderer();
renderer.setSize(sizes.width, sizes.height);
document.body.appendChild(renderer.domElement);

最后,我们调用renderer.render方法就可以把物体和摄像机放到场景中了

renderer.render(scene, camera);

但是,这时候在页面上看到的是一片黑,这是为什么呢?
在这里插入图片描述
在默认情况下,摄像机、物体都是放在正中心的位置,即(0, 0, 0)位置,所以此时相当于我们处于立方体的内部,那么从立方体内部往外看,自然什么都看不到,这时候我们需要把摄像机向后移或者把物体往前移动(也就是在Z轴上移动)即可

camera.position.z = 5;

在这里插入图片描述

需要注意的是,renderer.render相当于拍了一张照片,任何对于物体的修改或位置的移动,都需要重新执行一次renderer.render(scene, camera);来把场景和摄像机进行更新,即以后要做的动画,就是不断地一帧一帧地把物体实时地位置给渲染出来的过程

物体的变换

位置(position)

对于物体而言,其位置通常指的是它中心的坐标,一些实用的方法如下:

  • meth.position.length():获取场景中心点(0, 0, 0)到物体中心的距离,这个其实就可以理解为在空间直角坐标系中两点之间距离的计算
  • meth.position.distanceTo(someVector):物体中心到某个矢量点的距离,比如可以传camera.position来计算摄像机到物体的距离

position的类型是Vector3,可以在文档中查询Vector3包含的所有方法,这里不作列举

Axes Helper

拉出一个坐标系参照,可以配置仅在开发环境渲染辅助坐标系

const axesHelper = new THREE.AxesHelper();
scene.add(axesHelper);

scale

即缩放,对于下面这个物体
在这里插入图片描述
x轴缩放设为0.5时:
在这里插入图片描述
x轴缩放设为-0.25时:
在这里插入图片描述
可以看到,缩放设为负数并不会有什么特殊的含义,并且缩放也不会改变物体的position(中心点位置)

旋转rotation

旋转需要注意的是,旋转指的是以某根轴为中线进行旋转,比如旋转x轴 mesh.rotation.set(last - Math.PI * 0.01, 0, 0);,那就是以x轴为中线进行旋转,我写了个基本的动画,其旋转的方式如下:
在这里插入图片描述
同理,对于Y轴而言就是:
在这里插入图片描述

注意这里,旋转时应该使用JS内置的Math.PI常量进行旋转,即圆周率的倍数。当然也不是不能指定一个整数

设置旋转角度、位置、缩放,可以使用.x = number、.y = number、.z = number一个一个设置,也可以使用set一次性设置3个值

组内变换

如果物体多了,一般会给既定的物体分一个组,然后对这整个组进行操作,比如旋转或缩放等

在这里插入图片描述

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);

const mesh2 = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
mesh2.position.x = 1;
const group = new THREE.Group();
group.add(mesh, mesh2);
scene.add(group);

动画

通常使用requestAnimationFrame来实现,通过在回调函数中不断地重复调用requestAnimationFrame来做出动画效果

但是在不同设备上,屏幕的刷新率不同,那么requestAnimationFrame的执行速度也会不同,同理动画的速度也会不同

所以需要兼容不同设备刷新率情况下的动画,此时可以使用Date.now来计算实现

let lastTime = Date.now();
function animate() {
  const currentTime = Date.now();
  const deltaTime = currentTime - lastTime;
  lastTime = currentTime;
  requestAnimationFrame(animate);
  group.rotation.y += 0.01 * deltaTime;
  renderer.render(scene, camera);
}
animate();

此外,threeJS内部也提供了解决方案,使用内置的Clock类来实现

const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  group.rotation.y = clock.getElapsedTime() * Math.PI * 2;
  renderer.render(scene, camera);
}
animate();

Clock会从0开始计时,官网上的实现说的是优先用performance.now()实现,然后才是用Date.now实现
在这里插入图片描述
那么,有了动画,除了基本的旋转之外,就可以实现比如下面的这些小动画了:

指定每秒旋转一圈:

const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  group.rotation.y = clock.getElapsedTime() * Math.PI * 2;
  renderer.render(scene, camera);
}

因为getElapsedTime获取的是秒级单位,所以可以这样子写,2派就代表一个圆的直径,那么多少秒就是多少个圆的直径,也就是转多少圈了

结合三角函数

三角函数的图像中,sin(x)正弦函数随着x增大,其y值呈周期性地变化到1和-1
在这里插入图片描述
那么如果我们想让一个物体在Y轴移动出正弦函数曲线,就可以写成:

const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  group.position.y = Math.sin(clock.getElapsedTime());
  renderer.render(scene, camera);
}
animate();

在这里插入图片描述
如果想让它转圈,则可以对它的x也做类似地处理,不过呢要用余弦函数

const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  group.position.y = Math.sin(clock.getElapsedTime());
  group.position.x = Math.cos(clock.getElapsedTime());
  renderer.render(scene, camera);
}

在这里插入图片描述

使用动画库

GSAP是一个很牛B的动画库,官网是:https://gsap.com/

可以通过使用动画库API,更方便我们实现一些动画(比如直接调用预设)

import gsap from "gsap";
function animate() {
  requestAnimationFrame(animate);
  console.log(group.position);
  renderer.render(scene, camera);
}
gsap.to(group.position, {
  x: 3,
  y: 2,
  duration: 1,
});
gsap.to(group.position, {
  x: 0,
  y: -1,
  delay: 2,
  duration: 1,
});
animate();

可以看到控制台,它的原理就是在一秒内修改x和y分别到3和2,然后在2秒后,在1秒内把x和y分别改到0和-1
在这里插入图片描述

摄影机

透视摄像机(Perspective Camera)

前面已经讲了前两个参数,这里只讲后面两个参数nearfar

两个参数代表的是摄像机最近/最远可见的距离,这两个距离分别指的是近锥面到摄像机(人)和远锥面到摄像机(人)的距离
在这里插入图片描述
所以,只有far-near之间的阴影部分区域的物体,才可以被看到(即渲染在画布中)

near和far的默认值为 0.1 - 2000

正交摄像机(Orthographic Camera)

正交摄像机没有透视功能,他与透视摄像机最大的区别是"不会有近大远小"的效果,接收的参数是(左,右,上,下,近距,远距)

拿一个demo举例,透视相机从正前方和侧面看一个物体是这样:
在这里插入图片描述

相机位置:(0, 0, 3);物体位置:(0, 0, 0)

在这里插入图片描述

相机位置:(2, 1, 3);物体位置:(0, 0, 0)

当换成正交相机时,从正前方看,看到的图像是:
在这里插入图片描述
代码设置如下:

const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 100);
camera.position.z = 3;

测试会发现,无论怎么移动摄像机Z轴位置,看到的物体没有任何变化

移动X、Y轴位置,则是进行"左右" 或 "上下"的平移

代码表示的是,创建一个长宽均为2的正方形视框去观察画面中的物体

对于上面的例子,创建的物体是中心为(0, 0, 0),长宽高均为1的正方体

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });

刚刚把正交相机往正前方移动了3个单位长度,那么可以想到目前摄像机到物体正面的距离为3 - 0.5 = 2.5

假如把正交摄像机的近距设为> 2.5的值,那么就会看不到物体的正面了,比如把正交相机改为下面这样:

const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 2.51, 100);

此时画面显示为:
在这里插入图片描述

因为我们此时恰好深入到了物体的内部0.01的位置,从物体里往外看自然看不到物体本身

  • 26
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值