Three.js a470d3 - WebGPU PCF blur samples

by Joe Pea

HTML

<!-- set up a custom patch of the three/webgpu import to patch PCFShadowFilter -->
<script>{

  const gitCommit = 'a470d32cdbdfe23399af3a237a9879f2614fb5d6'

  const xhr = new XMLHttpRequest()
  xhr.addEventListener( 'load', () => writeImportmap( patchTsl() ) )
  xhr.open( 'GET', `https://cdn.jsdelivr.net/gh/mrdoob/three.js@${gitCommit}/build/three.webgpu.js`, false )
  xhr.send()

  function writeImportmap( patchedTslUrl ) {

    document.write( `
      <script type="importmap">
        {
          "imports": {
            "three": "https://cdn.jsdelivr.net/gh/mrdoob/three.js@${gitCommit}/build/three.module.js",
            "three/core": "https://cdn.jsdelivr.net/gh/mrdoob/three.js@${gitCommit}/build/three.core.js",
            "three/webgpu": "${patchedTslUrl}",
            "three/tsl": "https://cdn.jsdelivr.net/gh/mrdoob/three.js@${gitCommit}/build/three.tsl.js",
            "three/addons/": "https://cdn.jsdelivr.net/gh/mrdoob/three.js@${gitCommit}/examples/jsm/"
          }
        }
      <\/script>
    ` )

  }

  function patchTsl() {

    let source = xhr.responseText

    source = source.replaceAll('./three.core.js', 'three/core')

    source = source.replace(
      'let _rendererState',
      `
      _shadowFilterLib[ 1 ] = Fn( ( { depthTexture, shadowCoord, shadow, depthLayer } ) => {

        const depthCompare = (uv, compare) => {

            let depth = texture(depthTexture, uv);

            if (depthTexture.isArrayTexture) {

                depth = depth.depth(depthLayer);

            }

            return depth.compare(compare);

        };

        const mapSize = reference('mapSize', 'vec2', shadow).setGroup(renderGroup);
        const radius = reference('radius', 'float', shadow).setGroup(renderGroup);

        const texelSize = vec2(1).div(mapSize);
        const radiusScaled = radius.mul(texelSize.x);

        // Use IGN to rotate sampling pattern per pixel (phi = IGN * 2Ď€)
     ...

JavaScript

import * as THREE from 'three/webgpu';
import { Inspector } from 'three/addons/inspector/Inspector.js';
import { PLYLoader } from 'three/addons/loaders/PLYLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

let camera, scene, renderer, clock;
let dirLight, spotLight;
let dirGroup;

const config = {
  spotlightResolution: 256,
  spotlightRadius: 4,
  spotlightSamples: 8,
  spotlightBias: 0,
  spotlightNormalBias: 0,
  spotlightIntensity: 1,
  spotlightFocus: 1,

  dirlightResolution: 256,
  dirlightRadius: 4,
  dirlightSamples: 50,
  dirlightBias: 0,
  dirlightNormalBias: 0,
  dirlightIntensity: 1,

  animationSpeed: 1,
};

init();

function init() {

  initScene();
  initMisc();

  // Init gui
  const gui = renderer.inspector.createParameters( 'Settings' );

  const resolutions = [ 64, 128, 256, 512, 1024, 2048 ];

  const spotlightFolder = gui.addFolder( 'Spotlight Shadow' );

  spotlightFolder.add( config, 'spotlightResolution', resolutions ).name( 'resolution' ).onChange( function ( value ) {

    // WIP this is not working, changing the size freezes the rendering

    spotLight.shadow.mapSize.width = value;
    spotLight.shadow.mapSize.height = value;
    spotLight.needsUpdate = true;

  } );

  spotlightFolder.add( config, 'spotlightRadius', 0, 20 ).name( 'radius' ).onChange( function ( value ) {

    spotLight.shadow.radius = value;

  } );

  spotlightFolder.add( config, 'spotlightSamples', 1, 50, 1 ).name( 'samples' ).onChange( function ( value ) {

    spotLight.shadow.blurSamples = value;
    // spotLight.shadow.dispose()

  } );

  spotlightFolder.add( config, 'spotlightBias', -0.05, 0.05, 0.001 ).name( 'bias' ).onChange( function ( value ) {

    spotLight.shadow.bias = value;

  } );

  spotlightFolder.add( config, 'spotlightNormalBias', -1, 1, 0.001 ).name( 'normal bias' ).onChange( function ( value ) {

    spotLight.shadow.normalBias =...