Web3D学习-基础篇

150 阅读5分钟

Mesh网格对象

网格由几何体+材质组成,形成可视化3D物体,是Three.js中最基础的可视化对象。可控制移动(position)、旋转(rotation)、缩放(scale)。

网格中,最基本的构建是三角形,三角形是最简单的多边形,少于3个顶点就不能成为一个表面;

  • 三角形必然是平坦的,含4个或以上的顶点的多边形,不一定平坦,三个点确定一个平面,多余的点可能在这个面之上或者之下;
  • 三角形经多种转换之后,仍然是三角形,这对于仿射转换透视转换也成立。最坏的情况下,从三角形的边去看,三角形会退化为线段。在其它角度观察,仍能维持是三角形;
  • 几乎所有商用图形加速硬件都是为三角形光栅化而设计的;

Three.js渲染结构图

  • WebGLRenderer将场景绘制到画布上

  • WebGLRenderTarget缓冲,后台渲染

  • WebGLMultipleRenderTargets高级渲染器,常用于后期处理等 image.png

  • 所有的物体, 灯光, 纹理都要经过render才能看到, 也就是说 render 用来呈现结果, render 通过算法把 3D 场景投影成2D画面绘制到画布上

  • Mesh, Light 是单个对象, 而 Object3D, Group 可以添加n个子对象, 也就是可以对对象进行分组, 创建集合.

  • 所有的物体, 灯光, 纹理等3D对象都要放到场景中才能被查看, 也就是说 Scene 是最底层的容器, 而且Scene 是树结构的对象. 也就是说可以通过遍历拿到所有的子集

  • 相机在场景内, 也在场景外, 说明 Camera 比较特殊, 可以看向场景, 但看不到自己, 也可以不看向场景, 此时就是'黑屏', 可以对物体样式产生影响, 光线照到物体上与物体的材质产生反应, 比如变亮变暗, 变色等

  • Mesh(网格)继承自Object3D, 其自身包含两个部分, Geometry(几何体)和Material(材质)

  • Geometry(几何体)相当于HTML中的布局, 用来描述物体的形状, 具有物体顶点的相关信息, 比如顶点坐标, 索引等

  • Material(材质)相当于css, 用来描述物体的样式, 对应了webgl 中着色功能, 存储了物体的颜色, 阴影, 光泽度, 漫反射等, 这些效果可以通过 Texture 贴图来实现.

  • Texture(纹理)是种特殊的图像, 可以是png, img等格式, 也可以是canvas画布, 视频等形式

  • 隐含信息:

    相机也可以包含在Group中, 跟随物体移动

    对 Group 应用缩放移动等方法, 内部的3D对象会同时被操作, 不用单独给每个3D对象应用变换

    每个对象的变换都是相对于父级做相对运动, 如 #_5-3-正交相机 俯视视角

    正交相机没有近大远小的效果,呈现类似2d平面

    场景发生了移动, 观察者本地坐标不变, 但世界坐标发生了变化

    Geometry Material Texture 均可复用, 用于性能优化

    threejs 内置了丰富的 Geometry/Material, 也可以通过建模创建自定义几何体/材质

  • three.js中没有单位,都是向量

  • position.set 也可写为:position.x = 2,position.y = 2,position = 3;

  • 因为都是向量,所以向量长度:mesh.position.length()

  • mesh.position.distanceTo(camera.position)可以得到两个向量坐标的距离

  • .lookAt(new THREE.Vector3(2, 2, 3))可以让3D物体自动旋转朝向某个坐标

立方体中,6个顶点原因:

image.png 三角形是构成一个面的基本单位,因此一个面有6个顶点。查看代码:

    // 创建立方体的基础材质
    const material = new THREE.MeshLambertMaterial({
        color: 0x1890ff,
        wireframe: true, //开启网格透视效果
    })

渲染6个面不同的颜色:

    // 创建立方体的几何体
    const geometry = new THREE.BoxGeometry(1, 1, 1)
    console.log(geometry);
    let faces = []

    for ( let i=0; i<geometry.groups.length; i++) {
      // 重新生成新的材质
      const material = new THREE.MeshBasicMaterial({
        color: Math.random() * 0xffffff
      })

      faces.push(material)
    }
    // 创建3D物体对象
    const mesh = new THREE.Mesh(geometry, faces)

OrbitControls轨道控制

OrbitControls轨道控制对象,可用鼠标控制缩放旋转。canvas更新画布内容时,需要重新绘制整个画布,是three.js中动画的必要条件。实际控制的是,相机的位移

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

    // 创建轨道控制器
    const orbitControls = new OrbitControls(camera, canvas)
    orbitControls.enableDamping = true; //开启惯性
    let timer;
    // 优化前
    const tick = ()=>{
      clearTimeout(timer)
      timer = setTimeout(()=>{
        orbitControls.update()

        renderer.render(scene, camera)
        tick()
      })
    }
    tick()
    
    // 优化后
    const tick = ()=>{
      // 更新
      orbitControls.update()

      renderer.render(scene, camera)
      window.requestAnimationFrame(tick)
    }
    tick()

Clock时间对象

匀速直线运动公式:s=vt;

mesh.rotation.set(Math.PI / 4, 0, 0, 'XYZ'); // 'XYZ' 表示旋转顺序的字符串 默认为 xyz
mesh.scale.set(3, 1, 1); // 物体缩放

let timestamp = Date.now();
const tick = () => {
  const currentTime = Date.now();
  const deltaTime = currentTime - timestamp; // 两次渲染时间间隔

  timestamp = currentTime;
  mesh.rotation.y += 0.001 * deltaTime; // 可以保持匀速
  renderer.render(scene, camera);
  window.requestAnimationFrame(tick);
}

变速运动公式:s=v0*t+at²/2;

const clock = new THREE.Clock();
const tick = () => {
  const elapsedTime = clock.getElapsedTime(); // 获取时钟运行的总时长
  // clock.getDelta(); 两次渲染时间间隔

  // update objects
  mesh.rotation.y += 0.005 * elapsedTime; // 加速
  // 重新渲染整个场景
  renderer.render(scene, camera);
  // 调用下次更新函数
  window.requestAnimationFrame(tick);
}

实现旋转、位移、缩放动画:

    const clock = new THREE.Clock()
    const tick = ()=>{
      // 保留多为小数的秒级
      const elapsedTime = clock.getElapsedTime();
      // 旋转
      // mesh.rotation.y += elapsedTime / 1000;
      // 位移
      // mesh.position.x += elapsedTime / 1000;
      // 缩放
      // mesh.scale.x += elapsedTime / 1000;
      
      // 围绕坐标系原点 圆周运动
      // mesh.position.x = Math.cos(elapsedTime)
      // mesh.position.y = Math.sin(elapsedTime)

      // 镜头看向物体  圆周运动
      // camera.position.x = Math.cos(elapsedTime)
      // camera.position.y = Math.sin(elapsedTime)

      // 更新
      orbitControls.update()

      renderer.render(scene, camera)
      window.requestAnimationFrame(tick)
    }