什么是基础材质
想象我们给一个3D模型(比如一个立方体或球体)涂颜色。基础材质就像是用一种固定颜色的油漆直接刷上去,它完全忽略光线的影响。也就是说,无论场景中有没有灯光,物体看起来都一样亮,颜色不会变深或变浅。
如果我们给材质设置为红色,那么物体在任何角度下都是均匀的红色,不会有阴影或高光(高光是指物体表面反光时的亮点)。其效果会显得'扁平',不像真实物体那样有立体感。
为什么称其为基础材质
因为它简单直接,但有限制
- 优点:超级简单,不需要设置灯光。我们在做一些卡通风格、UI元素或背景物体,基础材质是完美的选择。比如,创建一个不需要真实感的标志或简单的3D文字时,它速度快、性能好(对电脑负担小)。
- 缺点:因为它忽略光线,物体看起来不够真实。如果我们想让物体有"金属感"或"塑料感",基础材质就做不到。例如,一个球体在灯光下应该有亮部和暗部,但基础材质会让它像一张剪纸一样平坦。
- 对比其他材质:three.js 还有更高级的材质,比如
MeshLambertMaterial
(它会响应光线,让物体有明暗变化)或MeshPhongMaterial
(能模拟高光,像反光的塑料)。
适用场景
- 简单原型或测试:当我们想快速搭建一个3D场景时,基础材质能节省省时间。比如,先做个草稿,再换成更高级的材质。
- 非真实渲染(NPR) :像卡通动画、游戏中的低多边形风格,基础材质能营造出"手绘"效果。
- 性能优化:如果网页需要加载很多物体,基础材质消耗资源少,能提升流畅度。但记住,它不支持纹理贴图(比如给物体贴图片),只能纯色或简单渐变。
顶点着色案例
效果如图

需要了解的API
MeshBasicMaterial
最基础的材质类型,无视光照,恒定显示颜色/纹理。 适用于简单模型、UI元素或性能敏感场景。
以下是一些常用的属性
属性 | 类型 | 默认值 | 作用 | 示例值 |
---|---|---|---|---|
color |
THREE.Color |
0xffffff (白色) |
材质基础色 | new THREE.Color('skyblue') |
map |
THREE.Texture |
null |
表面贴图纹理 | new THREE.TextureLoader().load('brick.jpg') |
wireframe |
boolean |
false |
是否显示为线框 | true (网格线效果) |
opacity |
number |
1.0 |
透明度 (需开启transparent ) |
0.5 (半透明) |
transparent |
boolean |
false |
是否允许透明 | 设为true 后opacity 生效 |
side |
enum |
THREE.FrontSide |
渲染面 | THREE.DoubleSide (双面渲染) |
fog |
boolean |
true |
是否受场景雾气影响 | false (穿透雾气) |
alphaMap |
THREE.Texture |
null |
透明通道贴图 | 用灰度图控制透明度 |
aoMap |
THREE.Texture |
null |
环境遮挡贴图 | 模拟阴影凹陷效果 |
visible |
boolean |
true |
是否可见 | false (隐藏材质) |
本案例会用到vertexColors
属性,它存在于基类Material
上,用来决定是否使用顶点着色,下面是实现代码
js
const geometry = new THREE.BoxGeometry( 1, 1, 1);
const att_pos = geometry.getAttribute( 'position' );
const data_color =[];
let i=0;
while ( i < att_pos.count ) {
data_color.push(Math.random(),Math.random(),Math.random()); // 生成随机颜色数据
i += 1;
}
const att_color = new THREE.BufferAttribute( new Float32Array( data_color ), 3 );
geometry.setAttribute( 'color', att_color ); // 设置颜色属性
const material = new THREE.MeshBasicMaterial( { vertexColors: true } ); // 启用顶点颜色
const box = new THREE.Mesh( geometry, material )
scene.add( box )
从代码可以发现,我们获取到几何体顶点位置数据,循环地点数量,生成一组随机颜色,然后设置几何体颜色属性,最后启用顶点着色实现上面案例效果
组合材质案例
假设我们现在有一个立方体,需要每个面设置不同的颜色该如何实现,先看效果图

这里涉及到材质相关问题,查看官网中关于Mesh
的说明

既然material
可以为一个数组,那就好办了
js
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
[0,1,2,3,4,5].forEach((mi,i) => {
geometry.groups[i].materialIndex = mi; // 设置每个组的材质索引
})
const materials = [
new THREE.MeshBasicMaterial( { color: 0x00ff00 } ),
new THREE.MeshBasicMaterial( { color: 0x0000ff } ),
new THREE.MeshBasicMaterial( { color: 0xffff00 } ),
new THREE.MeshBasicMaterial( { color: 0xff00ff } ),
new THREE.MeshBasicMaterial( { color: 0x00ffff } ),
new THREE.MeshBasicMaterial( { color: 0xff0000 } )
]
const box = new THREE.Mesh( geometry, materials );
scene.add( box );
线条基础材质案例
上面的案例都是作用在Mesh
上的,three.js提供了作用在line
上的基础材质
需要了解的API
LineBasicMaterial
LineBasicMaterial
是专用于绘制线段/线框的材质,无视光照且性能高效。适用于路径可视化、辅助线、轮廓绘制等场景。
一些常用属性
属性 | 类型 | 默认值 | 作用 | 限制说明 |
---|---|---|---|---|
color |
THREE.Color |
0xffffff |
线条颜色(支持16进制/RGB/颜色名) | - |
linewidth |
number |
1 |
线宽(像素) ,但多数平台仅支持1 (WebGL规范限制) |
实际渲染常固定为1 |
opacity |
number |
1 |
透明度(需配合transparent:true 生效) |
范围 [0, 1] |
transparent |
boolean |
false |
启用透明度 | - |
LineSegment
LineSegments
是 Three.js 中用于绘制独立线段集合的对象,适合需要断开连接的线段场景(如网格线、轮廓线)
EdgesGeometry
EdgesGeometry
是 Three.js 中用于智能提取几何体边缘的工具,可将任意几何体转化为线框结构(仅保留关键边线)。
了解这些概念后,我们的代码如下
js
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material_mesh = new THREE.MeshBasicMaterial( { color: 'deepskyblue' } );
const material_line = new THREE.LineBasicMaterial( { color: 'deeppink' });
const box = new THREE.Mesh( geometry, material_mesh );
box.add(
new THREE.LineSegments(
new THREE.EdgesGeometry( geometry ),
material_line
)
)
scene.add( box );
效果如下

贴图案例
效果如图

这个案例很简单,主要是利用canvas绘制图形,通过CanvasTexture
生成纹理, map
属性用来贴图
js
const canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = 64;
canvas.height = 64;
ctx.fillStyle = 'deepskyblue';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.lineWidth = 4;
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(canvas.width/2, canvas.height/2, 20 ,0 , Math.PI * 2);
ctx.closePath();
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.rect(0,0,canvas.width,canvas.height);
ctx.stroke();
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ map: new THREE.CanvasTexture(canvas) }));
scene.add(cube);
uv贴图案例
先看效果

首先我们要了解什么是uv
UV 是三维图形学中的通用概念,在Three.js 中用于将2D纹理精准映射到3D模型表面。可理解为:
- U 和 V 是二维纹理的坐标系(类似平面图的X/Y轴),范围固定为
[0, 1]
。 - 每个3D模型的顶点都对应一组UV坐标,定义该顶点在纹理图中的位置。
实现思路
通过canvas创建画布生成如下纹理

js
const CELL_SIZE = 4; // 单元格大小
const canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = 128;
canvas.height = 128;
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); // 创建线性渐变
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient; // 设置填充颜色为渐变
ctx.fillRect(0, 0, canvas.width, canvas.height);
let i = 0;
const len = CELL_SIZE * 2;
const cellsize = canvas.width / CELL_SIZE; // 计算单元格大小32
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = '32px arial';
while(i<len){
const gx = i%CELL_SIZE; // 计算当前单元格的x坐标索引 0,1,2,3...
const gy = Math.floor(i/CELL_SIZE); // 计算当前单元格的y坐标索引 0,1,2...
const x = cellsize * gx + cellsize / 2; // 计算当前单元格的中心点x坐标
const y = cellsize * gy + cellsize / 1.8; // 计算当前单元格的中心点y坐标
ctx.fillText(i, x, y);
i++;
}
const geometry = new THREE.BoxGeometry(1,1,1);
const texture = new THREE.CanvasTexture(canvas);
texture.magFilter = THREE.NearestFilter; // 设置纹理过滤方式
const material = new THREE.MeshBasicMaterial({map: texture});
什么,怎么生成了这样的纹理,别急,uv的作用就是用来定义顶点在纹理图中的位置,打印看看当前几何体的UV

我们的纹理将被贴在(0,0)左下角,(1,1)右上角位置,于是乎就出现了上面的情况,我们来修改UV调整显示
js
const setUVFace = (uv, faceIndex, cellIndex, order, gridSize) => {
const uvData = getUVData(faceIndex, cellIndex, gridSize);
setUVData(uv, uvData, order );
};
js
const getUVData = (foceIndex = 0, cellIndex = 0,gridSize = 4) => {
const cellX = cellIndex % gridSize;
const cellY = Math.floor(cellIndex / gridSize);
let di = 0;
const uvd = 1/gridSize; // 单元格宽度
const uvData = []; // 存储UV数据的数组
while(di<4){
const i = foceIndex * 4 + di; // 计算当前顶点的索引
const x = di % 2; // 0,1,0,1...
const y = 1 - 1*Math.floor(di/2); // 1,0,1,0...
const u = uvd * cellX + uvd * x;
const v = 1 - uvd * (cellY + 1) + y*uvd;
uvData.push({i,u,v});
di++;
}
return uvData;
}
const setUVData = (uv,uvData,order) => {
order = order || [0,1,2,3];
uvData.forEach((a,di,uvData) => {
const b = uvData[ order[di] ]; // 获取下一个顶点的UV坐标
uv.setXY(a.i,a.u,a.v); // 设置当前顶点的UV坐标
})
uv.needsUpdate = true;
}
从上面的图可以看出,我们这里需要分成4个单元格才能得到每个面有且只显示一个单独的数字

最终可以看到如下效果

发现没有,数字产生了锯齿很模糊,如何解决呢,可以通过扩大画布大小来解决
js
canvas.width = 1024;
canvas.height = 1024;

透明贴图案例
先看效果

透明贴图在之前章节有介绍 Three.js-硬要自学系列29之专项学习透明贴图什么是透明贴图
js
const canvas = document.createElement('canvas'), // 创建画布元素
ctx = canvas.getContext('2d'); // 获取画布的2D上下文
canvas.width = 100; // 设置画布宽度
canvas.height = 100; // 设置画布高度
ctx.fillStyle = 'deepskyblue'; // 设置填充颜色为深天蓝色
ctx.fillRect(0, 0, canvas.width, canvas.height); // 绘制矩形
ctx.strokeStyle = 'deeppink'; // 设置边框颜色为深粉
ctx.lineWidth = 6; // 设置边框宽度为10
ctx.strokeRect(8,8, canvas.width-16, canvas.height-16); // 绘制矩形边框
const texture = new THREE.CanvasTexture(canvas); // 创建纹理
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1), // 创建立方体几何
new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
opacity: 0.5,
color: '#fff'
})
)
scene.add(cube);