Skip to content

Commit 34f3fa6

Browse files
sunagmrdoob
andauthored
WebGPURenderer: DFG LUT instead of Analytical approximation. (#32061)
* Update DFGApprox.js * add `webgpu_pmrem_test` * Create webgpu_pmrem_test.jpg --------- Co-authored-by: Mr.doob <[email protected]>
1 parent b61441d commit 34f3fa6

File tree

3 files changed

+40
-45
lines changed

3 files changed

+40
-45
lines changed
-42 Bytes
Loading

examples/webgpu_pmrem_test.html

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,31 @@
44
<title>three.js webgpu - PMREM directional light test</title>
55
<meta charset="utf-8">
66
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7-
<link type="text/css" rel="stylesheet" href="main.css">
7+
<link type="text/css" rel="stylesheet" href="example.css">
88
</head>
99
<body>
1010

11-
<div id="container">
12-
<div id="info">
13-
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu -
14-
PMREM test by <a href="https://github.com/elalish" target="_blank" rel="noopener">Emmett Lalish</a>
15-
<br>
16-
<br>1: white metal. 2: white dielectric. 3: black dielectric.
11+
<div id="info">
12+
<a href="https://threejs.org/" target="_blank" rel="noopener" class="logo-link"></a>
13+
14+
<div class="title-wrapper">
15+
<a href="https://threejs.org/" target="_blank" rel="noopener">three.js</a><span>PMREM Directional Light Test</span>
16+
</div>
17+
18+
<small>
19+
1: white metal. 2: white dielectric. 3: black dielectric.
1720
<br>PMREM on: HDR with a single bright pixel. PMREM off: DirectionalLight.
1821
<br>The difference between these renders indicates the error in the PMREM approximations.
19-
</div>
22+
<br>PMREM test by <a href="https://github.com/elalish" target="_blank" rel="noopener">Emmett Lalish</a>
23+
</small>
2024
</div>
2125

2226
<script type="importmap">
2327
{
2428
"imports": {
2529
"three": "../build/three.webgpu.js",
26-
"three/tsl": "../build/three.webgpu.js",
30+
"three/webgpu": "../build/three.webgpu.js",
31+
"three/tsl": "../build/three.tsl.js",
2732
"three/addons/": "./jsm/"
2833
}
2934
}
@@ -36,9 +41,7 @@
3641
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3742
import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
3843

39-
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
40-
41-
import WebGPU from 'three/addons/capabilities/WebGPU.js';
44+
import { Inspector } from 'three/addons/inspector/Inspector.js';
4245

4346
let scene, camera, controls, renderer;
4447

@@ -53,17 +56,19 @@
5356
renderer = new THREE.WebGPURenderer( { antialias: true } );
5457
renderer.setPixelRatio( window.devicePixelRatio );
5558
renderer.setSize( width, height );
59+
renderer.setAnimationLoop( render );
60+
renderer.inspector = new Inspector();
5661

5762
// tonemapping
5863
renderer.toneMapping = THREE.ACESFilmicToneMapping;
5964
renderer.toneMappingExposure = 1;
6065

61-
await renderer.init();
62-
6366
document.body.appendChild( renderer.domElement );
6467

6568
window.addEventListener( 'resize', onWindowResize );
6669

70+
await renderer.init();
71+
6772
// scene
6873

6974
scene = new THREE.Scene();
@@ -77,7 +82,6 @@
7782
// controls
7883

7984
controls = new OrbitControls( camera, renderer.domElement );
80-
controls.addEventListener( 'change', render ); // use if there is no animation loop
8185
controls.minDistance = 4;
8286
controls.maxDistance = 20;
8387

@@ -99,7 +103,7 @@
99103
// angle of the pixel in steradians. This image is 1024 x 512,
100104
// so the value is 1 / ( sin( phi ) * ( pi / 512 ) ^ 2 ) = 27,490 nits.
101105

102-
const gui = new GUI();
106+
const gui = renderer.inspector.createParameters( 'Settings' );
103107
gui.add( { enabled: true }, 'enabled' )
104108
.name( 'PMREM' )
105109
.onChange( value => {
@@ -116,13 +120,11 @@
116120

117121
} );
118122

119-
render();
120-
121123
} );
122124

123125
}
124126

125-
async function createObjects() {
127+
function createObjects() {
126128

127129
let radianceMap = null;
128130
new HDRLoader()
@@ -141,7 +143,7 @@
141143

142144
for ( let y = 0; y <= 2; y ++ ) {
143145

144-
const material = new THREE.MeshPhysicalNodeMaterial( {
146+
const material = new THREE.MeshPhysicalMaterial( {
145147
roughness: x / 10,
146148
metalness: y < 1 ? 1 : 0,
147149
color: y < 2 ? 0xffffff : 0x000000,
@@ -158,8 +160,6 @@
158160

159161
}
160162

161-
render();
162-
163163
} );
164164

165165
const pmremGenerator = new THREE.PMREMGenerator( renderer );
@@ -177,8 +177,6 @@
177177

178178
renderer.setSize( width, height );
179179

180-
render();
181-
182180
}
183181

184182
function updateCamera() {
@@ -198,8 +196,7 @@
198196

199197
Promise.resolve()
200198
.then( init )
201-
.then( createObjects )
202-
.then( render );
199+
.then( createObjects );
203200

204201
</script>
205202
</body>
Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
1-
import { Fn, vec2, vec4 } from '../../tsl/TSLBase.js';
1+
import { Fn, vec2 } from '../../tsl/TSLBase.js';
2+
import { texture } from '../../accessors/TextureNode.js';
3+
import { getDFGLUT } from '../../../renderers/shaders/DFGLUTData.js';
24

3-
// Analytical approximation of the DFG LUT, one half of the
4-
// split-sum approximation used in indirect specular lighting.
5-
// via 'environmentBRDF' from "Physically Based Shading on Mobile"
6-
// https://www.unrealengine.com/blog/physically-based-shading-on-mobile
7-
const DFGApprox = /*@__PURE__*/ Fn( ( { roughness, dotNV } ) => {
5+
// Cached DFG LUT texture
6+
7+
let dfgLUT = null;
88

9-
const c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );
9+
// DFG LUT sampling for physically-based rendering.
10+
// Uses a precomputed lookup table for the split-sum approximation
11+
// used in indirect specular lighting.
12+
// Reference: "Real Shading in Unreal Engine 4" by Brian Karis
13+
14+
const DFGApprox = /*@__PURE__*/ Fn( ( { roughness, dotNV } ) => {
1015

11-
const c1 = vec4( 1, 0.0425, 1.04, - 0.04 );
16+
if ( dfgLUT === null ) {
1217

13-
const r = roughness.mul( c0 ).add( c1 );
18+
dfgLUT = getDFGLUT();
1419

15-
const a004 = r.x.mul( r.x ).min( dotNV.mul( - 9.28 ).exp2() ).mul( r.x ).add( r.y );
20+
}
1621

17-
const fab = vec2( - 1.04, 1.04 ).mul( a004 ).add( r.zw );
22+
const uv = vec2( roughness, dotNV );
1823

19-
return fab;
24+
return texture( dfgLUT, uv ).rg;
2025

21-
} ).setLayout( {
22-
name: 'DFGApprox',
23-
type: 'vec2',
24-
inputs: [
25-
{ name: 'roughness', type: 'float' },
26-
{ name: 'dotNV', type: 'vec3' }
27-
]
2826
} );
2927

3028
export default DFGApprox;

0 commit comments

Comments
 (0)