其他分享
首页 > 其他分享> > unity shader development[9]

unity shader development[9]

作者:互联网

以物理方式制作着色器

  在你在上一章学到的许多东西中,有三条规则是你实现的BRDF需要遵循的,以便被认为是基于物理的。将这些知识付诸实践的一个方法是审查定制的Phong表面着色器,看看它是否破坏了这些规则。该着色器遵循Phong的原始配方,而Phong是相当古老的,所以很有可能是这样。

分析 Phong

  清单9-1显示了自定义的Phong照明功能。让我们检查一下正向性、对等性和能量守恒。

清单 9-1. Phong的自定义照明函数

inline fixed4 LightingPhong (SurfaceOutput s, half3 viewDir, UnityGI gi)
{
	UnityLight light = gi.light;
	float nl = max(0.0f, dot(s.Normal, light.dir));
	float3 diffuseTerm = nl * s.Albedo.rgb * light.color;
	float3 reflectionDirection = reflect(-light.dir, s.Normal);
	float3 specularDot = max(0.0, dot(viewDir, reflectionDirection));
	float3 specular = pow(specularDot, _Shininess);
	float3 specularTerm = specular * _SpecColor.rgb * light.color.rgb;
	float3 finalColor = diffuseTerm.rgb + specularTerm;
	fixed4 c;
	c.rgb = finalColor;
	c.a = s.Alpha;
	#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
	c.rgb += s.Albedo * gi.indirect.diffuse;
	#endif
	return c;
}
检查正面性

  你可以确保BRDF的输出总是正的,用一个小技巧,在最终颜色前面放一个从0开始的Max函数。就本章而言,这样做是可以的,但一般来说,如果BRDF在数学上能保证这一点,那会更好。

检查对等性

  对等性不容易从着色器代码中检查。一般来说,任何公布的基于物理的BRDF都应该有这个属性。如果你想检查的话,可以查阅BRDF的研究论文,或者,如果你精通Mathematica等程序,甚至是手工计算,你可以自己做数学题并检查。Phong不是对等性的,所以这需要一些工作。

检查能量守恒

  有了正确的归一化因素,甚至Phong也可以是能量守恒的。一个能量守恒的镜面,当它集中在一个较小的区域时,会更亮,而当它分散在更多的地方时,则更暗。一般来说,这将对应于材料的粗糙程度,在基于微面理论的BRDF中。

改良后的Phong

  对我们来说,幸运的是,Lafortune和Willems在他们1994年的论文中完成了使Phong基于物理的工作。我们想要的归一化因子是:
在这里插入图片描述
你可以在清单9-2中看到修改后的、规范化的Phong函数的实现。

清单 9-2. 修改后的Phong作为一个自定义的照明函数

inline fixed4 LightingPhongModified (SurfaceOutput s, half3 viewDir, UnityGI gi)
{
	const float PI = 3.14159265358979323846;
	UnityLight light = gi.light;
	float nl = max(0.0f, dot(s.Normal, light.dir));
	float3 diffuseTerm = nl * s.Albedo.rgb * light.color;
	float norm = (n + 2) / (2 * PI);
	float3 reflectionDirection = reflect(-light.dir, s.Normal);
	float3 specularDot = max(0.0, dot(viewDir, reflectionDirection));
	float3 specular = norm * pow(specularDot, _Shininess);
	float3 specularTerm = specular * _SpecColor.rgb * light.color.rgb;
	float3 finalColor = diffuseTerm.rgb + specularTerm;
	fixed4 c;
	c.rgb = finalColor;
	c.a = s.Alpha;
	#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
	c.rgb += s.Albedo * gi.indirect.diffuse;
	#endif
	return c;
}

  就这样,我们实现了一个基于物理的BRDF。这可能是一个反常的过程。归根结底,就是把镜面反射率乘以归一化系数。归一化系数可以防止BRDF返回的光比它一开始收到的多。

  你以后会玩到有趣的BRDFs,但现在让我们担心的是,当你看它的时候,检查它是否有物理上的感觉,并学习如何制作一个方便的着色器。这个自定义的照明函数很适合在几章之前的旧的自定义Phong表面着色器中使用(见清单9-3)。

清单 9-3. 定制/修改的Phong表面着色器

Shader "Custom/ModifiedPhong" {
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_SpecColor ("Specular Material Color", Color) = (1,1,1,1)
		_Shininess ("Shininess (n)", Range(1,1000)) = 100
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		CGPROGRAM
		// Physically based Standard lighting model, and enable shadows on all light types
		#pragma surface surf PhongModified fullforwardshadows
		// Use shader model 3.0 target, to get nicer looking lighting
		#pragma target 3.0
		sampler2D _MainTex;
		struct Input {
			float2 uv_MainTex;
		};
		half _Shininess;
		fixed4 _Color;
		inline void LightingPhongModified_GI (
			SurfaceOutput s,
			UnityGIInput data,
			inout UnityGI gi)
		{
			gi = UnityGlobalIllumination (data, 1.0, s.Normal);
		}
		inline fixed4 LightingPhongModified (SurfaceOutput s, half3 viewDir, UnityGI gi)
		{
			const float PI = 3.14159265358979323846;
			UnityLight light = gi.light;
			float nl = max(0.0f, dot(s.Normal, light.dir));
			float3 diffuseTerm = nl * s.Albedo.rgb * light.color;
			float norm = (_Shininess + 2) / (2 * PI);
			float3 reflectionDirection = reflect(-light.dir, s.Normal);
			float3 specularDot = max(0.0, dot(viewDir, reflectionDirection));
			float3 specular = norm * pow(specularDot, _Shininess);
			float3 specularTerm = specular * _SpecColor.rgb * light.color.rgb;
			float3 finalColor = diffuseTerm.rgb + specularTerm;
			fixed4 c;
			c.rgb = finalColor;
			c.a = s.Alpha;
			#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
			c.rgb += s.Albedo * gi.indirect.diffuse;
			#endif
			return c;
		}
		UNITY_INSTANCING_CBUFFER_START(Props)
		UNITY_INSTANCING_CBUFFER_END
		void surf (Input IN, inout SurfaceOutput o) {
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Specular = _Shininess;
			o.Alpha = c.a;
		}
		ENDCG
	}
	FallBack "Diffuse"
}

  你要找的是,当镜面散开时,镜面亮度减少,而当镜面集中在一个小表面时,镜面亮度增加。由于我们身边有一个规范化前的Phong版本,我们可以很容易地检查到这一点。正如你在图9-1中看到的,原始的Phong(在右边)保持相同的亮度,而归一化的Phong(在左边)在镜面占据较大面积时亮度较低,占据较小面积时亮度较高,这是能量守恒的结果
在这里插入图片描述
  另一个方便的方法是比较绘制BRDF的行为图,以看出其中的差别。你会在第11章中看到更多关于这个问题的内容,在那里我们将介绍一个分析BRDF的程序,但是图9-2显示了对未来的一个小小的偷窥
在这里插入图片描述
图9-2. 原始 Phong 和修改后的 Phong 的极坐标图比较

  图9-2显示了两个版本的Phong,在图上画出了相互之间的对比。在图像的左边,两者都有n = 10。在图片的右边,对应于n = 100,你看到修改后的Phong的亮度急剧增加,而原始Phong的亮度保持不变。

总结

  正如你所看到的,即使是一个老式的BRDF,也有可能使其更具有物理基础。使用BlinnPhong也可以达到同样的效果,方法是查找归一化系数或自己推导。自己推导需要你对微积分很熟悉。

  虽然这个修改后的Phong是能量守恒的,但它仍然没有考虑到菲涅尔,所以这个Phong和从微面理论得出的BRDF之间仍然会有相当大的差异。

  现在你的武器库中已经有了一个基于物理的BRDF,你已经准备好学习后期处理效果了。它们将帮助着色器看起来更加真实。你需要使用HDR相机,确保你在线性空间中进行着色,并添加色调映射。

标签:development,light,BRDF,shader,unity,Phong,rgb,gi,float3
来源: https://blog.csdn.net/u013716859/article/details/121731042