UnityShader——球谐光照

昨天翻 Unit Europe 2017 发现一篇很有意思的演讲,作者因为游戏的特殊性,彻底摒弃了 Unity 原本的烘培系统,通过只烘培 Light Probe 的形式自己读取数据建立全局光照,文章提到了 GDC 的一篇讲 Light Probe 插值的演讲,过去翻了翻,果然,LightProbe里存储的是烘培后用球谐函数编码的全局光照,正好趁机会把球谐光照这块梳理下:

理论出自02年SIG的一篇论文,后面有人做了详细的笔记,国内也有博主翻译了一部分,不过并不详尽,因为大部分底层计算工作 Unity 已经帮我们做好了并提供了接口,因此并不需要我们自己实现,这里主要讲一下我自己认为比较核心的几个点的理解:

1、蒙特卡洛积分:
将计算机尤其是GPU上非常难以计算的积分简化为了加法,这是球谐光照的前提
2、投影:
球谐光照的实质就是将复杂的光照信号投影到基函数上存储,然后在使用的时候再将基函数上的数据加起来重建光照信号
3、伴随勒让德多项式
想比如正弦信号,伴随勒让德多项式作为基函数不仅是正交的,而且是归一化的, 这意味着其具有旋转不变性,适用于动态物体

Unity 使用了三阶的伴随勒让德多项式作为基函数,因为Unity 主要用来存储全局光等低频信号,其在欧拉坐标系下如下所示:


UnityShader——球谐光照_第1张图片

以法线方向作为 x,y,z 其在球面上如图所示:


UnityShader——球谐光照_第2张图片

Unity通过烘培时的光线追踪计算出其光照原始信号,然后投影到基函数并存储其系数,我们在Shader中可通过 ShadeSH9 函数获取重建信号,ShadeSH9 实现在 UnityCG.cginc 文件中,具体代码如下:

// normal should be normalized, w=1.0
half3 SHEvalLinearL0L1 (half4 normal) {
    half3 x;

    // Linear (L1) + constant (L0) polynomial terms
    x.r = dot(unity_SHAr,normal);
    x.g = dot(unity_SHAg,normal);
    x.b = dot(unity_SHAb,normal);

    return x;
}

// normal should be normalized, w=1.0
half3 SHEvalLinearL2 (half4 normal) {
    half3 x1, x2;
    // 4 of the quadratic (L2) polynomials
    half4 vB = normal.xyzz * normal.yzzx;
    x1.r = dot(unity_SHBr,vB);
    x1.g = dot(unity_SHBg,vB);
    x1.b = dot(unity_SHBb,vB);

    // Final (5th) quadratic (L2) polynomial
    half vC = normal.x * normal.x - normal.y * normal.y;
    x2 = unity_SHC.rgb * vC;

    return x1 + x2;
}

// normal should be normalized, w=1.0
// output in active color space
half3 ShadeSH9 (half4 normal) {
    // Linear + constant polynomial terms
    half3 res = SHEvalLinearL0L1(normal);

    // Quadratic polynomials
    res += SHEvalLinearL2(normal);

    if (IsGammaSpace())
        res = LinearToGammaSpace(res);

    return res;
}

三阶的基函数系数分别用了两个子函数来读取,其中

    // SH lighting environment
    half4 unity_SHAr;
    half4 unity_SHAg;
    half4 unity_SHAb;
    half4 unity_SHBr;
    half4 unity_SHBg;
    half4 unity_SHBb;
    half4 unity_SHC;

是 UnityShaderVariables.cginc 中的内置变量,用来存放存储的系数,在实际使用中,我们直接调用 ShadeSH9,配合LightProbe 即可读取到 precompute bake 生成的自发光信息


UnityShader——球谐光照_第3张图片

你可能感兴趣的:(U3D)