一、快速入门
创建第一个3D案例
基本概念
场景Scene、相机Camera、渲染器Renderer
第一步:创建3D场景
- 创建 三维场景
Scene - 创建 物体形状:几何体
Geometry - 创建 物体外观:材质
Material - 创建 物体:网格模型
Mesh - 设置 模型位置
.position .add()方法 将模型添加到场景中
// 引入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);第二步:创建透视投影相机
- 实例化一个 透视投影相机
PerspectiveCamera - 设置 相机位置
.position - 设置 相机观察目标
.lookAt() - 定义相机渲染输出的画布尺寸
- 设置参数 透视投影相机
PerspectiveCamera:视锥体
透视投影相机的四个参数fov, aspect, near, far构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上。
| 参数 | 含义 | 默认值 |
|---|---|---|
| fov | 相机视锥体竖直方向视野角度 | 50 |
| aspect | 相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height | 1 |
| near | 相机视锥体近裁截面相对相机距离 | 0.1 |
| far | 相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向 | 2000 |
![]() |
// 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对应的位置第三步:创建渲染器
- 创建WebGL渲染器
WebGLRenderer - 设置Canvas画布尺寸
.setSize() - 渲染器渲染方法
.render() - 将canvas画布插入到web页面中 渲染器Canvas画布属性
.domElement
/**
* 创建渲染器对象
*/
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()的参数表示坐标系坐标轴线段尺寸大小,你可以根据需要改变尺寸。
// AxesHelper:辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);- 材质半透明设置
设置材质半透明,这样可以看到坐标系的坐标原点。
const material = new THREE.MeshBasicMaterial({
color: 0x0000ff, //设置材质颜色
transparent:true,//开启透明
opacity:0.5,//设置透明度
});AxesHelper的xyz轴
three.js坐标轴颜色红R、绿G、蓝B分别对应坐标系的x、y、z轴,对于three.js的3D坐标系默认y轴朝上。

光源
基础网格材质MeshBasicMaterial不会受到光照影响。
漫反射网格材质MeshLambertMaterial会受到光照影响,该材质也可以称为Lambert网格材质,音译为兰伯特网格材质。
- 设置物体使用漫反射网格材质
- 创建点光源
- 设置光源位置
- 光源添加到场景
// 材质对象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
// 光源辅助观察
const pointLightHelper = new THREE.PointLightHelper(pointLight, 10); // 要模拟的光源, 辅助对象的尺寸
scene.add(pointLightHelper);相机控件OrbitControls
- 旋转:拖动鼠标左键
- 缩放:滚动鼠标中键
- 平移:拖动鼠标右键
- 引入扩展库OrbitControls.js
- 创建OrbitControls实例
- 监听控制器,执行渲染操作
// 引入轨道控制器扩展库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)没有特定方向,只是整体改变场景的光照明暗。
//环境光:没有特定方向,整体改变场景的光照明暗
const ambient = new THREE.AmbientLight(0xffffff, 0.4); // 光照颜色、光照强度
scene.add(ambient);平行光
平行光DirectionalLight (opens new window)就是沿着特定方向发射。
- 创建平行光
- 设置光源的方向position
- 设置光源指向对象 target,默认为原点
- 光源添加到场景
// 平行光
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
// DirectionalLightHelper:可视化平行光
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000); // 模拟光源,辅助对象尺寸,模拟光源的颜色
scene.add(dirLightHelper);动画渲染循环
- 请求动画帧
window.requestAnimationFrame- threejs可以借助HTML5的API请求动画帧
window.requestAnimationFrame实现动画渲染。 - requestAnimationFrame实现周期性循环执行
- requestAnimationFrame默认每秒钟执行60次,但不一定能做到,要看代码的性能
- threejs可以借助HTML5的API请求动画帧
- 创建渲染器对象后执行渲染操作就可以放到渲染循环中做了
- 设置了渲染循环,相机控件OrbitControls就不用再通过事件
change执行渲染操作了
/**
* 创建渲染器对象
*/
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画布宽高度动态变化,需要更新相机和渲染的参数,否则无法正常渲染。
// 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次为最佳状态。
- 引入Stats
- 创建Stats对象
- 将 stats.domElement 插入到web页面中
- 在渲染函数中调用方法 stats.update(),来刷新时间
//引入性能监视器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
// stats.domElement显示:渲染帧率 刷新频率,一秒渲染次数
stats.setMode(0); // 默认模式
// stats.domElement显示:渲染周期 渲染一帧多长时间(单位:毫秒ms)
stats.setMode(1);Threejs常见几何体简介
//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。
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可以提供一个镜面反射效果,该材质可以模拟具有镜面高光的光泽表面(例如涂漆木材)。
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 20, // 高光部分的亮度,默认30
specular: 0x444444, // 高光部分的颜色
});WebGL渲染器设置
- 渲染器锯齿属性
.antialias - 设置设备像素比
.setPixelRatio() - 设置背景颜色
.setClearColor()
// 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是数组或对象时,生成的交互界面是下拉菜单
javascriptgui.add(mesh.position, 'x', [-100, 0, 100]); gui.add(mesh.position, 'y', { 左: -100, // 可以用中文 中: 0, 右: 100 });单选框
如果
.add()改变属性的对应的数据类型如果是布尔值,那么交互界面就是一个单选框。javascriptconst 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()方法 设置交互界面每次改变属性值的间隔javascriptgui.add(ambient, 'intensity', 0, 2.0).name('环境光强度').step(0.1);.onChange()方法监听对应交互界面的属性变化javascriptconst 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()生成颜色值改变的交互界面javascriptgui.addColor(mesh.material, 'color');.addFolder()分组.addFolder()返回的子文件夹对象,同样具有gui对象的.add()、.onChange()、.addColor()等等属性。javascriptconst gui = new GUI(); //创建GUI对象 const obj = { color: 0x00ffff,// 材质颜色 }; // 创建材质子菜单 const matFolder = gui.addFolder('材质'); // 材质颜色color matFolder.addColor(material, 'color')关闭
.close()和展开.open()交互界面javascript// gui.close();//关闭菜单 matFolder.close();

