Three.js 基础知识

threejs 入门到放弃过程

官网下载:https://threejs.org/左边代码下载可以直接下载整个文档、示例和源码包

在本地使用 npm i 或 pnpm i 安装运行即可

整个threejs涉及的概念列表

英文中文说明
Scene场景包含所有3D物体和灯光的容器,是Three.js的核心之一。
Camera摄像机定义视角,将3D场景投影到2D画布上,常用的是PerspectiveCamera。
Renderer渲染器负责将场景渲染到屏幕上,常用的渲染器是WebGLRenderer。
Object3D3D对象所有3D对象的基类,包括Mesh、Light等,支持位置、旋转、缩放。
Mesh网格由Geometry和Material组成的3D物体,是场景中常见的对象类型。
Geometry几何体定义物体的形状,如BoxGeometry(立方体)、SphereGeometry(球体)等。
Material材质决定物体表面外观,如MeshBasicMaterial(基础材质)、MeshPhongMaterial(Phong材质)。
Light灯光为场景提供照明,包括AmbientLight(环境光)、PointLight(点光源)、DirectionalLight(平行光)等。
Animation动画通过Tween或Morph实现物体的动态效果,如旋转、移动等。
Loader加载器用于加载外部模型和资源,如OBJLoader(加载OBJ模型)、TextureLoader(加载纹理)。
Event事件处理用户输入和交互,如鼠标移动、点击等。
Particles粒子系统用于创建大量小物体,如烟花、星云等。
Post-processing后期处理通过ShaderMaterial增强视觉效果,如模糊、色调调整等。
Physics物理引擎虽然Three.js本身不提供,但可以集成如Cannon.js等物理引擎。
Shaders着色器通过自定义GLSL代码实现复杂视觉效果,如水波纹、火焰等。
WebGLWebGL基于OpenGL ES 2.0的API,在浏览器中渲染3D图形。
Math数学库提供向量、矩阵、颜色等工具,如Vector3、Matrix4、Color等。
Utils工具库提供辅助函数,如几何体生成、单位转换等。
SceneGraph场景图管理对象的层次结构,方便场景管理。

初始化项目

使用 vite创建一个 js 的项目

pnpm create vite 
Select a framework:
 Vanilla
 Vue
 React
 Preact
 Lit
 Svelte
 Solid
 Qwik
 Angular
 Marko
 Others
  Select a variant:
 TypeScript
 JavaScript

增加一个 css

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  width: 100vw;
  height: 100vh;
  box-sizing: border-box;
}

安装 threejs

pnpm add three

创建一个正方形

在 main.js 中书写以下代码

import "./style.css";
import * as THREE from "three";
init();
function init() {
  const scene = new THREE.Scene();
  //创建一个透视相机
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  camera.position.z = 6;
  camera.position.y = 10;
  camera.position.x = 8;
  camera.lookAt(0, 0, 0);
  scene.add(camera);

  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  document.body.appendChild(renderer.domElement);

  const cube = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshBasicMaterial({ color: 0x00ff00 })
  );
  scene.add(cube);
  controls.enableRotate = true;
  renderer.render(scene, camera);
}

坐标辅助器

AxesHelper 类实现

  // 坐标轴辅助器
  const axesHelper = new THREE.AxesHelper(5);
  scene.add(axesHelper);

轨道控制器

OrbitControls来实现,实际就是为相机控件,本质是改变相机的参数,如角度,透视,与模型的距离等,可以实现以下效果

  • 旋转:拖动鼠标左键
  • 缩放:滚动鼠标中键
  • 平移:拖动鼠标右键
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
const controls = new OrbitControls(camera, renderer.domElement);
 //添加阻尼
  controls.enableDamping = true;
  //添加阻尼系数
  controls.dampingFactor = 0.25;
  //是否启用缩放
  controls.enableZoom = true;
  //是否启用平移
  controls.enablePan = true;
  //是否启用旋转
  controls.enableRotate = true;
  controls.addEventListener("change", function () {
    // 浏览器控制台查看相机位置变化
    console.log("camera.position", camera.position);
  });

动画渲染循环

threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame实现动画渲染。

const clock = new THREE.Clock();
  function animate() {
    const spt = clock.getDelta() * 1000; //毫秒
    requestAnimationFrame(animate);
    controls.update();
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
    console.log("spt", spt); //每秒帧数
  }
  animate();

备注说明:对于部分高刷新率的电脑硬件设备,.requestAnimationFrame每秒钟默认调用函数执行次数也是有可能超过60次的,比如你的电脑显卡、显示器等硬件能够支持144hz刷新频率,.requestAnimationFrame的每秒执行上限,也可以接近144帧率。

自适应宽度

window.onresize = function () {
  // 重置渲染器输出画布canvas尺寸
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
  // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
  // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
  camera.updateProjectionMatrix();
};

光源

PointLight点光源

  // 点光源
  const pointLight = new THREE.PointLight(0xffffff, 0.5);
  pointLight.position.set(2, 2, 2);
  scene.add(pointLight);

环境光

AmbientLight环境光

  // 环境光
  const pointLight = new THREE.AmbientLight(0xffffff, 0.5);
  scene.add(pointLight);

聚光灯

SpotLight聚光灯

  const spotLight = new THREE.SpotLight(0xffffff, 1.0);
  spotLight.position.set(2, 2, 2);
  scene.add(spotLight);

平行光

DirectionalLight 平行光

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
  directionalLight.position.set(2, 2, 2);
  scene.add(directionalLight);

几何体和顶点

  // 创建一个三角形
  const geometry = new THREE.BufferGeometry();
  // 创建一个三角形的顶点
  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, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0,
  ]);
  // 设置顶点位置,3 个为一个 点 
  geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));

设置索引

  const geometry = new THREE.BufferGeometry();
  // 创建一个三角形的顶点
  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,
  ]);
  // 设置顶点位置
  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 geometry = new THREE.BufferGeometry();
  // 创建一个三角形的顶点
  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,
  ]);
  // 设置顶点位置
  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));

  geometry.addGroup(0, 3, 0);
  geometry.addGroup(3, 3, 1);
  console.log(geometry);

  // 创建一个材质
  const material1 = new THREE.MeshBasicMaterial({
    color: 0x00ff00,
    // wireframe: true,
    side: THREE.DoubleSide,
  });
  const material2 = new THREE.MeshBasicMaterial({
    color: 0x0000ff,
    // wireframe: true,
    side: THREE.DoubleSide,
  });
  // 创建一个网格
  const cube = new THREE.Mesh(geometry, [material1, material2]);
  // 添加到场景中
  scene.add(cube);
0%