<p>You can check this out (<a href="https://arxiv.org/pdf/2103.14024.pdf" rel="nofollow noreferrer">the appendix of PlenOctree, Section B.1</a>), since the SH implementation of 3D GS is directly borrowed from PlenOctree. I can walk you through it with an example. First, take a look at the real form of SH (I won't use Latex here since, you know it's painful):
<a href="https://i.sstatic.net/wyJ11.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/wyJ11.png" alt="enter image description here" /></a>
Where <span class="math-container">$P_l^{|m|}(\cdot)$</span> is the <a href="https://en.wikipedia.org/wiki/Associated_Legendre_polynomials" rel="nofollow noreferrer">associative Legendre Polynomial</a>:
<a href="https://i.sstatic.net/miPb8.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/miPb8.png" alt="enter image description here" /></a></p>
<p>(From Wikipedia)</p>
<p>So where are those <code>SH_C3</code>? They have some thing to do with:
<span class="math-container">$$
K_{lm} = \sqrt{\frac{2l+1}{4\pi}\frac{(l - |m|)!}{(l+|m|)!}}
$$</span>
Ok, so basically, we first get <span class="math-container">$\theta$</span> and <span class="math-container">$\phi$</span>, as the direction <span class="math-container">$(x, y, z)$</span> can be easily converted to the spherical coordinates <span class="math-container">$(\theta, \phi)$</span>. Therefore, the corresponding <span class="math-container">$\cos(\cdot), \sin(\cdot)$</span> values can be obtained.</p>
<p>Then we use the formula of associative Legendre Polynomial to get, say <span class="math-container">$m=0, l = 0$</span> case:
<span class="math-container">$$
P_0^0=1
$$</span>
We next evaluate SH basis function (given in the first image), for example: <span class="math-container">$Y_{00}, l=0,m=0$</span> will be <span class="math-container">$\sqrt{1 / 4\pi} \approx 0.282095$</span>, which equals to  <code>SH_C0</code> in the code. Other coefficients can be derived similarly: substitute <span class="math-container">$l, m$</span> with the order you need, then evaluate the whole formula and store the constant part. With all the precomputed coefficients, now you can multiply your learned SH coefficients with these precomputed ones. Hope this will help.</p>
<hr />
<p>The SH itself is computed in the way describe above. Yet the coefficients for the decomposition in 3D Cartesian space are a bit different:</p>
<p><a href="https://i.sstatic.net/mCU2rtDs.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/mCU2rtDs.png" alt="enter image description here" /></a></p>
<p>The image is taken from: <a href="https://3dvar.com/Green2003Spherical.pdf" rel="nofollow noreferrer">Spherical Harmonic Lighting: The Gritty Details</a>, page 22.</p>
<p>The code in diff-gaussian-rasterization:</p>
<pre class="lang-cpp prettyprint-override"><code>// cuda_rasterizer/auxiliary.cu
__device__ const float SH_C2[] = {
    1.0925484305920792f,
    -1.0925484305920792f,
    0.31539156525252005f,
    -1.0925484305920792f,
    0.5462742152960396f
};
// cuda_rasterizer/forward.cu
if (deg &gt; 1)
{
    float xx = x * x, yy = y * y, zz = z * z;
    float xy = x * y, yz = y * z, xz = x * z;
    result = result +
        SH_C2[0] * xy * sh[4] +
        SH_C2[1] * yz * sh[5] +
        SH_C2[2] * (2.0f * zz - xx - yy) * sh[6] +
        SH_C2[3] * xz * sh[7] +
        SH_C2[4] * (xx - yy) * sh[8];
}
</code></pre>
<p>The code implements exactly the same concept.</p>

CC BY-SA 4.0
2024-09-03T08:27:45.967Z
Sorry, this is not an answer, but a follow up question for Enigmatisms. I could not do "Add a Comment". Using the formula, the Y20 value is 0.5*sqrt(5/PI) which is 0.6307831305050401, not 1.0925484305920792 as in the code. Thanks for your kind help.
daviszhu88
CC BY-SA 4.0
2024-01-28T02:43:28.030Z
2024-09-04T10:50:29.350Z
2024-09-04T10:50:29.350Z
Enigmatisms
<p>I am trying to understand the function <a href="https://github.com/graphdeco-inria/diff-gaussian-rasterization/blob/main/cuda_rasterizer/forward.cu#L20" rel="nofollow noreferrer">computeColorFromSH</a>, which is a part of the differentiable gaussian rasterizer used by the paper <a href="https://arxiv.org/abs/2308.04079" rel="nofollow noreferrer">3D Gaussian Splatting for Real-Time Radiance Field Rendering</a>.</p>
<p>I am looking for the formula that the code is based on. Here's an excerpt for the <code>degree &gt; 2</code> part:</p>
<pre><code>            result = result +
                SH_C3[0] * y * (3.0f * xx - yy) * sh[9] +
                SH_C3[1] * xy * z * sh[10] +
                SH_C3[2] * y * (4.0f * zz - xx - yy) * sh[11] +
                SH_C3[3] * z * (2.0f * zz - 3.0f * xx - 3.0f * yy) * sh[12] +
                SH_C3[4] * x * (4.0f * zz - xx - yy) * sh[13] +
                SH_C3[5] * z * (xx - yy) * sh[14] +
                SH_C3[6] * x * (xx - 3.0f * yy) * sh[15];
</code></pre>
<p>It seems to be some kind of polynomial. The Wikipedia article on Spherical Harmonics has only integrals, so I'm guessing that this is a special case and maybe even a well-known one.</p>
<p>Is anyone familiar with this? Where I can read more about it?</p>

<3d><spherical-harmonics>
Understanding spherical harmonics code
accounted4
# Understanding spherical harmonics code

(Asked by: username_0 on 2024-01-27)

I am trying to understand the function [computeColorFromSH](https://github.com/graphdeco-inria/diff-gaussian-rasterization/blob/main/cuda_rasterizer/forward.cu#L20), which is a part of the differentiable gaussian rasterizer used by the paper [3D Gaussian Splatting for Real-Time Radiance Field Rendering](https://arxiv.org/abs/2308.04079).

I am looking for the formula that the code is based on. Here's an excerpt for the `degree > 2` part:

```
            result = result +
                SH_C3[0] * y * (3.0f * xx - yy) * sh[9] +
                SH_C3[1] * xy * z * sh[10] +
                SH_C3[2] * y * (4.0f * zz - xx - yy) * sh[11] +
                SH_C3[3] * z * (2.0f * zz - 3.0f * xx - 3.0f * yy) * sh[12] +
                SH_C3[4] * x * (4.0f * zz - xx - yy) * sh[13] +
                SH_C3[5] * z * (xx - yy) * sh[14] +
                SH_C3[6] * x * (xx - 3.0f * yy) * sh[15];

```

It seems to be some kind of polynomial. The Wikipedia article on Spherical Harmonics has only integrals, so I'm guessing that this is a special case and maybe even a well-known one.

Is anyone familiar with this? Where I can read more about it?

**Tags:** 3d, spherical-harmonics


---

## 1 answers

### Answer by username_1 on 2024-01-28. Score: 4 | Accepted answer

You can check this out ([the appendix of PlenOctree, Section B.1](https://arxiv.org/pdf/2103.14024.pdf)), since the SH implementation of 3D GS is directly borrowed from PlenOctree. I can walk you through it with an example. First, take a look at the real form of SH (I won't use Latex here since, you know it's painful):
[![enter image description here](https://i.sstatic.net/wyJ11.png)](https://i.sstatic.net/wyJ11.png)
Where $P\_l^{|m|}(\cdot)$ is the [associative Legendre Polynomial](https://en.wikipedia.org/wiki/Associated_Legendre_polynomials):
[![enter image description here](https://i.sstatic.net/miPb8.png)](https://i.sstatic.net/miPb8.png)

(From Wikipedia)

So where are those `SH_C3`? They have some thing to do with:
$$
K\_{lm} = \sqrt{\frac{2l+1}{4\pi}\frac{(l - |m|)!}{(l+|m|)!}}
$$
Ok, so basically, we first get $\theta$ and $\phi$, as the direction $(x, y, z)$ can be easily converted to the spherical coordinates $(\theta, \phi)$. Therefore, the corresponding $\cos(\cdot), \sin(\cdot)$ values can be obtained.

Then we use the formula of associative Legendre Polynomial to get, say $m=0, l = 0$ case:
$$
P\_0^0=1
$$
We next evaluate SH basis function (given in the first image), for example: $Y\_{00}, l=0,m=0$ will be $\sqrt{1 / 4\pi} \approx 0.282095$, which equals to `SH_C0` in the code. Other coefficients can be derived similarly: substitute $l, m$ with the order you need, then evaluate the whole formula and store the constant part. With all the precomputed coefficients, now you can multiply your learned SH coefficients with these precomputed ones. Hope this will help.

---

The SH itself is computed in the way describe above. Yet the coefficients for the decomposition in 3D Cartesian space are a bit different:

[![enter image description here](https://i.sstatic.net/mCU2rtDs.png)](https://i.sstatic.net/mCU2rtDs.png)

The image is taken from: [Spherical Harmonic Lighting: The Gritty Details](https://3dvar.com/Green2003Spherical.pdf), page 22.

The code in diff-gaussian-rasterization:

```cpp
// cuda_rasterizer/auxiliary.cu
__device__ const float SH_C2[] = {
    1.0925484305920792f,
    -1.0925484305920792f,
    0.31539156525252005f,
    -1.0925484305920792f,
    0.5462742152960396f
};
// cuda_rasterizer/forward.cu
if (deg > 1)
{
    float xx = x * x, yy = y * y, zz = z * z;
    float xy = x * y, yz = y * z, xz = x * z;
    result = result +
        SH_C2[0] * xy * sh[4] +
        SH_C2[1] * yz * sh[5] +
        SH_C2[2] * (2.0f * zz - xx - yy) * sh[6] +
        SH_C2[3] * xz * sh[7] +
        SH_C2[4] * (xx - yy) * sh[8];
}

```

The code implements exactly the same concept.

> **Comments:**
> **username_2**: Sorry, this is not an answer, but a follow up question for username_1. I could not do "Add a Comment". Using the formula, the Y20 value is 0.5*sqrt(5/PI) which is 0.6307831305050401, not 1.0925484305920792 as in the code. Thanks for your kind help.  

---

