Three.js 材质
目标版本: three.js r186。示例使用 WebGLRenderer;WebGPU 和 TSL 部分使用 WebGPURenderer。
本地覆盖(2026-11-10 由 Claude 为此 PC 添加;非原始技能的一部分)。我们的动画短片流水线 (
<ROOT>\studio,源自 mromidev/claude-animated-shorts) 固定使用 three 0.175 (r175)
- Remotion 4.0.290 + @react-three/fiber 9.1.2 + pmndrs postprocessing 6.37.2。保持 three 固定:shade.ts/cardshade.ts 对 three 着色器代码块进行字符串补丁。在处理该流水线时:
- 忽略需要 r176+ 的“常见错误”/API 建议(HDRLoader、Timer、PCFSoftShadowMap 弃用、setEffects、 RenderPipeline、outputBufferType、mesh.pivot、Capsule
height、TSL/WebGPU)。首先检查 API 是否存在于 r175 中。- 切勿使用 useFrame、Clock、Timer、delta time、setAnimationLoop 或 requestAnimationFrame 进行动画。Remotion 逐帧 渲染:每个值都从
useCurrentFrame()/ fps 推导(对于剪辑,使用mixer.setTime(frame / fps))。- 后期处理使用 pmndrs
postprocessing,而不是 three/addons 的 EffectComposer:不要添加 OutputPass(会导致双重 sRGB 编码)。- 卡通颜色:我们的合成器中的色调映射是 NEUTRAL;注意 ACES 默认值和
tonemapping_fragment的颜色偏差。- 对于其他 three.js 项目,以下原始 r186 指南按原样适用。
快速开始
import * as THREE from "three";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 3);
// PBR 材质 - 有光照、逼真表面的默认选择
const material = new THREE.MeshStandardMaterial({
color: 0x00aaff,
roughness: 0.35,
metalness: 0.1,
});
const mesh = new THREE.Mesh(new THREE.SphereGeometry(1, 48, 32), material);
scene.add(mesh);
scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 1)); // 有光照的材质没有灯光渲染为黑色
const light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(3, 3, 3);
scene.add(light);
function animate() {
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
材质类型概览
| 材质 | 使用场景 | 光照 |
|---|---|---|
| MeshBasicMaterial | 无光颜色、纹理、线框 | 无 |
| MeshLambertMaterial | 哑光漫射表面 | 漫反射 |
| MeshPhongMaterial | 光泽塑料、廉价高光 | 漫反射 + 高光 |
| MeshStandardMaterial | PBR 金属/粗糙度工作流 | PBR |
| MeshPhysicalMaterial | PBR 加上清漆、透射、绒面、彩虹色 | PBR |
| MeshToonMaterial | 卡通着色 | 卡通阶梯 |
| MeshMatcapMaterial | 将光照效果烘焙到 matcap 纹理中,无需灯光 | 已烘焙 |
| ShadowMaterial | 仅显示接收到的阴影的透明表面 | 仅阴影 |
| PointsMaterial | 点云 | 无 |
| LineBasicMaterial / LineDashedMaterial | 线条 | 无 |
| ShaderMaterial / RawShaderMaterial | 自定义 GLSL,仅限 WebGLRenderer | 自定义 |
颜色数据纹理(map、emissiveMap、specularMap、sheenColorMap、specularColorMap、LDR matcap、用作颜色的画布或视频)需要 texture.colorSpace = THREE.SRGBColorSpace,否则颜色会显得发白。标量贴图(normal、bump、displacement、roughness、metalness、ao、alpha、toon gradientMap)保留默认设置。HDR 加载器会自行设置色彩空间。
MeshBasicMaterial
无光照计算。速度快,始终可见。支持包含 reflectivity(0 到 1)和 combine(THREE.MultiplyOperation、THREE.MixOperation、THREE.AddOperation)的 envMap;specularMap(sRGB)遮盖环境贴图的贡献。
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;
const material = new THREE.MeshBasicMaterial({
color: 0xffffff, // 与贴图相乘
map,
transparent: true,
opacity: 0.5,
side: THREE.DoubleSide, // FrontSide(默认)、BackSide、DoubleSide
fog: true, // 受场景雾效影响
});
MeshLambertMaterial 和 MeshPhongMaterial
Lambert 仅支持漫反射。Phong 添加了高光;当 Standard 过于繁重时,它是廉价的替代方案。
const lambert = new THREE.MeshLambertMaterial({
color: 0x00ff00,
emissive: 0x111111, // 自发光颜色
emissiveIntensity: 1,
});
const phong = new THREE.MeshPhongMaterial({
color: 0x0000ff,
specular: 0x111111, // 高光颜色
shininess: 30, // 越高 = 高光越锐利
flatShading: false, // 平面与平滑法线
});
两者还支持 map、emissiveMap、specularMap(sRGB;遮盖环境贴图反射以及 Phong 的高光)、normalMap、bumpMap、displacementMap、aoMap、envMap、envMapIntensity 和 envMapRotation。
MeshStandardMaterial (PBR)
基于物理的渲染。推荐用于逼真效果。
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;
const ormMap = await new THREE.TextureLoader().loadAsync("orm.jpg"); // glTF ORM:ao 红色通道,roughness 绿色通道,metalness 蓝色通道;标量:保留默认设置
const material = new THREE.MeshStandardMaterial({
color: 0xffffff, // 与贴图相乘
roughness: 1.0, // 0 = 镜面,1 = 完全漫反射;与 roughnessMap(绿色通道)相乘
metalness: 1.0, // 0 = 电介质,1 = 金属;与 metalnessMap(蓝色通道)相乘,因此使用贴图时保留为 1
map,
roughnessMap: ormMap,
metalnessMap: ormMap,
normalMap: null, // 表面细节;通过 normalScale(一个 Vector2)缩放
aoMap: ormMap, // 环境光遮蔽(红色通道);强度通过 aoMapIntensity 控制
displacementMap: null, // 顶点位移;通过 displacementScale 缩放,通过 displacementBias 偏移
emissive: 0x000000,
emissiveIntensity: 1,
emissiveMap: null, // sRGB
envMap: null, // 或依赖 scene.environment(见下文)
fog: true,
});
lightMap(与 lightMapIntensity 一起)烘焙间接光照的方式与 aoMap 烘焙遮挡相同;HDR 或 EXR 格式的 lightMap 是 LinearSRGBColorSpace,加载器会设置它。
UV 通道:aoMap 和 lightMap 读取您选择的通道
每个使用 UV 映射的纹理都会采样由 texture.channel 选择的属性:0 读取 uv(默认),1 读取 uv1,2 读取 uv2,3 读取 uv3。aoMap 和 lightMap 不需要第二组 UV 坐标 —— 除非您更改 channel,否则它们采样 uv。GLTFLoader 将 TEXCOORD_1 加载为 uv1 并自行设置 channel。
const aoMap = await new THREE.TextureLoader().loadAsync("ao.jpg"); // 标量:保留默认设置
const material = new THREE.MeshStandardMaterial({ aoMap }); // 采样 uv:不需要额外属性
// 针对单独展开(烘焙的 AO 或光照贴图)的替代方案:将烘焙器的 u、v 对复制到 uv1
const bakedUv = new THREE.Float32BufferAttribute(mesh.geometry.attributes.position.count * 2, 2); // 在复制之前用零填充
mesh.geometry.setAttribute("uv1", bakedUv); mesh.material = material;
aoMap.channel = 1; // 此材质现在采样 uv1 而不是 uv
MeshPhysicalMaterial (高级 PBR)
扩展 MeshStandardMaterial,增加层和体积特性。其中大多数都有纹理变体:clearcoatMap、clearcoatRoughnessMap、clearcoatNormalMap、transmissionMap、thicknessMap、iridescenceMap、iridescenceThicknessMap、sheenColorMap、sheenRoughnessMap、anisotropyMap、specularIntensityMap、specularColorMap。
const material = new THREE.MeshPhysicalMaterial({
// 清漆(车漆、漆器)
clearcoat: 1.0, // 0 = 无,1 = 完全覆盖
clearcoatRoughness: 0.1,
// 透射(玻璃、水) - 折射其后的不透明物体和 scene.background
transmission: 1.0, // 0 = 不透明,1 = 完全透射
thickness: 0.5, // 用于折射的体积厚度
attenuationDistance: 1, // 世界单位;Infinity(默认)= 无吸收
attenuationColor: new THREE.Color(0xffaa66), // 白光在穿过 attenuationDistance 后变为此颜色
ior: 1.5, // 折射率,1 到 2.333
// 绒面(织物、天鹅绒)
sheen: 1.0,
sheenColor: new THREE.Color(0xffffff),
sheenRoughness: 0.5,
// 彩虹色(肥皂泡、油膜)
iridescence: 1.0,
iridescenceIOR: 1.3,
iridescenceThicknessRange: [100, 400], // 纳米
// 各向异性(拉丝金属);方向由 anisotropyRotation 控制
anisotropy: 1.0,
// 色散(棱镜玻璃);后向反射(路标、安全设备)
dispersion: 0.0,
retroreflectivity: 0.0,
// 高光覆盖:specularIntensity、specularColor(以及它们的贴图)
});
玻璃——具有光滑表面和后方可折射物体的透射材质:
const glass = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
metalness: 0,
roughness: 0,
transmission: 1,
thickness: 0.5,
ior: 1.5,
});
// transmission 折射其后方的物体和背景;scene.environment 添加其反射
MeshToonMaterial
卡通着色。渐变贴图是非颜色数据——保留其默认色彩空间。
const steps = new Uint8Array([120, 200]); // 两个着色阶梯
const gradientMap = new THREE.DataTexture(steps, 2, 1, THREE.RedFormat);
gradientMap.minFilter = THREE.NearestFilter; // 硬边缘;LinearFilter 会混合它们
gradientMap.magFilter = THREE.NearestFilter;
gradientMap.needsUpdate = true;
const material = new THREE.MeshToonMaterial({ color: 0x00ff00, gradientMap });
MeshNormalMaterial 和 MeshDepthMaterial
// 法线编码为 RGB - 用于调试和平坦示意效果
const normals = new THREE.MeshNormalMaterial({ flatShading: false, wireframe: false });
// 深度编码为灰度(BasicDepthPacking,默认);RGBADepthPacking 将其打包以便在着色器中读取
const depth = new THREE.MeshDepthMaterial();
PointsMaterial
用于点云。size 以像素为单位;启用 sizeAttenuation(默认,仅限透视相机)时,它乘以画布高度的一半并除以点在视图空间中的深度。
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.Float32BufferAttribute([0, 0, 0, 1, 0.5, 0, 0.5, 1, 0], 3));
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.1,
sizeAttenuation: true, // 随距离缩放
alphaTest: 0.5, // 与贴图或 alphaMap 一起使用:丢弃低于此 alpha 值的片段
});
const points = new THREE.Points(geometry, material);
scene.add(points);
LineBasicMaterial 和 LineDashedMaterial
WebGL 和 WebGPU 始终绘制 1 像素宽的线条图元;linewidth、linecap 和 linejoin 仅影响 SVGRenderer。要在 WebGLRenderer 下绘制粗线,请使用 three/addons/lines/ 中的 Line2、LineGeometry 和 LineMaterial(linewidth 以像素为单位);LineMaterial 是一个 ShaderMaterial,因此 WebGPURenderer 需要使用 three/addons/lines/webgpu/Line2.js 中的 Line2,它使用 Line2NodeMaterial。
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.Float32BufferAttribute([-1, 0, 0, 1, 0, 0], 3));
const solid = new THREE.LineBasicMaterial({ color: 0xffffff }); // 始终 1 像素
const dashed = new THREE.LineDashedMaterial({ color: 0xffffff, dashSize: 0.5, gapSize: 0.25 });
const line = new THREE.Line(geometry, dashed);
line.computeLineDistances(); // 必需,否则线条绘制为实线
scene.add(line);
ShaderMaterial (仅限 WebGLRenderer)
使用 Three.js 为您注入 uniform 的自定义 GLSL。它始终编译为 GLSL ES 3.00:texture()、textureLod() 和 dFdx 正常工作,texture2D / gl_FragColor 作为别名保留——让 glslVersion 保持未设置状态。ShaderMaterial、RawShaderMaterial 和 onBeforeCompile 没有 WebGPURenderer 路径;在那里,请使用节点材质和 TSL。
const timer = new THREE.Timer();
timer.connect(document); // 标签页隐藏时暂停
const material = new THREE.ShaderMaterial({
uniforms: { time: { value: 0 } },
vertexShader: `
varying vec2 vUv;
uniform float time;
void main() {
vUv = uv;
vec3 pos = position;
pos.z += sin(pos.x * 10.0 + time) * 0.1;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
varying vec2 vUv;
uniform float time;
void main() {
vec3 col = 0.5 + 0.5 * cos(time + vUv.xyx * 3.0);
gl_FragColor = vec4(col, 1.0);
}
`,
});
function animate() {
timer.update(); // 每帧第一件事,无参数
material.uniforms.time.value = timer.getElapsed();
renderer.render(scene, camera);
}
为您在着色器中声明的变量:modelMatrix、modelViewMatrix、projectionMatrix、viewMatrix、normalMatrix、cameraPosition,以及属性 position、normal、uv(此外,对于 InstancedMesh,还有 instanceMatrix,您需要自行应用:projectionMatrix * modelViewMatrix * instanceMatrix * vec4(pos, 1.0),以及与 vertexColors 一起的 color)。额外的 UV 集合和 tangent 未声明——请自行添加 in vec2 uv1;。
RawShaderMaterial (仅限 WebGLRenderer)
不会预先添加任何声明(仅从 glslVersion 添加 #version、SHADER_TYPE 和 SHADER_NAME 定义以及您的 defines),但渲染器仍然会设置 projectionMatrix、modelViewMatrix、viewMatrix、normalMatrix、modelMatrix 和 cameraPosition——在着色器中声明它们,切勿在 uniforms 中传递:同名的条目会在每次绘制时被覆盖(modelMatrix、modelViewMatrix、normalMatrix)或替换相机的值(projectionMatrix、viewMatrix、cameraPosition)。
const material = new THREE.RawShaderMaterial({
glslVersion: THREE.GLSL3, // 没有它,着色器是 GLSL ES 1.00,不支持 texture()
vertexShader: `
precision highp float;
precision highp int;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
in vec3 position;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
precision highp float;
precision highp int;
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`,
});
共享属性
跨越多种材质类型生效:
- 混合模式:
transparent: true启用透明度;blending: THREE.NormalBlending(默认)、AdditiveBlending、SubtractiveBlending、MultiplyBlending、CustomBlending(与blendSrc、blendDst、blendEquation一起使用)。 - 模板测试:
stencilWrite: true结合stencilRef、stencilFunc、stencilFail、stencilZFail、stencilZPass。 - 剔除模式:
side: THREE.FrontSide(默认)、BackSide、DoubleSide。 - 深度测试:
depthTest: true、depthWrite: true、depthFunc: THREE.LessEqualDepth。 - 多边形偏移:
polygonOffset: true、polygonOffsetFactor、polygonOffsetUnits(用于修复 z-fighting)。 - 渲染顺序:
renderOrder(整数)控制绘制顺序 - 强制单面:
forceSinglePass: true(用于双面渲染只是复制几何体的情况)。 - 片段丢弃:
alphaTest: 0.5丢弃低于此 alpha 值的片段。
环境响应
scene.environment 是一个 PMREMGenerator 生成的纹理立方体贴图,为场景中的所有标准/物理材质提供间接镜面反射和漫反射光照。遮罩:
- 材质 < 标准材质忽略
scene.environment envMap属性显式设置环境贴图;设置了它之后,材质会忽略scene.environmentenvMapIntensity(默认为 1)缩放反射贡献;设置为 0 可移除镜面反射
import { RGBELoader } from "three/addons/loaders/RGBELoader.js";
const rgbeLoader = new RGBELoader();
const texture = await rgbeLoader.loadAsync("environment.hdr");
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
// 为所有 PBR 材质设置:
scene.environment = envMap;
// 或逐个材质显式设置:
const material = new THREE.MeshStandardMaterial({ envMap, envMapIntensity: 0.5 });
texture.dispose(); // PMREMGenerator 复制了数据;原始纹理不再需要
pmremGenerator.dispose();
对于局部光照贴图,specularMap(sRGB)控制哪些区域接收环境贴图反射(sRGB)。
WebGPU 节点材质和 TSL
当使用 WebGPURenderer 时,所有基于 ShaderMaterial 的材质(包括 Line2NodeMaterial)都更倾向于使用节点 / TSL API。编译后的着色器输出 WGSL,而不是 GLSL。标准材质(MeshBasicNodeMaterial、MeshStandardNodeMaterial 等)在内部使用节点。您可以直接使用节点和 TSL 来构建自定义着色器:
import { MeshBasicNodeMaterial, uv, texture, mix, timerLocal } from "three/tsl";
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;
const material = new MeshBasicNodeMaterial();
material.colorNode = mix(
texture(map, uv().scale(2.0)), // 采样的颜色
texture(map, uv().scale(0.5)), // 较小的缩放
timerLocal().mul(0.5).sin() // 随时间淡入淡出
);
const mesh = new THREE.Mesh(new THREE.SphereGeometry(1, 48, 32), material);
raw 材质(例如 PointsNodeMaterial、SpriteNodeMaterial、VolumeNodeMaterial、Line2NodeMaterial)也可用。您可以使用节点构建自定义 effect:创建一个 MeshBasicNodeMaterial,设置 colorNode、opacityNode、positionNode,然后将它用作自定义 ShaderMaterial 的替代方案。
常见错误
- 贴图在材质上显得发白或褪色:检查颜色纹理(map、emissiveMap、specularMap)是否设置了
texture.colorSpace = THREE.SRGBColorSpace。标量纹理(roughness、metalness、normal、ao、displacement、toon gradientMap)保留默认的texture.colorSpace(通常是THREE.LinearSRGBColorSpace)。光线贴图(用于烘焙间接光)是 HDR 或 EXR,应由加载器设置为LinearSRGBColorSpace。 - 标准/物理材质渲染为黑色:检查场景中是否有灯光(定向光、点光、环境光、半球光)或有效的
scene.environment。 - 深度问题:检查
depthWrite和depthTest设置,尤其是在使用透明或混合材质时。 - 线宽无效:
linewidth仅适用于 SVGRenderer;对于 WebGLRenderer 中的粗线,使用Line2/LineMaterial。 - ShaderMaterial 的
texture2D错误:如果显式设置glslVersion导致 GLSL ES 1.00,则texture2D会失败。将其保持未设置状态以获得 GLSL ES 3.00。 - 残留的
outputBuffer引用:outputBuffer(通常用于OnBeforeCompile补丁着色器)在 r186+ 中已弃用——使用renderer.outputBufferType。