先来完成上节课的Lambert+Phone
思路:给Lambert上一个Tint,然后加上Phone(用视角方向点乘光的反射方向)
Properties段,这两个参数后面要在VertiexInput前定义一个一样的
Properties {
_MainCol ("颜色",color) = (1.0,1.0,1.0,1.0)
_SpecularPow ("高光次幂",range(1,90)) = 30
}
输入结构
// 输入结构
struct VertexInput {
float4 vertex : POSITION; //顶点信息 Get
float3 normal : NORMAL; //法线信息 Get
};
输出结构
// 输出结构
struct VertexOutput {
float4 posCS : SV_POSITION; //裁剪空间(暂时理解为屏幕空间)顶点位置
float4 posWS : TEXCOORD0; //世界空间顶点位置
float3 nDirWS : TEXCOORD1; //世界空间法线方向
};
顶点Shader
// 输出结构 >>> 顶点Shader >>> 输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; //新建输出结构
o.posCS = UnityObjectToClipPos( v.vertex ); //变换顶点位置 OS > CS
o.posWS = mul(unity_ObjectToWorld, v.vertex); //变换顶点位置 OS > WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); //变换法线方向 OS > WS
return o;
}
像素Shader
// 输出结构 >>> 像素
float4 frag(VertexOutput i) : COLOR {
// 准备向量
float3 nDir = i.nDirWS;
float3 lDir = _WorldSpaceLightPos0.xyz;
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
// float3 hDir = normalize(vDir+lDir);
float3 rDir = reflect(-lDir,nDir);
// float3 vDir = (_WorldSpaceCameraPos.xyz - i.posWS.xyz);
// 准备点积结果
float nDotI = dot(nDir,lDir); //点乘法线方向和光方向实现Lambert
float vDotr = dot(vDir,rDir); //点乘法线方向和半角方向
// 光照模型
float lambert = max(0.0,nDotI); //对点积结果进行截断处理
float Phone = pow(max(0.0,vDotr),_SpecularPow);
float3 finalRGB = _MainCol*lambert + Phone;
// 返回结果
return float4(finalRGB,1.0);
了解一下BRDF双向反射分布函数
官方解释:双向反射分布函数(bidirectional reflectance distribution function、BRDF)是一个定义光线在 不透明表反射的四次元函数,基本式为: ,在这里 是指光线的入射方向,另外 是指光线反射的方向, 除此之外,还有一个 代表法线,这个值的意义是在 方向的反射光线的辐射率和同一点上从 方向射入 的光线的辐射率的比值。每一个 方向可以被参数化 为方位角 和天顶角 因此BRDF是一个四维函数。 BRDF的单位是 sr, 其中 (sr) 是球面度的单位。
通俗解释:想象你有一个不透明的桌面,一个激光反射器。你先让激光向下垂直地射在那个桌面上,这样你就可以在桌面上看到一个亮点,接着你从各个不同方向来观察那个亮点,你会法线亮点的亮度随着观察方向的不同二发生了改变。然后你站着不动,改变激光反射方向和桌面的夹角,你又会发现亮点的亮度发生了改变。这就是说,一个表面对不同的光线入射角的组合,拥有不同的反射率。BRDF就是来对这种反射性质进行定义的。