之前我们探讨过一些基于Surface Shader的高光知识,现在我们深入来了解其中的一种光照类型:Phong光照。
我们知道,高光其实是描述了一种较为理想的情况下,大部分光线都沿着与入射角相等的反射方向照射,也就是说,当我们的眼睛正好在反射方向上时,可以看到大部分光线,当眼睛偏离这个方向时,亮度就会急剧下降,Phong光照就是用来描述这种情况的。Phong光照的光照方程如下:
Ispecular = Iincomingkspecularmax(0, R·V)nshininess
其中,Iincoming表示入射光颜色,kspecular表示高光反射率,或者说材料的颜色,因为颜色的产生就是通过反射,R和V分别表示反射光方向和眼睛(view)的方向,通过这两个点乘,就可以得到和重合程度成正比的一个值,再将这个值通过nshininess乘方之后就可以体现出“急剧”这个特点,所以这个nshininess越大,“光点”就会越小。
下一步就是如何获得方程所需要的参数。其中Iincoming可以通过Unity为我们传入,kspecular和nshininess通过Shader接口获得,V我们讨论Lambert的时候介绍过,至于R,可以用R = 2N(N·L) - L获得(L是光照方向),推导过程也比较简单,唯一需要注意的是这几个方向都是单位向量,不要认为它们是有长度的。我们也可以不用这种方法,Cg中提供了float3 reflect(float3 I, float3 N)这个方法可以计算反射方向,不过这里的参数I是从光源指向物体表面的,与我们通常说的光照方向正好相反。
如果只有高光的话,由于强度是急剧下降的,模型的大部分会变黑。为了防止这种情况,我们可以使用环境光来提高一下亮度。环境光用来表示光线经过物体间复杂的相互反射造成原来没有光直射的地方也被照亮的情况,比如白天的时候室内的大部分地方我们都可以看得清的,虽然没有光直接照射它们。环境光需要在Edit->Render Settings里面设置,Shader中使用直接用UNITY_LIGHTMODEL_AMBIENT表示环境光的颜色,也只有颜色这一个信息可以用。此外,我们还可以加入上篇说到的漫反射光照来提高亮度。都加上之后的代码如下:
Shader "Custom/Specular" {
Properties {
_Ambient("Ambient Color", Color) = (1, 1, 1, 1)
_Lambert("Lambert Color", Color) = (1, 1, 1, 1)
_Specular("Specular Color", Color) = (1, 1, 1, 1)
_Shininess("Mat Shininess", Range(0.1, 20)) = 1
}
SubShader {
Pass
{
Tags{"LightMode" = "ForwardBase"} //处理“第一个像素光”
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" //包含大多数内置变量,由于某些未知原因_LightColor0被除外
uniform float4 _LightColor0;
uniform float4 _Ambient;
uniform float4 _Lambert;
uniform float4 _Specular;
uniform float _Shininess;
struct v2f
{
float4 vertPos : SV_POSITION;
float4 vertColor : COLOR;
};
v2f vert(appdata_full IN)
{
v2f o;
float3 normalDir = normalize(mul(float4(IN.normal, 0.0), _World2Object).xyz); //法线方向从模型坐标到世界坐标
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //ForwardBase处理的一定是方向光所以_WorldSpaceLightPos0直接表示方向
float3 viewDir = normalize(float3(_WorldSpaceCameraPos - mul(_Object2World, IN.vertex).xyz));
float3 reflectDir;
if(dot(normalDir, lightDir) < 0.0)
{
reflectDir = float3(0.0, 0.0, 0.0);
}
else
{
reflectDir = reflect(-lightDir, normalDir);
}
float3 ambientColor = UNITY_LIGHTMODEL_AMBIENT.rgb * _Ambient.rgb;
float3 lambertColor = _LightColor0.rgb * _Lambert.rgb * max(0, dot(normalDir, lightDir));
o.vertColor = float4(ambientColor + lambertColor + _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(reflectDir, viewDir)), _Shininess), 1.0); //光照方程
o.vertPos = mul(UNITY_MATRIX_MVP, IN.vertex);
return o;
}
float4 frag(v2f v) : COLOR
{
return v.vertColor;
}
ENDCG
}
Pass
{
Tags{"LightMode" = "ForwardAdd"} //其他的pixel light
Blend One One //颜色叠加
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4 _LightColor0;
uniform float4 _Ambient;
uniform float4 _Lambert;
uniform float4 _Specular;
uniform float _Shininess;
struct v2f
{
float4 vertPos : SV_POSITION;
float4 vertColor : COLOR;
};
v2f vert(appdata_full IN)
{
v2f o;
float3 normalDir = normalize(mul(float4(IN.normal, 0.0), _World2Object).xyz);
float3 lightDir;
float atten; //表示颜色衰减
if(_WorldSpaceLightPos0.w == 0.0) //处理方向光
{
atten = 1.0; //方向光无衰减
lightDir = normalize(_WorldSpaceLightPos0.xyz);
}
else //处理点光
{
float3 vertex2Light = _WorldSpaceLightPos0.xyz - mul(_Object2World, IN.vertex).xyz; //顶点到点光的向量
atten = 1.0 / length(vertex2Light); //点光线性衰减,当然如果你愿意也可以以其他形式衰减
lightDir = normalize(vertex2Light);
}
float3 viewDir = normalize(float3(_WorldSpaceCameraPos - mul(_Object2World, IN.vertex).xyz));
float3 reflectDir;
if(dot(normalDir, lightDir) < 0.0)
{
reflectDir = float3(0.0, 0.0, 0.0);
}
else
{
reflectDir = reflect(-lightDir, normalDir);
}
float3 ambientColor = UNITY_LIGHTMODEL_AMBIENT.rgb * _Ambient.rgb;
float3 lambertColor = _LightColor0.rgb * _Lambert.rgb * max(0, dot(normalDir, lightDir)) * atten;
o.vertColor = float4(ambientColor + lambertColor + _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(reflectDir, viewDir)), _Shininess), 1.0); //方程加入衰减
o.vertPos = mul(UNITY_MATRIX_MVP, IN.vertex);
return o;
}
float4 frag(v2f v) : COLOR
{
return v.vertColor;
}
ENDCG
}
}
FallBack "Diffuse"
}
效果如下:
三种光分别用不同颜色展示了。
最近很累就什么也不想说了,就这样了。