test
接上面的一篇文章:薄膜干涉。上篇博客最后讲到利用fresnel equation实现薄膜干涉。其实fresnel equation同时也提供了光的折射信息,因为能量是守恒的,有多少光被反射,就有多少剩下的被折射了。
本文中将试着利用fresnel equation做一个肥皂泡,实现光的薄膜干涉和色散效果。
当一束光L射向介质的界面时候,反射光R,折射光T的大小都可以用fresnel方程来计算。关于公式的描述,可以参考wiki和这篇blog.
fresnel方程的代码如下
/* Amplitude reflection coefficient (s-polarized) */
float rs(float n1, float n2, float cosI, float cosT) {
return (n1 * cosI - n2 * cosT) / (n1 * cosI + n2 * cosT);
}
/* Amplitude reflection coefficient (p-polarized) */
float rp(float n1, float n2, float cosI, float cosT) {
return (n2 * cosI - n1 * cosT) / (n1 * cosT + n2 * cosI);
}
/* Amplitude transmission coefficient (s-polarized) */
float ts(float n1, float n2, float cosI, float cosT) {
return 2 * n1 * cosI / (n1 * cosI + n2 * cosT);
}
/* Amplitude transmission coefficient (p-polarized) */
float tp(float n1, float n2, float cosI, float cosT) {
return 2 * n1 * cosI / (n1 * cosT + n2 * cosI);
}
float thinFilmTrans(float cos0, float lambda, float thickness, float n0, float n1, float n2) {
float PI = 3.1415926535897932384626433832795;
// compute the phase change term (constant)
float d10 = (n1 > n0) ? 0 : PI;
float d12 = (n1 > n2) ? 0 : PI;
float delta = d10 + d12;
// now, compute cos1, the cosine of the reflected angle
float sin1 = pow(n0 / n1, 2) * (1 - pow(cos0, 2));
if (sin1 > 1) return 1.0; // total internal reflection
float cos1 = sqrt(1 - sin1);
// compute cos2, the cosine of the final transmitted angle, i.e. cos(theta_2)
// we need this angle for the Fresnel terms at the bottom interface
float sin2 = pow(n0 / n2, 2) * (1 - pow(cos0, 2));
if (sin2 > 1) return 1.0; // total internal reflection
float cos2 = sqrt(1 - sin2);
// get the reflection transmission amplitude Fresnel coefficients
float alpha_s = rs(n1, n0, cos1, cos0) * rs(n1, n2, cos1, cos2); // rho_10 * rho_12 (s-polarized)
float alpha_p = rp(n1, n0, cos1, cos0) * rp(n1, n2, cos1, cos2); // rho_10 * rho_12 (p-polarized)
float beta_s = ts(n0, n1, cos0, cos1) * ts(n1, n2, cos1, cos2); // tau_01 * tau_12 (s-polarized)
float beta_p = tp(n0, n1, cos0, cos1) * tp(n1, n2, cos1, cos2); // tau_01 * tau_12 (p-polarized)
// compute the phase term (phi)
float phi = (2 * PI / lambda) * (2 * n1 * thickness * cos1) + delta;
// finally, evaluate the transmitted intensity for the two possible polarizations
float ts = pow(beta_s, 2) / (pow(alpha_s, 2) - 2 * alpha_s * cos(phi) + 1);
float tp = pow(beta_p, 2) / (pow(alpha_p, 2) - 2 * alpha_p * cos(phi) + 1);
// we need to take into account conservation of energy for transmission
float beamRatio = (n2 * cos2) / (n0 * cos0);
// calculate the average transmitted intensity (if you know the polarization distribution of your
// light source, you should specify it here. if you don't, a 50%/50% average is generally used)
float t = beamRatio * (ts + tp) / 2;
// and finally, derive the reflected intensity
return t;
}
函数thinFilmTrans的返回值就是特定波长的光线折射到薄膜内部的能量,相应的1-thinFilmTrans就是光的反射量。以上全部是物理定律。
在frag程序中,我们利用fresnel方程计算红绿蓝光的折射率,红绿蓝光的波长分别设定为650, 510, 470, 单位是nm。
fixed trans_red = thinFilmTrans(ndv, wavelength.r, filmthickness, externalIOR, thinfilmIOR-0.05, internalIOR);
fixed trans_green = thinFilmTrans(ndv, wavelength.g, filmthickness, externalIOR, thinfilmIOR, internalIOR);
fixed trans_blue = thinFilmTrans(ndv, wavelength.b, filmthickness, externalIOR, thinfilmIOR+0.05, internalIOR);
fixed4 fresnel_factor = fixed4(trans_red, trans_green, trans_blue, 1.0);
光的反射角永远等于入射角,红绿蓝光都一样。但折射角会和光的波长有关,这就形成了“色散”。对此,我们需要对红绿蓝光分别计算折射的方向。其中红光的折射率偏小,而蓝光的折射率会偏大一些。
fixed3 refractDir_r = refract(normalize(-worldViewDir), normalize(worldNormal), 1/(internalIOR-0.02));
fixed3 refractDir_g = refract(normalize(-worldViewDir), normalize(worldNormal), 1 / (internalIOR ));
fixed3 refractDir_b = refract(normalize(-worldViewDir), normalize(worldNormal), 1 / (internalIOR + 0.02));
现在我们在cubemap上分别对折射光线和反射光线采样,然后在用fresnel系数对它们进行混合,就得到了透明肥皂泡。
//reflection
fixed4 reflectCol = texCUBE(_Cubemap, reflectDir);
//refraction
fixed refract_r = texCUBE(_Cubemap, refractDir_r).r;
fixed refract_g = texCUBE(_Cubemap, refractDir_g).g;
fixed refract_b = texCUBE(_Cubemap, refractDir_b).b;
fixed4 refractCol = fixed4(refract_r, refract_g, refract_b, 1.0);
//mixing
fixed4 col = lerp( reflectCol , refractCol , fresnel_factor) ;
最后可以在加上一些噪声和动画,就形成了下面的效果