Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question about L2 spherical harmonics calculation in UnityCG.cginc

Discussion in 'Shaders' started by Darkisu, Feb 11, 2019.

  1. Darkisu

    Darkisu

    Joined:
    Oct 24, 2018
    Posts:
    1
    I noticed a spherical harmonics calculation in UnityCG.cginc where the L2 spherical harmonics lighting decompression was calculated as below:

    half4 vB = normal.xyzz * normal.yzzx;
    x1.r = dot(unity_SHBr,vB);
    x1.g = dot(unity_SHBg,vB);
    x1.b = dot(unity_SHBb,vB);


    My question is that according to this Wikipedia page, the calculation of Y(2, 0) is :
    SH Y20.JPG
    Then why is the SHEvalLinearL2(half4 normal) function calculate Y(2, 0) simply multiplying Z^2 with a value?
    Won't it cause the result contribution among Z axis a little bit stronger than other two axis?
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    There is the mathematical formula of spherical harmonics and then there is the practical one. In the conversion, a few things commonly happen:
    - The constant multiplier is removed or simplified, which doesn't actually change the result. The main importance is that the values fall in the target range if needed. (For example 0 to 1 if needed by the texture/rendertexture.)
    - The harmonics are reordered, which also doesn't change the result. This to maximize vector operations. (Like normal.xyzz * normal.yzzx)

    Without the constant multipliers and assuming r=1 (normalized normal vector), the L2 SH coefficients are:
    • 1
    • y
    • z
    • x
    • xy
    • yz
    • 2zz-xx-yy
    • xz
    • xx-yy
    Besides reordering, the only one that is different in Unity is indeed 2zz-xx-yy, which is just implemented as zz. If I look at the contour of the original formula, I can understand the optimization:

    (Third row, middle image)

    The zz approximation loses the (negative) yellow torus, but maintains the double peak at positive and negative z. In essence, it just adds Y0,0 to Y2,0:
    • 1 = xx+yy+zz
    • 2zz-xx-yy + 1 =
    • 2zz-xx-yy+xx+yy+zz =
    • 3zz
    If you then rescale again, you end up with just z^2, which written differently is:
    • z^2 = A*Y2,0 + B*Y0,0 (for specific values of A and B.)
     
    Darkisu and Invertex like this.