Unity Shader学习笔记(15)立方体纹理、反射、折射、菲涅尔反射
发表于2017-12-29
对立方体纹理、反射、折射、菲涅尔反射这些概念不了解,也不知道这些是干什么用的,现在不需要担心了,本篇文章会一一给大家做出解答。
立方体纹理(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 对比(通过公式可以看出两者值是一样的):
Unity Shader学习笔记系列教程: