首页 > 代码库 > 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教程-第四课:自定义光照模型(方程)