概述
反射、折射,在实现水面、玻璃等效果的时候,都多多少少会用到一些。本文主要简单介绍一些实现反射和折射的知识。使用天空盒来代表环境的信息。
一、简单的反射
反射,表现为物体反射它周围的环境。根据观察者的视角,反射会影响到物体本身的颜色表现。镜子就是一个反射性物体,它会根据观察者的视角反射它周围的环境。
在中学的物理课中,应该就学习过入射角、反射角等概念。反射的原理并不难,只是根据视角方向、法线方向,计算出反射方向,用来采样立方体贴图(此处是天空盒)。下图简单描述了这个过程:
已知视角方向viewDir、法线方向normal,现在要求的是反射方向reflectDir。这里,有一个内置函数reflect可以得到反射方向。注意上图,视角方向viewDir是从相机指向物体的。但是很多时候,我们计算得到的视角方向都是从物体指向相机的,这时候,使用reflect函数的话,需要对视角方向取反。
获得了反射方向后,就可以使用反射方向对立方体贴图采样,得到反射的环境信息。这一步没什么特别要介绍的,只是使用内置的SAMPLE_TEXTURECUBE函数采样立方体贴图。最后会得到如下的效果:
二、光照计算与反射
上面得到了简单的反射效果,最后物体表现的完全是环境的信息。现在,我们想让体现一部分物体本身的信息。
主要的思路大概是:先计算环境光照、物体的漫反射颜色,得到物体的本身的颜色;然后计算反射环境的颜色。在二者之间做一个插值,这样,既可以体现物体本身的颜色,又可以体现反射的环境颜色。
三、简单的折射
折射,与反射的原理比较相似。折射是光线由于传播介质的改变而产生的方向变化。在常见的类水表面上所产生的现象就是折射,光线不是直直地传播,而是弯曲了一点。比如将筷子插进水里面,看起来水下面的筷子好像弯了。
当给定入射方向时,我们可以根据斯涅耳定律(Snell’s Law)来计算折射角,当光从介质1沿着和表面法线夹角为
θ
1
\theta_1
θ1的方向斜射入介质2时,我们可以使用如下公式计算折射光线与法线的夹角
θ
2
\theta_2
θ2:
η
1
s
i
n
θ
1
=
η
2
s
i
n
θ
2
\eta_1 sin\theta_1 = \eta_2 sin\theta_2
η1sinθ1=η2sinθ2
其中
η
1
\eta_1
η1和
η
2
\eta_2
η2分别是两种介质的折射率。下图简单描述了这种关系:
方便的是,Unity内置的函数refract可以帮助我们计算得到折射的方向。
有了折射方向后,就可以像计算反射一样计算折射了。
四、菲涅尔反射
在上面第二部分,只是简单地在物体本身颜色和环境的反射颜色之间进行插值。但其实为了得到更好的效果,我们一般会使用菲涅尔反射(Fresnel reflection)来根据视角方向控制反射的程度。
我们一般使用菲涅尔等式来计算菲涅尔反射,菲涅尔等式一般是近似公式,其中一个著名的近似公式就是Schlick菲涅尔近似等式:
F
s
c
h
l
i
c
k
(
v
,
n
)
=
F
0
+
(
1
−
F
0
)
(
1
−
v
⋅
n
)
5
F_{schlick}(v,n)=F_0+(1-F_0)(1-v\cdot n)^5
Fschlick(v,n)=F0+(1−F0)(1−v⋅n)5
其中,
F
0
F_0
F0是一个反射系数,用来控制菲涅尔反射的强度;
v
v
v是视角方向;
n
n
n是法线方向。
根据计算的数值,就可以在物体本身的颜色和反射的环境颜色之间进行插值了。
五、总结
本篇文章,没有什么比较复杂的知识,只是简单介绍了反射、折射、菲涅尔方程等内容,用一个简单的过程来对相应的效果产生一个比较直观的印象。
参考
- [1] 立方体贴图
- [2] 《Unity Shader入门精要》第10章