diff --git a/examples/screenshots/webgl_pmrem_test.jpg b/examples/screenshots/webgl_pmrem_test.jpg
index d99ddcc72f6897..0b8428e6004e71 100644
Binary files a/examples/screenshots/webgl_pmrem_test.jpg and b/examples/screenshots/webgl_pmrem_test.jpg differ
diff --git a/examples/webgpu_pmrem_test.html b/examples/webgpu_pmrem_test.html
index 670392723126b5..9ba44ee9930a6a 100644
--- a/examples/webgpu_pmrem_test.html
+++ b/examples/webgpu_pmrem_test.html
@@ -4,26 +4,31 @@
-
-
three.js webgpu -
- PMREM test by
Emmett Lalish
-
-
1: white metal. 2: white dielectric. 3: black dielectric.
+
+
+
+
+
three.jsPMREM Directional Light Test
+
+
+
+ 1: white metal. 2: white dielectric. 3: black dielectric.
PMREM on: HDR with a single bright pixel. PMREM off: DirectionalLight.
The difference between these renders indicates the error in the PMREM approximations.
-
+
PMREM test by
Emmett Lalish
+
diff --git a/src/nodes/functions/BSDF/DFGApprox.js b/src/nodes/functions/BSDF/DFGApprox.js
index 1170ed0aa6227b..9c25a36b0d154d 100644
--- a/src/nodes/functions/BSDF/DFGApprox.js
+++ b/src/nodes/functions/BSDF/DFGApprox.js
@@ -1,30 +1,28 @@
-import { Fn, vec2, vec4 } from '../../tsl/TSLBase.js';
+import { Fn, vec2 } from '../../tsl/TSLBase.js';
+import { texture } from '../../accessors/TextureNode.js';
+import { getDFGLUT } from '../../../renderers/shaders/DFGLUTData.js';
-// Analytical approximation of the DFG LUT, one half of the
-// split-sum approximation used in indirect specular lighting.
-// via 'environmentBRDF' from "Physically Based Shading on Mobile"
-// https://www.unrealengine.com/blog/physically-based-shading-on-mobile
-const DFGApprox = /*@__PURE__*/ Fn( ( { roughness, dotNV } ) => {
+// Cached DFG LUT texture
+
+let dfgLUT = null;
- const c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );
+// DFG LUT sampling for physically-based rendering.
+// Uses a precomputed lookup table for the split-sum approximation
+// used in indirect specular lighting.
+// Reference: "Real Shading in Unreal Engine 4" by Brian Karis
+
+const DFGApprox = /*@__PURE__*/ Fn( ( { roughness, dotNV } ) => {
- const c1 = vec4( 1, 0.0425, 1.04, - 0.04 );
+ if ( dfgLUT === null ) {
- const r = roughness.mul( c0 ).add( c1 );
+ dfgLUT = getDFGLUT();
- const a004 = r.x.mul( r.x ).min( dotNV.mul( - 9.28 ).exp2() ).mul( r.x ).add( r.y );
+ }
- const fab = vec2( - 1.04, 1.04 ).mul( a004 ).add( r.zw );
+ const uv = vec2( roughness, dotNV );
- return fab;
+ return texture( dfgLUT, uv ).rg;
-} ).setLayout( {
- name: 'DFGApprox',
- type: 'vec2',
- inputs: [
- { name: 'roughness', type: 'float' },
- { name: 'dotNV', type: 'vec3' }
- ]
} );
export default DFGApprox;