Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified examples/screenshots/webgl_pmrem_test.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 23 additions & 26 deletions examples/webgpu_pmrem_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,31 @@
<title>three.js webgpu - PMREM directional light test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<link type="text/css" rel="stylesheet" href="example.css">
</head>
<body>

<div id="container">
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu -
PMREM test by <a href="https://github.com/elalish" target="_blank" rel="noopener">Emmett Lalish</a>
<br>
<br>1: white metal. 2: white dielectric. 3: black dielectric.
<div id="info">
<a href="https://threejs.org/" target="_blank" rel="noopener" class="logo-link"></a>

<div class="title-wrapper">
<a href="https://threejs.org/" target="_blank" rel="noopener">three.js</a><span>PMREM Directional Light Test</span>
</div>

<small>
1: white metal. 2: white dielectric. 3: black dielectric.
<br>PMREM on: HDR with a single bright pixel. PMREM off: DirectionalLight.
<br>The difference between these renders indicates the error in the PMREM approximations.
</div>
<br>PMREM test by <a href="https://github.com/elalish" target="_blank" rel="noopener">Emmett Lalish</a>
</small>
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.webgpu.js",
"three/tsl": "../build/three.webgpu.js",
"three/webgpu": "../build/three.webgpu.js",
"three/tsl": "../build/three.tsl.js",
"three/addons/": "./jsm/"
}
}
Expand All @@ -36,9 +41,7 @@
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

import WebGPU from 'three/addons/capabilities/WebGPU.js';
import { Inspector } from 'three/addons/inspector/Inspector.js';

let scene, camera, controls, renderer;

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

// tonemapping
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;

await renderer.init();

document.body.appendChild( renderer.domElement );

window.addEventListener( 'resize', onWindowResize );

await renderer.init();

// scene

scene = new THREE.Scene();
Expand All @@ -77,7 +82,6 @@
// controls

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

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

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

} );

render();

} );

}

async function createObjects() {
function createObjects() {

let radianceMap = null;
new HDRLoader()
Expand All @@ -141,7 +143,7 @@

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

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

}

render();

} );

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

renderer.setSize( width, height );

render();

}

function updateCamera() {
Expand All @@ -198,8 +196,7 @@

Promise.resolve()
.then( init )
.then( createObjects )
.then( render );
.then( createObjects );

</script>
</body>
Expand Down
36 changes: 17 additions & 19 deletions src/nodes/functions/BSDF/DFGApprox.js
Original file line number Diff line number Diff line change
@@ -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;
Loading