【《Unity 2018 Shaders and Effects Cookbook》提炼总结】(六)自定义光照模型
自定义漫反射光照模型
如果你熟悉Unity4,你可能知道它提供的默认shader基于成为Lambertian reflectance.下图显示了使用标准着色器(右)和漫反射Lambert(左)渲染的相同几何体:
基于Lambertian reflectance的Shader被归类为非真实感,现实世界中没有任何物体真的像这样.然而Lambertian reflectance仍然经常用于低聚游戏,因为它们在复杂几何形状的面之间产生了整齐的比对.用于计算Lambertianreflectance的照明模型也非常有效,使其非常适合移动游戏.
Unity已经为我们提供了可以用于Shader的照明函数,它被称为Lambertian lighting model.它是比较基本和有效的反射形式之一,所以即使是今天我们也可以在很多游戏中找到它.因为它已经在Unity Surface Shader 中构建,我们认为最好从这个开始,然后在它的基础上构建.
a.创建一个shader命名为SimpleLambert.
b.创建一个Material命名为SimpleLambertMat.
c.首先使用以下内容替换Shader中的属性模块并移走SubShader部分的相关变量.
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
e. 更改着色器地#pragma指令,使其使用我们自定义光照模型,而不是标准光照模型。
#pragma surface surf SimpleLambert
h.使用一个非常简单的surface函数,它只根据其UV数据对纹理进行采样:
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
i.添加一个名为LightingSimpleLambert()的函数,该函数将包含Lambertian reflectance的以下代码:
//Allows us to use the SimpleLambert lighting mode
half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
{
//First calculate the dot product of the light direction and the surface's normal
half NdotL = dot(s.Normal, lightDir);
//Next,set what color should be returned
half4 color;
color.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
color.a = s.Alpha;
//Return the calculated color
return color;
}
j.保存脚本赋予脚本我们会发现和之前有些不同。
效果图:
源码如下
Shader "Custom/SimpleLambert" {
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf SimpleLambert
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
//Allows us to use the SimpleLambert lighting mode
half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
{
//First calculate the dot product of the light direction and the surface's normal
half NdotL = dot(s.Normal, lightDir);
//Next,set what color should be returned
half4 color;
color.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
color.a = s.Alpha;
//Return the calculated color
return color;
}
ENDCG
}
FallBack "Diffuse"
}
它是如何运行的
如#pragma指令用于指定要使用的surface函数。选择不同的光照模型的工作方式类似:SimpleLambert强制Cg查找名为LightingSimpleLambert()的函数。注意开头的Lighting,在指令中省略。
Lighting function有三个参数:surface output(包含反照率和透明度等物理属性),光源的方向以及衰减。
根据Lambertian reflectance,表面反射的光量取决于入射光和表面法线之间的角度。如果你打过台球,你肯定对这个概念很熟悉,球的方向取决于其与墙壁的入射角。如果你以90度的角度撞墙,球将会回到你身边,如果你以非常低的角度击中它,它的方向将基本不变,Lambertian model做出相同的假设。如果光线都会被反射回来。角度越低,反射回的光线就越少。概念如图。
这个简单的概念必须转化为数学公式。两个单位向量之间的角度可以通过一个名为点积的运算符来计算。当点积等于零时,两个矢量是正交的,这意味他们形成90度角。当它等于1(或-1)的时候,它们彼此平行,Cg有一个名为dot()的函数,它可以非常有效的实现点积。
下图显示了照射在复杂表面上的光源(太阳)。L表示光线方向(在shader中成为lightDir),N 表示曲面的法线。光线以与撞击表面相同的角度反射:
Lambertian reflectance仅仅使用NdotL点积作为光强的乘法系数:
当N和L平行时,所有光线都会反射回光源,使几何体看起来更亮。_LightColor0变量包含计算的灯光的颜色。
在Unity5之前,灯光的强度时不同的,如果你使用基于Lambertian模型的旧漫反射shader,我们可能回注意到NdotL乘以(NdotL * atten * 2),而不是(NdotL * atten * 2).如果要从Unity4导入自定义shader,则需要手动更正此设置。但是,Legacy Shaders的设计考虑了这一方面。
当点积为负时,光来自三角形的相对侧。对于不透明的几何图形而言,这不是问题,因为正面未面向相机的三角形被剔除(丢弃)而不呈现。
当你为着色器进行原型设计时,这个基本的Lambert是一个很好的起点,因为你可以在编写着色器的核心功能方面获得很多成就,同时不必担心基本的光照功能。 Unity为我们提供了一个照明模型,它已经完成了为您创建Lambert照明的任务。如果你查看在Data文件夹下的Unity安装目录中找到的UnityCG.cginc文件,你会注意到你可以使用Lambert和BlinnPhong照明模型。在使用#pragma surface surf Lambert编译着色器的那一刻,您告诉着色器在UnityCG.cginc文件中使用Unity的Lambert Lighting功能实现,这样我们就不必一遍又一遍地编写该代码。
以上均基于Unity2018.1.0f,源码可见我的GitHub
https://github.com/xiaoshuivv/ShadersUnity2018.1.0f.git