查看原文
其他

Unity 5着色器系统代码介绍(下)

2017-07-31 Unity官方 Unity官方平台

Unity 5着色器系统代码介绍(上)一文中,对着色器系统的工作原理做了介绍,今天这篇文章继续深入介绍,将目光聚焦在标准着色器的光照函数。

重新回到Standard.shader,这次在UnityStandardCoreForward.shader中,我们将选择另一个“不简单”的那个分支。它将我们引向UnityStandardCore.shader,而我们感兴趣的是fragForwardBaseInternal函数。


简化版本用作参考:


与上一节中的版本不同,最终的颜色由对UNITY_BRDF_PBS、UNITY_BRDF_GI和Emission的调用结果相加得出。

Emission与简单版本中的相同。UNITY_BRDF_PBS和UNITY_BRDF_GI是包含文件中定义的函数别名。在下面这些包含文件中进行查找:


UnityStandardBRDF和UnityPBSLighting看起来最像,所以先查看它们。它们就在UnityPBSLighting.cginc中,不同的着色器目标会选择不同的函数。

选择BRDF1_Unity_PBS,它就在UnityStandardBRDF.cginc中,它看起来是最逼真的可用BRDF,而BRDF3_Unity_PBS则是消耗最低的版本。

如你所见,这是个大函数,因此跳过一些与优化相关的细节,依次逐块的进行讲解,首先从这个非常有用的注释开始:


注释给出了使用的公式,以及引用与作用。NDF(法线分布函数)有多个选择,但这里仅介绍GGX,因为我觉得它更好。

下面对注释中的公式进行简单的介绍:


  • kD: 漫反射率

  • pi: π常量

  • kS: 镜面反射率

  • D: 法线分布

  • V: 几何可见度系数

  • F: 菲涅尔反射率



以上就是光照函数的全部。下面来深入介绍全局光照对最终结果的贡献。

本节我们将介绍全局光照贡献的计算方式。过程有些麻烦,因为进行关键计算的代码隐匿在质量选择层的层层定义之后。

所以让我们查看下所有与全局光照有关的函数和结构体,它们就位于我们前面三节提及的代码中。

在UnityStandardCore.cginc中,fragForwardBaseInternal如下:


在片段前向基本函数中,FragmentGI被用于计算全局光照数据:“gi”,它被传递给UNITY_BRDF_PBS 和UNITY_BRDF_GI(它们的定义分别对应着不同的质量级别)。

在UnityStandardBRDF.cginc中, BRDF1_Unity_PBS如下:


这是UNITY_BRDF_PBS部分,它接受gi,用它来计算着色像素的颜色。

以下两个定义至少需要定义一个:

  • LIGHTMAP_ON

  • DYNAMICLIGHTMAP_ON


还有一堆额外的定义,用来控制代码的跳转,或决定函数的选择:

  • DIRLIGHTMAP_SEPARATE

  • DIRLIGHTMAP_COMBINED

  • UNITY_BRDF_PBS_LIGHTMAP_INDIRECT

  • UNITY_BRDF_GI

  • UNITY_SHOULD_SAMPLE_SH

  • UNITY_SPECCUBE_BLENDING

  • UNITY_SPECCUBE_BOX_PROJECTION

  • _GLOSSYREFLECTIONS_OFF

  • UNITY_SPECCUBE_BOX_PROJECTION



全局光照数据的流转基本是这样的,从基本结构体流向与定义相关的函数:


  • 结构体UnityGI(在UnityLightingCommon.cginc中), 保存着多个UnityLight,取决于光照贴图的类型

  • 结构体UnityGIInput(在UnityLightingCommon.cginc中)保存着计算GI所需的其他不同信息,被用于许多函数中

  • 函数UNITY_BRDF_GI(在UnityPBSLighting.cginc中),在fragForwardBaseInternal中用于计算对BRDF的间接贡献(通过调用BRDF_Unity_Indirect)

  • 函数BRDF_Unity_Indirect(在UnityPBSLighting.cginc中),将UNITY_BRDF_PBS_LIGHTMAP_INDIRECT 的结果与传入的colour相加

  • 函数UNITY_BRDF_PBS_LIGHTMAP_INDIRECT(在UnityPBSLighting.cginc中),被定义为BRDF2_Unity_PBS(但一条注释说也可以使用BRDF1_Unity_PBS ,以获得更佳质量)

  • 函数BRDF2_Unity_PBS 或BRDF1_Unity_PBS,我们在前面一节中见过。这里用于计算间接贡献

  • 函数FragmentGI(在UnityStandardCore.cginc中)填充必要的数据,包括来自反射探针的数据,然后传递给UnityGlobalIllumination

  • 函数UnityGlobalIllumination:(4个版本,不同的签名)传递数据给UnityGI_Base 和UnityGI_IndirectSpecular

  • 函数UnityGI_Base(在UnityGlobalIllumination.cginc中)对光照贴图进行采样和解码,混合实时衰减和应用遮蔽

  • 函数UnityGI_IndirectSpecular(在UnityGlobalIllumination.cginc中),计算反射,对盒型投影进行矫正(如果已激活),应用遮蔽



对于了解全貌同样有用的东西:


  • 结构体UnityIndirect(在UnityLightingCommon.cginc中)仅包含一个漫反射和一个镜面反射颜色

  • 结构体UnityLight(在UnityLightingCommon.cginc中)保存光源的颜色、方向和NdotL

  • 纹理立方体unity_SpecCube0 和unity_SpecCube1: 反射探针

  • 结构体Unity_GlossyEnvironmentData: 保存粗糙度和反射UV

  • 函数ResetUnityGI: 清空一个UnityGI结构体

  • 函数ResetUnityLight: 清空一个UnityLight 结构体

  • 函数ShadeSHPerPixel: 对每个像素进行Spherical Harmonics采样


了解以上内容后,在修改标准着色器时,不会意外的将全局光照。    


本文来源于:shadercat.com

原作者:Claudia Doppioslash


推荐阅读

Unity 5着色器系统代码介绍(上)

Unity 5着色器编程经验分享

《Trifox》中的遮挡处理和溶解着色器(上)

《Trifox》中的遮挡处理和溶解着色器(下)

Unity 5.3中的GGX着色器


点击“阅读原文”进入Unity官方中文社区

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存