Los nodos de utilidad integrados
El catálogo de ruido, patrones, mapeos de UV, ajustes de color y modos de mezcla que Three.js ya trae, con sus firmas reales.
Ruido
Three.js trae la biblioteca de ruido de MaterialX, que es de calidad de producción y está probada. Los nombres siguen la convención del estándar:
import { mx_noise_float, mx_noise_vec3, mx_noise_vec4, mx_fractal_noise_float, mx_fractal_noise_vec2, mx_fractal_noise_vec3, mx_fractal_noise_vec4, mx_worley_noise_float, mx_worley_noise_vec2, mx_worley_noise_vec3, mx_cell_noise_float} from 'three/tsl';Las firmas que importan:
mx_noise_float( texcoord = uv(), amplitude = 1, pivot = 0 )mx_fractal_noise_vec3( position = uv(), octaves = 3, lacunarity = 2, diminish = 0.5, amplitude = 1 )El ruido fractal es el que se usa para casi todo lo orgánico: el número de octavas controla el detalle, la lacunaridad cuánto sube la frecuencia entre octavas, y el factor de disminución cuánto baja la amplitud.
import { mx_fractal_noise_float, positionLocal, mix, color } from 'three/tsl';const veta = mx_fractal_noise_float( positionLocal.mul( 3 ), 5, 2, 0.55 ) .mul( 0.5 ).add( 0.5 ) .toVar( 'veta' );material.colorNode = mix( color( 0xe8e2d6 ), color( 0x6b5f4b ), veta );El ruido de Worley, también llamado celular, da patrones de celdas y sirve para escamas, piedra, burbujas y craquelado. mx_cell_noise_float da un valor constante por celda, útil para variación aleatoria discreta.
Un aviso: mx_noise_vec2 no existe. Está comentado en el código fuente. Las variantes disponibles de mx_noise son float, vec3 y vec4; las de mx_fractal_noise sí incluyen vec2.
Y hay tres nodos de ruido más ligeros para casos simples:
import { rand, hash, triNoise3D, checker } from 'three/tsl';const aleatorio = rand( uv() ); // pseudoaleatorio determinista desde un vec2const tablero = checker( uv().mul( 8 ) ); // patron de damaschecker( coord = uv() ) es un damero clásico y es sorprendentemente útil como textura de depuración: aplicado a un modelo, muestra de un vistazo si sus UV están bien distribuidas.
Mapeos de coordenadas
Triplanar. Aplicar una textura a una geometría sin UV, proyectándola desde los tres ejes y mezclando según la normal. Es la solución estándar para terrenos, rocas y geometría procedural.
import { triplanarTexture, texture, float } from 'three/tsl';material.colorNode = triplanarTexture( texture( miTextura ), null, null, float( 0.5 ) );La firma completa, del código fuente:
triplanarTextures( textureXNode, textureYNode = null, textureZNode = null, scaleNode = float( 1 ), positionNode = positionLocal, normalNode = normalLocal )triplanarTexture es un alias que delega en triplanarTextures. Si solo das la primera textura, se usa para los tres ejes; si das las tres, cada eje usa la suya, que es lo que permite tener por ejemplo hierba arriba y roca en los laterales.
Matcap. Las coordenadas para un material de captura de material se obtienen de un nodo ya evaluado:
import { matcapUV, texture } from 'three/tsl';material.colorNode = texture( miMatcap, matcapUV );Ojo: matcapUV es un valor, no una función. No lleva paréntesis.
Equirectangular. Para muestrear un panorama esférico a partir de una dirección:
import { equirectUV, texture, positionWorldDirection } from 'three/tsl';material.colorNode = texture( miPanorama, equirectUV( positionWorldDirection ) );Su parámetro tiene por defecto positionWorldDirection, así que equirectUV() sin argumentos ya hace lo habitual.
Rotación. rotate( position, rotation ) gira un vector, con dos argumentos.
Y hay más en el mismo territorio: spritesheetUV para animación por hojas de sprites, pointUV para las coordenadas dentro de un punto, rotateUV para girar coordenadas de textura.
Color: ajustes y mezcla
import { saturation, vibrance, hue, luminance, grayscale, posterize } from 'three/tsl';Las tres primeras comparten firma: ( color, adjustment = float( 1 ) ), donde uno significa sin cambio.
import { output, saturation, vec4 } from 'three/tsl';// Baja la saturacion a la mitad, conservando el resto del calculo.material.outputNode = vec4( saturation( output.rgb, 0.5 ), output.a );luminance es distinta: es una función normal de JavaScript con argumentos posicionales, luminance( color, coeficientes ), y sus coeficientes por defecto salen del sistema de gestión de color de Three.js, así que respetan el espacio de trabajo activo. Usarla es preferible a escribir a mano el producto escalar con los coeficientes de siempre.
Modos de mezcla
Los cinco modos clásicos de composición, todos con firma ( base, blend ):
import { blendBurn, blendDodge, blendScreen, blendOverlay, blendColor } from 'three/tsl';material.colorNode = blendOverlay( texturaBase, texturaDetalle );Los cuatro primeros trabajan con vec3; blendColor trabaja con vec4 y respeta el alfa, que lo hace el adecuado para superponer una capa con transparencia.
Pantalla, tiempo y osciladores
| Nodo | Qué da |
|---|---|
screenUV |
coordenada normalizada de pantalla, de 0 a 1 |
screenCoordinate |
coordenada en píxeles |
screenSize |
tamaño del framebuffer en píxeles |
screenDPR |
la densidad de píxeles |
viewportUV, viewportSize, viewportCoordinate |
equivalentes referidos al viewport |
time |
segundos transcurridos |
deltaTime |
segundos del último frame |
frameId |
número de frame, tipo entero sin signo |
Y los cuatro osciladores, todos con la misma firma ( t = time ), que devuelven valores en el rango de cero a uno con periodo uno:
import { oscSine, oscSquare, oscTriangle, oscSawtooth } from 'three/tsl';material.emissiveNode = color( 0x33ffcc ).mul( oscSine() );material.emissiveNode = color( 0xff2244 ).mul( oscSquare( time.mul( 4 ) ) );Remapeo y rangos
import { remap, remapClamp, range } from 'three/tsl';// De un rango a otro.const v = remap( ruido, -1, 1, 0.2, 0.8 );// Igual, pero recortando fuera del rango de entrada.const w = remapClamp( altura, 0, 10, 0, 1 );range( min, max ) es distinto y muy útil con instancias: genera un valor aleatorio pero estable por instancia dentro del rango dado, lo que permite variar color, tamaño o velocidad entre miles de copias sin ningún atributo adicional.
import { range, color, mix } from 'three/tsl';// Cada instancia recibe su propio tono.material.colorNode = mix( color( 0x2244ff ), color( 0xffaa22 ), range( 0, 1 ) );