three.js基础

three.js基础

_
image-aYYq.png

三维场景Scene

可以把三维场景Scene对象理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界。

物体形状:几何体Geometry

https://threejs.org/manual/#zh/primitives

具体自带的API可以查询

Three.js提供了各种各样的几何体API,用来表示三维物体的几何形状。

image-hoGV.png

文档搜索关键词geometry你可以看到threejs提供各种几何体相关API,具体使用方法,也可以参考文档。

//创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(100, 100, 100); 

Geometry 长方体设置

const width =  7.1;  

const height = 8;  

const depth = 8;  

const widthSegments =  8;  

const heightSegments =  8;  

const depthSegments = 10;  

const geometry = new THREE.BoxGeometry(
	width, height, depth,
	widthSegments, heightSegments, depthSegments );
image-eFcQ.png

CircleGeometry 平面圆

image-ktLo.png
const radius = 7;  

const segments = 24;  

const thetaStart = Math.PI * 0.00;  

const thetaLength = Math.PI * 0.32;  

const geometry = new THREE.CircleGeometry(
	radius, segments, thetaStart, thetaLength );
// thetaStart 默认值为0 thetaLength默认值为2pi

ConeGeometry 锥形

image-AouX.png
const radius =  4.0;  // 锥形底部圆形大小


const height =  7.1;  // 锥形高


const radialSegments = 20;  // 锥形侧面边长


const geometry = new THREE.ConeGeometry( radius, height, radialSegments );
image-Rjin.png
const radius = 6;  

const height = 8;  

const radialSegments = 17;  // 底部圆线的数量(侧边线长数)

const heightSegments =  5;  // 侧边垂直方向线的数量

const openEnded = false;  // 底部圆是否显示
const thetaStart = Math.PI * 0.25;  

const thetaLength = Math.PI * 1.52;  
// thetaStart 默认值为0 thetaLength默认值为2pi 扇形的边框

const geometry = new THREE.ConeGeometry(
	radius, height,
	radialSegments, heightSegments,
	openEnded,
	thetaStart, thetaLength );

物体外观:材质Material

如果你想定义物体的外观效果,比如颜色,就需要通过材质Material相关的API实现。

threejs不同材质渲染效果不同,下面就以threejs最简单的网格基础材质MeshBasicMaterial(opens new window)为例给大家实现一个红色材质效果。

image-eINq.png
//创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
    color: 0xff0000,//0xff0000设置材质颜色为红色
}); 

物体:网格模型Mesh

实际生活中有各种各样的物体,在threejs中可以通过网格模型Mesh(opens new window)表示一个虚拟的物体,比如一个箱子、一个鼠标。

// 两个参数分别为几何体geometry、材质material
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh

模型位置.position

实际生活中,一个物体往往是有位置的,对于threejs而言也是一样的,你可以通过位置属性.position定义网格模型Mesh在三维场景Scene中的位置。

const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);

.add()方法

在threejs中你创建了一个表示物体的虚拟对象Mesh,需要通过.add()方法,把网格模型mesh添加到三维场景scene中。

scene.add(mesh); 

透视投影相机PerspectiveCamera


Threejs提供了正投影相机OrthographicCamera(opens new window)和透视投影相机PerspectiveCamera(opens new window)

透视投影相机PerspectiveCamera本质上就是在模拟人眼观察这个世界的规律。

// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();

gui

// 实例化一个gui对象
const gui = new GUI();

//改变交互界面style属性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';

//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变
const obj = {
    x: 30,
};
// gui增加交互界面,用来改变obj对应属性
gui.add(obj, 'x', 0, 100);

// .add()创建的交互界面,会默认显示所改变属性的名字,为了通过交互界面更好理解你改变的某个对象属性,你可以通过.name()方法改变gui生成交互界面显示的内容。
const gui = new GUI();//创建GUI对象 
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度');
gui.add(directionalLight, 'intensity', 0, 2.0).name('平行光强度');

// 步长.step()方法可以设置交互界面每次改变属性值间隔是多少。
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度').step(0.1);

// 当gui界面某个值的时候,.onChange()方法就会执行,这时候你可以根据需要通过.onChange()执行某些代码。
const obj = {
    x: 30,
};
// 当obj的x属性变化的时候,就把此时obj.x的值value赋值给mesh的x坐标
gui.add(obj, 'x', 0, 180).onChange(function(value){
    mesh.position.x = value;
	// 你可以写任何你想跟着obj.x同步变化的代码
	// 比如mesh.position.y = value;
});


// .addColor()生成颜色值改变的交互界面
const obj = {
    color:0x00ffff,
};
// .addColor()生成颜色值改变的交互界面
gui.addColor(obj, 'color').onChange(function(value){
    mesh.material.color.set(value);
});

.add()方法参数3和4数据类型:数字

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

其他参数,可以一个或多个,数据类型也可以不同,gui会自动根据参数形式,自动生成对应的交互界面。

参数3和参数4,分别是一个数字,交互界面是一个鼠标可以拖动的拖动条,可以在一个区间改变属性的值

// 参数3、参数4数据类型:数字(拖动条)
gui.add(obj, 'x', 0, 180).onChange(function (value) {
    mesh.position.x = value;
});

.add()方法参数3数据类型:数组

参数3是一个数组,生成交互界面是下拉菜单

const obj = {
    scale: 0,
};
// 参数3数据类型:数组(下拉菜单)
gui.add(obj, 'scale', [-100, 0, 100]).name('y坐标').onChange(function (value) {
    mesh.position.y = value;
});

.add()方法参数3数据类型:对象

参数3是一个对象,生成交互界面是下拉菜单

const obj = {
    scale: 0,
};
// 参数3数据类型:对象(下拉菜单)
gui.add(obj, 'scale', {
    left: -100,
    center: 0,
    right: 100
    // 左: -100,//可以用中文
    // 中: 0,
    // 右: 100
}).name('位置选择').onChange(function (value) {
    mesh.position.x = value;
});

.add()方法对应属性的数据类型:布尔值

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

const obj = {
    bool: false,
};
// 改变的obj属性数据类型是布尔值,交互界面是单选框
gui.add(obj, 'bool').name('是否旋转');
gui.add(obj, 'bool').onChange(function (value) {
    // 点击单选框,控制台打印obj.bool变化
    console.log('obj.bool',value);
});

当GUI交互界面需要控制的属性比较多的时候,为了避免混合,可以适当分组管理,这样更清晰。

gui交互界面不分组

gui交互界面不分组,只有一个默认的总的菜单。

const gui = new GUI(); //创建GUI对象 
//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变
const obj = {
    color: 0x00ffff,// 材质颜色
    specular: 0x111111,// 材质高光颜色
};


// 材质颜色color
gui.addColor(obj, 'color').onChange(function(value){
    material.color.set(value);
});
// 材质高光颜色specular
gui.addColor(obj, 'specular').onChange(function(value){
    material.specular.set(value);
});

// 环境光强度
gui.add(ambient, 'intensity',0,2);
// 平行光强度
gui.add(directionalLight, 'intensity',0,2);
// 平行光位置
gui.add(directionalLight.position, 'x',-400,400);
gui.add(directionalLight.position, 'y',-400,400);
gui.add(directionalLight.position, 'z',-400,400);

.addFolder()分组


new GUI()
实例化一个gui对象,默认创建一个总的菜单,通过gui对象的.addFolder()方法可以创建一个子菜单,当你通过GUI控制的属性比较多的时候,可以使用.addFolder()进行分组。

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

const gui = new GUI(); //创建GUI对象 
const obj = {
    color: 0x00ffff,// 材质颜色
};
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
matFolder.close();
// 材质颜色color
matFolder.addColor(obj, 'color').onChange(function(value){
    material.color.set(value);
});
// 材质高光颜色specular
matFolder.addColor(obj, 'specular').onChange(function(value){
    material.specular.set(value);
});

// 环境光子菜单
const ambientFolder = gui.addFolder('环境光');
// 环境光强度
ambientFolder.add(ambient, 'intensity',0,2);

// 平行光强度
dirFolder.add(directionalLight, 'intensity',0,2);
// 平行光位置
dirFolder.add(directionalLight.position, 'x',-400,400);
dirFolder.add(directionalLight.position, 'y',-400,400);
dirFolder.add(directionalLight.position, 'z',-400,400);

关闭.close()和展开.open()交互界面

gui对象创建的总菜单或gui.addFolder()创建的子菜单都可以用代码控制交互界面关闭或开展状态。

const gui = new GUI(); //创建GUI对象 
gui.close();//关闭菜单
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
matFolder.close();//关闭菜单

.addFolder()套娃——子菜单嵌套子菜单

.addFolder()创建的对象,同样也具有.addFolder()属性,可以继续嵌套子菜单

// 平行光子菜单
const dirFolder = gui.addFolder('平行光');
dirFolder.close();//关闭菜单
// 平行光强度
dirFolder.add(directionalLight, 'intensity',0,2);
const dirFolder2 = dirFolder.addFolder('位置');//子菜单的子菜单
dirFolder2.close();//关闭菜单
// 平行光位置
dirFolder2.add(directionalLight.position, 'x',-400,400);
dirFolder2.add(directionalLight.position, 'y',-400,400);
dirFolder2.add(directionalLight.position, 'z',-400,400);

针集合体的操作

// 几何体xyz三个方向都放大2倍
geometry.scale(2, 2, 2);
// 几何体沿着x轴平移50
geometry.translate(50, 0, 0);
// 几何体绕着x轴旋转45度
geometry.rotateX(Math.PI / 4);
// 几何体旋转、缩放或平移之后,查看几何体顶点位置坐标的变化
// BufferGeometry的旋转、缩放、平移等方法本质上就是改变顶点的位置坐标
console.log('顶点位置数据', geometry.attributes.position);

针对Mesh的操作

通过模型位置属性.position可以设置模型在场景Scene中的位置。模型位置.position的默认值是THREE.Vector3(0.0,0.0,0.0),表示坐标原点。

mesh.position.y = 80;
mesh.position.set(80,2,10);

平移

执行.translateX().translateY()等方法本质上改变的都是模型的位置属性.position

网格模型沿着x轴正方向平移100,可以多次执行该语句,每次执行都是相对上一次的位置进行平移变换。

// 等价于mesh.position = mesh.position + 100;
mesh.translateX(100);//沿着x轴正方向平移距离100

沿着Z轴负方向平移距离50。

mesh.translateZ(-50);

沿着自定义的方向移动。

//向量Vector3对象表示方向
const axis = new THREE.Vector3(1, 1, 1);
axis.normalize(); //向量归一化
//沿着axis轴表示方向平移100
mesh.translateOnAxis(axis, 100);

缩放

属性.scale表示模型对象的xyz三个方向上的缩放比例,.scale的属性值是一个三维向量对象Vector3,默认值是THREE.Vector3(1.0,1.0,1.0)

x轴方向放大2倍

mesh.scale.x = 2.0;

网格模型xyz方向分别缩放0.5,1.5,2倍

mesh.scale.set(0.5, 1.5, 2)

克隆.clone()和复制.copy()

克隆.clone()、复制.copy()是threejs很多对象都具有的方法,比如三维向量对象Vector3、网格模型Mesh、几何体、材质。

克隆.clone()

克隆.clone()简单说就是复制一个和原对象一样的新对象,下面以三维向量对象Vector3给大家举例,其他的threejs对象都可以参照类似的写法。

const v1 = new THREE.Vector3(1, 2, 3);
console.log('v1',v1);
//v2是一个新的Vector3对象,和v1的.x、.y、.z属性值一样
const v2 = v1.clone();
console.log('v2',v2);

复制.copy()

复制.copy()简单说就是把一个对象属性的属性值赋值给另一个对象,下面以三维向量对象Vector3给大家举例,其他的threejs对象都可以参照类似的写法。

const v1 = new THREE.Vector3(1, 2, 3);
const v3 = new THREE.Vector3(4, 5, 6);
//读取v1.x、v1.y、v1.z的赋值给v3.x、v3.y、v3.z
v3.copy(v1);

Mesh克隆.clone()

通过mesh克隆.clone()一个和mesh一样的新模型对象mesh2。

const mesh2 = mesh.clone();
mesh2.position.x = 100;

通过克隆.clone()获得的新模型和原来的模型共享材质和几何体

// 改变材质颜色,或者说改变mesh2颜色,mesh和mesh2颜色都会改变
// material.color.set(0xffff00);
mesh2.material.color.set(0xffff00);

几何体和材质克隆.clone()

const mesh2 = mesh.clone();
// 克隆几何体和材质,重新设置mesh2的材质和几何体属性
mesh2.geometry = mesh.geometry.clone();
mesh2.material = mesh.material.clone();
// 改变mesh2颜色,不会改变mesh的颜色
mesh2.material.color.set(0xff0000);

threejs应用程序的结构

UmiJS 3.x 字体资源异步加载问题解决方案 2025-12-30

评论区