什么是光照模型?
光照模型就是一个公式,使用这个公式来计算在某个点的光照效果。
标准光照模型
在标准光照模型里,我们把进入摄像机的光分为下面几个部分
- 自发光(Self-luminous)(如萤火虫)
- 高光反射(Specular)
- 漫反射(Diffuse)
- Diffuse = 直射光颜色 * max(0,cosθ(光和法线夹角)) 这个max 后面的0其实就是夹角大于90度的时候当做90度来处理。
- cosθ = 光的向量 · 顶点的法线 (向量的点乘)
向量点乘和叉乘
点乘:(标量)代表a在b上的投影和b的乘积
a · b = |a| |b| cosθ
叉乘: (矢量) a和b为邻边的平行四边形的面积
a × b = |a| |b| sinθ
Tags { "LightMode"="ForwardBase" } 只有定义正确的LightMode才能得到一些Unity的内置光照变量
#include “Lighting.cginc” 包含Unity内置的文件,这样才可以使用unity内置的一些变量
模拟漫反射:
1. 逐顶点光照(在shader顶点函数中使用漫反射公式)
片元函数里传进的color值后在顶点函数后进行差值运算传进来
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
Shader "Custom/MyDiffuseShader-Vert"
{
Properties
{
_Diffuse("Diffuse color",Color) = (1,1,1,1)
}
SubShader
{
pass
{
Tags { "LightMode"="ForwardBase" }
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 第一个直射光的位置
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
fixed3 color:color;
};
v2f vert(a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获取环境光
fixed3 normalDir = normalize(mul(i.normal,(float3x3)unity_WorldToObject)); //这个矩阵把一个方向从世界空间转换到模型空间 这样放在后面就是模型到世界了
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说光的位置就是光的方向,因为光是平行的。
fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir),0) * _Diffuse;//取得漫反射的颜色
o.color = diffuse + ambient;
return o;
}
float4 frag(v2f i):SV_TARGET
{
return fixed4(i.color,1);
}
ENDCG
}
}
}
这里有一点是漫反射时候计算的diffuse是物体的颜色和漫反射光的颜色相乘,而最后的环境光却是相加的
可能环境光的影响更大点把。且可能盖过主体色,漫反射光则是和主体色相互融合。
2. 逐片元光照(片元函数中使用漫反射公式)
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
Shader "Custom/MyDiffuseShader-Frag"
{
Properties
{
_Diffuse("Diffuse color",Color) = (1,1,1,1)
}
SubShader
{
pass
{
Tags { "LightMode"="ForwardBase" }
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 第一个直射光的位置
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
fixed3 worldNormalDir:color;
};
v2f vert(a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertex);
o.worldNormalDir = mul(i.normal,(float3x3)unity_WorldToObject);
return o;
}
float4 frag(v2f i):SV_TARGET
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获取环境光
fixed3 normalDir = normalize(i.worldNormalDir); //这个矩阵把一个方向从世界空间转换到模型空间 这样放在后面就是模型到世界了
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说光的位置就是光的方向,因为光是平行的。
fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir),0) * _Diffuse;//取得漫反射的颜色
fixed3 tempColor = diffuse + ambient;
return fixed4(tempColor,1);
}
ENDCG
}
}
}
- 相比两种方式逐片元的方式表现效果更好
- 因为像素个数比顶点来的要多,逐顶点方式的像素颜色是由顶点颜色进行差值得来的。
- 性能方面肯定是逐顶点的要好的。
对比如下