-
Notifications
You must be signed in to change notification settings - Fork 41
/
ThinFilmInterference.osl
607 lines (492 loc) · 14.4 KB
/
ThinFilmInterference.osl
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
/* OSL shader to calculate thin-film interference
v3: Modified : 03.23.2021 by Saul Espinosa for Redshift 3d GPU Renderer, added metadata, new labels, ACES output.
v1.6, 2016 prutser. License for the *code*: CC BY-NC-SA 4.0
Note: the output of the code may explicitly be used for commercial renders.
v1.6: Allow double layers for more flexibility. Use the #define statements below to make the code only calculate
*/
#include "stdosl.h"
#define SINGLELAYER
#define DOUBLELAYER
#define NONVACUUM
#define EXPOSE_WAVELENGTH
#ifdef DOUBLELAYER
#define SINGLELAYER
#endif
#ifndef NONVACUUM
#define IOR_outside 1
#endif
#ifndef EXPOSE_WAVELENGTH
#define wavelength color(0.65,0.532,0.45)
#endif
struct complex {
color r;
color i;
};
shader thin_film
[[ string help = "Advanced Multi-Layered Thin Film Interference",
string label = "Thin Film Interference" ]]
(
#ifdef SINGLELAYER
float thickness_T = 1
[[
string help = "thickness of layer 1 in micrometers",
string label = "Top Layer Thickness",
string page = "1 : Top Layer",
float min = 0,
float max = 25
]],
float IOR_top = 1.6
[[
string help = "IOR for top layer",
string label = "Top Layer IOR",
string page = "1 : Top Layer",
float min = 0,
float max = 25
]],
color k_top = 0
[[
string help = "Extinction coefficient of top layer",
string label = "Top Layer Extinction",
string page = "1 : Top Layer",
color min = 0,
color max = 1
]],
#endif
#ifdef DOUBLELAYER
float thickness_M = 0
[[
string help = "thickness of layer 2 in micrometers",
string label = "Middle Layer Thickness",
string page = "2 : Middle Layer",
float min = 0,
float max = 25
]],
float IOR_middle = 1
[[
string help = "IOR for middle layer",
string label = "Mid Layer IOR",
string page = "2 : Middle Layer",
float min = 0,
float max = 25
]],
color k_middle = 0
[[
string help = "Extinction coefficient of top layer",
string label = "Mid Layer Extinction",
string page = "2 : Middle Layer",
color min = 0,
color max = 1
]],
#endif
// Substrate Parameters
float IOR_base_material = 1
[[
string help = "IOR for Substrate Base layer",
string label = "Base Layer IOR",
string page = "3 : Base Layer",
float min = 0,
float max = 25
]],
color k_base_material = 0
[[
string help = "Extinction coefficient of substrate layer",
string label = "Base Layer Extinction",
string page = "3 : Base Layer",
color min = 0,
color max = 1
]],
#ifdef EXPOSE_WAVELENGTH
color wavelength = color(0.65,0.532,0.45)
[[
string help = "RGB wavelengths to use for the interference",
string label = "Wavelength",
string page = "4 : Extra",
color min = 0,
color max = 1
]],
#endif
#ifdef NONVACUUM
color IOR_outside = 1
[[
string help = "the index of refraction of the ‘outside world’. Typically 1, but under water would be 1.33, for example",
string label = "IOR of Environment",
string page = "4 : Extra",
color min = 0,
color max = 3
]],
normal Normal = N
[[
string page = "4 : Extra"
]],
int aces = 0
[[ string widget = "checkBox",
string label = "ACES",
string page = "4 : Extra",
int connectable = 0 ]],
#endif
// Outputs
output color outColor = 0
[[
string help = "outColor: Reflected percentage of the light, per color (RGB), at the angle between the normal and the ray of light"
]],
output color outColor_N = 0
[[
string help = "outColor_N: reflected percentage of the light, per color (RGB), at normal incidence",
]],
output color Transmission = 1
[[
string help = "Transmission: transmitted percentage of light, per color. Can be hooked up to a refractive shader"
]],
output color Trans_N = 1
[[
string help = "Trans_N: transmitted percentage of light at normal incidence"
]]
)
{
// ACES sRGB Transform
matrix aces_tm = matrix(
0.6131, 0.0701, 0.0206, 0,
0.3395, 0.9164, 0.1096, 0,
0.0474, 0.0135, 0.8698, 0,
0, 0, 0, 1);
void cmult(complex x, complex y, output complex z)
{
complex tmp; //pass by reference issues, if in reality x == z
tmp.r = x.r*y.r-x.i*y.i;
tmp.i = x.r*y.i + x.i*y.r;
z.r = tmp.r; z.i = tmp.i;
}
void cmult(color x, complex y, output complex z)
{
z.r = x*y.r;
z.i = x*y.i;
}
void cmult(complex x, color y, output complex z)
{
z.r = x.r*y;
z.i = x.i*y;
}
void cdiv(complex x, complex y, output complex z)
{
complex ystar;
ystar.r = y.r;
ystar.i = -y.i;
cmult(x,ystar,z);
z.r = z.r/color((ystar.r*ystar.r+ystar.i*ystar.i));
z.i = z.i/color((ystar.r*ystar.r+ystar.i*ystar.i));
}
void cadd(complex x, complex y, output complex z)
{
z.r = x.r + y.r;
z.i = x.i + y.i;
}
void cadd(complex x, color y, output complex z)
{
z.r = x.r + y;
z.i = x.i;
}
void cadd(color x, complex y, output complex z)
{
z.r = x + y.r;
z.i = y.i;
}
void csub(complex x, complex y, output complex z)
{
z.r = x.r - y.r;
z.i = x.i - y.i;
}
void csub(color x, complex y, output complex z)
{
z.r = x - y.r;
z.i = -y.i;
}
void csub(complex x, color y, output complex z)
{
z.r = x.r - y;
z.i = x.i;
}
void csqrt(complex x, output complex z)
{
color r = sqrt(sqrt(color(x.r*x.r) + color(x.i*x.i)));
color phi = atan2(x.i,x.r)/2;
z.r = r*cos(phi);
z.i = r*sin(phi);
}
void cexp(complex x, output complex z)
{
complex tmp;
tmp.r = exp(x.r)*cos(x.i);
tmp.i = exp(x.r)*sin(x.i);
z.r=tmp.r;z.i=tmp.i;
}
void kz(complex epsilon, color k, color kx, output complex kz_)
{
complex root;
cmult(epsilon,k*k,root); //eps*k^2
csub(root, kx*kx, root); //eps*k^2-kx^2
csqrt(root, kz_);
}
// void kx_k(float cosi, color wavelength, color n, output color kx, output color k)
// {
// k = M_PI*2/wavelength;
// kx = sin(acos(cosi))*k*n;
// }
void rp(complex kzi, complex kzk, complex ei, complex ek, output complex _rp)
{
//return (ek kzi - ei kzk)/(ek kzi + ei kzk);
complex ek_kzi; complex ei_kzk;
cmult(ek, kzi, ek_kzi);
cmult(ei, kzk, ei_kzk);
complex N; complex D;
csub(ek_kzi, ei_kzk, N);
cadd(ek_kzi, ei_kzk, D);
cdiv(N,D,_rp);
}
void rp2tp(complex rp, complex ei, complex ek, output complex _tp)
{
complex tmp;
cdiv(ei, ek, tmp); //tmp = ei/ek
csqrt(tmp,tmp); //tmp = sqrt(ei/ek)
cadd(1,rp,_tp); //tp = 1 + rp
cmult(_tp,tmp,_tp); //tp = (1+rp)*sqrt(ei/ek)
}
void rs(complex kzi, complex kzk, output complex _rs)
{
//return (kzi - kzk)/(kzi + kzk);
complex N; complex D;
csub(kzi, kzk, N);
cadd(kzi, kzk, D);
cdiv(N,D,_rs);
}
#ifdef SINGLELAYER
void FR(complex r01, complex r12, complex kz1, color d, output complex _R, output complex D)
{
// R = (r01+r12*exp(2*1i*kz1*d))/(1+r01*r12*exp(2i*kz1*d))
complex im = {color(0),color(1)};
complex exponent;
cmult(2,im,exponent); //2i;
cmult(exponent, kz1,exponent); //2i*kz1;
cmult(d, exponent, exponent); //2i*kz1*d
cexp(exponent, exponent); //exp(2i*kz1*d)
cmult(r12, exponent, exponent); //r12*exp(2i*kz1*d)
complex Num;
cadd(r01, exponent, Num); // N = r01+r12.*exp(2*1i.*kz1*d)
cmult(r01,exponent, exponent); //exponent = r01*r12.*exp(2*1i.*kz1*d)
cadd(1, exponent,D); // D = 1+r01*r12.*exp(2*1i.*kz1*d)
cdiv(Num,D, _R);
}
void FT(complex t01, complex t12, complex kz1, color d, complex D, output complex _T)
{
complex im = {color(0),color(1)};
complex Num;
cmult(im, kz1,Num); //1i*kz1;
cmult(d, Num, Num); //1i*kz1*d
cexp(Num, Num); //exp(1i*kz1*d)
cmult(t01,Num,Num);
cmult(t12,Num,Num);
cdiv(Num,D,_T);
}
#endif
#if defined(SINGLELAYER) && !defined(DOUBLELAYER)
void RperpRpar(color kx, color k, complex eps_0, complex eps_1, complex eps_2, float thickness_T, output complex Rp, output complex Rs, output complex Tp, output complex Ts, int calcT)
{
complex kz0; complex kz1; complex kz2;
kz(eps_0, k, kx, kz0);
kz(eps_1, k, kx, kz1);
kz(eps_2, k, kx, kz2);
complex r01; complex r12; complex D; // Denominator, reuse for speed
complex t01; complex t12;
rp(kz0,kz1,eps_0,eps_1, r01);
rp(kz1,kz2,eps_1, eps_2, r12);
FR(r01,r12,kz1,thickness_T,Rp, D);
if (calcT)
{
//Transmission = t01*t12*(exp(1i*kz1*thickness_T))/(1+r01*r12*(exp(2i*kz1*thickness_T)))
rp2tp(r01, eps_0,eps_1, t01);
rp2tp(r12, eps_1, eps_2, t12);
FT(t01,t12,kz1,thickness_T,D, Tp);
}
// Now S-polarized light
rs(kz0,kz1, r01);
rs(kz1,kz2, r12);
FR(r01,r12,kz1,thickness_T,Rs, D);
if (calcT)
{
cadd(1,r01,t01);
cadd(1,r12,t12);
FT(t01,t12,kz1,thickness_T,D, Ts);
}
}
#endif
#ifdef DOUBLELAYER
void RperpRpar(color kx, color k, complex eps_0, complex eps_1, complex eps_2, complex eps_3, float thickness_T, float thickness_M, output complex Rp, output complex Rs, output complex Tp, output complex Ts, int calcT)
{
complex kz0; complex kz1; complex kz2; complex kz3;
kz(eps_0, k, kx, kz0);
kz(eps_1, k, kx, kz1);
kz(eps_2, k, kx, kz2);
kz(eps_3, k, kx, kz3);
complex r01; complex r12; complex r23; complex D; // Denominator, reuse for speed
complex t01; complex t12; complex t23;
rp(kz0,kz1,eps_0, eps_1, r01);
rp(kz1,kz2,eps_1, eps_2, r12);
rp(kz2,kz3,eps_2, eps_3, r23);
// calculate last layer first
FR(r12,r23,kz2,thickness_M,Rp, D);
// because we reuse D, do not continue with thin film calculation before calculating partial Tp
if (calcT)
{
//Transmission = t01*t12*(exp(1i*kz1*thickness_T))/(1+r01*r12*(exp(2i*kz1*thickness_T)))
rp2tp(r12, eps_1,eps_2, t12);
rp2tp(r23, eps_2, eps_3, t23);
FT(t12,t23,kz2,thickness_M,D, Tp);
}
FR(r01,Rp,kz1,thickness_T,Rp,D);
if (calcT)
{
//Transmission = t01*t12*(exp(1i*kz1*thickness_T))/(1+r01*r12*(exp(2i*kz1*thickness_T)))
rp2tp(r01, eps_0,eps_1, t01);
FT(t01,Tp,kz1,thickness_T,D, Tp);
}
// Now S-polarized light
rs(kz0,kz1, r01);
rs(kz1,kz2, r12);
rs(kz2,kz3, r23);
FR(r12,r23,kz2,thickness_M,Rs, D);
if (calcT)
{
cadd(1,r12,t12);
cadd(1,r23,t23);
FT(t12,t23,kz2,thickness_M,D, Ts);
}
FR(r01,Rs,kz1,thickness_T,Rs, D);
if (calcT)
{
cadd(1,r01,t01);
FT(t01,Ts,kz1,thickness_T,D, Ts);
}
}
#endif
#if !defined(SINGLELAYER)
void RperpRpar(color kx, color k, complex eps_0, complex eps_2, output complex Rp, output complex Rs, output complex Tp, output complex Ts, int calcT)
{
complex kz0; complex kz2;
kz(eps_0, k, kx, kz0);
kz(eps_2, k, kx, kz2);
rp(kz0,kz2,eps_0,eps_2, Rp);
rs(kz0,kz2, Rs);
if (calcT)
{
rp2tp(Rp,eps_0,eps_2, Tp);
cadd(Rs,1,Ts);
}
}
#endif
int calcT = isconnected(Transmission);
color k = M_PI*2/wavelength; //denominator is wavelength in um
complex Rp; complex Rs; complex Ts; complex Tp;
#ifdef DOUBLELAYER
float local_d1 = thickness_T; float local_d2 = thickness_M;
#endif
complex nsub = {IOR_base_material, k_base_material};
complex eps3; complex eps0;
if (backfacing())
{
cmult(nsub,nsub,eps0);
eps3.r=IOR_outside; eps3.i=0;
}
else
{
cmult(nsub,nsub,eps3);
eps0.r=IOR_outside; eps0.i=0;
}
// the following is nonsense for backfacing metallic substrates
// but so is a ray of light coming out of a metallic substrate
color kx = sin(acos(dot(I,Normal)))*k*sqrt(eps0.r);
#ifdef SINGLELAYER
complex nfilm = {IOR_top, k_top};
complex eps1;
cmult(nfilm,nfilm,eps1);
#endif
#ifdef DOUBLELAYER
nfilm.r = IOR_middle; nfilm.i =k_middle;
complex eps2;
cmult(nfilm,nfilm,eps2);
if (backfacing())
{
complex tmp = eps1;
eps1 = eps2;
eps2 = tmp;
local_d1 = thickness_M; local_d2 = thickness_T;
}
#endif
#ifdef DOUBLELAYER
RperpRpar(kx, k, eps0, eps1, eps2, eps3, local_d1, local_d2, Rp, Rs, Tp, Ts, calcT);
#elif defined(SINGLELAYER)
RperpRpar(kx, k, eps0, eps1, eps3, thickness_T, Rp, Rs, Tp, Ts, calcT);
#else //no layer
RperpRpar(kx, k, eps0, eps3, Rp, Rs, Tp, Ts, calcT);
#endif
color out_film = (Rp.r*Rp.r+Rp.i*Rp.i+Rs.r*Rs.r+Rs.i*Rs.i)*0.5;
float r = out_film[0], g = out_film[1], b = out_film[2];
// ACES Output
if (aces == 0)
outColor = out_film;
else
{
outColor = transform(aces_tm, vector(r,g,b));
}
if(calcT)
{
#ifdef DOUBLELAYER
if (k_top == 0 && k_middle == 0) //no absorption in the film
#endif
#if defined(SINGLELAYER) && !defined(DOUBLELAYER)
if (k_top == 0) //no absorption in the film
#endif
#ifdef SINGLELAYER
Transmission = 1-outColor; // Quick shortcut
else
{ // I'm going to be pragmatic here.
// Strictly speaking, the angle of refraction depends not only on the IOR, but also on the absorbance of the substrate (k).
// But, since this isn't supported in ray tracers anyway, I'm going to let it slide.
// Moreover, this is only really an issue when k ~ 1, and then the light is absorbed within about a wavelength anyway.
Transmission = (Tp.r*Tp.r+Tp.i*Tp.i+Ts.r*Ts.r+Ts.i*Ts.i)*0.5;
Transmission = backfacing()? Transmission/IOR_base_material:Transmission*IOR_base_material;
}
#endif
#ifndef SINGLELAYER
Transmission = 1-outColor;
#endif
}
calcT = isconnected(Trans_N);
if (isconnected(outColor_N) || calcT)
{
#ifdef DOUBLELAYER
RperpRpar(0, k, eps0, eps1, eps2, eps3, thickness_T, thickness_M, Rp, Rs, Tp, Ts, calcT);
#elif defined(SINGLELAYER)
RperpRpar(0, k, eps0, eps1, eps3, thickness_T, Rp, Rs, Tp, Ts, calcT);
#else //no layer
RperpRpar(0, k, eps0, eps3, Rp, Rs, Tp, Ts, calcT);
#endif
color outColor_N = (Rp.r*Rp.r+Rp.i*Rp.i+Rs.r*Rs.r+Rs.i*Rs.i)*0.5;
if (calcT)
{
#ifdef DOUBLELAYER
if (k_top == 0 && k_middle == 0) //no absorption in the film
#elif defined(SINGLELAYER)
if (k_top == 0) //no absorption in the film
#else
color Trans_N = 1-outColor_N;
#endif
#ifdef SINGLELAYER
color Trans_N = 1-outColor_N; // Quick shortcut
else
{
Trans_N = (Tp.r*Tp.r+Tp.i*Tp.i+Ts.r*Ts.r+Ts.i*Ts.i)*0.5;
Trans_N = backfacing()? Trans_N/IOR_base_material:Trans_N*IOR_base_material;
}
#endif
}
}
}