Saltar al contenido

La familia de node materials

Las diecisiete clases que trae r184, qué relación tienen con los materiales clásicos, y el mecanismo por el que un slot vacío no cuesta nada.

three/webgpu exporta diecisiete clases de material de nodos:

Node material Equivalente clásico
NodeMaterial la base, sin equivalente directo
MeshBasicNodeMaterial MeshBasicMaterial
MeshLambertNodeMaterial MeshLambertMaterial
MeshPhongNodeMaterial MeshPhongMaterial
MeshStandardNodeMaterial MeshStandardMaterial
MeshPhysicalNodeMaterial MeshPhysicalMaterial
MeshToonNodeMaterial MeshToonMaterial
MeshNormalNodeMaterial MeshNormalMaterial
MeshMatcapNodeMaterial MeshMatcapMaterial
MeshSSSNodeMaterial sin equivalente, dispersión subsuperficial
PointsNodeMaterial PointsMaterial
SpriteNodeMaterial SpriteMaterial
LineBasicNodeMaterial LineBasicMaterial
LineDashedNodeMaterial LineDashedMaterial
Line2NodeMaterial el material de líneas gruesas de los addons
ShadowNodeMaterial ShadowMaterial
VolumeNodeMaterial sin equivalente, renderizado volumétrico

La correspondencia con los nombres clásicos es literal: añade Node antes de Material. Y el constructor acepta el mismo objeto de parámetros:

JS
import * as THREE from 'three/webgpu';const material = new THREE.MeshPhysicalNodeMaterial( {  color: 0x0000ff,  metalness: 0.9,  roughness: 0.5,  clearcoat: 1.0,  clearcoatRoughness: 0.1,  normalMap: mapaNormal,  normalScale: new THREE.Vector2( 0.15, 0.15 )} );

Migrar un proyecto sin tocar shaders es, por tanto, una sustitución de nombres. Si el material clásico funcionaba, el de nodos funciona igual.

Dos ausencias que conviene notar. MeshDepthNodeMaterial y MeshDistanceNodeMaterial no existen: en el sistema de nodos, la generación de shadow maps y de profundidad se resuelve por otra vía, con los slots de sombra de la clase base. Y InstancedPointsNodeMaterial tampoco está en el catálogo de r184.

Los slots de la clase base

NodeMaterial declara veintitrés propiedades que terminan en Node, todas inicializadas a null. Las que se usan a diario son estas:

Slot Qué sustituye
colorNode el color difuso base
positionNode la posición del vértice
normalNode la normal de la superficie
opacityNode la opacidad
alphaTestNode el umbral del alpha test
envNode la contribución del entorno
aoNode la oclusión ambiental
depthNode la profundidad escrita
outputNode el color final, tras iluminación y salida
fragmentNode el fragment shader entero
vertexNode el vertex shader entero

Y las de menor uso: lightsNode, backdropNode, backdropAlphaNode, maskNode, maskShadowNode, geometryNode, mrtNode, contextNode, castShadowNode, receivedShadowNode, castShadowPositionNode y receivedShadowPositionNode.

Cuidado con los dos últimos, porque el nombre corto que uno espera —shadowPositionNode— no existe. Son dos slots distintos, uno para cuando el objeto proyecta sombra y otro para cuando la recibe.

Los slots de cada subclase

Cada material añade lo suyo. MeshStandardNodeMaterial declara tres:

Texto
emissiveNode, metalnessNode, roughnessNode

MeshPhysicalNodeMaterial hereda esos tres y añade diecisiete más, uno por cada capa del modelo físico:

Texto
clearcoatNode, clearcoatRoughnessNode, clearcoatNormalNode,sheenNode, sheenRoughnessNode,iridescenceNode, iridescenceIORNode, iridescenceThicknessNode,specularIntensityNode, specularColorNode, iorNode,transmissionNode, thicknessNode,attenuationDistanceNode, attenuationColorNode,dispersionNode, anisotropyNode

Y MeshBasicNodeMaterial no declara ninguno propio: se conforma con los de la base, que es coherente con que su modelo de iluminación sea el más simple.

Por qué un slot vacío no cuesta nada

El mecanismo es una línea repetida por todo NodeMaterial.js, siempre con la misma forma:

JS
let colorNode = this.colorNode ? vec4( this.colorNode ) : materialColor;const opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity;const metalnessNode = this.metalnessNode ? float( this.metalnessNode ) : materialMetalness;let roughnessNode = this.roughnessNode ? float( this.roughnessNode ) : materialRoughness;

Cada slot es un ternario evaluado en tiempo de construcción del grafo, en JavaScript. Si el slot es null, se usa el accesor por defecto, que lee la propiedad correspondiente del material. Si tiene un nodo, se usa ese.

No hay ninguna rama en el shader generado. No hay coste en tiempo de ejecución. El material sin slots asignados produce exactamente el mismo grafo que produciría si el sistema de slots no existiera. Y un material con un slot asignado produce un grafo donde ese subárbol está sustituido, y nada más.

Compara esto con lo que hacía falta en el sistema clásico para conseguir lo mismo: encontrar el chunk correcto, sustituir una cadena, declarar la clave de caché, y esperar que la próxima versión no renombre nada.