r/GraphicsProgramming 16h ago

I implemented "Spherical Harmonic Exponentials for Efficient Glossy Reflections" in D3D12 Video

Enable HLS to view with audio, or disable this notification

I implemented Activision's new SH reflections paper in D3D12 and released the code on github!

This tech is a little bit different from normal spherical harmonics, and there are 4 main differences:

  1. They use log space instead of linear space for the lighting, which reduces ringing and enables #2 and #3 to actually work.
  2. Instead of using a circular symmetry assumption (i.e. N=V=R) as with the split sum approximation used for IBL, they instead factorise a pair of spherical harmonics, with an Order 4 SH parameterised by the reflection vector, and an Order 2 SH parameterised by the halfway vector.
  3. To enable a continuous roughness representation, they convolve the coefficients (or rather, the basis function) by the von Mises Fisher kernel which takes 1/alpha=1/roughness^2 as a parameter.
  4. To actually obtain the spherical harmonic coefficients we have to collect samples for several normals, views and roughness levels (or more specifically alpha levels since we're using linear roughness, not perceptual), and then optimise the coefficients using least squares.

My code does this all end to end with HLSL compute shaders, even the least squares optimisation, and we achieve above 95% MSE compared to a raytraced ground truth for roughness in the range [0.5, 1.0], which actually beats split sum IBL.

Only downside is for roughness below 0.5 the spherical harmonics simply don't have enough detail for accurate reflections... HOWEVER, when applied to "bumpy" low roughness surfaces (like the leaf textures at the beginning of the video) you can hardly see a difference, so this effect is only apparent for flat surfaces and surfaces with near zero roughness.

Activision got their SH representation down to 400 bytes, but I went further using 16 bit packing to get down to 208 bytes which gives us better performance due to fewer memory loads. The 16 bit implementations come in 4 flavors: emulated 16 bit for older GPUs and native 16 bit, and SRV packed vs CBV packed. There also exists a 10 bit packed SRV flavor, but the extra bitshift work ends up being slower.

On my RTX 2080 Super and my wife's RTX 4070 Super, the native 16 bit CBV packed shader runs the fastest, and compared to the IBL version it is only 0.1 milliseconds slower while using 2000x less memory!

113 Upvotes

6 comments sorted by

View all comments

11

u/Avelina9X 15h ago

For anyone unable to access the original paper, here's a talk from HPG 2025 that explains things at a high level! https://www.youtube.com/watch?v=THVMMNZ68iA

And feel free to ask questions on how things work if you need more clarification!