banner
banner
banner
NEWS LETTER

threeJS-基础知识

Scroll down

基础知识

  • Blender:制作3D文字
  • 科大讯飞星火:生成故事单元剧情
  • Blockade Labs: 根据故事情节生成全景图片(要使用英文单词)
  • TTS-Vue:生成语音作旁白
  • 将文本,图片,音频,按钮(按钮文本,跳转场景的索引值),场景旋转的起始和结束,持续时间存放到对象数组中。按照步骤,载入图片,页面搭建,监听跳转函数等完成交互式2D故事游戏
  • leiapix:将静态图片生成3D图片
  • three.js官网: 官网
  • 导入three.js的文件后

第一步: 创建场景、相机和渲染器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 引入three.js
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 );

第二步: 创建几何体、材质、网格 并添加到场景中

1
2
3
4
5
6
7
8
9
10
11
// 创建几何体 --- 决定形状
const geometry = new THREE.BoxGeometry(1,1,1);

// 创建材质 --- 决定外观
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })

// 创建网格
const cube = new THREE.Mesh(geometry,material)

// 将网格添加到3场景中
scene.add(cube)

第三步: 使用世界坐标辅助器和轨道控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 设置相机的位置 --- 调整视角方便看到坐标轴
camera.position.z = 5
camera.position.y = 2
camera.position.x = 2
camera.lookAt(0,0,0);

// 添加世界坐标辅助器 --- 绿y,红x,蓝z
const axesHelper = new THREE.AxesHelper( 5 );
scene.add(axesHelper)

// 添加轨道控制器,选择要监听的dom元素
const controls = new OrbitControls( camera, renderer.domElement );
// 设置带阻尼的惯性
controls.enableDamping = true
// 设置阻尼系数
controls.dampingFactor = 0.05
// 设置旋转速度 --- 自动
controls.autoRotate = true
// 设置旋转速度 --- 手动旋转的速度
// controls.rotateSpeed = 0.05

倒数第二步: 渲染函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 渲染函数
const animate = () => {
// 更新轨道控制器
controls.update()
// 请求动画帧,每次调用渲染函数都会调用animate,即循环渲染
requestAnimationFrame(animate)
// 旋转
// cube.rotation.x += 0.01
// cube.rotation.y += 0.01
// 渲染
renderer.render(scene, camera)
}

animate()

最后一步: 监听窗口的变化

  • 监听窗口的变化,仪表窗口缩小,物体没有跟着自适应回到视图的中心
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 监听窗口变化
    window.addEventListener("resize", () => {
    // 重置渲染器宽高比
    renderer.setSize(window.innerWidth, window.innerHeight)
    // 重置相机宽高比
    camera.aspect = window.innerWidth / window.innerHeight
    // 更新相机投影矩阵
    camera.updateProjectionMatrix()
    })

认识向量(Vector3)、父元素和子元素的position的关系、缩放、旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 通过案例来分析,首先先创建一个父元素的网格和子元素的网格

// 创建几何体
const geometry = new THREE.BoxGeometry(1,1,1);

// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const parentMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 })

// 创建父元素的线框模式
parentMaterial.wireframe = true

// 创建cube的父元素
let parentCube = new THREE.Mesh(geometry,parentMaterial)
// 创建子元素的网格
const cube = new THREE.Mesh(geometry,material)

// 在父元素中添加子元素
parentCube.add(cube)
// 设置父元素的坐标轴
parentCube.position.set(-3,0,0)
// 设置父元素的放大
parentCube.scale.set(2,2,2)
// 设置父元素绕着x轴旋转45du
parentCube.rotation.x = Math.PI / 4


// 设置子元素在x轴的位移 ---- position可是一个三维向量
cube.position.set(3,0,0)
// 设置子元素的放大
cube.scale.set(2,2,2)
// 设置cube绕着x轴旋转
cube.rotation.x = Math.PI / 4

// 将网格的父元素添加到场景中
scene.add(parentCube)

  • position

    • 子元素的坐标轴相对于父元素
    • 父元素的position设置的是它的局部坐标,没有父元素,元素设置的position就是世界坐标
  • scale

    • 对于缩放,不管cube原本的大小是什么,只要父元素缩放,子元素也会跟着缩放,
    • 公式:父元素(x,y,z),子元素的缩放为(x1,y1,z1),最终子元素的缩放(xx1,yy1,z*z1)
    • 所以,如果遇到与缩放有关的,只需要缩放父元素
  • rotation

    • rotation: 欧拉角(x,y,z,xyz),旋转的顺序不同,效果不一样,默认的旋转顺序为xyz
    • 同样都是相对于父元素
  • 结果

    • 此时父元素的坐标为(-3,0,0),二子元素相对于父元素的坐标为(0,0,0)位于原点
    • 此时父元素的大小放大2倍,子元素的大小在父元素的放大后的大小再放大2倍
    • 此时父元素绕着x轴旋转45°,子元素在相对于父元素旋转45°后,变为90°

学习监听事件

  • 在渲染器后区监听
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // 监听按钮,点击触发全屏
    const btn = document.createElement("button")
    btn.innerHTML = "点击全屏"
    btn.style.position ="absolute"
    btn.style.top ="10px"
    btn.style.left="10px"
    btn.style.zIndex="999"
    btn.onclick = () => {
    // 画布全屏 -- 看不到按钮
    // renderer.domElement.requestFullscreen()
    document.body.requestFullscreen()
    }
    // 将按钮添加到body上
    document.body.appendChild(btn)
    // 经停按钮点击退出全屏
    const escbtn = document.createElement("button")
    escbtn.innerHTML = "退出全屏"
    escbtn.style.position ="absolute"
    escbtn.style.top ="10px"
    escbtn.style.left="100px"
    escbtn.style.zIndex="999"
    escbtn.onclick = () => {
    // 退出全屏
    document.exitFullscreen()
    }
    document.body.appendChild(escbtn)

属性参数的管理工具

  • 方便快速开发
  • lil.gui
  • 能自动识别属性值是什么类型,并展示对应的样式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // 创建GUI
    const gui = new GUI()
    / 控制事件
    //添加按钮
    // add(事件对象,函数名)
    // name(别名)
    gui.add(eventObj, "Fullscreen").name("全屏")
    gui.add(eventObj, "ExitFullscreen")
    // 控制立方体的位置
    // gui.add(cube.position, "x", -5 ,5).name("立方体x轴位置")
    / 控制一组属性,展示拖到条
    // 存放在组中
    let folder = gui.addFolder("立方体位置")
    // add(元素,元素的属性)
    // min/max()设置最小值/最大值
    // onChange: 只要改变就会触发
    // onFinishChange: 当拖拽结束后再触发
    folder.add(cube.position, "x").min(-10).max(10).step(1).name("立方体x轴位置").onChange((val) => {console.log("立方体x轴位置",val)})
    folder.add(cube.position, "y").min(-10).max(10).step(1).name("立方体y轴位置").onFinishChange((val) => {console.log("立方体y轴的位置",val)})
    folder.add(cube.position, "z").min(-10).max(10).step(1).name("立方体z轴位置")
    / 控制单个属性,展示勾选框
    gui.add(parentMaterial, "wireframe").name("父元素线框模式")
    / 控制单个元素选择颜色,展示颜色选择器
    // colorParams中的cubeColor相当于该元素的默认颜色,所以,如元素已经有颜色,则cubeColor最好与之相等
    let colorParams = { cubeColor: "#00ff00"}
    gui.addColor(colorParams, "cubeColor").name("立方体颜色").onChange((val) => {
    cube.material.color.set(val)
    })

几何体怎么来的

在js中所有的图形都是由三角形组成的

  • 两种方式创建

第一种: 顶点需要一个一个列出来,不可共用,冗余

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建几何体
const geometry = new THREE.BufferGeometry()
// 创建顶点数据,顶点是有顺序的,每三个为一个顶点,逆时针为正面,
const vertices = new Float32Array([
-1.0,-1.0,0.0, // 按x,y,z顺序
1.0, -1.0,0.0,
1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0
])
// 创建顶点属性
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3))
// 创建材质
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00, // 设置材质的颜色
side: THREE.doubleSize, // 正面/反面都能看到
wireframe: true
})
const plane = new THREE.Mesh(geometry, material)
scene.add(plane)

第二种:顶点可以共用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建几何体
const geometry = new THREE.BufferGeometry()
// 这里的索引值number代表vertices中的一组数据,由x,y,z组成的数据,即0代表 -1.0,-1.0,0.0这一组数据
// 创建顶点数据
const vertices = new Float32Array([
-1.0,-1.0,0.0, // 按x,y,z顺序
1.0, -1.0,0.0,
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0
])
// 创建顶点属性
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3))
// 创建索引
const indices = new Uint16Array([0,1,2,2,3,0])
// 创建索引属性
geometry.setIndex(new THREE.BufferAttribute(indices,1))
// 创建材质
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00, // 设置材质的颜色
side: THREE.doubleSize, // 正面/反面都能看到
wireframe: true
})
const plane = new THREE.Mesh(geometry, material)
scene.add(plane)

材质创建的两种方式

第一种: 创建之后单独的设置

1
2
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
material.wireframe = true

第二种: 在构造函数中定义

1
2
3
4
5
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00, // 设置材质的颜色
side: THREE.doubleSize, // 正面/反面都能看到
wireframe: true
})

组的概念和创建一个立方体

组的概念(划分组)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// BufferGeometry():用来创建由顶点组成的图形
const cubegeometry = new THREE.BufferGeometry()
// 使用索引绘制,这里的索引number代表vertices中的一组数据,由x,y,z组成的数据
const vertices = new Float32Array([
-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0
])
// 创建顶点属性
cubegeometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3))
// 创建索引
const indices = new Uint16Array([0,1,2,2,3,0])
// 创建索引属性
cubegeometry.setIndex(new THREE.BufferAttribute(indices,1))
// 设置2个顶点组,形成2个材质
// addGroup(从哪个顶点开始,添加几个顶点,用那个材质)
cubegeometry.addGroup(0,3,0)
cubegeometry.addGroup(3,3,1)
// 创建材质
const cubematerial = new THREE.MeshBasicMaterial({color: 0x00ff00})
const cubematerial1 = new THREE.MeshBasicMaterial({color: 0x0000ff})
// 设置了组,并将材质以数组的方式传递则,以对应组的材质展示;
// 设置了组,只传递一个材质,则全部组都使用这个材质
const plane = new THREE.Mesh(cubegeometry, [cubematerial, cubematerial1])
scene.add(plane)

给几何体的每一个面添加不同的材质

1
2
3
4
5
BoxGeometry(x,y,z) :可以创建几何体
const cubegeometry = new THREE.BoxGeometry(1,1,1) // 立方体
// 创建六个材质然后以数组的方式添加即可
const cube = new THREE.Mesh(cubegeometry, [cubematerial, cubematerial1,cubematerial2,cubematerial3,cubematerial4,cubematerial5])
scene.add(cube)

官方提供的几何体 – 文档搜geometry

网格基础材料 — 上编辑器上看

  • 贴图/Map:

    • 如果使用一张带纹理的png图片,边缘透明,贴在一个正/长方形上,边缘也会自动生成纹理(勾选上透明性即消失)
  • 透明贴图/alpha Map:

    • 使用一张由黑色,白色和0.5白色的图片贴图,则黑色为完全透明,白色完全不透明,0.5白色为半透明。即使用贴图后在使用透明贴图,则只看得见白色区域的图和0.5白色区域的半透明图和后面的物体
  • 环境贴图/env Map:

    • 先在上面的背景放上一张全景图/环境的图,环境和环境贴图页放上相同的全景图,可以调节反射率
  • 高光贴图:

    • 在环境中,金属的反射亮度,找一张亮一点的图
  • 光照贴图:

    • 贴的图,代表光照射进来的颜色,比如,光通过玻璃照进来,而玻璃贴了窗纸,窗纸就是这个图,最后呈现的光展示在物体上也要跟环境贴图搭上
  • 环境光遮蔽贴图:

    • 就是给边缘缝隙添加阴影的

代码实现各种贴图

1
2
3
4
5
6
// 创建场景fog(线性雾)
// scene.fog = new THREE.Fog(0x999999, 0.1, 50)
// 创建场景指数fog
scene.fog = new THREE.FogExp2(0x999999, 0.1)
// 创建场景的背景(背景最好与雾的颜色相接近)
scene.background = new THREE.Color(0x999999)

载入模型

第一步 引入相关资源

1
2
3
4
5
6
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"
// 带入gltf加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"
// 载入draco解压器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"

创建实例化

1
2
3
4
5
6
// 实例化加载器gltf
const gltfLoader = new GLTFLoader()
// 创建实例化加载器draco
const dracoLoader = new DRACOLoader()
// 加载环境贴图
let rgbLoader = new RGBELoader()

设置模型路径和环境贴图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 设置Draco路径
dracoLoader.setDecoderPath("../../public/draco/")
// 设置gltf加载器Draco解码器
gltfLoader.setDRACOLoader(dracoLoader)

gltfLoader.load(
// 模型路径
"../../public/texture/module/table_chair.glb",
// 加载完成的回调
(gltf) => {
// 添加模型到场景
scene.add(gltf.scene)
}
)

// 加载环境贴图
rgbLoader.load("../../public/texture/brown_photostudio_02_1k.hdr", (envMap) => {
// 设置球型的映射
envMap.mapping = THREE.EquirectangularReflectionMapping;
// 设置环境贴图
scene.environment = envMap
})

光线投射

threeJS-光线投射

补间动画

threeJS-补间动画TWEEN

物理网格材质

首先需要安装npm i gsap 动画库
看车模型案例

其他文章
cover
threeJS-机器人展示案例
  • 24/11/01
  • 11:01
  • ThreeJS
目录导航 置顶
  1. 1. 基础知识
    1. 1.1. 第一步: 创建场景、相机和渲染器
    2. 1.2. 第二步: 创建几何体、材质、网格 并添加到场景中
    3. 1.3. 第三步: 使用世界坐标辅助器和轨道控制器
    4. 1.4. 倒数第二步: 渲染函数
    5. 1.5. 最后一步: 监听窗口的变化
    6. 1.6. 认识向量(Vector3)、父元素和子元素的position的关系、缩放、旋转
      1. 1.6.1. position
      2. 1.6.2. scale
      3. 1.6.3. rotation
      4. 1.6.4. 结果
    7. 1.7. 学习监听事件
    8. 1.8. 属性参数的管理工具
    9. 1.9. 几何体怎么来的
      1. 1.9.1. 第一种: 顶点需要一个一个列出来,不可共用,冗余
      2. 1.9.2. 第二种:顶点可以共用
    10. 1.10. 材质创建的两种方式
      1. 1.10.1. 第一种: 创建之后单独的设置
      2. 1.10.2. 第二种: 在构造函数中定义
    11. 1.11. 组的概念和创建一个立方体
      1. 1.11.1. 组的概念(划分组)
      2. 1.11.2. 给几何体的每一个面添加不同的材质
      3. 1.11.3. 官方提供的几何体 – 文档搜geometry
    12. 1.12. 网格基础材料 — 上编辑器上看
      1. 1.12.1. 贴图/Map:
      2. 1.12.2. 透明贴图/alpha Map:
      3. 1.12.3. 环境贴图/env Map:
      4. 1.12.4. 高光贴图:
      5. 1.12.5. 光照贴图:
      6. 1.12.6. 环境光遮蔽贴图:
      7. 1.12.7. 代码实现各种贴图
    13. 1.13.
    14. 1.14. 载入模型
      1. 1.14.1. 第一步 引入相关资源
      2. 1.14.2. 创建实例化
      3. 1.14.3. 设置模型路径和环境贴图
    15. 1.15. 光线投射
    16. 1.16. 补间动画
    17. 1.17. 物理网格材质
请输入关键词进行搜索