# 薄膜干涉

# 光程(optical path difference)

先说一下前置知识

光程就是是指在均匀介质中,行径的几何路径的长度 s 与光在该介质中的折射率 n 的乘积,也可以光程可认为是在相等时间内光在真空中的路程。

当一个灯源的两条光到达我们眼睛之前,会经过不同地方反射,那么他们走过的路程不同,到达我们眼睛的时候就会产生不同的干扰,那么我们就要求出他的光程差

Untitled

Untitled

在入射时,一道光多走了绿色的部分,反射时,另一道光多走了红色的部分。因此,两道光的路程差就是红、绿两部分的差值。

用三角函数表示就是

dsinθLdsinθV=nwdsin{\theta_L}-dsin{\theta_V} = n·w

sinθLsinθV=nwdsin{\theta_L}-sin{\theta_V} = \frac{n·w}d

# 泡沫薄膜干涉

首先来分析泡沫的薄膜是怎么干涉光的传播的

Untitled

可以看到光在 A 处入射,AD 表示反射,AB 表示折射,BC 是 AB 的反射,到 C 处又产生折射和反射。如果是车漆,则最下层的 “其他介质” 是金属,如果是泡泡,其他介质就还是空气了。

那么这个光程就是

OPD=n2(AB+BC)n1AD=n22dcosθROPD = n_2(\overline{AB}+\overline{BC})-n_1\overline{AD} = {n_2}2dcos\theta_R

其中 n2 是折射率,空气是 1,水是 1.33,d 就是薄膜厚度

# 光波

在实时渲染的时候,为了简化光照模型,会把物体反弹到我们眼睛的光简单相加,但现实并不是这样,最终的颜色取决于它们的光波如何相互作用。

Untitled

当两个波为互相重叠的时候,波峰相同,那么这束到我们眼睛的光就会放大,但互相抵消的话,就会收不到任何光,就像两个水波,中间部分为互相抵消就不产生波纹

Untitled

紫色的 w 是 400nm,红色的波是 700nm

Untitled

用函数拟合的话

""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Based on GPU Gems
// Optimised by Alan Zucconi
inline fixed3 bump3y (fixed3 x, fixed3 yoffset)
{
float3 y = 1 - x * x;
y = saturate(y-yoffset);
return y;
}

fixed3 spectral_zucconi6 (float w)
{
// w: [400, 700]
// x: [0, 1]
fixed x = saturate((w - 400.0)/ 300.0);
const float3 c1 = float3(3.54585104, 2.93225262, 2.41593945);
const float3 x1 = float3(0.69549072, 0.49228336, 0.27699880);
const float3 y1 = float3(0.02312639, 0.15225084, 0.52607955);
const float3 c2 = float3(3.90307140, 3.21182957, 3.96587128);
const float3 x2 = float3(0.11748627, 0.86755042, 0.66077860);
const float3 y2 = float3(0.84897130, 0.88445281, 0.73949448);
return
bump3y(c1 * (x - x1), y1) +
bump3y(c2 * (x - x2), y2) ;
}

还有其他函数

最后代码是

""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// --- Diffraction grating effect ---
float3 L = gi.light.dir;
float3 V = viewDir;
float3 N = worldNormal;

// Reminder:
// thetaL = angle from L to N
// thetaR = angle from reflected L inside material to N
// From Snell's Law:
// N1 * sin(thetaL) = N2 * sin(thetaR)
float cos_thetaL = dot(N, L);
float thetaL = acos(cos_thetaL);

float sin_thetaR = (_N1 / _N2) * sin(thetaL);
float thetaR = asin(sin_thetaR);
float u = _N2 * 2 * d * abs(cos(thetaR));//光程差

fixed3 color = 0;
for (int n = 1; n <= _Order; n++)
{
// Constructive interference
float wavelength = u / n;
color += spectral_zucconi6(wavelength);
}
color = saturate(color);

当然还有引擎里还有简单做法,直接用 matcap

""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
half NdotV =  dot(normal_dir, view_dir * _Tile);
// 蓝色
half3 Mask01 = saturate(acos(NdotV)*2/UNITY_PI * half3(0,0,1));
// 蓝色和白色叉乘得到绿色,这里是为了拿到绿色通道 --- 可以手动计算下看结果,有saturate所以-1会修正
// --- 绿色和白色叉乘得到红色
// --- 红色和白色叉乘得到蓝色
half3 CrossColor = cross (half3(0,0,1) , half3(1,1,1));
// 绿色和红色
half3 Mask02 = saturate (asin(NdotV)*2/UNITY_PI * CrossColor);
// 得到红,绿,蓝三色通道
half3 AddMask = Mask01 + Mask02;
// 转灰度
half Rgb2Gray = saturate(0.2989 * AddMask.r + 0.587 * AddMask.g + 0.114 * AddMask.b);

half4 col = tex2D(_BubbleTex, half2(NdotV,NdotL));
half4 basecolor = tex2D(_MainTex,i.uv);
outcolor = lerp(col ,basecolor ,Rgb2Gray );

更新于

请我喝[茶]~( ̄▽ ̄)~*

Natsuneko 微信支付

微信支付

Natsuneko 支付宝

支付宝

Natsuneko 贝宝

贝宝