Search Unity

Compressing ndotl ramp to zonal harmonics

Discussion in 'Shaders' started by neoshaman, Aug 22, 2017.

  1. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Hello!

    I was wondering, let's say I have a ndotl ramp, that is rather smooth, I thought it could be a good candidate to be compressed into a zonal harmonics since it can be assume to represent a light lobe symmetrical around the light direction.

    However I don't really know how to manipulate spherical harmonics and derive their data, most entry level document is full of required jargon and knowledge, some of which seems only tangential to a practical manipulation. Is there way I get a better understanding of spherical harmonic encoding that will lead to a solution of my problem or a practical way to do what I want as it already exist to manipulate SH objects without getting everything?
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Talk about jargon. I feel like I'm reading a paper here and I'm really not sure what you want here.

    With a ndotl ramp, you mean a texture lookup using ndotl as uv? Which you want to compress into a formula? You can probably just do that using a nth order polynomial.

    Spherical harmonics are really just a conversion to frequencies like a Fourier transform, but 2D on a sphere instead of 1D. The first four terms are just 1, x, y and z, which is really not that complicated. The next 5 are the second order elements, like 2xz or xx - zz. Then it continues with 7 3rd order elements, 9 4th order elements, et cetera.

    The real point of spherical harmonics is that they are optimized for the 2D surface of a sphere. That doesn't really apply to a lookup based only on ndotl.
     
  3. Pode

    Pode

    Joined:
    Nov 13, 2013
    Posts:
    145
    @neoshaman : I think a good starting point would be the way Unity is using and encoding SH themselves in the engine.

    The 'BasicRenderPipeline.cs' from their ScriptableRenderLoop is a good entry point, I think : https://github.com/Unity-Technologi...es/BasicRenderPipeline/BasicRenderPipeline.cs
    Look for that part :
    Code (CSharp):
    1.    
    2. // ambient lighting spherical harmonics values
    3.         const int kSHCoefficients = 7;
    4.         Vector4[] shConstants = new Vector4[kSHCoefficients];
    5.         SphericalHarmonicsL2 ambientSH = RenderSettings.ambientProbe * RenderSettings.ambientIntensity;
    6.         GetShaderConstantsFromNormalizedSH(ref ambientSH, shConstants);
    7.  
    and the GetShaderConstantsFromNormalizedSH implementation.

    There is also a repo from google on that topic : https://github.com/google/spherical-harmonics

    If you know how to compile Eigen and use it, you can use their tools for your project.

    Edit : I think you can also find references on the forum, like there : https://forum.unity3d.com/threads/spherical-harmonic-debug-shader.156019/#post-1892007
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Zonal spherical harmonics are a spherical Fourier series. If you just need a lookup along a single value, you probably really just want to use a Fourier series. I'll also say if your ramp is anything more than a two color gradient, a spherical harmonic is unlikely to map well to it. They work well for diffuse lighting because anything kind of close looks good enough, but if you've got a specific falloff shape and mid colors you want, there's no guarantee a spherical harmonic will keep either of those.
     
  5. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Thanks!

    The reasoning I got with SH and more precisely ZH is because I wanted to compress a pre integrated skin shader LUT texture, I realized they were basically symmetrical light lobe and therefore suitable for SH approximation (I found out later The order 1889 did the same).

    SH are (supposedly) cheap to compute and already integrated in the unity workflow, it would save texture memory and computation for low end hardware (mali400), I hoped. They also have the property of being rotationally invariant (great for normal lighting) and also would have smooth distribution of data around the sphere (instead of over sampling near the pole as we would have with a fourrier transform on a sphere, at least that's how I would understand it).

    I'm intermittantly working on a simplified skin shader for low end. For example replacing the mipmap lod sampling of the normal by a lerp with a fraction of the surface normal and using vertex color for the diffuse color. I also wanted to see if I could compressed the LUT of shadowgun and use it to drive the skin shader too.

    But reading the answer I realized that a LUT texture is already that, in that it's equally distributed on a normalized hemicircle (though I'm not sure mathematically, that beyond my level) and the symmetry take care of the sphere distribution anyway. And since a texture is already linear data, using linear approximation might work too we are already sampling in the linear domain.

    Still the main problem is the same, I have no idea and don't know where to start about transforming arbitrary data sample into a function, either Fourier, SH, ZH, or polynomial, There is code I can analyze (Sébastien Lagarde works notably) about the process of turning cubemap into SH but I had no idea how to translate it into different sampling method and how it exactly weighted the formula to find appropriate SH.

    Ideally I would like to experiment with SH stuff too. I have read a bunch of paper and article, but they seem to skip the jump between the math and practical implementation, (something PBR had, you could compare the math and the code to understand what was happening) ... I'm not skill enough in the advance math to be able to translate it.
     
  6. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    @Pode
    I'm aware of these code, but don't allow me to do what I want as is, especially the encoding of light into SH data, mostly because the light I'm targeting go beyond the saturated Ndotl while they derive the SH from the light cone and distance (the code was already on the script reference). And it's less about using the SH data (like for example shadeSH in the cginc, which I know the origin of the swizzled optimization) but being able to generate it (and dealing with "ringing" issues).
     
  7. DominoM

    DominoM

    Joined:
    Nov 24, 2016
    Posts:
    460
    Not sure if it is what you are looking for, but there is some info on baking SH in this talk:
     
  8. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Thanks but no that's not it lol, what they did is still amazing though.
     
  9. Pode

    Pode

    Joined:
    Nov 13, 2013
    Posts:
    145
    Without using SH for the moment, does a Least Square fitting of our data points works ? (like there, for example : https://blog.demofox.org/2016/12/22/incremental-least-squares-curve-fitting/https://blog.demofox.org/2016/12/22/incremental-least-squares-curve-fitting/
    You will have errors at both 0 and 1 extremums of your function, but it can be a good start to iterate from.

    Here's a way to store the polynomial of a function in a texture, and use the hardware texture sampler at your advantage : https://blog.demofox.org/2016/02/22/gpu-texture-sampler-bezier-curve-evaluation/ Does doing the opposite (putting your data points yourself in the texture, and use the GPU to extrapolate the curve) would work ?
     
  10. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Yes, it does look daunting in the beginning, but I just did some test with spherical harmonics the past two week and it was fine really. Most pages start with the elegant single formula that also handles complex numbers. Nice math, but a bit far from a practical implementation. The table of real spherical harmonics on wikipedia got me started with some practical implementing.

    For Y1,-1 you still have something like:
    sqrt (3 / 4pi) * y / r

    Since we like normalizing anyway, you can skip the r, since we'll always have it at 1. The factor of 0.4886025... also isn't really needed for our purpose. You'd apply it both during encoding and decoding, so you just want something that uses the maximum range without clamping. So I went for the beautiful factor of 1. That turns the formula into just y with a range of -1 to 1 over the sphere. My full list of the first 9 spherical harmonics is:
    - 0.5
    - y
    - z
    - x
    - 2xy
    - 2yz
    - z^2 - (x^2 + y^2) / 2
    - 2xz
    - x^2 - y^2

    Those are all very practical and stay in a -1 to 1 range. Note that those are all z-up. In Unity I switch y and z to make it z-up.
     
    Last edited: Aug 25, 2017
  11. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    @jvo3dc Thanks, I'm more stump by other aspects though, I can black box fixed stuff I don't understand when they are fixed like the basis function, I get that each band have a specific but fixed equation and all the works is really in the coefficients and finding them, which is were I'm being stopped lol.

    For example given an arbitrary source that wouldn't be a cubemap or the type of light we have (directional, point and spot), for which algorithm already exist, how would I found those coefficient? Which is the type of problem I'm facing.

    Anyway I overlook something crucial in the question I was asking, I would need 7 fetch per channel on the texture, I save memory but it cost compute, I feel dumb lol, still worth pursuing for higher hardware.

    @Pode
    I took a while to answer this thread because I was processing the implication of the texture bezier lol, I first tried to solve it without the article lol! I have to spend a lot more time with that least thing though ... But that does look like something with existing implementation, I'll look into that.
     
  12. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    neoshaman likes this.