Skip to content

一、快速入门

更新: 12/19/2025字数: 0 字阅读: 0 分钟

创建第一个3D案例

基本概念

场景Scene、相机Camera、渲染器Renderer

第一步:创建3D场景

  1. 创建 三维场景Scene
  2. 创建 物体形状:几何体Geometry
  3. 创建 物体外观:材质Material
  4. 创建 物体:网格模型Mesh
  5. 设置 模型位置.position
  6. .add()方法 将模型添加到场景中
javascript
// 引入three.js
import * as THREE from 'three';
/**
 * 创建3D场景对象Scene
 */
const scene = new THREE.Scene();
/**
 * 创建网格模型
 */
//创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(50, 50, 50);
// 材质对象Material
// 设置为基础网格材质
const material = new THREE.MeshBasicMaterial({
  color: 0x0000ff, //设置材质颜色
});
const mesh = new THREE.Mesh(geometry, material); // 网格模型对象Mesh
// 设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);
scene.add(mesh); //网格模型添加到场景中 

// console.log('三维场景',scene);

第二步:创建透视投影相机

  1. 实例化一个 透视投影相机PerspectiveCamera
  2. 设置 相机位置.position
  3. 设置 相机观察目标.lookAt()
  4. 定义相机渲染输出的画布尺寸
  5. 设置参数 透视投影相机PerspectiveCamera:视锥体

透视投影相机的四个参数fov, aspect, near, far构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上。

参数含义默认值
fov相机视锥体竖直方向视野角度50
aspect相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height1
near相机视锥体近裁截面相对相机距离0.1
far相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向2000
javascript
// width和height用来设置渲染后,输出的画布宽高度。
const width = 800; //宽度
const height = 500; //高度
/**
 * 透视投影相机设置
 */
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);

camera.position.set(200, 200, 200); //相机在Three.js三维坐标系中的位置

//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); //坐标原点
// camera.lookAt(0, 10, 0);  //y轴上位置10
// camera.lookAt(mesh.position);//指向mesh对应的位置

第三步:创建渲染器

  1. 创建WebGL渲染器WebGLRenderer
  2. 设置Canvas画布尺寸.setSize()
  3. 渲染器渲染方法.render()
  4. 将canvas画布插入到web页面中 渲染器Canvas画布属性.domElement
javascript
/**
 * 创建渲染器对象
 */
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
renderer.render(scene, camera); //执行渲染操作,类比相机的拍照动作
//three.js执行渲染命令会输出一个canvas画布,也就是一个HTML元素,你可以插入到web页面中
document.body.appendChild(renderer.domElement);

三维坐标系

  • 创建辅助观察坐标系

THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小,你可以根据需要改变尺寸。

javascript
// AxesHelper:辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
  • 材质半透明设置

设置材质半透明,这样可以看到坐标系的坐标原点。

javascript
const material = new THREE.MeshBasicMaterial({
  color: 0x0000ff, //设置材质颜色
  transparent:true,//开启透明
  opacity:0.5,//设置透明度
});
  • AxesHelper的xyz轴

three.js坐标轴颜色红R、绿G、蓝B分别对应坐标系的xyz轴,对于three.js的3D坐标系默认y轴朝上

光源

基础网格材质MeshBasicMaterial不会受到光照影响。

漫反射网格材质MeshLambertMaterial会受到光照影响,该材质也可以称为Lambert网格材质,音译为兰伯特网格材质。

  1. 设置物体使用漫反射网格材质
  2. 创建点光源
  3. 设置光源位置
  4. 光源添加到场景
javascript
// 材质对象Material
// 基础网格材质MeshBasicMaterial不受光照影响
// 漫反射网格材质;MeshLambertMaterial
const material = new THREE.MeshLambertMaterial({
  color: 0x00ffff, //设置材质颜色
});

/**

 * 光源设置
 */
//点光源
const pointLight = new THREE.PointLight(0xffffff, 1.0); // 光照颜色、光照强度
pointLight.decay = 0.0;//光源光照强度不随距离改变衰减
//点光源位置
// pointLight.position.set(400, 0, 0);//点光源放在x轴上
pointLight.position.set(400, 200, 300);//偏移光源位置,观察渲染效果变化
scene.add(pointLight); //点光源添加到场景中
  • 点光源辅助观察PointLightHelper
javascript
// 光源辅助观察
const pointLightHelper = new THREE.PointLightHelper(pointLight, 10); // 要模拟的光源, 辅助对象的尺寸
scene.add(pointLightHelper);

相机控件OrbitControls

  • 旋转:拖动鼠标左键
  • 缩放:滚动鼠标中键
  • 平移:拖动鼠标右键
  1. 引入扩展库OrbitControls.js
  2. 创建OrbitControls实例
  3. 监听控制器,执行渲染操作
javascript
// 引入轨道控制器扩展库OrbitControls.js
import {
  OrbitControls
} from 'three/addons/controls/OrbitControls.js';

// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
  renderer.render(scene, camera); //执行渲染操作
  // 浏览器控制台查看相机位置变化
  console.log('camera.position',camera.position);
});//监听鼠标、键盘事件

环境光

环境光AmbientLight (opens new window)没有特定方向,只是整体改变场景的光照明暗。

javascript
//环境光:没有特定方向,整体改变场景的光照明暗
const ambient = new THREE.AmbientLight(0xffffff, 0.4); // 光照颜色、光照强度
scene.add(ambient);

平行光

平行光DirectionalLight (opens new window)就是沿着特定方向发射。

  1. 创建平行光
  2. 设置光源的方向position
  3. 设置光源指向对象 target,默认为原点
  4. 光源添加到场景
javascript
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
directionalLight.position.set(80, 100, 50);
// 方向光指向对象网格模型mesh,可以不设置,默认的位置是0,0,0
directionalLight.target = mesh;
scene.add(directionalLight);
  • 平行光辅助观察DirectionalLightHelper
javascript
// DirectionalLightHelper:可视化平行光
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000); // 模拟光源,辅助对象尺寸,模拟光源的颜色
scene.add(dirLightHelper);

动画渲染循环

  • 请求动画帧window.requestAnimationFrame
    • threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame实现动画渲染。
    • requestAnimationFrame实现周期性循环执行
    • requestAnimationFrame默认每秒钟执行60次,但不一定能做到,要看代码的性能
  • 创建渲染器对象后执行渲染操作就可以放到渲染循环中做了
  • 设置了渲染循环,相机控件OrbitControls就不用再通过事件change执行渲染操作了
javascript
/**
 * 创建渲染器对象
 */
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
// renderer.render(scene, camera); //执行渲染操作
//three.js执行渲染命令会输出一个canvas画布,也就是一个HTML元素,你可以插入到web页面中
document.body.appendChild(renderer.domElement);

// 渲染循环
// requestAnimationFrame实现周期性循环执行
// requestAnimationFrame默认每秒钟执行60次,但不一定能做到,要看代码的性能
function render() {
  renderer.render(scene, camera); //执行渲染操作
  mesh.rotateY(0.01); //每次绕y轴旋转0.01弧度
  requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
}
render();

// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// // 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
// controls.addEventListener('change', function () {
//     renderer.render(scene, camera); //执行渲染操作
// }); //监听鼠标、键盘事件

canvas画布宽高度动态变化

canvas画布宽高度动态变化,需要更新相机和渲染的参数,否则无法正常渲染。

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

通过stats.js扩展库 查看threejs渲染帧率

通过stats.js库可以查看three.js当前的渲染性能,具体说就是计算three.js的渲染帧率(FPS)。

所谓渲染帧率(FPS),简单说就是three.js每秒钟完成的渲染次数,一般渲染达到每秒钟60次为最佳状态。

  1. 引入Stats
  2. 创建Stats对象
  3. 将 stats.domElement 插入到web页面中
  4. 在渲染函数中调用方法 stats.update(),来刷新时间
javascript
//引入性能监视器stats.js,显示帧率
import Stats from 'three/addons/libs/stats.module.js';
//创建stats对象
const stats = new Stats();
//Stats.domElement:web页面上输出计算结果,一个div元素
document.body.appendChild(stats.domElement);

// 渲染循环
function render() {
  stats.update();//渲染循环中执行stats.update()来刷新时间
  renderer.render(scene, camera); 
  mesh.rotateY(0.01);
  requestAnimationFrame(render);
}
render();
  • 设置显示模式 setMode
javascript
// stats.domElement显示:渲染帧率  刷新频率,一秒渲染次数 
stats.setMode(0); // 默认模式

// stats.domElement显示:渲染周期  渲染一帧多长时间(单位:毫秒ms)
stats.setMode(1);

Threejs常见几何体简介

javascript
//BoxGeometry:长方体
const geometry = new THREE.BoxGeometry(100, 100, 100);
// SphereGeometry:球体
const geometry = new THREE.SphereGeometry(50);
// CylinderGeometry:圆柱
const geometry = new THREE.CylinderGeometry(50,50,100);
// PlaneGeometry:矩形平面
const geometry = new THREE.PlaneGeometry(100,50);
// CircleGeometry:圆形平面
const geometry = new THREE.CircleGeometry(50);

Three.js的材质默认正面可见,反面不可见,对于矩形平面PlaneGeometry圆形平面如果你想看到两面,可以设置side: THREE.DoubleSide

javascript
const material = new THREE.MeshLambertMaterial({
  color: 0x00ffff, 
  // side: THREE.FrontSide, // 默认只有正面可见
  side: THREE.DoubleSide,// 双面可见
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

高光网格材质Phong

MeshPhongMaterial可以提供一个镜面反射效果,该材质可以模拟具有镜面高光的光泽表面(例如涂漆木材)。

javascript
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
  color: 0xff0000,
  shininess: 20, // 高光部分的亮度,默认30
  specular: 0x444444, // 高光部分的颜色
});

WebGL渲染器设置

  • 渲染器锯齿属性.antialias
  • 设置设备像素比.setPixelRatio()
  • 设置背景颜色.setClearColor()
javascript
// WebGL渲染器设置
const renderer = new THREE.WebGLRenderer({
  antialias:true,//开启优化锯齿
});
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

// 不同硬件设备的屏幕的设备像素比window.devicePixelRatio值可能不同
console.log('查看当前屏幕设备像素比',window.devicePixelRatio);

// 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);

renderer.setClearColor(0x444444, 1); //设置背景颜色

gui.js扩展库(可视化调整三维场景)

  • 引入gui.js

    javascript
    // 引入dat.gui.js的一个类GUI
    import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
  • 创建一个GUI对象

    javascript
    // 实例化一个gui对象
    const gui = new GUI();
    
    // 改变交互界面style属性
    gui.domElement.style.right = '0px';
    gui.domElement.style.width = '300px';
  • .add()方法快速创建UI交互界面

    格式:.add(控制对象,对象具体属性,其他参数)

    • 拖动条

      javascript
      // 一个网格模型
      const geometry = new THREE.BoxGeometry(20, 20, 20);
      const material = new THREE.MeshLambertMaterial({color: 0x00ffff});
      const mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);
      
      // 通过GUI改变mesh.position对象的xyz属性
      gui.add(mesh.position, 'x', 0, 180);
      gui.add(mesh.position, 'y', 0, 180);
      gui.add(mesh.position, 'z', 0, 180);
    • 下拉菜单

      参数3是数组或对象时,生成的交互界面是下拉菜单

      javascript
      gui.add(mesh.position, 'x', [-100, 0, 100]);
      
      gui.add(mesh.position, 'y', {
        左: -100, // 可以用中文
        中: 0,
        右: 100
      });
    • 单选框

      如果.add()改变属性的对应的数据类型如果是布尔值,那么交互界面就是一个单选框。

      javascript
      const obj = {
          rotate: false,
      };
      gui.add(obj, 'rotate').name('是否旋转');
      
      // 渲染循环
      function render() {
          // 当gui界面设置obj.rotate为true,mesh执行旋转动画
          if (obj.rotate) mesh.rotateY(0.01);
          renderer.render(scene, camera);
          requestAnimationFrame(render);
      }
      render();
  • .name()方法 改变gui生成交互界面显示的内容

    javascript
    //光源设置
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(100, 60, 50);
    scene.add(directionalLight);
    const ambient = new THREE.AmbientLight(0xffffff, 0.4);
    scene.add(ambient);
    
    // 通过GUI改变光照强度,范围0-2
    gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度');
    gui.add(directionalLight, 'intensity', 0, 2.0).name('平行光强度');
  • 步长.step()方法 设置交互界面每次改变属性值的间隔

    javascript
    gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度').step(0.1);
  • .onChange()方法监听对应交互界面的属性变化

    javascript
    const obj = {
        x: 30,
    };
    // 当obj的x属性变化的时候,就把此时obj.x的值value赋值给mesh的x坐标
    gui.add(obj, 'x', 0, 180).onChange(function(value){
      // 你可以写任何你想跟着obj.x同步变化的代码
        mesh.position.x = value;
    });
  • .addColor()生成颜色值改变的交互界面

    javascript
    gui.addColor(mesh.material, 'color');
  • .addFolder()分组

    .addFolder()返回的子文件夹对象,同样具有gui对象的.add().onChange().addColor()等等属性。

    javascript
    const gui = new GUI(); //创建GUI对象 
    const obj = {
        color: 0x00ffff,// 材质颜色
    };
    // 创建材质子菜单
    const matFolder = gui.addFolder('材质');
    // 材质颜色color
    matFolder.addColor(material, 'color')
  • 关闭.close()和展开.open()交互界面

    javascript
    // gui.close();//关闭菜单
    
    matFolder.close();