Saltar al contenido

Standard y Physical: los dos materiales PBR

Qué hace MeshStandardMaterial que Phong no puede hacer, en qué se diferencia MeshPhysicalMaterial, y por qué los dos necesitan un envMap para verse bien.

Standard: cinco números y una imagen

Todo MeshStandardMaterial se reduce a un puñado de parámetros escalares que puedes sustituir por texturas:

propiedad rango qué controla
color color el albedo, el color base de la superficie
roughness 0 a 1 cuánto se dispersa la reflexión; 0 espejo, 1 mate
metalness 0 a 1 si la superficie es conductora; en la práctica 0 o 1
emissive color luz que emite la superficie, sin iluminarla
envMap textura el entorno que se refleja
envMapIntensity número cuánto pesa ese entorno

Los valores por defecto son roughness: 1 y metalness: 0, es decir, un dieléctrico completamente mate. Ese material sin luces se ve negro, y sin envMap se ve plano. Ninguna de las dos cosas es un fallo del material.

MeshStandardMaterial sombrea por fragmento, calcula el término difuso y el especular con una BRDF de microfacetas, y usa el mapa de entorno tanto para la reflexión especular como para la luz ambiental difusa. Ese último punto es el que decide si tu escena parece de 2005 o de ahora: la iluminación indirecta de un PBR viene entera del entorno, y sin entorno las zonas en sombra son negro puro.

JS
import * as THREE from 'three';import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));renderer.setSize(window.innerWidth, window.innerHeight);renderer.toneMapping = THREE.ACESFilmicToneMapping;document.body.appendChild(renderer.domElement);const scene = new THREE.Scene();scene.background = new THREE.Color(0x11111b);// Entorno procedural: una habitacion con focos. Sin fichero externo.const pmrem = new THREE.PMREMGenerator(renderer);const habitacion = new RoomEnvironment();scene.environment = pmrem.fromScene(habitacion, 0.04).texture;habitacion.dispose();const camera = new THREE.PerspectiveCamera(  45, window.innerWidth / window.innerHeight, 0.1, 100);camera.position.set(0, 0, 6);const geometria = new THREE.SphereGeometry(0.7, 64, 32);// Una rejilla: metalness en filas, roughness en columnas.for (let fila = 0; fila < 2; fila++) {  for (let col = 0; col < 5; col++) {    const material = new THREE.MeshStandardMaterial({      color: 0xd4af37,      metalness: fila,      roughness: col / 4    });    const esfera = new THREE.Mesh(geometria, material);    esfera.position.set((col - 2) * 1.6, fila === 0 ? 0.9 : -0.9, 0);    scene.add(esfera);  }}renderer.setAnimationLoop(() => renderer.render(scene, camera));

Esa rejilla es la referencia visual que conviene tener a mano. La fila de metales cambia de color a lo largo de la rugosidad porque el color del metal está en su reflexión; la fila de dieléctricos mantiene el color base y solo cambia el brillo. Si entiendes esa diferencia, entiendes el modelo entero.

Physical: el mismo material con capas

MeshPhysicalMaterial extiende MeshStandardMaterial. No es un material alternativo: es el mismo, con propiedades adicionales que por defecto están apagadas. Un MeshPhysicalMaterial recién creado, sin tocar nada, produce exactamente el mismo píxel que un MeshStandardMaterial equivalente, y casi el mismo coste, porque las capas se compilan condicionalmente.

Lo que añade, en una frase cada uno:

  • clearcoat: una segunda capa especular fina encima, como una laca.
  • transmission: transparencia refractiva de verdad, con thickness e ior.
  • sheen: un lóbulo de retrodispersión en los bordes, para tejidos.
  • iridescence: interferencia de película fina, el arcoíris de una pompa.
  • anisotropy: reflexión estirada en una dirección, metal cepillado.
  • specularIntensity y specularColor: control fino del especular de los dieléctricos.
  • dispersion: separación cromática al refractar, con transmission.

Cada una tiene su propia lección en el nivel siguiente. Aquí lo que importa es el criterio de cuándo saltar de Standard a Physical, y es sorprendentemente simple: cuando el material que quieres tiene dos superficies. Un coche pintado es pintura más barniz. Una pompa es jabón más aire. Un jersey es fibra más pelusa. Un vaso es cristal más interior. Si tu material tiene una sola superficie, Standard le basta.

El coste, con orden

De más barato a más caro por píxel, para un mismo número de luces:

  1. MeshBasicMaterial: no ilumina, es una multiplicación.
  2. MeshLambertMaterial: un producto escalar por luz.
  3. MeshToonMaterial: lo mismo más una búsqueda en el gradiente.
  4. MeshPhongMaterial: añade un lóbulo especular por luz.
  5. MeshStandardMaterial: BRDF de microfacetas, Fresnel, y muestreo del entorno prefiltrado.
  6. MeshPhysicalMaterial: lo anterior más una BRDF por cada capa activa.

Los saltos no son uniformes. De Lambert a Phong hay poco. De Phong a Standard hay bastante, sobre todo por el muestreo del entorno. De Standard a Physical depende por completo de qué actives: clearcoat añade un lóbulo especular más y una normal más; transmission es el caso grave, porque obliga al renderer a hacer una copia de la escena a un render target para poder muestrear lo que hay detrás del objeto, lo que significa dibujar la escena dos veces.

Si estás optimizando y ves que el fragment shader es el cuello, la primera pregunta es cuántos materiales Physical hay en pantalla y cuántos de ellos tienen transmission activo. La segunda es si esas mismas superficies se distinguirían de un Standard bien parametrizado en un vídeo a treinta fotogramas por segundo. La respuesta honesta suele ser que no.