Three.js 재질
대상: three.js r186. 예제는 WebGLRenderer를 사용하며, WebGPU 및 TSL 섹션에서는 WebGPURenderer를 사용합니다.
로컬 오버라이드 (2026-11-10, 이 PC의 Claude가 추가, 원본 스킬 아님). 당사의 애니메이션 쇼트 파이프라인 (
<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))에서 파생하세요.- 후처리는 three/addons EffectComposer가 아닌 pmndrs
postprocessing을 사용합니다: 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 | 매캡 텍스처에 구워진 조명 효과, 조명 불필요 | 구워짐 |
| ShadowMaterial | 수신하는 그림자만 표시하는 투명 표면 | 그림자 전용 |
| PointsMaterial | 포인트 클라우드 | 없음 |
| LineBasicMaterial / LineDashedMaterial | 선 | 없음 |
| ShaderMaterial / RawShaderMaterial | 사용자 정의 GLSL, WebGLRenderer 전용 | 사용자 정의 |
색상 데이터 텍스처(map, emissiveMap, specularMap, sheenColorMap, specularColorMap, LDR 매캡, 색상으로 사용되는 캔버스 또는 비디오)는 texture.colorSpace = THREE.SRGBColorSpace가 필요합니다. 그렇지 않으면 색상이 바랜 것처럼 렌더링됩니다. 스칼라 맵(normal, bump, displacement, roughness, metalness, ao, alpha, toon gradientMap)은 기본값을 유지합니다. HDR 로더는 자체 색상 공간을 설정합니다.
MeshBasicMaterial
조명 계산이 없습니다. 빠르고 항상 보입니다. envMap을 reflectivity(0~1) 및 combine(THREE.MultiplyOperation, THREE.MixOperation, THREE.AddOperation)과 함께 지원합니다; specularMap(sRGB)은 env-map 기여도를 마스킹합니다.
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;
const material = new THREE.MeshBasicMaterial({
color: 0xffffff, // map과 곱해짐
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, // 평평한 vs 부드러운 법선
});
둘 다 map, emissiveMap, specularMap(sRGB; env-map 반사 및 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, // map과 곱해짐
roughness: 1.0, // 0 = 거울, 1 = 완전 확산; roughnessMap(초록)과 곱해짐
metalness: 1.0, // 0 = 유전체, 1 = 금속; metalnessMap(파랑)과 곱해지므로 map과 함께 사용 시 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 샘플링: 추가 속성 불필요
// 별도의 UV 언랩(구워진 AO 또는 라이트 맵)의 대안: 베이커의 u,v 쌍을 uv1으로 복사
const bakedUv = new THREE.Float32BufferAttribute(mesh.geometry.attributes.position.count * 2, 2); // 복사할 때까지 0으로 채워짐
mesh.geometry.setAttribute("uv1", bakedUv); mesh.material = material;
aoMap.channel = 1; // 이 재질은 이제 uv 대신 uv1을 샘플링합니다.
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,
});
// 투과는 뒤에 있는 불투명 객체와 배경을 굴절시킵니다. 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, // map 또는 alphaMap 사용 시: 이 알파 미만의 프래그먼트 폐기
});
const points = new THREE.Points(geometry, material);
scene.add(points);
LineBasicMaterial 및 LineDashedMaterial
WebGL과 WebGPU는 항상 1px 너비의 선 프리미티브를 그립니다. linewidth, linecap 및 linejoin은 SVGRenderer에만 영향을 미칩니다. WebGLRenderer에서 두꺼운 선을 그리려면 three/addons/lines/의 Line2와 LineGeometry, LineMaterial을 사용하십시오(linewidth 픽셀 단위). LineMaterial은 ShaderMaterial이므로 WebGPURenderer는 Line2NodeMaterial을 사용하는 three/addons/lines/webgpu/Line2.js의 Line2가 필요합니다.
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 }); // 항상 1px
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 유니폼이 자동으로 주입되는 사용자 정의 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, // 없으면 셰이더는 texture()가 없는 GLSL ES 1.00이 됨
vertexShader: `
precision highp float;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
in vec3 position;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
precision highp float;
uniform vec3 cameraPosition;
out vec4 fragColor;
void main() {
fragColor = vec4(cameraPosition, 1.0);
}
`,
});
WebGPU 및 TSL 노드 재질
WebGPURenderer에서 모든 셰이더는 TSL(TS Shader Language)로 작성된 노드 그래프로 구성되며, Fn() 또는 wgslFn()으로 함수로 래핑됩니다. 재질은 MeshStandardNodeMaterial, MeshPhysicalNodeMaterial 등과 같은 ...NodeMaterial 하위 클래스를 사용하여 WebGL RNM과 동일한 속성 API를 공유하며, .output = outputNode를 지원합니다.
import { MeshPhysicalNodeMaterial, color, uniform, timerLocal, mix, vec3, positionWorld, normalWorld, uv, textureLoad, Fn, remap } from 'three/tsl';
const material = new MeshPhysicalNodeMaterial();
material.colorNode = color(0x00aaff);
material.metalnessNode = uniform(0.1);
material.roughnessNode = uniform(0.35);
// 사용자 정의 노드 속성
material.customNode = { time: timerLocal() };
// 수정자: 재질 출력을 수정하는 함수
// 메모: `Fn()`으로 생성된 모든 `modifier`는 재질의 `.output`을 유일한 인수로 받습니다.
material.outputModifier = Fn(() => {
const time = material.customNode.time;
const pulse = mix(0.8, 1.2, remap(Math.sin(time.toVar()), -1, 1, 0, 1));
return pulse;
});
const mesh = new THREE.Mesh(new THREE.SphereGeometry(1, 48, 32), material);
outputModifier가 지정되지 않으면 .output = ...을 직접 사용할 수 있습니다. 물리적 출력을 교체하는 것이 아니라 포스트 이펙트 스타일로 수정합니다.
웹GPU 재질은 또한 내부 빌트인 propertyNode(textureLoad(...), 'propName')을 통해 내부 속성(예: roughness, metalness)에 연결할 수 있는 텍스처를 허용합니다. 적절한 파이프라인을 사용하려면 propertyNode의 텍스처 로드에 대한 문서를 참조하십시오.
TSL 셰이더(일반 노드 재질 출력)
사용자 정의 재질 출력을 위해 Fn()을 .output = 설정과 함께 사용할 수 있습니다.
// 부동 UV 색상의 기본 예제
const colorNode = Fn(() => {
const time = timerLocal();
const uvs = uv();
const col = vec3(0.5).add(vec3(0.5).mul(Math.sin(time.add(uvs.xyx.mul(3.0)))));
return col;
});
const material = new THREE.MeshBasicNodeMaterial(); // 노드 버전 사용
material.outputNode = colorNode;
이것은 WebGL ShaderMaterial의 '시간에 따라 변하는 컬러 UV' 예제에 해당하는 WebGPU TSL입니다.
Blending
transparent: true | depthWrite | ||
|---|---|---|---|
| Normal | (no blend; true sets blending: THREE.NormalBlending) | 기본 동작 | 기본값 true |
| Additive | blending: THREE.AdditiveBlending ([1, 1] src/dst factors) | opacity < 1 또는 map 알파 필요 | 기본값 true |
| Subtractive | blending: THREE.SubtractiveBlending ([0, 1 - src]) | transparent 필요 | 기본값 true |
| Multiply | blending: THREE.MultiplyBlending ([dst, 0]) | transparent 필요 | 기본값 true |
| Custom | blendSrc, blendDst, blendEquation 및 blending: THREE.CustomBlending 설정 | transparent 필요 | 상황에 따라 다름 |
불투명 재질에 가산 블렌딩이 필요할 때도 있습니다. blending을 설정하기 전에 재질을 transparent: true로 설정하십시오. 그렇지 않으면 transparent가 블렌딩 모드를 재설정합니다.
const material = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true, // 블렌딩 모드 설정 전에 설정
blending: THREE.AdditiveBlending,
depthWrite: false, // 가산 또는 반투명 재질의 경우 일반적으로 false
});
Stencil (스텐실)
스텐실 버퍼를 사용하여 재질의 픽셀 가시성을 제한합니다.
const stencilMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
stencilWrite: true, // 이 재질이 스텐실 버퍼에 씁니다
stencilRef: 1, // 이 재질이 쓸 값
stencilFunc: THREE.AlwaysStencilFunc, // 쓰기 방법: 항상 통과
stencilZPass: THREE.ReplaceStencilOp, // 통과 시 현재 값을 stencilRef로 교체
});
다음 재질은 스텐실 버퍼를 읽어 픽셀의 가시성을 제한할 수 있습니다.
const maskMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
stencilWrite: false, // 스텐실에 쓰지 않음
stencilRef: 1,
stencilFunc: THREE.EqualStencilFunc, // stencilRef(1)과 같은 스텐실 버퍼 값이 있는 픽셀만 통과
});
일반적인 함정
- 조명 재질은 조명이 없으면 검게 보입니다. 광원(
HemisphereLight,DirectionalLight등)을 추가하십시오. - MeshStandardMaterial / MeshPhysicalMaterial은 조명이 필요합니다.
envMap또는scene.environment가 조명을 제공하지 않는 한 말입니다. - MeshStandardMaterial / MeshPhysicalMaterial의 노출은
renderer.toneMappingExposure로 제어됩니다. 기본값은 1입니다. HDR 환경 맵의 경우 더 낮출 수 있습니다. - 텍스처 색상 공간을 잊지 마십시오: 색상 데이터 맵(알베도, 이미시브, 반사 등)은 sRGB여야 합니다. 거칠기, 금속성, 법선 범프 등의 스칼라 데이터는 기본값(Linear sRGB)을 유지합니다.
MeshPhysicalMaterial전송: 불투명 객체와 배경이 뒤에 있고 굴절을 활성화하려면 전송이 필요합니다.scene.environment또는envMap은 반사에 기여합니다.ShaderMaterial:uniforms개체는 플랫(uniform 값은 깊은 복사되지 않음)입니다. Uniforms만 변경할 수 있습니다. 전체 개체를 교체하면 런타임 오류가 발생합니다.RawShaderMaterial:glslVersion: THREE.GLSL3을 설정하지 않으면 셰이더는texture()를 지원하지 않는 GLSL ES 1.00으로 컴파일됩니다. 내장 유니폼을uniforms에 절대 넣지 마십시오.- WebGPU 재질:
MeshStandardNodeMaterial,MeshPhysicalNodeMaterial등은outputModifier를 통해 또는.output을 직접 설정하여 셰이더 그래프 조작을 지원합니다. 긴밀하게 통합된 WebGPU 지원을 위해 WebGPURenderer + TSL 조합을 사용하십시오.
Material (재질) API 참조
일반적인 속성:
transparent, opacity, alphaTest, alphaHash, alphaToCoverage, depthTest, depthWrite, depthFunc, stencilWrite, stencilRef, stencilFunc, stencilFail, stencilZFail, stencilZPass, wireframe, flatShading, side, shadowSide, fog, blending, blendSrc, blendDst, blendEquation, blendSrcAlpha, blendDstAlpha, blendEquationAlpha, colorWrite, dithering.
더 많은 정보는 three.js 문서를 방문하십시오. 대상: r186.