美文网首页unity3D技术分享征服Unity3d
关于间接高光的水平遮蔽(Horizon Occlusion)

关于间接高光的水平遮蔽(Horizon Occlusion)

作者: 恶毒的狗 | 来源:发表于2020-01-10 21:14 被阅读0次

    Horizon Occlusion

    第一次看到 Horizon Occlusion 是在读 Lux Plus 源码的时候。

    Horizon Occlusion 是关于 间接高光水平遮蔽 计算,典型的代码如下:

    // Horizon Occlusion
    #if defined (UNITY_PASS_FORWARDBASE)
        #if LUX_HORIZON_OCCLUSION
            gi.indirect.specular *= GetHorizonOcclusion(viewDir, s.Normal, s.worldNormalFace, HORIZON_FADE);    
        #endif
    #endif
    
    // Direct lighting uses the Lux BRDF
    half4 c = Lux_BRDF1_PBS(s.Albedo, s.Specular, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir,
            // Deferred expects these inputs to be calculates up front, forward does not. So we simply fill the input struct with zeros.
            half3(0, 0, 0), 0, 0, 0, 0,
            nl,
            ndotlDiffuse,
            gi.light, gi.indirect, specularIntensity, s.Shadow);
    
    c.rgb += UNITY_BRDF_GI (s.Albedo, s.Specular, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, s.Occlusion, gi);
    

    在物理着色之前,先计算 间接高光的水平遮蔽,即这里的 GetHorizonOcclusion 函数,函数就3行,代码如下:

    // Horizon Occlusion for Normal Mapped Reflections: http://marmosetco.tumblr.com/post/81245981087
    float GetHorizonOcclusion(float3 V, float3 normalWS, float3 vertexNormal, float horizonFade)
    {
        float3 R = reflect(-V, normalWS);
        float specularOcclusion = saturate(1.0 + horizonFade * dot(R, vertexNormal));
        // smooth it
        return specularOcclusion; // * specularOcclusion;
    }
    

    题外话,今天在读 Advanced Terrain Grass 代码的时候又看到了这个函数,再一看作者,原来是同一个人,:)

    作者 forst 有很多经典的插件,确实让我学到了很多,这里给他做一个广告:forst的商店主页

    好,现在回归主题,那么什么是 间接高光的水平遮蔽 呢?

    原理和实现

    关于 Horizon Occlusion这篇文章 写的很清楚了,这里再啰嗦一遍。

    我们知道Unity 间接高光 的计算依赖 视线向量 相对 法线反射向量反射向量 的计算代码如下:

    g.reflUVW   = reflect(-worldViewDir, Normal);
    

    考虑一个完全的镜面,我们不可能接收到镜面下面的环境反光,但是当我们引入 法线贴图 后,法线会偏转,这个时候我们的反射射线可能会达到镜面的下面,如下图所示:

    image

    如果以这个反射射线去计算环境高光,就会 漏光 了,如下图:

    image

    Horizon Occlusion 就是针对这种 漏光 的处理,加了 Horizon Occlusion 后效果如下:

    image

    下面看一下作者的计算方式:

    float GetHorizonOcclusion(float3 V, float3 normalWS, float3 vertexNormal, float horizonFade)
    {
        float3 R = reflect(-V, normalWS);
        float specularOcclusion = saturate(1.0 + horizonFade * dot(R, vertexNormal));
        // smooth it
        return specularOcclusion; // * specularOcclusion;
    }
    

    代码中的 R 即前文提到的 视线向量 相对于 法线 的反射向量。

    注意,这里的法线是经过 法线贴图 偏转后的法线,可能会导致 反射向量 指向模型表面的下方。

    这个时候我们还需要 顶点法线顶点法线 可以做为模型正确的面向参考。

    我们把 反射向量顶点法线点乘,如果是一个负值,就表明反射向量指向了表面下方,这个时候,我们就需要做反射光遮蔽了。

    作者这里给了一个参数 horizonFade 来控制遮蔽强度,代码还是很简单的。

    结尾

    好了,Horizon Occlusion 就介绍到这里。

    最后吹一下 Advanced Terrain Grass,代码看得差不多了,还是有不少值得学习的,后面慢慢写,先留图一张:

    image

    个人主页

    本文的个人主页链接:https://baddogzz.github.io/2020/01/10/Horizon-Occlusion/

    好了,拜拜。

    相关文章

      网友评论

        本文标题:关于间接高光的水平遮蔽(Horizon Occlusion)

        本文链接:https://www.haomeiwen.com/subject/jubmactx.html