DFGLUT
DFGLUT samples from a built-in 16×16 DFG (Distribution–Fresnel–Geometry) lookup texture and returns a vec2 coefficient for PBR specular image-based lighting, using roughness and dotNV as UV coordinates for the classic split-sum environment BRDF.
Core Advantages
By using a precomputed DFG LUT texture, it replaces expensive analytical BRDF integration with a single texture lookup, keeping the visual result consistent with three.js' standard PBR shading while being fast, stable across platforms, and easy to combine or compare with DFGApprox.
Common Uses
Providing proper PBR energy compensation for prefiltered environment maps so that metals and dielectrics keep physically plausible specular intensity over different roughness values
Reusing the same environment specular BRDF as MeshStandardMaterial / glTF inside custom PBR NodeMaterials, ensuring that custom shading stays visually consistent with the built-in pipeline
Comparing the LUT-based result against DFGApprox when debugging or studying PBR behavior over different roughness and view angles
How to adjust
DFGLUT itself has no tunable properties; its behavior is fully determined by the two inputs `roughness` and `dotNV`. `roughness` (mapped to the LUT's U axis) typically comes from a material roughness map or constant: higher values produce broader, more diffused highlights with re-distributed energy. You can remap it (for example using `pow(roughness, n)`) to tweak the perceived roughness curve. `dotNV` (mapped to the LUT's V axis) is the dot product of the surface normal and view direction, usually clamped into [0, 1] to avoid sampling outside the valid range. Internally DFGLUT uses a 16×16 half-float RG DataTexture with `LinearFilter` and `ClampToEdgeWrapping`, so values outside [0, 1] are clamped to the edge while linear filtering provides smooth transitions between roughness and view angles. Compared to `DFGApprox`, DFGLUT is preferable when you want maximum visual consistency with three.js' standard PBR and stable results across platforms; DFGApprox is useful when you want to avoid any texture dependency and are comfortable with an analytical approximation instead.
Code Examples
1// 1. Prepare inputs: roughness and dotNV (dot of normal and view direction)
2const roughness = roughnessNode; // 0.0 - 1.0
3const dotNV = clamp( normalView.dot( positionViewDirection ), 0.0, 1.0 );
4
5// 2. Sample the DFG LUT to get the specular IBL coefficients (vec2)
6const dfg = DFGLUT.call( { roughness, dotNV } );
7
8// 3. Build specular IBL using the split-sum formula
9const prefilteredEnv = texture( envMap, reflectionVector );
10const specularIBL = prefilteredEnv.mul( f0.mul( dfg.x ).add( dfg.y ) );
11
12meshStandardNodeMaterial( {
13 envNode: specularIBL
14} );