Skip to content
On this page

three.js

1.Three三要素相机,场景,几何体初始化

1.引入Three.js

js
import * as THREE from "three";

2.创建场景

js
// 1.创建一个场景
const scene = new THREE.Scene();

3.初始化一个相机

js
// 1.创建一个相机
const camera = new THREE.PerspectiveCamera(75, window, innerWidth / window.innerHeight, 0.1, 1000); // 角度,长宽比.可视近端,可视远端
// 2.相机的位置设置(x,y,z)坐标位置
camera.position.set(0,0,100);
// 3.相机放在场景当中
scene.add(camera);

4.初始化一个几何体

js
// 1.场景添加物体
// 1.1 创建 长宽高 值为1 的立方体  几何
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
// 1.2 给几何体设定材质
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
// 1.3 根据几何体和材质创建物体
const cube = new THREE.Mesh( geometry, material );
// 1.4 物体添加到场景中
scene.add( cube );

5.初始化一个**渲染器,**用来通过相机把画面渲染到

js
// 7.初始化渲染器
const renderer = new THREE.WebGL1Renderer();
// 8.设置渲染的尺寸大小
renderer.setSize(window.innerWidth,window.innerHeight);
// 9.将webgl渲染的canvas内容添加到body上
document.body.appendChild(renderer.domElement);
// 10.使用渲染器,通过相机将场景渲染出来
// renderer.render(scene,camera);

6.创建渲染函数,每一帧渲染,

js
// 创建渲染函数,每一帧渲染
function render(){
    renderer.render(scene,camera);
    // 请求动画帧,JavaScript会请求上次函数执行的结果继续执行这个函数
    requestAnimationFrame(render)
}
render()

2.几何体的移动,旋转,缩放

移动

js
// 修改物体的位置
cube.position.set(5,0,0)
cube.position.x = 5
cube.position.y = 5
cube.position.z = 5

缩放

js
// 物体的缩放
cube.scale.set(3,2,1)
cube.scale.x= 5
cube.scale.y = 2.5
cube.scale.z = 5

旋转

js
// 物体的旋转
cube.rotation.set(40,0,0,"XYZ")

3.Group概念 容器

引入概念

如果你要实现一个机器人在跑步,那么机器人的头、四肢、躯干等都是要整体移动的,group可以将一系列的mesh模型组成一个整体,这样就可以实现整体移动了。

js
// 创建group容器
const group = new THREE.Group()
// 创建每一个物体
const cube1 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xfff000 }))
const cube2 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xff0000 }))
const cube3 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xf00ff0 }))
// 将每一个物体添加到group容器当中
group.add(cube1,cube2,cube3)
// 将容器添加到场景当中
scene.add(group)
// 所以我们只需要移动group 就可以实现容器里面每个物体的移动,缩放,旋转
group.position.set(2,0,0)
group.scale.set(2,2,2)
group.rotation.set(Math/2,0,0,"XYZ")

4.GSAP动画库

4.1 什么是“GSAP”?

GreenSock 动画平台 (GSAP) 是一套行业知名的工具套件,用于超过 1100 万个网站,其中包括超过 50% 的获奖网站!你可以在任何框架中使用GSAP来制作 JavaScript***可以触及的几乎所有东西。***无论您是想为 UI、SVG、Three 还是 React 组件制作动画.js - GSAP 都能满足您的需求

核心库包含创建超、跨浏览器友好动画所需的一切。这就是我们将在本文中逐步介绍的内容。

除了核心,还有各种各样的插件您无需学习它们即可开始使用**,但它们可以帮助解决特定的动画挑战,例如基于滚动的动画可拖动的交互、变形等。

4.2 使用

下载:

js
npm i gasp

引入:

js
// 导入动画库
import { gsap } from 'gsap'

使用:

js
// 设置动画
// 哪个元素(物体),移动哪个轴 多少距离,所用时间 间隔多长时间
var animation = gsap.to(cube.position, {
    // x轴移动10距离
    x: 5,
    // 完成动画所需时间
    duration: 3,
    // 往返运动
    yoyo: true,
    // 延迟开启动画 单位S
    delay: 3,
    // 动画移动速率
    // ease: "elastic.out(1, 0.3)", 

    // 动画重复次数,无限次:-1
    repeat: -1,
    // 动画完成之后的回调
    onComplete: () => {
        console.log("动画完成");
    },
    // 动画开始之后的回调
    onStart: () => {
        console.log("动画开始");
    }
})
gsap.to(cube.rotation, { x: 180, duration: 3 })

// 点击停止动画
window.addEventListener('dblclick', () => {
    console.log(animation);
    if (animation.isActive()) {
        // 暂停动画
        animation.pause();
    } else {
        // 恢复动画
        animation.resume()
    }

})

2.二维平面PlaneGeometry

1.PlaneGeometry几何体介绍

PlaneGeometry可以用来创建非常简单的矩形,创建这种几何体非常简单,

js
const geom = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)

按照如上语句便可以创建

建一个简单的二维矩形平面,接下来介绍一下它的常用属性

属性必须描述
width该属性指定矩形的宽度
height该属性指定矩形的高度
widthSegments该属性指定矩形的宽度应该分成几段
heightSegments该属性指定矩形的高度应该分成几段
  • 通过width属性调整平面的宽度
  • 通过height属性调整平面的高度
  • 通过widthSegments属性调整平面宽度分的段数
  • 通过heightSegments属性调整平面高度分的段数>

3.轨道控制器

初始化镜头轨道控制器 OrbitControls ,通过它可以对三维场景用鼠标 🖱 进行缩放、平移、旋转等操作,本质上改变的不是场景,而是相机的位置参数。可以选择通过设置 controls.enableDamping 为 true 来开启控制器的移动惯性,这样在使用鼠标交互过程中就会感觉更加流畅和逼真

第一步:

js
//导入控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

第二步:

js
// 实例化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 阻尼开启
controls.enableDamping = true;

4.TextGeometry文本几何体

4.1 TextGeometry文本几何体介绍

TextGeometry可以用来创建三维文本。其实是通过指定字体创建字符,然后像拉伸几何体那样把文字拉伸,继而得到三维文本。创建TextGeometry文本几何体有很多参数与ExtrudeGeometry几何体一样,具体如下:

属性必须描述
size该属性指定文本大小,默认值是100
height该属性指定文本可以厚度,默认值是50
font该属性指定文本的字体,是一个THREE.Font对象
bevelEnabled该属性指定文本拉伸时是否启用斜角,默认false
bevelThickness该属性指定文本拉伸体斜角厚度,默认值是10
bevelSize该属性指定文本拉伸体斜角的高度。默认值是8
bevelSegments该属性指定文本拉伸体斜角的分段数,段数越多斜角越光滑,默认值是3
curveSegments该属性指定文本拉伸时拉伸曲线的分段数,段数越多曲线越光滑,默认值是4

4.2 TextGeometry文本几何体使用

4.2.1 字体文件引入

示例中使用的是helvetiker字体,通过文件 helvetiker_regular.typeface.js导入字体,将字体文件helvetiker_regular.typeface.js 放在public/font目录下,字体文件可以在这里下载helvetiker_regular.typeface.js引入过程如下:

const publicPath = process.env.BASE_URL
JS
const textLoader = new THREE.FontLoader()
//导入字体
textLoader.load(
  `${publicPath}font/helvetiker_regular.typeface.js`,
  font => {
	// 这里可以获取到font就是创建文本几何体需要的字体
})

4.2.2 3D字体对象的创建

js
// 创建几何体	
const geometry = new TextGeometry( 'Hello three.js!', {
		font: font,
		size: 80,
		height: 5,
		curveSegments: 12,
		bevelEnabled: true,
		bevelThickness: 10,
		bevelSize: 8,
		bevelSegments: 5
} );
// 创建材质
const meshMaterial = new THREE.MeshStandardMaterial({
  color: 0xeeffff
})
// 创建文字网格对象
this.mesh = new THREE.Mesh(geometry, meshMaterial)
// 网格对象添加到场景中
scene.add(this.mesh)

5.阴影

阴影的基本使用

  1. 材质要满足能够对光照有反应
  2. 设置渲染器开启阴影的计算 renderer.shadowMap.enabled = true;
  3. 设置光照投射阴影 directionalLight.castShadow = true;
  4. 设置物体投射阴影 sphere.castShadow = true;
  5. 设置物体接收阴影 plane.receiveShadow = true;

只有三种光可以支持阴影

  • PointLight 点光源
  • DirectionalLight 方向光
  • SpotLight 聚光灯

1.设置渲染器开启阴影的计算

js
// 告诉我们的渲染器处理阴影映射
renderer.shadowMap.enabled = true
js
// 设置物体投射阴影
sphere.castShadow = true;
js
// 设置物体接收阴影 
plane.receiveShadow = true;
js
// 设置光照投射阴影 
directionalLight.castShadow = true;

优化阴影

设置阴影贴图的分辨率

js
// 设置阴影贴图的分辨率 -- 提高流畅度
directionalLight.shadow.mapSize.set(4096, 4096);

定向光摄像机助手

js
// 创建定向光相机助手
const directionalLightHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
scene.add(directionalLightHelper)

关闭定向光摄像机助手

js
directionalLightHelper.visible = false

修改物体距离光相机的距离

js
// 修改定向光相机 距离物体的距离
directionalLight.shadow.camera.near = 0.5;
// 光所到达的最远距离
directionalLight.shadow.camera.far = 16;
// 这下面四个值的正数一定要相等且对称
directionalLight.shadow.camera.top = 2;
directionalLight.shadow.camera.bottom = -2;
directionalLight.shadow.camera.left = -2;
directionalLight.shadow.camera.right = 2;

设置阴影贴图模糊度

js
// 设置阴影贴图模糊度
directionalLight.shadow.radius = 20;

设置阴影贴图的类型 这里采用的是PCF软阴影(边缘更好看-更丝滑)

js
// 设置阴影贴图的类型 这里采用的是PCF软阴影
renderer.shadowMap.type = THREE.PCFShadowMap

聚光灯-SpotLight

js
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
scene.add(light);
//直线光源
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(5, 5, 5);
spotLight.castShadow = true;
spotLight.intensity = 2;
// 添加光相机助手
const spotLightHelper = new THREE.CameraHelper(spotLight.shadow.camera)
scene.add(spotLightHelper)
js
// 设置阴影贴图模糊度
// spotLight.shadow.radius = 20;
// 设置阴影贴图的分辨率
spotLight.shadow.mapSize.set(1024, 1024);

// console.log(directionalLight.shadow);
spotLight.target = sphere;
// 角度
spotLight.angle = Math.PI / 6;
// 距离
spotLight.distance = 0;
// 明暗交界处
spotLight.penumbra = 0;
// 光的强弱
spotLight.decay = 0;
scene.add(spotLight);

gui.add(sphere.position, "x").min(-5).max(5).step(0.1);
gui
  .add(spotLight, "angle")
  .min(0)
  .max(Math.PI / 2)
  .step(0.01)
  .name('角度');
gui.add(spotLight, "distance").min(0).max(10).step(0.01).name('距离');
gui.add(spotLight, "penumbra").min(0).max(1).step(0.01).name('明暗交界处');
gui.add(spotLight, "decay").min(0).max(5).step(0.01).name('光的强弱');

点光源 -PointLight

物理库cannon.js的使用

1.基本使用

下载

js
npm i cannon

引入

js
import * as CANNON from 'cannon-es'

创建

js
// 初始化Cannon中的物理世界World
const world = new CANNON.World()

添加重力

js
// 物理世界中是要有重力加速度的,需要为其设定大小及方向:
// 设置物理世界中的重力,设置为y轴负方向的-9.8 m/s²,模拟真实世界
world.gravity.set(0, - 9.82, 0)

在物理世界添加物体

Box , Cylinder , Plane , Sphere , Etc

第一步,确定形状 :

相当于threejs里面的==BoxGeometry==几何体

js
// 创建球体 只有一个参数 radius 就是半径 值为数字
const shape = new CANNON.Sphere(radius)

第二步,添加body :

相当于ThreeJS 里面==MeshBasicMaterial==几何体材质

js
const body = new CANNON.Body({
        mass: 1,
        position: new CANNON.Vec3(0, 3, 0),
        shape: shape,
        material: defaultMaterial
    })
  • mass : 弹性运动的主要属性: 质量(mass)是安装在弹簧的端部的物体的重量或分量。 伸展系数(stiffness)是弹性运动的困难度,这通常对应于它的厚度,以及如何紧密它盘绕。
  • position : 位置 三维向量x,y,z
  • shape : 形状
  • material

第三步,将物体添加到物理世界world中

相当于ThreeJs将物体添加到场景中

js
world.addBody(body)

第四步,更新物理世界渲染

js
const clock = new THREE.Clock()
let oldElapsedTime = 0

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()
    const deltaTime = elapsedTime - oldElapsedTime
    oldElapsedTime = elapsedTime

    // Update physics
    //第一个参数:设置更新物理世界cannon.world的步长timestep,这里选用60Hz的速度,即1.0 / 60.0:
    world.step(1 / 60, deltaTime, 3)

2.copy方法

之前

js
// 将物理世界物体的坐标同步传给Threejs物体
	object.mesh.position.x = object.body.position.x
	object.mesh.position.y = object.body.position.y
	object.mesh.position.z = object.body.position.z

现在copy()

js
object.mesh.position.copy(object.body.position)
	object.mesh.quaternion.copy(object.body.quaternion)

3.两个物体接触之间的碰撞

js
const concreteMaterial = new CANON.Material('concrete')
const plasticMaterial = new CANNON.Material( 'plastic')
const conctePlasticContactMaterial = new CANON.ContactMaterial(
    concreteMaterial,
    plasticMaterial,
    {
        //摩擦力
        friction: 0.1,
        // 归还
        restitution: 0.7
    }
 )
world.addContactMaterial(concretePlasticContactMaterial)

小球(塑料)

js
const body = new CANNON.Body({
        mass: 1,
        position: new CANNON.Vec3(0, 3, 0),
        shape: shape,
        material: plasticMaterial
    })
   world.addBody( sphereBody)

地板(混凝土)

js
// Floor
const floorShape = new CANNON.Plane()
const floorBody = new CANINON.Body( )
fLoorBody.material = concreteMaterial 
floorBody.mass = 0
floorBody.addShape(floorShape)
floorBody.quaternion. setFromAxisAngle( 
	new CANNON.Vec3(- 1, 0, 0),
	Math.PI * 0.5
)
world. addBody(floorBody)

以下代码是对上面的优化 减少代码量

js
// Default material
const defaultMaterial = new CANNON.Material('default')
const defaultContactMaterial = new CANNON.ContactMaterial(
    defaultMaterial,
    defaultMaterial,
    {
        friction: 0.1,
        restitution: 0.7
    }
)
world.defaultContactMaterial = defaultContactMaterial

4.施加力的作用在物体上面

applyLocalForce()

  • 第一个参数,力的方向以及力度
  • 第二个参数,受力物体的坐标
js
sphereBody.applyLocalForce(new CANNON.Vec3(150,0,0), new CANNON.Vec3(0,0,0))

GLSL语言 - 学习

OpenGL着色语言(OpenGL Shading Language)是用来在OpenGL中着色编程的语言,它是一种类C语言,下面先学习一下基础语法。

日志

没有控制台,因此无法记录值。这是因为代码针对每个顶点和每个片段执行。记录一个值是没有意义的。

缩进

缩进不重要,可以随意。

分号

任何指令的结尾都需要分号。哪怕忘记一个分号都可能导致编译错误,使得整个材料都不起作用。

变量

GLSL是一种强类型语言,如C语言一般。

不能在操作中混合使用floatint,会报错

glsl
float a = 1.0;
int b = 2;
float c = a * b;

但是可以通过类型转换进行操作:

glsl
float a = 1.0;
int b = 2;
float c = a * float(b);

Vector 2

如果我们想存储具有x和y属性的2个坐标这样的值,我们可以使用vec2

glsl
vec2 foo = vec2(1.0, 2.0);

空的vec2将导致错误:

glsl
vec2 foo = vec2();

我们可以在创建vec2后更改这些属性:

glsl
vec2 foo = vec2(0.0);
foo.x = 1.0;
foo.y = 2.0;

执行vec2与浮点相乘等操作将同时操作x和y属性:

glsl
vec2 foo = vec2(1.0, 2.0);
foo *= 2.0;

Vector 3

vec3与vec2类似,但具有第三个名为z的属性。当需要3D坐标时,使用它非常方便:

glsl
vec3 foo = vec3(0.0);
vec3 bar = vec3(1.0, 2.0, 3.0);
bar.z = 4.0;

虽然我们可以使用x、y和z,但我们也可以使用r、g和b。这只是语法糖,结果完全一样。当我们使用vec3存储颜色时,它非常有效:

glsl
vec3 purpleColor = vec3(0.0);
purpleColor.r = 0.5;
purpleColor.b = 1.0;

vec3可以通过部分使用vec2来创建:

glsl
vec2 foo = vec2(1.0, 2.0);
vec3 bar = vec3(foo, 3.0);

我们也可以使用vec3的一部分来生成vec2:

glsl
vec3 foo = vec3(1.0, 2.0, 3.0);
vec2 bar = foo.xy;

Vector 4

最后,vec4的原理与前两个类似,但第四个值命名为wa(颜色alpha)

glsl
vec4 foo = vec4(1.0, 2.0, 3.0, 4.0);
vec4 bar = vec4(foo.zw, vec2(5.0, 6.0));

还有其他类型的变量,如mat2、mat3、mat4或sampler2D,我们将在后面看到这些变量。

在着色器内,一般命名以gl_开头的变量是着色器的内置变量,除此之外webgl_和_webgl还是着色器保留字,自定义变量不能以webgl_或_webgl开头。变量声明一般包含<存储限定符><数据类型><变量名称>,以attribute vec4 a_Position为例,attribute表示存储限定符,vec是数据类型,a_Position为变量名称。

原生函数

GLSL有许多内置的经典函数,如sin、cos、max、min、pow、exp、mod、clamp,也有非常实用的函数,如cross、dot、mix、step、smoothstep、length、distance、reflect、refract、normalize。

不幸的是,没有对初学者友好的文档,而且大多数时候,我们在网络上进行搜索,结果通常出现在以下网站:

Kronos Group OpenGL reference pages:本文档涉及OpenGL,但您将看到的大多数标准函数都与WebGL兼容。不要忘了WebGL只是一个访问OpenGL的JavaScript API。 Book of shaders documentation:《Book of shaders》主要关注片元着色器,与Three.js无关。但是这是一个很好的学习资源,它有自己的词汇表。

内置函数

1.step()

这个图案看起来是基于上一个图案,但是图案颜色值要么是 0.0 要么是 1.0,而不是渐变色。在 GLSL 中可以使用 if 条件判断语句来实现这一效果,但是应该避免使用条件判断语句以避免性能问题。我们可以使用 step(edge, value) 方法来实现这个功能,它接收两个参数:edge 表示一个临界值,第二个参数 value 是传入的参数,当传入参数小于临界值时,该方法返回 0.0,当传入参数大于临界值时,该方法返回 1.0

glsl
float strength = mod(vUv.y * 10.0, 1.0);
strength = step(0.5, strength);

2.mod() 模运算

现在我们来实现这种重复的渐变效果,此时需要用到模运算,模运算返回两数之间的余数,如:

  • 0.81.0 值为 0.8
  • 1.21.0 值为 0.2

在多数语言中,我们通常使用 % 运算符进行模运算,但是在 GLSL 中需要使用 mod() 方法。

glsl
float strength = mod(vUv.y * 10.0, 1.0);

3.abs()区域取值

来看看另一个例子,为了得到下面的结果,我们需要给 vUv.x设置一个偏移量,使它的值保持在 -0.50.5 之间。然后我们需要它的值始终是正数,也就是它的值从 0.5 变化到 0.0 再变化到 0.5。为了实现这一功能,我们可以使用 abs(...) 方法。

glsl
float strength = abs(vUv.x - 0.5);

Released under the MIT License.