光照模型的公式:
Cdiffuse = (Clight * Mdiffuse)max(0, n * I)
漫反射 = 环境光 * 材质的漫反射系数 * 表面法线与光源方向的点积。[0, 1]
1、逐顶点光照模型的实践。
Shader "Custom/DiffuseVertex"
{
Properties
{
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
//由于颜色属性的范围在 0到1 之间,所以这里用 fixed 类型就可以了
fixed4 _Diffuse; //漫反射公式中的参数之一的材质漫反射属性。
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f//【输出结构体同时也是片元着色器(frag)的输入结构体】
{
float4 pos : SV_POSITION;
float3 color : COLOR; //这里也可以使用 TEXCOORD0 语义
};
v2f vert(a2v v)
{
v2f o;
//Transform the vertex from object space to projection space
//顶点着色器最基本的任务就是把顶点位置从模型空间转换到裁剪空间中。
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//Get ambient term
//通过内置变量UNITY_LIGHTMODEL_AMBIENT获得环境光。
//【1】
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//【2】
//Transform the normal fram object space to world space
//在计算法线和光源方向之间的点积时,只有两者处于同一坐标系空间下的计算才有意义。
//在这里,选择了世界坐标空间。
//由a2v得到的顶点法线是位于模型空间下的,因此我们首先需要把法线转换到世界空中。
//可以使用顶点变换矩阵的逆转置矩阵对法线进行相同的变换
//因此我们首先得到模型空间到世界空间的变换矩阵的逆矩阵_World2Object
//【这是从当前vert输入的顶点信息中获取模型空间到世界空间的变换矩阵的逆矩阵】
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));
//【3】
//Get the light direction in world space
//通过_WorldSpaceLightPoas0来获得光源的方向。
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
//【4】
//compute diffuse term
//通过_LightColor0获得访问该Pass处理的光源的颜色和强度信息。
//(需要结合正确的LinghtMode)
//在得到了世界空间中的法线和光源方向后,对它们进行归一化操作。
//得到点积结果后,为了防止结果为负值,为此使用了 saturate 函数,把值限定在[0,1]之间。
//最后再与光源颜色和强度以及材质的漫反射颜色相乘即可得到最终的漫反射光照部分。
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
//最后,对环境光和漫反射光部分相加,得到最终的光照结果。
o.color = ambient + diffuse;
return o;
}
fixed4 frag(v2f v) : SV_Target
{
return fixed4(v.color, 1);
}
ENDCG
}
}
}
Cdiffuse = (Clight * Mdiffuse)max(0, n * I)
在这个例子中是:
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
1、通过 UNITY_LIGHTMODEL_AMBIENT 获得环境光。
2、
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));
把点的法线与_World2Object逆世界矩阵相乘再归一化得到 世界的法线。
3.
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
归一化当前场景的光源。
4.
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
光照公式:
_LightColor0 代表是的是环境光,然后是材质光,世界法线与光源的点积限定[0, 1]
2、逐片元基础光照实现:
Shader "Custom/DiffuseFragment"
{
Properties
{
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
return o;
}
fixed4 frag(v2f i) :SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal, worldLight));
fixed3 color = diffuse + ambient;
return fixed4(color, 1);
}
ENDCG
}
}
}
通过两种实现的方法进行对比,可以知道:
o.worldNormal 是对输入的顶点进行计算后得到 当前模型的顶点在世界空间坐标系下的
法线。
然后再进行光照公式的计算。
3、Lambert Diffuse 公式如果:
【其实就是对结果进行了 α的缩放然后再加上 β 的插值】
半兰伯特光照计算
Shader "Custom/DiffuseHalfLambert"
{
Properties
{
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
float3 _Diffuse;
//先不管变量名,这里需要的数据是顶点和法线
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
//这里需要输出的也是顶点和法线。
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
//先对顶点进行坐标空间转换
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//把模型空间下的法线转换到世界空间下的法线。
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//获取环境光
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//获取世界光源
float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
//进行半兰伯特光照计算。
float3 lambert = dot(worldLight, i.worldNormal) * 0.5 + 0.2; //原值是0.5
//基础光照公式。
float3 diffuse = _LightColor0.rgb * _Diffuse.rgb * lambert;
float3 color = ambient + diffuse;
return fixed4(color, 1);
}
ENDCG
}
}
}