Unity shader学习笔记(四)高光反射
发表于2018-04-28
但是我们会发现物体的背光面是个全黑的颜色,现实生活中物体的背光面并不是全黑的,而是可以看到物体的大概形状,并不是全黑的,之前使用的计算方式是兰伯特光照模型。要实现此种现象,那么就需要使用半兰伯特光照模型来实现



半兰伯特光照模型
Diffuse = 直射光颜色 *(cosθ*0.5+0.5)
兰伯特光照模型
Diffuse = 直射光颜色 * max(0,cosθ) θ是指光照方向与法线方向的夹角
半兰伯特光效果如图:

高光反射的计算 (Blinn光照模型)
Specular = 直射光*pow(max(cosθ,0),高光的参数)
对于上述公式的解释:
θ是指反射光方向和反射点到相机方向的夹角
pow的次方的意思 就是cosθ的高光参数次方
max(cosθ,0)作用是防止在物体背光面,如果高光参数为偶数,那么最后的值会从负数变为正数
使用的示例代码如下:
Properties{
_Diffuse("Diffuse Color",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,200)) = 10
}
SubShader{
Pass{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#include "Lighting.cginc"
//unity系统定义好的类 可以使用一些内置的变量来简化计算操作
//_LightColor0 该变量表示第一个直射光的颜色
//_WorldSpaceLightPos0 该变量表示第一个直射光的位置(世界坐标下)
//UNITY_LIGHTMODEL_AMBIENT 该变量表示系统的环境光
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
half _Gloss;
struct a2v {
float4 vertex : POSITION; //系统自动将顶点坐标传入vertex
float3 normal : NORMAL; //系统自动将法线传入 变量normal
};
struct v2f {
float4 position:SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v x) {
v2f f;
//UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间
f.position = mul(UNITY_MATRIX_MVP, x.vertex);
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 normalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject));
//暂时没有考虑alpha属性
fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的颜色
//计算高光
//计算反射光夹角
fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz);
fixed3 specular = _LightColor0.rgb*pow(max(dot(reflectDir, viewDir), 0), _Gloss);
f.color = diffuse +ambient+ specular;
return f;
}
fixed4 frag(v2f f) :SV_TARGET{
//将计算得到的颜色值输出到视野中的物体上
return fixed4(f.color, 1);
}
ENDCG
}
}
代码里面有一个reflect方法,这个方法是用来计算入射光的反射光与法线的夹角。
函数reflect中第一个参数是入射光向量,第二个参数是法线向量,计算的时候是通过入射光向量与法线之间的夹角计算得出反射光向量与法线的夹角,因此传入的时候需要将入射光进行翻转,否则入射光与法线的夹角是(180-(入射光向量与法线向量夹角))°。然后再通过宏定义的变量得到顶点到视野的夹角。最后只要使用公式进行计算即可。
高光效果如图:

高光的区域范围是由高光反射系数来决定的,定义一个参数用于控制高光范围
_Gloss("Gloss",Range(8,200)) = 10
并在公式中进行使用,最后可以进行自由的控制高光范围的大小。
Blinn-Phone光照模型 :Blinn光照模型的改进
计算公式修改为:
Specular = 直射光*pow(max(cosθ,0),高光的参数)
θ这里是指法线和x的夹角 x是光的方向和摄像机视角方向的平分线(中间线)
如图

那么最后只要对vert里面代码做出如下修改即可。
v2f vert(a2v x) {
v2f f;
//UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间
f.position = mul(UNITY_MATRIX_MVP, x.vertex);
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 normalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject));
//暂时没有考虑alpha属性
fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的颜色
//计算高光
//计算反射光夹角
//fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz);
//平分线
fixed3 halfDir = normalize(viewDir + lightDir);
//控制高光颜色
fixed3 specular = _Specular * _LightColor0.rgb*pow(max(dot(normalDir, halfDir), 0), _Gloss);
f.color = diffuse +ambient+ specular;
return f;
}
fixed4 frag(v2f f) :SV_TARGET{
//将计算得到的颜色值输出到视野中的物体上
return fixed4(f.color, 1);
}
ENDCG
}
这个B-P模型比之前的Blinn光照模型进行了优化,之前我们会在背光面查看到一个高光点,但是在B-P中则不会看到
UnityCG.cginc中一些常用的函数和宏定义变量
_LightColor0 该变量表示第一个直射光的颜色
_WorldSpaceLightPos0 该变量表示第一个直射光的位置(世界坐标下)
UNITY_LIGHTMODEL_AMBIENT 该变量表示系统的环境光
_World2Object 这个矩阵用来把一个方向从世界空间转换到模型空间
UNITY_LIGHTMODEL_AMBIENT用来获取环境光
//摄像机方向(视角方向)
float3 WorldSpaceViewDir(float4 v) 根据模型空间中的顶点坐标 得到 (世界空间)从这个点到摄像机的观察方向
float3 UnityWorldSpaceViewDir(float4 v) 世界空间中的顶点坐标==》世界空间从这个点到摄像机的观察方向
float3 ObjSpaceViewDir(float4 v) 模型空间中的顶点坐标==》模型空间从这个点到摄像机的观察方向
//光源方向
float3 WorldSpaceLightDir(float4 v) 模型空间中的顶点坐标==》世界空间中从这个点到光源的方向
float3 UnityWorldSpaceLightDir(float4 v) 世界空间中的顶点坐标==》世界空间中从这个点到光源的方向
float3 ObjSpaceLightDir(float4 v) 模型空间中的顶点坐标==》模型空间中从这个点到光源的方向
//方向转换
float3 UnityObjectToWorldNormal(float3 norm) 把法线方向 模型空间==》世界空间
float3 UnityObjectToWorldDir(float3 dir) 把方向 模型空间=》世界空间
float3 UnityWorldToObjectDir(float3 dir) 把方向 世界空间=》模型空间
使用内置的函数方法可以使得代码的书写更加简单
需要将程序中的代码
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
替换为
fixed3 lightDir = normalize(WorldSpaceLightDir(mul(x.vertex, unity_WorldToObject)).xyz);
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz);
替换为
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(mul(x.vertex, (float3x3)unity_WorldToObject).xyz));
法线的计算直接替换为
//直接使用方法从模型空间转换为世界空间
fixed3 normalDir = normalize(UnityObjectToWorldNormal(x.normal));
替换修改后代码如下:
v2f vert(a2v x) {
v2f f;
//UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间
f.position = mul(UNITY_MATRIX_MVP, x.vertex);
//某一点的光源
//fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 lightDir = normalize(WorldSpaceLightDir(mul(x.vertex, unity_WorldToObject)).xyz);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
//直接使用方法从模型空间转换为世界空间
fixed3 normalDir = normalize(UnityObjectToWorldNormal(x.normal));
//暂时没有考虑alpha属性
fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的颜色
//计算高光
//计算反射光夹角
//fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(mul(x.vertex, (float3x3)unity_WorldToObject).xyz));
//平分线
fixed3 halfDir = normalize(viewDir + lightDir);
//控制高光颜色
fixed3 specular = _Specular * _LightColor0.rgb*pow(max(dot(normalDir, halfDir), 0), _Gloss);
f.color = diffuse +ambient+ specular;
return f;
}
来自:https://blog.csdn.net/u013106366/article/details/53328527
