Alan Wolfe is pretty spot on, but I will sum up any way :)
- render the back-faces of your "unit"-sized-box, [-1;1]
- sample the zbuffer and transform into light-local coordinates (see our slides for a fast way to do that).
- early-out of the pixelshader if outside of the box, i.e. if any coordinate in the local-position is outside of -1;1.
- then do the dot-product between the normal and the lightdir, in whatever space is convenient (you can extract the worldspace-lightdir from the object-to-world matrix).
- projecting a texture is easy, the UV-coordinates are simply the localspace xy (moved to [0;1])
- using a falloff texture is eays, the coordinate is simply the localspace z (moved to [0;1])
In an actual game, you of course want to use some sort of acceleration to discard early; we used old-school stencil culling (and should have used depthbounds), but likely tiled culling, clustered culling or similar is better.
You could use zgreater testing to discard pixels where background is entirely behind the light, but iirc on most modern GPUs this will not actually be an advantage, as there will be no early-discard due to the hierarchical zbuffer being primed in the direction of zless.
dot(worldNormal, lightDir)
but how to limit it. $\endgroup$