反射与折射

只考虑平行光,且物体表面光滑。

​ 高光反射:光射到物体表面直接被反射出来被成为高光反射,既图中红色所画出的光线。

​ 漫反射:光射向物体表面,折射在物体内部的光经过不断地折射,再次射出物体,此被称为漫反射,既图中地蓝色所画出的光线。

BRDF光照模型:用于模拟真实地光物理。

SSS材质:用于描述光物理,属于BRDF中次表面反射其中地一部分。

标准光照模型

​ 该光照为直接光照,由光直接照射在物体表面,在反射到摄像机上,其中包含自发光,高光反射,漫反射,环境光(用于描述间接光照)。

间接光照

​ 间接光照:例如在一个红色的方块和一个蓝色的小球,当光照射到方块上经反射后照射在蓝色小球上呈现红色,在被照射到的地方,月接近方块,颜色越红,此被称为间接光照。

自发光

​ 自发光:由物体直接发光射向摄像机(自发光就等于材质颜色,本身不对周围物体造成影响,由unity中引入了全局光照之后,可以对此中影响进行模拟)。

漫反射

​ 当光照垂直于物体表面射入时没有漫反射,漫反射的强度由光的入射角度决定,颜色由光的颜色和被照射物体的材质影响。强度可以使用入射角的cos去计算。cosθ可以使用向量的叉乘公式去计算。

向量n和向量l为单位向量。

顶点漫反射

代码简单实现,此处未计算环境光(逐顶点计算)。

环境光获取:

1
2
3
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

o.color = diffuse + ambient;

片元漫反射

代码简单实现:

二者之间的区别:

​ 在片元着色器中的处理更加细节,二者呈现的漫反射远看没有什么区别,近看可以法线,在顶点着色器中处理的物体表面的阴影边缘可以明显看到顶点,在片元中就没有这种情况。

半兰伯特漫反射

​ 概念:将计算公式中的点乘的结果乘上0.5加上0.5,将其范围固定到0~1之间,可以解决,前两者的阴影地方过黑的问题。

高光反射

Phong模型

计算照射到摄像机中光(紫色的向量代表)的强度和颜色。

当θ = 0度时,为最大值 1。反射的到摄像机内

当θ = 90度时,为最小值 0. 此时没有反射光进入到摄像机内

其中向量r,向量l和向量n都为单位向量。

所以最后公式为:

Blinn-Phong模型

当θ=0度时,法线向量n等于向量h。

当θ=90度时,法线向量n垂直于向量h。

Phong顶点高光反射

1
2
3
4
//高光反射
fixed3 reflectDir = normalize(reflect(-worldLight, worldNom));//计算反射方向向量
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - UnityObjectToWorldDir(v.vertex));
fixed3 specular = _LightColor0.rgb * _Specular * pow(max(0, dot(reflectDir, viewDir)), _Gloss);

效果图:

Phong片元高光反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = worldNormal;
o.worldPos = UnityObjectToWorldDir(v.vertex);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldLightDir, normalize(i.worldNormal)));

//高光反射
fixed3 reflectDir = normalize(reflect(-worldLightDir, i.worldNormal));
fixed3 viewDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

fixed3 color = ambient + diffuse + specular;
return float4(color, 1);
}

效果图:

BlinnPhong高光反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldLightDir, normalize(i.worldNormal)));

//高光反射
//fixed3 reflectDir = normalize(reflect(-worldLightDir, i.worldNormal));
fixed3 viewDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(i.worldNormal, halfDir)), _Gloss);

fixed3 color = ambient + diffuse + specular;
return float4(color, 1);
}

该模型是将公式中reflectDir与viewDir的点积,更换成了法线和半角向量的点积。效果图如下:

unity库函数

​ 在写shader过程中尽可能去使用unity的一个库函数,因为库函数中会进行一些判断,去避免一些不必要的麻烦。

视角坐标的获取,在获取后进行normalize,有些库函数中并不进行normalize。

1
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(UnityObjectToWorldDir(v.vertex)));

世界光的获取

1
fixed3 worldLight = UnityWorldSpaceLightDir(UnityObjectToWorldDir(v.vertex));