Saltar al contenido

AmbientLight y HemisphereLight: las dos luces que no son luces

Por qué AmbientLight es físicamente falsa y qué le falta exactamente, cómo HemisphereLight aproxima el cielo y el suelo con una sola operación, y para qué sirve cada una en una escena moderna.

Qué falsifica AmbientLight

La luz que llega a un punto de una superficie tiene dos orígenes: la que viene directamente de una fuente y la que ha rebotado al menos una vez en otra geometría. El segundo término, la iluminación indirecta, es el caro: exige resolver una integral sobre todo el hemisferio visible desde ese punto, con visibilidad incluida. Ningún renderizador forward en tiempo real lo hace.

AmbientLight sustituye esa integral entera por una constante:

JS
const ambiente = new THREE.AmbientLight( 0x404040, 1.0 );scene.add( ambiente );

Y en el shader se traduce, literalmente, a sumar ambientLightColor * diffuseColor al resultado, sin más. Sin producto escalar con la normal, sin atenuación, sin oclusión. Ahí están las tres mentiras, y merece la pena separarlas porque tienen consecuencias distintas.

No depende de la normal. Una cara que mira al cielo y otra que mira al suelo reciben lo mismo. Como el sombreado de una superficie curva es precisamente la variación del producto escalar a lo largo de la superficie, una esfera iluminada solo con ambiente se ve como un círculo plano de color uniforme. Es la prueba más rápida de que una escena está sobreiluminada con ambiente: si los objetos han perdido volumen, sobra ambiente.

No depende de la posición. El interior de una caja recibe tanto ambiente como su exterior. En la realidad, un rincón recibe mucha menos luz indirecta que una superficie abierta, porque su hemisferio visible está tapado. Esa diferencia es exactamente lo que captura un mapa de oclusión ambiental, y es también la razón por la que aoMap y AmbientLight van juntos: el aoMap de Three.js solo atenúa la contribución indirecta, es decir, el ambiente y el entorno. Un aoMap sin ambiente ni entorno no hace nada visible.

No tiene dirección. No hay reflejo especular posible. La AmbientLight no contribuye al término GGX de un MeshStandardMaterial; solo al difuso. Una escena metálica —metalness = 1, difuso nulo por definición— iluminada únicamente con ambiente sale negra, y es correcto que salga negra.

HemisphereLight: la primera mentira con gradiente

HemisphereLight corrige la mentira más dañina de las tres —la independencia de la normal— por un coste ridículo. Recibe dos colores, cielo y suelo, y mezcla entre ellos según hacia dónde mire la normal:

JS
// Cielo azulado arriba, rebote calido de tierra abajo.const hemi = new THREE.HemisphereLight( 0x87ceeb, 0xb97a57, 1.5 );scene.add( hemi );

El cálculo en el fragment shader es de dos líneas. Con N la normal en espacio de vista y L el eje del hemisferio también en espacio de vista, el factor de mezcla es dot(N, L) * 0.5 + 0.5, y el color resultante es mix(groundColor, skyColor, factor). Es decir: una normal que apunta exactamente hacia arriba recibe el color del cielo puro, una que apunta hacia abajo recibe el del suelo puro, y una horizontal recibe la media exacta de los dos.

Ese dot es todo lo que hace falta para recuperar el volumen. Una esfera iluminada solo con una HemisphereLight ya tiene forma: se ve más clara arriba y más oscura abajo, que es justo lo que ocurre al aire libre en un día nublado.

En términos de radiometría, una HemisphereLight es la aproximación de orden más bajo posible a un entorno: dos muestras direccionales interpoladas linealmente. Un entorno real, muestreado con armónicos esféricos de segundo orden, usa nueve coeficientes por canal y captura además la asimetría lateral y los gradientes cruzados. Pero esas dos muestras cuestan cero texturas, cero memoria de vídeo y tres operaciones de ALU, y en muchas escenas la diferencia perceptual frente a un entorno no justifica descargar cuatro megabytes de HDRI.

Cuándo bastan y cuándo no

El criterio no es estético, es de material. Estas dos luces solo alimentan el término difuso, así que su utilidad se derrumba en cuanto la escena tiene superficies reflectantes.

Escena Ambient + Hemisphere Entorno IBL
Estilizada, roughness alto, sin metales suficiente innecesario
Producto con metal o barniz inservible, sale negro o mate obligatorio
Terreno exterior grande, materiales mates suficiente y mucho más barato caro sin ganancia clara
Interior con ventanas insuficiente, no hay direccionalidad del rebote necesario, o lightmaps

Cuando la respuesta es «suficiente», la combinación que funciona casi siempre es esta:

JS
// Relleno direccional barato: da volumen y color de rebote.const hemi = new THREE.HemisphereLight( 0xb9d5ff, 0x6b4f3a, 1.2 );scene.add( hemi );// Fuente principal: da forma dura, especular y sombra.const sol = new THREE.DirectionalLight( 0xfff2e0, 3.0 );sol.position.set( 5, 10, 7 );sol.castShadow = true;scene.add( sol );

Dos luces, una sombra, ninguna textura de entorno. Es el punto de partida correcto para cualquier escena que tenga que funcionar en móvil, y a partir de ahí se añade solo lo que se justifique midiendo.