光线被均匀的反射到不同方向上去
兰伯特定律:在平面某点漫反射光的光强与该反射点的法向量和入射光角度的余弦值成正比。
入射光的强度会随着距离衰减
入射光强和传播距离的平方成反比,越远光强越小
漫反射=(入射光的颜色和强度 点乘 材质的漫反射系数(Kd,点会吸收多少能量,和颜色相关,比如黑吸收所有能量))×max(0,表面法线 点乘 光源方向)
结果:
兰伯特模型=逐顶点漫反射光照模型(UnityShader是加了环境光的)
Shader "TurBoQShader/Chapter6/TDVL(逐顶点光照漫反射)"
{
Properties{
// 为了得到材质的!漫反射颜色,声明color类型的属性,初值设置为白色
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
}
SubShader{
Pass{
//指定该Pass的光照模式,LightMode属于Pass标签
Tags{
"LightMode"="ForwardBase"}
CGPROGRAM//包裹CG代码片
//顶点着色器和片元着色器的名字
#pragma vertex vert
#pragma fragment frag
//为了使用Unity内置的变量,如_LightColor0,引入内置文件
#include "Lighting.cginc"
//为了使用Properties内的属性,定义fixed4,得到兰伯特定律所需的属性之一,漫反射属性
fixed4 _Diffuse;
//顶点着色器的输入结构体a2v,顶点着色器的输出结构体和片元着色器的输入结构体v2f
struct a2v{
float4 vertex:POSITION;
float3 normal :NORMAL;//访问顶点的法线,NORMAL语义告诉unity把模型顶点的法线信息存到normal中
};
struct v2f{
float4 pos:SV_POSITION;
fixed3 color: COLOR;//得到顶点着色器中计算得到的颜色
};
v2f vert(a2v v)//顶点着色器代码,逐顶点的漫反射计算写在里面
{
v2f o;//返回值,顶点着色器基本任务是把顶点位置从模型空间转换到裁剪空间,因此使用Unity内置的模型*世界*投影矩阵UNITY_MATRIX_MVP完成坐标转换
o.pos=UnityObjectToClipPos(v.vertex);//新版本o.pos=mul(UNITY_MATRIX_MVP,v.vertex)为这行代码
//!漫反射颜色和顶点法线已经知道,还需要知道光源的颜色和强度信息,光源方向由_WorldSpaceLightPos0获得(一个平行光适用,多了不能得到正确的值)
//兰伯特定律中的法线和光源方向点积需要在同一坐标空间下,这里选择世界坐标空间,a2v得到的顶点法线是模型空间下的,所以模型空间到世界空间变换的逆矩阵_World2Object
//mul函数得到世界空间
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;//UNITY_LIGHTMODEL_AMBIENT得到环境光
fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));//normalize 归一化向量,mul将法线从模型空间转化为世界空间
fixed3 worldLigth=normalize(_WorldSpaceLightPos0.xyz);//normalize 归一化向量
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLigth));//防止点积结果为负,saturate截取参数到【0,1】
//入射光的颜色和强度 由_LightColor0得到
o.color=ambient+diffuse;//环境光加漫反射的光
return o;
}
fixed4 frag(v2f i):SV_Target{
return fixed4(i.color,1.0);
}
ENDCG
}
}
FallBack"Diffuse"
}
但是罗伯特模型光线照不到的地方会是黑色,所以《半条命》公司提出半罗伯特光照模型
漫反射=(入射光的颜色和强度 点乘 材质的漫反射系数)×(0.5*归一化(表面法线 点乘 光源方向)+0.5)
之前是0,现在是有明暗变化的
高光反射和观测方向有关系,高光方向接近镜面反射的方向,所以观察方向接近反射方向的时候,能看到高光反射
观察方向和入射方向的二分之一角(半程向量)和法线越接近,高光反射越明显
夹角余弦不是非常准确,容忍度太高(即45度左右还有很多高光,是错误的),所以p来做改变,Blinn-Phong大约是100多
漫反射加高光反射
高光反射=(入射光的颜色和强度 点乘 材质的高光反射系数)×max(0,视角方向 点乘 反射方向)
反射方向=入射光线方向-2(法线 点乘 入射光线方向)法线
Cg提供的计算反射方向的函数reflect(入射光线方向,法线方向)
Phong:
Shader "TurBoQShader/Chapter6/SVL(逐顶点光照高光反射(也是Phong模型))"
{
Properties{
_Diffuse("Diffuse",Color)=(1,1,1,1)
_Specular("Specular",Color)=(1,1,1,1)//高光反射的颜色
_Gloss("Gloss",Range(8.0,256))=20//高光区域的大小
}
SubShader{
Pass{
Tags{
"LightMode"="ForwardBase"}//指定该Pass的光照模式,LightMode属于Pass标签
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
//为了使用Properties内的属性,定义相同类型的变量
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v{
float4 vertex :POSITION;
float3 normal:NORMAL;
};
struct v2f{
float4 pos:SV_POSITION;
fixed3 color :COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));//normalize截取前三行三列表面法线
fixed3 worldLigthDir=normalize(_WorldSpaceLightPos0.xyz);//normalize截取前三行三列光源方向
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLigthDir));//漫反射=入射光颜色强度*材质的漫反射系数*dot(光源方向,表面法线)
//漫反射与周之前相同,先计算入射光线关于表面法线的反射方向reflectDir,然后计算出世界空间下的视角方向viewDir,specular计算得高光反射的部分
fixed3 reflectDir=normalize(reflect(-worldLigthDir,worldNormal));
fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-mul(unity_ObjectToWorld,v.vertex).xyz);
fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir,viewDir)),_Gloss);//高光反射=入射光颜色强度*材质的高光反射系数*dot(视角方向,
//反射方向(由表面法线和光源方向求得))
o.color=ambient+diffuse+specular;//环境光加漫反射光加高光反射光
return o;
}
fixed4 frag(v2f i):SV_Target{
return fixed4(i.color,1.0);
}
ENDCG
}
}
FallBack "Specular"
}
高光反射=(入射光的颜色和强度 点乘 材质的高光反射系数)×max(0,法线 点乘 视角和入射的中心向量)
硬件中,摄像机和光源距离模型足够远的话,Blinn快于Phong,因为(视角方向和光源方向也就是H固定),但是不固定时,phong快,都是经验模型