Unity Shader 学习笔记(15) 立方体纹理、反射、折射、菲涅尔反射
参考书籍:《Unity Shader 入门精要》
立方体纹理(Cubemap)
是环境映射(Environment Mapping)的一种实现方法。用于模拟物体周围的环境,使用了环境映射的物体可以让物体像镜子一样反射周围环境。
采样方法:用一个三维纹理坐标表示方向,原点是立方体中心。交点就是采样的结果。
天空盒子(Skybox)
用于模拟室内背景。
创建环境映射的立方体纹理方法有三种:
- 由特殊布局的纹理创建。提供如立方体展开图的交叉布局、全景布局等。
- 创建一个Cubemap,把6张纹理拖拽到面板中。
- 由脚本生成,可以从任意位置观察场景的图像存储到6张图像中。调用Camera.RenderToCubemap(cubemap)生成,创建cubemap要标记Readable才能修改。
反射(Reflection)
用环境映射纹理代替了高光反射。
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_ReflectColor ("Reflection Color", Color) = (1, 1, 1, 1) // 反射颜色
_ReflectAmount ("Reflect Amount", Range(0, 1)) = 1 // 反射程度
_Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {} // 环境纹理
}
struct v2f {
...
fixed3 worldViewDir : TEXCOORD2;
fixed3 worldRefl : TEXCOORD3;
SHADOW_COORDS(4)
};
v2f vert(a2v v) {
v2f o;
...
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); //计算视角方向
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal); // 计算反射方向
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
...
// 对立方体采样,CG的texCUBE函数。
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
// 对漫反射和立方体做插值,作为漫反射和高光反射的颜色。
fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount) * atten;
return fixed4(color, 1.0);
}
折射(Refraction)
斯涅耳定律(Snell’s Law),n为折射率:
- n1sinθ1 = n2sinθ2
在实际中,需要计算两次折射,第一次是光线进入内部,第二次是内部射出。但要在实时渲染时模拟第二次折射比较复杂,所以通常只模拟第一次折射。
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_RefractColor ("Refraction Color", Color) = (1, 1, 1, 1) // 折射颜色
_RefractAmount ("Refraction Amount", Range(0, 1)) = 1 // 折射程度
_RefractRatio ("Refraction Ratio", Range(0.1, 1)) = 0.5 // 折射比例
_Cubemap ("Refraction Cubemap", Cube) = "_Skybox" {} // 模拟折射环境纹理
}
v2f vert(a2v v) {
v2f o;
...
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
// 计算折射方向,第一个参数为入射方向,第二个参数是表面法线,第三个参数是入射折射率和出射比值。
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
...
// 对立方体采样
fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
// 混合漫反射颜色和折射颜色
fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;
return fixed4(color, 1.0);
}
菲涅尔反射(Fresnel reflection)
即光线照射物体时,一部分发生反射,另一部分进入物体发生折射或散射。
近处湖面可以直接看到湖底(折射),远处湖面就只能看到镜面反射了。
Schilick菲涅尔近似等式(F0为反射系数,v为视角方向,n为表面法线):
Empricial菲涅尔近似等式:
使用Schilick菲涅尔近似等式实现:
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_FresnelScale ("Fresnel Scale", Range(0, 1)) = 0.5 // 对应F0
_Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}
}
v2f vert(a2v v) {
v2f o;
...
// 同反射计算
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
...
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb;
// Schlick菲涅尔近似等式
fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);
fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;
return fixed4(color, 1.0);
}
普通反射和菲涅尔反射系数都为 0 对比:
普通反射和菲涅尔反射系数都为 0.2 对比:
普通反射和菲涅尔反射系数都为 1 对比(通过公式可以看出两者值是一样的):