【unity shader】高级光照 -- fresnel方程和肥皂泡

test
接上面的一篇文章:薄膜干涉。上篇博客最后讲到利用fresnel equation实现薄膜干涉。其实fresnel equation同时也提供了光的折射信息,因为能量是守恒的,有多少光被反射,就有多少剩下的被折射了。
本文中将试着利用fresnel equation做一个肥皂泡,实现光的薄膜干涉色散效果

fresnel方程

当一束光L射向介质的界面时候,反射光R,折射光T的大小都可以用fresnel方程来计算。关于公式的描述,可以参考wiki和这篇blog.
【unity shader】高级光照 -- fresnel方程和肥皂泡_第1张图片

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) ;

最后可以在加上一些噪声和动画,就形成了下面的效果

你可能感兴趣的:(unity-shader)