Unity3D Shader详解(2)

上篇大致介绍了一下Shader的基本脚本属性和原理,本篇将会简单介绍一下,除了纹理外,另一个对Shader呈现效果至关重要的因素……灯光。​
上篇我们使用的是自带的标准全阴影正向渲染和ShaderModel3.0​Target,本篇我们将亲手搭建一个灯光算法,就像上篇的Shader纹理算法一样。​
正题
上篇使用的是自带的预设,本篇我们来从无到有写一个新的Shader。
创建一个空Shader,或者创建个预设把代码删除。​​
Shader命名随个人喜好~​
Properties里定义几个属性:​
_EmissiveColor("Emissive Color", Color ) = (1,1,1,1)

_AmbientColor("Ambient Color",Color) = (1,1,1,1)

_MySliderValue("Pow Value",Range(0,10)) = 2.5

SubShader内,依然是先定义:
Tags { "RenderType"="Opaque" }

LOD 200

我们上篇自带的CG编译命令是#pragma surface surf Standard fullforwardshadows

pragma target 3.0

。为了不妨碍我们自定义的灯光效果,我们将这两句命令删除。
在**CGPROGRAM
**下面加上:

pragma surface surf BasicDiffuse​​​

获取Properties属性:​

float4 _EmissiveColor; ​

f
loat4 _AmbientColor; ​

float _MySliderValue;

struct Input { ​

float2 uv_MainTex;

};

计算表面:​

void surf(Input IN, inout SurfaceOutput o){ ​

float4 c;

c = pow((_EmissiveColor + _AmbientColor), _MySliderValue);
pow为乘方,该句意思为float4 c的值为
自发光+环境色之和的_MySliderValue乘方,我们可以通过自定义的滑竿值
_MySliderValue来调整整体颜色强度

o.Albedo = c.rgb;

o.Alpha = c.a;

}

灯光计算:


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;​

}​

后面和Unity自带预设一样,加上​
ENDCG
以及
FallBack "Diffuse"
结尾,Shader结束。​

正常不报错的话,回到Unity里使用该Shader,你会发现该Shader呈现效果和默认的Lambert是一样的。但是,我们却能够在Shader中随意修改它的灯光属性!​​

在上篇的#pargma surface指令中,我们告诉了Shader使用内置的
Standard fullforwardshadows​
,而本篇则改为了我们后面自己写的BasicDiffuse来计算,当然,照明函数的名字和照明类型依然可以自定义。Lighting后面的Name可以替换为你想要的名字,类型有如下三种:​

half4 LightingName(SurfaceOutput s,half3 lightDir, half atten){}​

没有视角方向传入,使用正向渲染。​

half4 LightingName(SurfaceOutput s,half3 lightDir,half3 viewDir,half atten){}​

有视角方向传入,使用正向渲染。​

half4 LightingName_PrePass(SurfaceOutput s,half4 light){}​

使用延迟渲染

那么,照明函数的工作原理是什么样子的呢?就拿本篇的照明函数举例,函数中有一个CG函数dot,或者称它为“点积”。点积函数返回的是一个-1至1的浮点值,以两个方向的夹角为判断条件。如果是同向且平行那么则返回1,不同向平行则返回-1,如果互相垂直则返回0,因此我们可以通过点积来做灯光计算。把光源到我们需要渲染的UV点的直线作为一个光照方向,被照射的UV点的法线(Normal)则是另一个方向,如果法线和光线为垂直,那么这个UV点则在明暗交界线上,而法线越朝向光源,点积返回的值则会越趋近与1(根据光的方向计算方式也可以是-1),而达到1的时候,也就是该UV点正面朝向光源的时候,也即使照明最强,完全接受光源颜色等信息的时候。因此得到如下表达式:
float
difLight = max(
0
,dot(s.
Normal
,lightDir));

为了完成照明,我们还需要加上物体本身的表面信息,如颜色之类。所以我们需要把surf中的
o.Albedo作为s.Albedo传入,并用我们计算的灯光叠加上去。因为照明的返回值为0(无灯光)至1(完全被照亮),因此灯光的叠加方法并不是加法,而是乘法。然后在叠加上灯光衰减方法
(difLight * atten *
2
)
,因此得到如下表达式:

col.
rgb
= s.Albedo *_LightColor0.rgb * (difLight * atten *
2
);​​

通过两行表达式,一个简单的灯光算法就完成了。
然而,到这一步,你会发现,我们所做的灯光看起来非常的奇怪,亮的地方有渐变过程,但是阴影部分却是死黑的,真实世界里的物体大多都受到漫反射和环境的影响,从而使其阴影部分变得柔和。如果使用物理灯光来计算,那么随着灯光数量和物体数量增多,性能消耗则会成倍攀升。因此我们可以使用一种非物理灯光来模拟真实世界的漫反射计算。我们将会使用到的是HalfLambert照明技术,这个技术相关资料可以查看

(https://developer.valvesoftware.com/wiki/Half-Life)。下面我们来在Unity中实现HalfLambert照明。​
inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir,fixed atten){​

        float difLight = max(0,dot(s.Normal,lightDir));​

        float hLambert =

difLight * 0.5 +0.5;
float4 col;​

        col.rgb = s.Albedo *_LightColor0.rgb * (

hLambert * atten * 2);

        col.a = s.Alpha;​

        return col;​

}​

我们通过dot获得灯光与法线的点积,一个-1至1的返回值区间,通过max来限定最小返回值为0之后,再乘以0.5,返回值则变为了一个0至0.5的区间,在这基础上加上0.5则变为了一个0.5至1的区间。因此,正面朝向光源依然是1,而不受光源影响的背面则变为了0.5。从而达到了以非物理计算的方法来模拟受到漫反射影响的阴影部分效果。区间对比图如下:

Unity3D Shader详解(2)_第1张图片

你可能感兴趣的:(Unity3D Shader详解(2))