Custom Lighting Models in Standard glTF Materials
Standard glTF 2.0 assets strictly adhere to a standardized Physically Based Rendering (PBR) metallic-roughness workflow, offering consistent rendering across platforms at the cost of shading flexibility. Injecting custom lighting models—such as stylized toon shading, custom anisotropy, or subsurface scattering—into these assets without breaking compatibility requires an architectural approach known as a hook-based material plugin architecture (or modular shader chunk injection). This article breaks down how this architectural pattern works, why it decouples surface parameters from light evaluation, and how it is implemented in modern real-time rendering engines.
The Material Plugin and Hook Architecture
The primary architectural pattern used to inject custom lighting into standard glTF materials is the Hook-Based Shader Plugin Architecture. Instead of writing an entirely custom shader from scratch—which requires manually re-implementing UV transformations, morph targets, skinning, normal mapping, and environment reflections—this approach uses the engine’s existing PBR shader template as a base.
The host rendering engine defines structured interception points (hooks or chunks) throughout the shader compilation pipeline:
- Vertex and Attribute Stage: Standard glTF geometry, vertex colors, joint indices, and morph target weights are evaluated using the standard runtime pipeline.
- Surface Property Unpacking: Standard glTF textures (base color, metallic-roughness, normal, emissive, occlusion) are sampled and unpacked into a generic surface state struct.
- Lighting Evaluation Hook: The standard metallic-roughness Bidirectional Reflectance Distribution Function (BRDF) evaluation is intercepted and replaced with a custom function.
- Final Compositing: Engine-level post-processing, tone mapping, and fog application remain intact.
Decoupling Surface Attributes from BRDF Evaluation
A key requirement for this architecture is the clean separation of material surface data from the lighting equations:
- Data Ingestion Layer: The standard glTF loader parses standard materials and maps textures directly to standard GPU registers.
- Surface Intermediate Representation (IR): The engine populates a unified surface descriptor containing normal vectors, view vectors, roughness, metallic values, and base color.
- Pluggable BRDF Layer: The custom lighting model is injected as an alternate mathematical formulation that consumes the Surface IR.
By keeping the data ingestion layer intact, the original glTF geometry, UV coordinates, and texture channels remain fully operational, while the direct light accumulation and indirect ambient evaluation are re-routed through custom mathematical models.
Engine Implementations
Modern graphics engines implement this pattern through specific modular systems:
- Three.js (
onBeforeCompileand Node Material System): Developers intercept the GLSL compilation step usingonBeforeCompileto search for specific shader chunks (such as<lights_fragment_begin>or<lights_fragment_end>) and inject custom lighting evaluation logic via string manipulation or Abstract Syntax Tree (AST) injection. In the WebGPU Node Material system, lighting models can be swapped natively by overriding thelightingModelproperty of standard PBR nodes. - Babylon.js (
MaterialPluginBase): Babylon.js exposes a formalMaterialPluginBaseclass. This class allows developers to register custom uniforms, add new shader defines, and hook into specific shader compilation phases (prepareDefines,getCustomCode) to selectively overwrite lighting calculations in standard glTF PBR materials. - Filament and Custom Renderers: Engines using
offline-compiled shader variants often define custom lighting models
through configuration flags in the material definition (e.g., custom
surfaceShadingfunctions), letting the compiler swap standard Cook-Torrance specular for custom models while preserving material inputs.
Leveraging glTF Extensions for Custom Lighting Data
To drive custom lighting models using asset-specific parameters
without corrupting core glTF schemas, the architecture pairs with custom
glTF vendor extensions (e.g., EXT_ or custom vendor
prefixes) or extras fields:
- Asset Definition: Extra parameters (such as
rim-light thresholds, subsurface color ramps, or ramp textures) are
serialized inside the glTF JSON tree under
materials[i].extrasor a formal extension block. - Loader Hooking: The glTF loader's material extension hook intercepts material instantiation, reading the extra metadata.
- Uniform Binding: The material plugin automatically binds these custom properties to the newly injected shader uniforms.
This end-to-end flow preserves standard DCC-to-engine workflows (Blender, Maya) while executing specialized real-time shading.