three.js基础

发布时间:2024年01月11日

三要素

1.场 景(scene):放置物体的容器

2.摄像机(camera):类似人眼,可调位置,角度等信息,展示不同画面

3.渲染器(renderer):接收场景和摄像机,计算在浏览器上渲染的最终 2D 画面

import * as THREE from 'three';

let scene, camera, renderer? // 场景,摄像机,渲染器

let controls? ?// 轨道控制器 作用:调整轨道控制器属性,影响摄像机细节

let cube? // 物体对象

function init() {
    // 1 创建3D场景对象Scene
    scene = new THREE.Scene();

    // 1.1 实例化一个透视投影相机对象
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 5

    // 1.2 创建渲染器对象  antialias: true开启抗锯齿效果
    renderer = new THREE.WebGLRenderer({ antialias: true });

    // 1.3 定义threejs输出画布的尺寸(单位:像素px)
    // const width = 800; //宽度
    // const height = 500; //高度
    renderer.setSize(window.innerWidth, window.innerHeight); //设置three.js渲染区域的尺寸(像素px)
    document.body.append(renderer.domElement)

    // 传入场景摄像机渲染画面,注意+后续这段代码挪到循环渲染函数中
    renderer.render(scene, camera)
}

立方体(cube)绘制流程

1.几何图形?

2.材质:网格基础材质-线面纯颜色描绘表面

3.网格物体(Mesh):将有材质的图形放置网格物体中

function createCube() {
    // 2. 创建图形,宽高深为 1 单位(立方缓冲几何体)
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    // 2.1. 创建材质,颜色为绿色 0x00ff00 (网格基础材质-线面纯颜色描绘表面)
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    // 2.2. 创建网格物体对象,传入图形和材质(网格物体对象)
    cube = new THREE.Mesh(geometry, material);
    // 2.3. 把物体加入到场景中
    scene.add(cube);
}

轨道控制器:

// 3. 单独引入 OrbitControls 轨道控制器构造函数
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

function controlsCreate() {
    // 3.1 创建轨道控制器
    controls = new OrbitControls(camera, renderer.domElement)
}

详细控制

? 垂直旋转范围控制? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 水平旋转范围控制

? ? ?

function controlsCreate() {
    // 3.1 创建轨道控制器
    controls = new OrbitControls(camera, renderer.domElement)
    // 5 添加阻尼效果
    controls.enableDamping = true
    // 5.2 开启自动旋转轨道控制器带动摄像机一起旋转
    // controls.autoRotate = true
    // 5.3垂直角度范围控制(0 上面,Math.PI 下面 上=>下 范围0-Math.PI)
    // controls.maxPolarAngle = Math.PI
    // controls.minPolarAngle = 0
    // // 5.4水平角度范围控制(0 上面,Math.PI 下面 上=>下 范围0-Math.PI)
    // controls.maxAzimuthAngle = 1.5 * Math.PI
    // controls.minAzimuthAngle = 0.5 * Math.PI
    // 5.5控制摄像机缩放比例
    // controls.maxDistance = 10
    // controls.minDistance = 2
}

渲染循环-更新场景渲染?

function renderLoop() {
    // 4. 在渲染循环中更新场景渲染
    renderer.render(scene, camera)

    // 4.1手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
    controls.update()

    // 4.2 根据当前计算机浏览器刷新帧率(默认 60 次/秒),不断递归调用此函数渲染最新的画面状态
    // 好处:当前页面切换到后台,暂停递归
    requestAnimationFrame(renderLoop)
}

坐标轴

// 添加坐标轴辅助对象
function createHelper() {
    // 5. 创建坐标轴对象,设置长度
    const axesHelper = new THREE.AxesHelper(5)
    // 5.1. 添加到场景中
    scene.add(axesHelper)
}

适配场景大小

// 适配窗口函数
function renderResize() {
    window.addEventListener('resize', () => {
        // 调整渲染器画布大小
        renderer.setSize(window.innerWidth, window.innerHeight)
        // 调整摄像机宽高比
        camera.aspect = window.innerWidth / window.innerHeight
        // 更新视椎体空间
        camera.updateProjectionMatrix()
    })
}

移动立方体

// 移动立方体
function moveCube() {
    // 位移 position
    cube.position.x = 5
    // cube.position.set(5, 5, 0)

    // 旋转 rotation 值为eular角对象 (弧度制)
    // 在轴正方向来看物体,默认是绕着轴进行逆时针旋转
    cube.rotation.x = Math.PI / 4
    // // cube.rotation.set(Math.PI / 4, 0, 0)

    // // 缩放 scale (Vector3 三维向量对象,中心原点不动,向 2 边拉伸/缩小)
    cube.scale.z = 2
    // cube.scale.set(1, 1, 2)
}

上述方法顺序调用

/**准备环境--S */
init() // 初始化
controlsCreate()   // 创建轨道控制器
createHelper()     // 添加坐标轴辅助
renderResize()     // 适配窗口函数
/**准备环境--E */

/**准备内容--S */
createCube()       // 创建立方体
moveCube()         // 移动立方体
/**准备内容--E */

/**准备逻辑--S */
renderLoop()       // 渲染循环
/**准备逻辑--E */

GUI工具的使用

// 6. 引入dat.gui工具库
import * as dat from 'dat.gui';

创建gui函数
function createGUI() {
    // 创建gui工具对象
    const gui = new dat.GUI();
    // 添加具体控制器使用
    //gui.add()添加图形用户界面工具
    //参数1:关联D0M对象,JS对象,3D物体对象
    //参数2:对象其中的某个属性,给这个属性关联用户界面工具(从而快速调整它的值)

    //6.0(字符串->输入框)
    gui.add(document, 'title') 
    //6.1控制立方体显示/隐藏(布尔->多选框)
    gui.add(cube, 'visible')
    //6.2轨道控制器回归初始角度(函数->按钮)
    gui.add(controls, 'reset')  

    // 6.3控制立方体颜色(找属性方式:文档 -> 打印 -> 百度
    //  效果:立方体默认颜色和文字 <=> 显示在工具标签上
    const colorObj = {
        'col': `#${cube.material.color.getHexString()}`
    }
    gui.addColor(colorObj, 'col').onChange(val => {
        cube.material.color = new THREE.Color(val);
    })

    //  6.4创建gui分组
    const folder = gui.addFolder('位移')
    folder.add(cube.position, 'x', 0, 5, 0.1)
    folder.add(cube.position, 'y', 0, 5, 0.1)
    folder.add(cube.position, 'z', 0, 5, 0.1)

    // 6.5 下拉菜单(关键:第三个参数为对象时->下拉菜单)
    // 对象中属性名->下拉菜单选项名
    // 初始值匹配后会影响下拉菜单默认选中哪一项
    gui.add({ type: '1' }, 'type', { '方案1': '1', '方案2': '2' }).onChange(val => {
        // val 方案对象的 '1','2'
        switch (val) {
            case '1':
                cube.position.set(0, 0, 0)
                break;
            case '2':
                cube.position.set(2, 2, 2)
                break;
        }
    })
}

注意:在创建gui工具中需要使用到物体,所以要在创建完物体后调这个方法

案例-五颜六色立方体

修改创建立方体材质函数

function createCube() {
    const geometry = new THREE.BoxGeometry(1, 1, 1);

    // 1. 定义颜色数组(x 正负,y 正负,z 正负)
    const colorArr = ['red', 'green', 'blue', 'pink', 'orange', 'write']
    // 2. 每个颜色字符串映射成材质对象
    const materialArr = colorArr.map(colorStr => {
        return new THREE.MeshBasicMaterial({
            color: colorStr
        })
    })
    // 3. 把材质数组传入 Mesh 构造新的物体
    cube = new THREE.Mesh(geometry, materialArr)

    // 把物体加入到场景中
    scene.add(cube);
}

?案例-多个立方体

修改创建立方体材质函数-思路:数据影响视图

数据包含:立方体的颜色(color),立方体的大小(whd) ,立方体的位置(xyz)

扩展:

Math.floor();? ?向下取整? Math.floor(1.6);//1

Math.random(); 生成一个大于0.0,小于1的随机数,不包括1.0,即[0.0,1.0)

function createCube() {
    // 1. 定义数据对象,描绘每个立方体的信息
    const cubeIndolist = []
    for (let i = 0; i < 5; i++) {
        cubeIndolist.push({
            color: `rgb(${Math.floor(Math.random() * (255 - 0 + 1) + 0)},${Math.floor(Math.random() * (255 - 0 + 1) + 0)},${Math.floor(Math.random() * (255 - 0 + 1) + 0)})`,
            w: Math.floor(Math.random() * (3 - 1 + 1) + 1),
            h: Math.floor(Math.random() * (3 - 1 + 1) + 1),
            d: Math.floor(Math.random() * (3 - 1 + 1) + 1),
            x: Math.floor(Math.random() * (5 - -5 + 1) + -5),
            y: Math.floor(Math.random() * (5 - -5 + 1) + -5),
            z: Math.floor(Math.random() * (5 - -5 + 1) + -5),
        })
    }

    // 2. 针对每个数据对象,创建物体
    cubeIndolist.map(cubeObj => {
        const { color, w, h, d, x, y, z } = cubeObj
        const geometry = new THREE.BoxGeometry(w, h, d);
        const material = new THREE.MeshBasicMaterial({ color });
        cube = new THREE.Mesh(geometry, material)
        cube.position.set(x, y, z)
        scene.add(cube);
    })
}

性能监视器的使用

后续考虑3d场景的性能优化

1.单独引入 Stats 附加组件

2.创建性能监视器

3.设置监视器面板类型(0, 1, 2)

4.设置监视器位置并添加 DOM

// 1. 单独引入 stats 组件
import Stats from 'three/examples/jsm/libs/stats.module.js'

// 性能监视器
let stats

function renderLoop() {
  renderer.render(scene, camera)
  controls.update()
  // 性能监视器数值不断更新
  stats.update()
  requestAnimationFrame(renderLoop)
}

function createStats() {
  // 2. 创建性能监视器
  stats = new Stats()
  // 3. 设置监视器面板类型(0:fps-每秒传输帧数,1:ms-每帧刷新用时,2:mb-内存占用)
  stats.setMode(0)
  // 4. 设置监视器位置并添加 DOM
  stats.domElement.style.position = 'fixed'
  stats.domElement.style.left = '0'
  stats.domElement.style.top = '0'
  document.body.appendChild(stats.domElement)
}

/**准备内容--S */
createStats() //创建性能监视器
renderLoop() // 渲染循环
/**准备内容--E */

删除物体

如何废置对象(How to dispose of objects)?

内存泄漏:比如有一些对象或者物体在浏览器的计算机内存当中,但是我们的变量可能不在指向它了,但是它依旧占用这个内存,就会越叠越多,意外的全局变量、遗忘的定时器、?使用不当的闭包、遗漏的 DOM 元素、网络回调等都会造成内存泄漏。

//在创建立方体函数中给每个立方体起个名字 cube.name = 'cu'

function removeCube() {
  // 1. 给 window 双击绑定事件举例删除
  window.addEventListener('dblclick', () => {
    // 2. 调用 three.js 相关废置函数
    const arr = scene.children.filter(obj => obj.name === 'cu')
    const cube = arr[0]
    if (cube) {
      cube.geometry.dispose() // 移除图形数据
      cube.material.dispose() // 移除材质数据
      // 3. 再从场景中移除物体
      scene.remove(cube)
    }
  })
}

three.js 物体分组管理

three.js物理分组详解它几乎和Object3D是相同的,其目的是使得组中对象在语法上的结构更加清晰。three.js物理分组详解

1.新建分组
2.分组中加入物体
3.把分组加入到场景中

// 1. 新建分组
let group = new THREE.Group()

function createCube() {
  const cubeInfoArr = []
  for (let i = 0; i < 2500; i++) {
    cubeInfoArr.push({
      color: `rgb(${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)})`,
      w: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      h: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      d: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      x: Math.floor(Math.random() * (5 - -5 + 1) + -5),
      y: Math.floor(Math.random() * (5 - -5 + 1) + -5),
      z: Math.floor(Math.random() * (5 - -5 + 1) + -5),
    })
  }
  cubeInfoArr.map(cubeObj => {
    const { color, w, h, d, x, y, z } = cubeObj
    const geometry = new THREE.BoxGeometry(w, h, d)
    const material = new THREE.MeshBasicMaterial({ color })
    const cube = new THREE.Mesh(geometry, material)
    cube.position.set(x, y, z)
    cube.name = 'cu'
    // 2. 分组中加入物体
    group.add(cube)
  })
  // 3. 把分组加入到场景中
  scene.add(group)
}


function removeCube() {
  window.addEventListener('dblclick', () => {
    group.children.map(obj => {
      obj.geometry.dispose()
      obj.material.dispose()
      group.remove(obj)
    })
    // 把组对象移除掉
    scene.remove(group)
  })
}

文章来源:https://blog.csdn.net/luoxiaonuan_hi/article/details/134123163
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。