简单的逐像素光照shader,这个只用于练习的。
Shader "Custom/05-frament Vertex"
{
Properties
{
_Diffuse("DiffuseColor",Color)=(1,1,1,1)
}
SubShader
{
pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#include "Lighting.cginc" //引入light 的库
#include "UnityCG.cginc"
#pragma fragment frag
#pragma vertex vert
float4 _Diffuse; //使用属性 需要在这里再声明
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float3 NormalDir:COLOR;
float4 position:SV_POSITION;
};
v2f vert(a2v v)
{
v2f f;
f.position=UnityObjectToClipPos(v.vertex)
f.NormalDir= UnityObjectToWorldNormal(v.normal);
return f;
}
//逐像素 光照
float4 frag(v2f f):SV_TARGET
{
float3 LightDir=normalize(_WorldSpaceLightPos0.xyz);
float3 NormalDir=normalize(f.NormalDir);
float3 Ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
float3 diffuse=_LightColor0.rgb*max(0,dot(NormalDir,LightDir))*_Diffuse.rgb;
float3 newColor=Ambient+diffuse;//环境光+漫反射
return float4(newColor,1);
}
ENDCG
}
}
Fallback "VertexLit"
}
UnityObjectToClipPos(v.vertex) 这个是 把模型顶点坐标转换到裁剪空间里
UnityObjectToWorldNormal(float3 normal) 把模型的法线信息转换到世界坐标系里去。-
漫反射光照计算公式(兰伯特光照模型):
diffuse=_LightColor0.rgb*Color.rgb*saturate(dot(normal,LightDir));
其中 _LightColor0是 内置光照变量,默认是场景中的方向光照,物体法线,物体法线就是垂直于物体的线。
Color是自己定义的属性,saturate 相当于max(0,dot(N,L));
关于点积
点积:a.b=|a|*|b|*cosθ ; 由于方向向量 模 都为1,因此 dot(a,b)=1*1*cosθ。它的几何意义是 b在a方向上的 投影。
当夹角>90° 时,就是背光,cos>90°都是小于0的(90°~270°),0在颜色里就是黑色,代表没有任何的RGB值。
缺点就是 >90的地方 都是黑的看不到模式的细节。改善可以使用半兰伯特模型,公式是 dot(N,L)*0.5 +0.5 N:法线方向,L:光线方向;
float3 LightDir=normalize(_WorldSpaceLightPos0.xyz);
float3 NormalDir=normalize(f.NormalDir);
float3 Ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
float3 diffuse;
diffuse=_LightColor0.rgb*max(0,dot(NormalDir,LightDir))*_Diffuse.rgb;
在Vert中对每个顶点着色,叫逐顶点,
放在frag中对每个像素着色,叫逐像素,计算更加密集,越消耗性能,得到的质量越好。
当定点法线 位于迎光面的时候,它的法线跟光的夹角是最小的时候,取得的值最大。
关于 光线衰弱以及 阴影的处理
可以使用 #pragma multi_compile_fwdbase 以及 Tags{"LightMode"="ForwardBase"}
这样 可以 使用Unity内置的一些 宏, 告诉Unity,帮我们准备好下面要使用的一些变量信息;
只有这样 ,才可以使用一些内置宏。
在v2f 结构体 最后 一行, 添加SHADOW_COORDS(x) 这个x是最后一个通道的编号,这样 阴影信息可以存储在这个通道里。
在顶点作色器里 使用 TRANSFER_SHADOW(o); 使用上面的通道,转换模型阴影信息。
在片元着色器中 使用 UNITY_LIGHT_ATTENUATION(atten, i, worldPos); 可以得到 atten,它是光照衰弱系数,有了它 就可以得到 衰弱后的光照了,
fixed4(ambient + (diffuse + specular) * atten, 1.0);
高光的计算
物体的高光,
物体高光是我们视角看到的最亮的部分,
它的计算当然和视角有关,另一个和它有关的就是它的法线信息;还有一个就是光照的方向;
它的公式就是 Cspecular=max(0,dot(NormalDir,HalfDir))*SpecularColor;
这里的 HalfDir= normalize(ViewDir+LightDir); normalize是 归一化,要得到任何单位方向,都需要把它们归一化。