map, alphaMap y emissiveMap: el color y lo que se ve
Los tres mapas que afectan directamente al color de salida, qué canal usa cada uno, y por qué el mapa emisivo no ilumina nada.
map: el color base
map es el albedo en un dieléctrico y el F0 en un metal, como ya sabes. Se multiplica por la propiedad color del material, que vale blanco por defecto para que la multiplicación sea neutra.
Esa multiplicación es útil de verdad: con una sola textura en escala de grises y distintos valores de color tienes variantes de un mismo objeto sin duplicar memoria. Es como se hacen los coches de distintos colores en un configurador, o los uniformes de equipo en un videojuego.
Lo que hay que tener claro es que la multiplicación ocurre después de que ambos se hayan llevado al espacio lineal. La textura se decodifica de sRGB si la has marcado como tal, y el color se decodifica desde el hexadecimal que le pasaste. Multiplicar en lineal es lo correcto físicamente y es lo que hace que un color de valor medio realmente reduzca a la mitad la luz reflejada.
import * as THREE from 'three';const albedo = new THREE.TextureLoader().load('/texturas/tela_color.jpg');albedo.colorSpace = THREE.SRGBColorSpace; // es color: obligatorioconst material = new THREE.MeshStandardMaterial({ map: albedo, color: 0x8899ff, // tinta la textura roughness: 0.8, metalness: 0});map puede llevar canal alfa, y si lo lleva se usa junto con transparent o alphaTest. Es la vía más simple para un recorte, y evita cargar una segunda textura.
alphaMap y el canal verde
Cuando el alfa va en una textura aparte, se usa alphaMap, y aquí está la sorpresa que documentan todos los materiales de Three.js con el mismo párrafo: se lee el canal verde, no el alfa y no la luminancia.
El motivo es de precisión. En los formatos comprimidos de la familia DXT y en el RGB565 sin comprimir, el canal verde tiene un bit más que el rojo y el azul, porque el ojo humano es más sensible al verde y los formatos aprovechan ese sesgo. Para una máscara de un solo valor, el verde es el canal con mejor calidad disponible.
La consecuencia práctica: si guardas tu máscara en escala de grises, funciona, porque los tres canales valen lo mismo. Si la guardas solo en el rojo para ahorrar, verás negro.
alphaMap no decide por sí solo el mecanismo de transparencia. Necesita acompañarse:
// Recorte duro, sin problemas de orden. Hojas, vallas, texto.const hoja = new THREE.MeshStandardMaterial({ map: colorHoja, alphaMap: mascaraHoja, alphaTest: 0.5, side: THREE.DoubleSide, forceSinglePass: true});// Degradado real de opacidad. Cristal esmerilado, humo, desvanecidos.const velo = new THREE.MeshStandardMaterial({ map: colorVelo, alphaMap: mascaraVelo, transparent: true, depthWrite: false});Y hay un detalle de espacio de color que se cuela: una alphaMap no es una textura de color. Guarda una máscara, un dato. No hay que marcarla como sRGB. Marcarla mal no da ningún error y cambia sutilmente el umbral efectivo de tu alphaTest.
emissiveMap y la trampa del negro
emissiveMap marca dónde la superficie emite luz propia. El resultado se multiplica por dos cosas: la propiedad emissive del material y el escalar emissiveIntensity. Así lo compone el renderer:
uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );Y aquí está la trampa: emissive vale negro por defecto. Negro multiplicado por cualquier textura es negro. Asignar emissiveMap sin tocar nada más no produce absolutamente ningún efecto visible, que es exactamente el mismo patrón que ya viste con sheenColor.
La configuración correcta son tres líneas:
const pantalla = new THREE.MeshStandardMaterial({ map: carcasa, emissiveMap: pixeles, emissive: 0xffffff, // sin esto no se ve nada emissiveIntensity: 2.0, // por encima de 1 para que el bloom lo capte roughness: 0.4});emissiveMap sí es una textura de color y hay que marcarla como SRGBColorSpace.
Subir emissiveIntensity por encima de uno es lo habitual cuando hay post-proceso: un valor de dos o tres deja la zona emisiva fuera del rango estándar, que es la señal que busca un pase de bloom para decidir qué resplandece. Sin post-proceso, cualquier valor por encima de uno se recorta a blanco tras el mapeo de tonos y no aporta nada.
Un apunte sobre los canales
Resumiendo las convenciones de esta lección, que no son intuitivas y conviene tener a mano:
| mapa | canal que se lee | espacio de color |
|---|---|---|
map |
RGB, y alfa si lo hay | SRGBColorSpace |
alphaMap |
verde | ninguno, es dato |
emissiveMap |
RGB | SRGBColorSpace |
La columna de la derecha es la que produce errores silenciosos, y tiene nivel propio. La de en medio es la que produce texturas que salen negras sin explicación.