Materiales de three.js
Orientado a three.js r186. Los ejemplos usan WebGLRenderer; la sección de WebGPU y TSL usa WebGPURenderer.
Anulación local (añadido el 2026-11-10 por Claude para este PC; no forma parte del skill original). Nuestro pipeline de cortos animados (
<ROOT>\studio, de mromidev/claude-animated-shorts) está fijado en three 0.175 (r175)
- Remotion 4.0.290 + @react-three/fiber 9.1.2 + pmndrs postprocessing 6.37.2. Mantén three fijado: shade.ts/cardshade.ts parchea cadenas de shaders de three. Al trabajar en ese pipeline:
- Ignora "Errores comunes" / consejos de API que necesiten r176+ (HDRLoader, Timer, PCFSoftShadowMap deprecation, setEffects, RenderPipeline, outputBufferType, mesh.pivot, Capsule
height, TSL/WebGPU). Primero verifica que la API exista en r175.- Nunca animes con useFrame, Clock, Timer, delta time, setAnimationLoop o requestAnimationFrame. Remotion renderiza cuadro a cuadro: deriva cada valor de
useCurrentFrame()/ fps (ymixer.setTime(frame / fps)para clips).- El post-procesado es pmndrs
postprocessing, no three/addons EffectComposer: no agregues un OutputPass (doble codificación sRGB).- Colores toon: el mapeo de tonos es NEUTRAL en nuestro compositor; ten cuidado con los valores por defecto ACES y el teñido de
tonemapping_fragment.- Para otros proyectos de three.js, la guía original de r186 a continuación aplica tal como está escrita.
Inicio rápido
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);
// Material PBR: la opción por defecto para superficies iluminadas y realistas
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)); // los materiales iluminados se renderizan en negro sin luces
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);
Resumen de tipos de material
| Material | Caso de uso | Iluminación |
|---|---|---|
| MeshBasicMaterial | Color sin iluminación, texturas, wireframes | Ninguna |
| MeshLambertMaterial | Superficies mates difusas | Difusa |
| MeshPhongMaterial | Plástico brillante, especular barato | Difusa + especular |
| MeshStandardMaterial | Flujo de trabajo PBR metal/rugosidad | PBR |
| MeshPhysicalMaterial | PBR más capa transparente, transmisión, brillo, iridiscencia | PBR |
| MeshToonMaterial | Sombreado cel | Pasos toon |
| MeshMatcapMaterial | Aspecto iluminado horneado en una textura matcap, no necesita luces | Horneado |
| ShadowMaterial | Superficie transparente que solo muestra las sombras que recibe | Solo sombras |
| PointsMaterial | Nubes de puntos | Ninguna |
| LineBasicMaterial / LineDashedMaterial | Líneas | Ninguna |
| ShaderMaterial / RawShaderMaterial | GLSL personalizado, solo WebGLRenderer | Personalizada |
Las texturas de datos de color (map, emissiveMap, specularMap, sheenColorMap, specularColorMap, matcap LDR, un canvas o video usado como color) necesitan texture.colorSpace = THREE.SRGBColorSpace, o los colores se renderizan descoloridos. Los mapas escalares (normal, bump, displacement, roughness, metalness, ao, alpha, toon gradientMap) mantienen el valor por defecto. Los cargadores HDR establecen su propio espacio de color.
MeshBasicMaterial
Sin cálculos de iluminación. Rápido, siempre visible. Soporta envMap con reflectivity (0 a 1) y combine (THREE.MultiplyOperation, THREE.MixOperation, THREE.AddOperation); specularMap (sRGB) enmascara la contribución del env-map.
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;
const material = new THREE.MeshBasicMaterial({
color: 0xffffff, // multiplicado con el map
map,
transparent: true,
opacity: 0.5,
side: THREE.DoubleSide, // FrontSide (por defecto), BackSide, DoubleSide
fog: true, // afectado por la niebla de la escena
});
MeshLambertMaterial y MeshPhongMaterial
Lambert solo es difuso. Phong añade un brillo especular; es la alternativa barata cuando Standard es demasiado pesado.
const lambert = new THREE.MeshLambertMaterial({
color: 0x00ff00,
emissive: 0x111111, // color de autoiluminación
emissiveIntensity: 1,
});
const phong = new THREE.MeshPhongMaterial({
color: 0x0000ff,
specular: 0x111111, // color del brillo
shininess: 30, // más alto = brillo más nítido
flatShading: false, // sombreado plano vs suave
});
Ambos también soportan map, emissiveMap, specularMap (sRGB; enmascara el reflejo del env-map y el brillo en Phong), normalMap, bumpMap, displacementMap, aoMap, envMap, envMapIntensity y envMapRotation.
MeshStandardMaterial (PBR)
Renderizado basado en física. Recomendado para resultados realistas.
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 rojo, roughness verde, metalness azul; escalar: mantener por defecto
const material = new THREE.MeshStandardMaterial({
color: 0xffffff, // multiplicado con el map
roughness: 1.0, // 0 = espejo, 1 = completamente difuso; multiplicado por roughnessMap (verde)
metalness: 1.0, // 0 = dieléctrico, 1 = metal; multiplicado por metalnessMap (azul), así que mantén 1 con un map
map,
roughnessMap: ormMap,
metalnessMap: ormMap,
normalMap: null, // detalle de superficie; escala con normalScale (un Vector2)
aoMap: ormMap, // oclusión ambiental (rojo); intensidad con aoMapIntensity
displacementMap: null, // desplazamiento de vértices; escala con displacementScale, desplazamiento con displacementBias
emissive: 0x000000,
emissiveIntensity: 1,
emissiveMap: null, // sRGB
envMap: null, // o confiar en scene.environment (ver más abajo)
fog: true,
});
lightMap (con lightMapIntensity) hornea la luz indirecta de la misma manera que aoMap hornea la oclusión; un lightMap HDR o EXR es LinearSRGBColorSpace, que los cargadores establecen.
Canales UV: aoMap y lightMap leen el canal que elijas
Cada textura con mapeo UV muestrea el atributo seleccionado por texture.channel: 0 lee uv (por defecto), 1 lee uv1, 2 lee uv2, 3 lee uv3. aoMap y lightMap no necesitan un segundo conjunto UV: muestrean uv a menos que cambies channel. GLTFLoader carga TEXCOORD_1 como uv1 y establece channel por sí mismo.
const aoMap = await new THREE.TextureLoader().loadAsync("ao.jpg"); // escalar: mantener por defecto
const material = new THREE.MeshStandardMaterial({ aoMap }); // muestrea uv: no se necesita atributo adicional
// Alternativa para un unwrap separado (un mapa de AO o luz horneada): copia los pares u,v de tu baker en uv1
const bakedUv = new THREE.Float32BufferAttribute(mesh.geometry.attributes.position.count * 2, 2); // lleno de ceros hasta que los copies
mesh.geometry.setAttribute("uv1", bakedUv); mesh.material = material;
aoMap.channel = 1; // este material ahora muestrea uv1 en lugar de uv
MeshPhysicalMaterial (PBR avanzado)
Extiende MeshStandardMaterial con características de capa y volumen. Existen variantes de textura para la mayoría de ellas: clearcoatMap, clearcoatRoughnessMap, clearcoatNormalMap, transmissionMap, thicknessMap, iridescenceMap, iridescenceThicknessMap, sheenColorMap, sheenRoughnessMap, anisotropyMap, specularIntensityMap, specularColorMap.
const material = new THREE.MeshPhysicalMaterial({
// Capa transparente (pintura de coche, laca)
clearcoat: 1.0, // 0 = ninguno, 1 = capa completa
clearcoatRoughness: 0.1,
// Transmisión (vidrio, agua) - refracta los objetos opacos y el scene.background detrás
transmission: 1.0, // 0 = opaco, 1 = completamente transmisivo
thickness: 0.5, // grosor del volumen para refracción
attenuationDistance: 1, // unidades del mundo; Infinity (por defecto) = sin absorción
attenuationColor: new THREE.Color(0xffaa66), // la luz blanca se vuelve de este color después de attenuationDistance
ior: 1.5, // índice de refracción, 1 a 2.333
// Brillo (tela, terciopelo)
sheen: 1.0,
sheenColor: new THREE.Color(0xffffff),
sheenRoughness: 0.5,
// Iridiscencia (burbujas de jabón, manchas de aceite)
iridescence: 1.0,
iridescenceIOR: 1.3,
iridescenceThicknessRange: [100, 400], // nanómetros
// Anisotropía (metal cepillado); dirección con anisotropyRotation
anisotropy: 1.0,
// Dispersión (vidrio prismático); retroreflectividad (señales de tráfico, equipos de seguridad)
dispersion: 0.0,
retroreflectivity: 0.0,
// Anulaciones especulares: specularIntensity, specularColor (más sus mapas)
});
Vidrio: transmisión con una superficie lisa y algo detrás para refractar:
const glass = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
metalness: 0,
roughness: 0,
transmission: 1,
thickness: 0.5,
ior: 1.5,
});
// La transmisión refracta los objetos opacos y el fondo detrás; scene.environment añade sus reflejos
MeshToonMaterial
Sombreado cel. El mapa de gradiente no son datos de color: mantén su espacio de color por defecto.
const steps = new Uint8Array([120, 200]); // dos pasos de sombreado
const gradientMap = new THREE.DataTexture(steps, 2, 1, THREE.RedFormat);
gradientMap.minFilter = THREE.NearestFilter; // pasos duros; LinearFilter los mezcla
gradientMap.magFilter = THREE.NearestFilter;
gradientMap.needsUpdate = true;
const material = new THREE.MeshToonMaterial({ color: 0x00ff00, gradientMap });
MeshNormalMaterial y MeshDepthMaterial
// Normales codificadas como RGB: depuración y apariencias ilustrativas planas
const normals = new THREE.MeshNormalMaterial({ flatShading: false, wireframe: false });
// Profundidad como escala de grises (BasicDepthPacking, por defecto); RGBADepthPacking la empaqueta para leerla de vuelta en un shader
const depth = new THREE.MeshDepthMaterial();
PointsMaterial
Para nubes de puntos. size está en píxeles; con sizeAttenuation (por defecto, solo cámaras en perspectiva) se multiplica por la mitad de la altura del canvas y se divide por la profundidad del punto en espacio de vista.
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, // escala con la distancia
alphaTest: 0.5, // con un map o alphaMap: descarta fragmentos por debajo de este alfa
});
const points = new THREE.Points(geometry, material);
scene.add(points);
LineBasicMaterial y LineDashedMaterial
WebGL y WebGPU siempre dibujan primitivas de línea de 1 píxel de ancho; linewidth, linecap y linejoin solo afectan al SVGRenderer. Para líneas gruesas con WebGLRenderer usa Line2 con LineGeometry y LineMaterial de three/addons/lines/ (linewidth en píxeles); LineMaterial es un ShaderMaterial, así que WebGPURenderer necesita Line2 de three/addons/lines/webgpu/Line2.js, que usa 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 }); // siempre 1 px
const dashed = new THREE.LineDashedMaterial({ color: 0xffffff, dashSize: 0.5, gapSize: 0.25 });
const line = new THREE.Line(geometry, dashed);
line.computeLineDistances(); // requerido, o la línea se dibuja sólida
scene.add(line);
ShaderMaterial (solo WebGLRenderer)
GLSL personalizado con uniforms de three.js inyectados por ti. Siempre compila como GLSL ES 3.00: texture(), textureLod() y dFdx funcionan, y texture2D / gl_FragColor siguen siendo alias; deja glslVersion sin establecer. ShaderMaterial, RawShaderMaterial y onBeforeCompile no tienen ruta para WebGPURenderer; allí, usa materiales nodo y TSL.
const timer = new THREE.Timer();
timer.connect(document); // pausa cuando la pestaña está oculta
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(); // lo primero en cada fotograma, sin argumento
material.uniforms.time.value = timer.getElapsed();
renderer.render(scene, camera);
}
Declarados por ti en el shader: modelMatrix, modelViewMatrix, projectionMatrix, viewMatrix, normalMatrix, cameraPosition, y los atributos position, normal, uv (además de instanceMatrix en un InstancedMesh, que aplicas tú mismo: projectionMatrix * modelViewMatrix * instanceMatrix * vec4(pos, 1.0), y color con vertexColors). Los conjuntos uv extra y tangent no están declarados: añade in vec2 uv1; tú mismo.
RawShaderMaterial (solo WebGLRenderer)
No se antepone ninguna declaración (solo #version de glslVersion, las definiciones SHADER_TYPE y SHADER_NAME, y tus defines), pero el renderizador aún establece projectionMatrix, modelViewMatrix, viewMatrix, normalMatrix, modelMatrix y cameraPosition: decláralos en el shader y nunca los pases en uniforms: una entrada con el mismo nombre se sobrescribe en cada dibujo (modelMatrix, modelViewMatrix, normalMatrix) o reemplaza el valor de la cámara (projectionMatrix, viewMatrix, cameraPosition).
const material = new THREE.RawShaderMaterial({
glslVersion: THREE.GLSL3, // sin ello el shader es GLSL ES 1.00, que no tiene texture()
vertexShader: `
precision highp float;
in vec3 position;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
precision highp float;
uniform vec3 color;
out vec4 fragColor;
void main() {
fragColor = vec4(color, 1.0);
}
`,
uniforms: { color: { value: new THREE.Color(0x00ff00) } },
});
ShaderMaterial con onBeforeCompile (WebGLRenderer)
onBeforeCompile es una función retrollamada en ShaderMaterial o en un material integrado que recibe el shader antes de la compilación y te permite parchear sus cadenas. Ejemplo: MeshBasicMaterial con un reemplazo de #define .... Si manipulas shader.vertexShader o shader.fragmentShader, actualiza la variable shader.uniforms cuando añadas uniforms adicionales (el three integrado no puede inferirlos de las nuevas cadenas). No es compatible con WebGPURenderer; usa node materials y TSL.
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
material.onBeforeCompile = (shader) => {
shader.uniforms.time = { value: 0 }; // añade un uniform
shader.fragmentShader = shader.fragmentShader.replace(
`#include <color_fragment>`,
`
#include <color_fragment>
diffuseColor.rgb *= 0.5 + 0.5 * sin(vUv.x * 10.0 + uniforms.time.value);
`
);
};
Materiales comunes compartidos
Blending
const material = new THREE.MeshBasicMaterial({
transparent: false,
blending: THREE.NormalBlending, // NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending
blendEquation: THREE.AddEquation, // cuando blending = CustomBlending: AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation
blendSrc: THREE.SrcAlphaFactor, // ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
blendDst: THREE.OneMinusSrcAlphaFactor,
blendEquationAlpha: null, // por defecto igual que blendEquation; nulo = deshabilitado
blendSrcAlpha: null,
blendDstAlpha: null,
premultipliedAlpha: false,
alphaTest: 0, // descarta fragmentos por debajo de este alfa
alphaToCoverage: false, // MSAA: muestra parcialmente cubiertos los fragmentos borde alfa
});
Stencil
const material = new THREE.MeshBasicMaterial({
stencilWrite: true,
stencilRef: 0,
stencilFunc: THREE.AlwaysStencilFunc, // NeverStencilFunc, LessStencilFunc, EqualStencilFunc, LessEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc, GreaterEqualStencilFunc, AlwaysStencilFunc
stencilFail: THREE.KeepStencilOp, // ZeroStencilOp, KeepStencilOp, ReplaceStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp, InvertStencilOp
stencilZFail: THREE.KeepStencilOp,
stencilZPass: THREE.KeepStencilOp,
stencilWriteMask: 0xff,
});
Profundidad
const material = new THREE.MeshBasicMaterial({
depthWrite: true,
depthTest: true,
depthFunc: THREE.LessEqualDepth, // NeverDepth, LessDepth, EqualDepth, LessEqualDepth, GreaterDepth, NotEqualDepth, GreaterEqualDepth, AlwaysDepth
depthPacking: THREE.BasicDepthPacking, // BasicDepthPacking o RGBADepthPacking; solo en MeshDepthMaterial
});
Lado y sombras
const material = new THREE.MeshStandardMaterial({
side: THREE.FrontSide, // FrontSide (por defecto), BackSide, DoubleSide
shadowSide: null, // FrontSide, BackSide, DoubleSide o null (heredar de side)
colorWrite: true,
toneMapped: true,
polygonOffset: false,
polygonOffsetFactor: 0, // cuando polygonOffset es true: mueve la profundidad del fragmento por polygonOffsetFactor * DZ + r * polygonOffsetUnits (r depende de la implementación)
polygonOffsetUnits: 0,
forceSinglePass: false, // para DoubleSide en WebGLRenderer: renderizar ambas caras en un solo pase en lugar de dos; limita el blend, pero es más rápido
});
Respuesta al entorno
El envMap de un material anula la vista previa del entorno por material. scene.environment es la envMap por defecto para todos los materiales PBR de la escena - no es necesario asignar envMap a cada uno. Si un material establece envMap, se usa en lugar de scene.environment para ese material escénico. MeshPhysicalMaterial y MeshStandardMaterial usan imagen de entorno HDR; MeshBasicMaterial y MeshPhongMaterial usan LDR. Remapéalos con PMREMGenerator.
const rgbeLoader = new THREE.RGBELoader();
rgbeLoader.load("venice_sunset_1k.hdr", (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture; // Textura HDR ya PMREM por RGBELoader
scene.background = texture; // o texture con THREE.LinearToneMapping aplicado
scene.backgroundIntensity = 0.5;
scene.backgroundBlurriness = 0.2;
scene.backgroundRotation = new THREE.Euler(0, Math.PI, 0);
scene.environmentIntensity = 1;
scene.environmentRotation = new THREE.Euler(0, Math.PI, 0);
});
Para cargar un envMap LDR, se necesita PMREMGenerator:
const loader = new THREE.TextureLoader();
loader.load("venice_sunset_ldr.jpg", (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
texture.mapping = THREE.EquirectangularReflectionMapping;
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
texture.dispose(); // no se necesita la equirrectangular original
pmremGenerator.dispose();
scene.environment = envMap;
});
LOD de material con envMap y el bug de MeshPhysicalMaterial en r186
Los LOD de MeshPhysicalMaterial encapsulan MeshStandardMaterial, no MeshPhysicalMaterial - por lo tanto ignoran las propiedades físicas como clearcoat y transmission. Para evitarlo, envuelve el LOD en objetos manuales:
const lod = new THREE.LOD();
const group = new THREE.Group(); // no LOD; el group invisibiliza sus hijos
for (let i = 0; i < 3; i++) {
const mesh = new THREE.Mesh(geometry, physicalMaterial.clone());
group.add(mesh);
lod.addLevel(group, i * 10); // distancia en unidades del mundo
}
Materiales TSL (WebGPU + NodeMaterial)
WebGPU y la ruta NodeMaterial de WebGL soportan sombreado definido con TSL, el lenguaje de sombreado de nodos de three.js: crea Fn() que llaman a builtins de three/tsl y devuelven el sombreador final. Los materiales de nodo integrados (MeshBasicNodeMaterial, MeshStandardNodeMaterial, etc.) usan TSL internamente y exponen material.colorNode, material.opacityNode, material.positionNode para anulaciones sin parchear cadenas GLSL, pero sigue siendo TSL escribir o anular sus sombreadores.
import { Fn, float, uv, sin, add, mul, vec3, positionLocal, uniform, timerLocal, normalize, modelWorldMatrix, cameraPosition, positionWorld, dot, pow, vec4, clamp, normalWorld, lightPositionWorld, sub, length, max, diffuseColor, lightingModel, shadowModel, emissiveColor, output } from 'three/tsl';
import { NodeMaterial } from 'three'; // soportado tanto en WebGL como WebGPU
// Materiales de nodo (también funcionan en WebGL si el renderizador lo soporta: renderer.backend.isWebGLBackend === true)
const { MeshBasicNodeMaterial, MeshStandardNodeMaterial } = THREE;
// UV animadas en un material básico
const time = timerLocal();
const uvNode = uv();
const animatedUv = uvNode.add(time.mul(0.1)); // desplazar UV con el tiempo
const material = new MeshBasicNodeMaterial();
material.colorNode = animatedUv.xyy; // usar UV como color
material.opacityNode = float(0.8); // alpha constante
Sombreadores TSL personalizados
Define un Fn que devuelva el color final del fragmento. El timerLocal devuelve nanosegundos desde que comenzó el renderizador, los demás valores como uv, positionLocal y normalWorld son automáticos.
// Sombreador de fragmento personalizado
const myFragmentFn = Fn(() => {
const time = timerLocal();
const uvNode = uv();
const r = sin(uvNode.x.mul(10).add(time)).mul(0.5).add(0.5);
const g = sin(uvNode.y.mul(10).add(time.mul(1.5))).mul(0.5).add(0.5);
const b = sin(uvNode.x.add(uvNode.y).mul(10).add(time.mul(0.8))).mul(0.5).add(0.5);
return vec3(r, g, b);
});
const material = new MeshBasicNodeMaterial();
material.colorNode = myFragmentFn();
Línea TSL: Line2NodeMaterial
Para líneas anchas en WebGPU, usa los módulos webgpu en lugar de los predeterminados:
import { Line2 } from 'three/addons/lines/webgpu/Line2.js';
import { LineMaterial } from 'three/addons/lines/webgpu/Line2NodeMaterial.js'; // nombre de la exportación
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute([-1, 0, 0, 1, 0, 0], 3));
const material = new LineMaterial({ color: 0xff0000, linewidth: 10 }); // en píxeles
const line = new Line2(geometry, material);
line.computeLineDistances();
scene.add(line);
Trabajar con efectos de post-procesado (tsl)
Con WebGPURenderer (que habilita el soporte de post-procesado con rtt), puedes definir pases de post-procesado usando TSL para definir el sombreador de fragmento del pase:
import { PostProcessing } from 'three/addons/postprocessing/PostProcessing.js';
import { output, Fn, uv, texture, vec4 } from 'three/tsl';
const postProcessing = new PostProcessing(renderer);
const pass = postProcessing.addPass(({ camera, scene, renderer }) => {
// renderiza la escena en un color attachment
// y devuelve un Fn para el sombreado final del píxel
});
// O para un paso de post-procesado simple sin escena:
const customPass = postProcessing.addPass({
width: window.innerWidth,
height: window.innerHeight,
colorFormat: THREE.RGBAFormat,
// ...
});
customPass.setSize(window.innerWidth, window.innerHeight);
Errores comunes
Sin luces
MeshStandardMaterial, MeshPhysicalMaterial, MeshLambertMaterial, MeshPhongMaterial y MeshToonMaterial renderizan negros sin luces en la escena. Añade al menos una AmbientLight o HemisphereLight.
Textura sin SRGBColorSpace
Una textura de color cargada (color.jpg, matcap, specularMap) renderiza descolorida si su colorSpace sigue siendo LinearSRGBColorSpace (el valor por defecto de Texture). Los cargadores de texturas HDR (RGBELoader, EXRLoader) establecen su propio colorSpace; otras texturas de datos de color necesitan texture.colorSpace = THREE.SRGBColorSpace.
Sin normalMap
Usar normales planas sin normalMap (es decir, sin detalle de superficie normal-map) produce facetas planas visibles; especialmente notable en iluminación PBR. Para superficies suaves sin mapa, normalMap puede ser null.
Sin mapeo de tonos
Por defecto tres.js usa NoToneMapping, que renderiza colores lineales HDR sin comprimir. Ajusta renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; para un resultado agradable a la vista.
Sin antialiasing
Crea el renderizador con antialias: true. O usa SMAA de post-procesado para un antialiasing de mayor calidad.
Líneas finas
LineBasicMaterial y LineDashedMaterial dibujan siempre de 1 píxel de ancho. Para líneas más gruesas, usa Line2 de three/addons/lines/ (WebGL) o three/addons/lines/webgpu/Line2.js (WebGPU).
MeshPhysicalMaterial: propiedades ignoradas en LOD
Los LOD envuelven MaterialPhysicalMaterial para replicar MeshStandardMaterial; las propiedades físicas se pierden. Usa un group de mallas físicas como se muestra arriba.
falta un normalMap del activo
Si un activo 3D tiene un normalMap pero el material no lo tiene, renderiza facetas planas visibles. Aplica la textura al slot correcto: material.normalMap = map;.
Materiales de cámara: puntos y líneas con profundidad fija
Cuando necesites puntos/líneas del tamaño de píxeles de la pantalla, por ejemplo un retículo 3D que no escala con la distancia, establece sizeAttenuation: false en PointsMaterial, o una línea con Line2 manteniendo la posición de la cámara.
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 10, // píxeles, no unidades del mundo
sizeAttenuation: false,
});
Tabla de códigos de retorno del cargador
| Tipo de textura | colorSpace | Ejemplo |
|---|---|---|
| Color (map, emissiveMap, ...) | SRGBColorSpace | color.jpg |
| Escalar (roughnessMap, metalnessMap, normalMap, ...) | LinearSRGBColorSpace (por defecto) | orm.jpg (solo verde/azul), ao.jpg (rojo) |
| HDR (envMap) | LinearSRGBColorSpace (establecido por el cargador) | venice_sunset_1k.hdr |
| LDR envMap | SRGBColorSpace (configuración manual) | venice_sunset_ldr.jpg |