首页 > 代码库 > Unity shader教程-第四课:自定义光照模型(方程)

Unity shader教程-第四课:自定义光照模型(方程)

本文首发地址:http://98jy.net/article/22

更多文章,请入传送门

----------------------------------------------


在我们前面的课程中,有一行代码如下


#pragma surface surf Lambert
这行代码指示unity在编译我们的shader代码的时候,选用“Lambert”这个光照模型。它的格式是

#pragma surface surf LightModel


其中LightModel是unity自带的光照模型的名字或者我们自己按照unity的要求写好的。那么什么是光照模型呢?光照模型是反映某种物体的材质对光怎么反应的数学抽象,简单举个例子来说,你可以想象一个光子作为入射光从远处击中物体表面的某个部分,然后被反射、折射或者吸收。如果我们给定入射光以一定的数值,给定材质以一定的数值,最后发射或者折射等变化后这个光相关的数值(方向、方向上的能量)和原来的关系,就是我们的光照模型,有些地方也把这个称为光照方程。


光照模型或者光照方程最后都会由相关的数学公式表达出来,然后经引擎用代码变现。在Unity中,我们可以通过打开unity安装目录下Data\CGIncludes的Lighting.cginc看到这个方程的代码,为方便,我把它拷贝如下:


inline fixed4 LightingLambert (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
	fixed diff = max (0, dot (s.Normal, lightDir));
	
	fixed4 c;
	c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
	c.a = s.Alpha;
	return c;
}

方法名字上,比我们在#pragma中看到的“Lambert”,多出了“Lighting”这几个字。这个是unity的规定:光照模型在#pragma代码中不用带上"Lighting"字眼,unity在编译的时候会加上这个关键字后去寻找方法。因此,我们上述的#pragma surface surf Lambert这句话,对应的方法就是LightingLambert()。


光照模型的代码在surface代码运行后被unity运行,surface代码输出unity定义好的一系列物体的”表面“的数值(决定物体的材质信息),然后光照模型计算光和该材质交互的结果输出,成为我们最后看到的颜色。


那我们可以自己写一个光照方程并且让Unity使用我们的自定义的方程吗?答案是肯定的。下面的章节我们就开始尝试写一个自己的光照模型的方法。


步骤:


  • 使用我们原来的shader代码做基础
  • 改动#pragma一样代码中的Lambert到BasicDiffuse
  • 把下面的代码加入到SubShader{}块中


inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3
lightDir, fixed atten)
{
float difLight = max(0, dot (s.Normal, lightDir));
float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
col.a = s.Alpha;
return col;
}
  • 保存该shader,回到Unity。


Unity会开始编译我们的shader,如果没有差错,那么新的shader会应用到对应的物体上。从场景中的物体表现来看,没有任何差别(因为BasicDiffuse的代码逻辑跟Lambert的一模一样)。但是在这个背后,Unity的确使用了我们的光照模型"BasicDiffuse",而不再使用它提供的"Lambert"。


Unity用#pragma这个预编译指令(的确,这是它的正式名字) 来让我们选择光照模型。有3中类型的关照模型的原型我们可以选择:


  • half4 LightingName (SurfaceOutput s, half3 lightDir, half atten) {}
  • half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {}
  • half4 LightingName_PressPass (SurfaceOutput s, half4 light) {}

第一个用在普通渲染不需要观察者方向的场合。第二个用在普通渲染需要观察者方向的场合。第三个是unity被配置成延迟渲染的时候需要的。


上面的方法中用到一个dot()方法,这也是cg语言的一个库方法。用来计算两个方向之间的角度相关的值。在Lambert光照模型中, 每个点的入射光的方向和它本身的法线位置之间的夹角,决定了反射出去的光的数值的强度。入射光和法线的方向的角度越小,结果中的数值就越大。

Unity shader教程-第四课:自定义光照模型(方程)