文章目录
1.前言
所谓的光照效果,反映到屏幕上就是一个个像素问题,所以光照的计算公式就是计算的一个一个颜色值。这些公式基本属于经验公式范畴,只是让人看起来像真正的光效。
光分为环境光、自发光、漫反射以及高光反射,反映到像素上就是颜色的叠加。即最终在片元着色器中返回的颜色值=环境光颜色+自发光颜色+漫反射颜色+高光反射颜色。下面依次对这些光进行说明。
2.环境光Ambient
环境光在Unity中可以设置,即windows-lighting中即可看到,在2018中则是window-rendering-lightingsettings中设置。在shader中通过如下变量可以获取到:UNITY_LIGHTMODEL_AMBIENT。但是一般计算中我们只需要颜色的rgb值。
3.自发光Emissive
自发光是最简单的,直接简单粗暴的暴露一个颜色值,包此值当作自发光颜色。
4.漫反射diffuse
漫反射计算参考一下光照模型进行计算。
4.1 兰伯特定律LambertLaw
兰伯特定律认为反射光线跟光源方向与法线夹角有关系(夹角余弦值),所以漫反射计算公式如下:
漫反射颜色 = 光源颜色 x 材质的漫反射颜色 x Max(0,Dot(法线,指向光源的方向)
其中:
光源颜色:变量_LightColor0代表光源颜色。
材质漫反射颜色:自定义的颜色值。
法线:可以获取到每个顶点的法线,计算式采用单位向量。
光源方向:指从当前位置到光源的方向,计算式采用单位向量。
由于实际中当法线与光源方向夹角大于90°时,已经不会有反射效果,此时两者余弦值为负值,所以此时应该取0。
4.1.1 逐顶点计算
逐顶点计算漫反射时,在顶点着色器中计算,此时计算量小,但是效果差。在计算世界坐标系下的法线时,采用如下所示:
//获取法线(统一到世界坐标系下),不做归一化效果要优于归一化。
//fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
上述注释掉的方法也是UnityObjectToWorldNormal方法,当然UnityCG种方法比注释掉的方法复杂一点。完整代码如下:
Shader "LL/Light/DiffuseVertex_Lambert"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1,1,1,1)
}
SubShader
{
Tags {
"LightMode"="ForwardBase" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
float3 color : COLOR;
};
fixed4 _Diffuse;
v2f vert (a2v v)
{
v2f o;
o.position = UnityObjectToClipPos(v.vertex);
//获取环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//获取法线(统一到世界坐标系下)。
//fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
//fixed3 worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
//fixed3 worldNormal = mul((float3x3)unity_ObjectToWorld, v.normal);
//获取环境光方向
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
//计算漫反射强度
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
//总强度叠加效果
o.color = ambient + diffuse;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.color,1);
}
ENDCG
}
}
Fallback "Diffuse"
}
注:在计算光源方向时,应该是光源方向应该是光源位置减顶点位置,但是在真正计算时考虑不同情况,计算方式不同,UnityCG中也存在计算方法,直接用光源位置是一种情况,本文是示例,所以只采用最简单的方法。
4.1.2 逐片元计算
逐片元计算方法与顶点计算方法一致,只是在片元着色器中进行颜色计算,在顶点着色器中将法线传递过来即可,如下所示:
Shader "LL/Light/DiffuseFragment_Lambert"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1,1,1,1)
}
SubShader
{
Tags {
"LightMode"="ForwardBase" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"