forked from google/filament
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Filament.md.html
4315 lines (2978 loc) · 242 KB
/
Filament.md.html
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
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<meta charset="utf-8">
<style>img { max-width: 100%; }</style>
**Physically Based Rendering in Filament**
![](images/filament_logo.png)
# About
This document is part of the [Filament project](https://github.com/google/filament). To report errors in this document please use the [project's issue tracker](https://github.com/google/filament/issues).
## Authors
- [Romain Guy](https://github.com/romainguy), [@romainguy](https://twitter.com/romainguy)
- [Mathias Agopian](https://github.com/pixelflinger), [@darthmoosious](https://twitter.com/darthmoosious)
# Overview
Filament is a physically based rendering (PBR) engine for Android. The goal of Filament is to offer a set of tools and APIs for Android developers that will enable them to create high quality 2D and 3D rendering with ease.
The goal of this document is to explain the equations and theory behind the material and lighting models used in Filament. This document is intended as a reference for contributors to Filament or developers interested in the inner workings of the engine. We will provide code snippets as needed to make the relationship between theory and practice as clear as possible.
This document is not intended as a design document. It focuses solely on algorithms and its content could be used to implement PBR in any engine. However, this document explains why we chose specific algorithms/models over others.
Unless noted otherwise, all the 3D renderings present in this document have been generated in-engine (prototype or production). Many of these 3D renderings were captured during the early stages of development of Filament and do not reflect the final quality.
## Principles
Real-time rendering is an active area of research and there is a large number of equations, algorithms and implementation to choose from for every single feature that needs to be implemented (the book *Rendering real-time shadows*, for instance, is a 400 pages summary of dozens of shadows rendering techniques). As such, we must first define our goals (or principles, to follow Brent Burley's seminal paper Physically-based shading at Disney [#Burley12]) before we can make informed decisions.
Real-time mobile performance
: Our primary goal is to design and implement a rendering system able to perform efficiently on mobile platforms. The primary target will be OpenGL ES 3.x class GPUs.
Quality
: Our rendering system will emphasize overall picture quality. We will however accept quality compromises to support low and medium performance GPUs.
Ease of use
: Artists need to be able to iterate often and quickly on their assets and our rendering system must allow them to do so intuitively. We must therefore provide parameters that are easy to understand (for instance, no specular power).
We also understand that not all developers have the luxury to work with artists. The physically based approach of our system will allow developers to craft visually plausible materials without the need to understand the theory behind our implementation.
For both artists and developers, our system will rely on as few parameters as possible to reduce trial and error and allow users to quickly master the material model.
In addition, any combination of parameter values should lead to physically plausible results. Physically implausible materials must be hard to create.
Familiarity
: Our system should use physical units everywhere possible: distances in meters or centimeters, color temperatures in Kelvin, light units in lumens or candelas, etc.
Flexibility
: A physically based approach must not preclude non-realistic rendering. User interfaces for instance will need unlit materials.
Deployment size
: While not directly related to the content of this document, it bears emphasizing our desire to keep the rendering library as small as possible so any application can bundle it without increasing the binary to undesirable sizes.
## Physically based rendering
We chose to adopt PBR for its benefits from an artistic and production efficient standpoints, and because it is compatible with our goals.
Physically based rendering is a rendering method that provides a more accurate representation of materials and how they interact with light when compared to traditional real-time models. The separation of materials and lighting at the core of the PBR method makes it easier to create realistic assets that look accurate in all lighting conditions.
# Notation
$$
\newcommand{NoL}{n \cdot l}
\newcommand{NoV}{n \cdot v}
\newcommand{NoH}{n \cdot h}
\newcommand{VoH}{v \cdot h}
\newcommand{LoH}{l \cdot h}
\newcommand{fNormal}{f_{0}}
\newcommand{fDiffuse}{f_d}
\newcommand{fSpecular}{f_r}
\newcommand{fX}{f_x}
\newcommand{aa}{\alpha^2}
\newcommand{fGrazing}{f_{90}}
\newcommand{schlick}{F_{Schlick}}
\newcommand{nior}{n_{ior}}
\newcommand{Ed}{E_d}
\newcommand{Lt}{L_{\bot}}
\newcommand{Lout}{L_{out}}
\newcommand{cosTheta}{\left< \cos \theta \right> }
$$
The equations found throughout this document use the symbols described in table [symbols].
Symbol | Definition
:---------------------------:|:---------------------------|
$v$ | View unit vector
$l$ | Incident light unit vector
$n$ | Surface normal unit vector
$h$ | Half unit vector between $l$ and $v$
$f$ | BRDF
$\fDiffuse$ | Diffuse component of a BRDF
$\fSpecular$ | Specular component of a BRDF
$\alpha$ | Roughness, remapped from using input `perceptualRoughness`
$\sigma$ | Diffuse reflectance
$\Omega$ | Spherical domain
$\fNormal$ | Reflectance at normal incidence
$\fGrazing$ | Reflectance at grazing angle
$\chi^+(a)$ | Heaviside function (1 if $a > 0$ and 0 otherwise)
$n_{ior}$ | Index of refraction (IOR) of an interface
$\left< \NoL \right>$ | Dot product clamped to [0..1]
$\left< a \right>$ | Saturated value (clamped to [0..1])
[Table [symbols]: Symbols definitions]
# Material system
The sections below describe multiple material models to simplify the description of various surface features such as anisotropy or the clear coat layer. In practice however some of these models are condensed into a single one. For instance, the standard model, the clear coat model and the anisotropic model can be combined to form a single, more flexible and powerful model. Please refer to the [Materials documentation](./Materials.md.html) to get a description of the material models as implemented in Filament.
## Standard model
The goal of our model is to represent standard material appearances. A material model is described mathematically by a BSDF (Bidirectional Scattering Distribution Function), which is itself composed of two other functions: the BRDF (Bidirectional Reflectance Distribution Function) and the BTDF (Bidirectional Transmittance Function).
Since we aim to model commonly encountered surfaces, our standard material model will focus on the BRDF and ignore the BTDF, or approximate it greatly. Our standard model will therefore only be able to correctly mimic reflective, isotropic, dielectric or conductive surfaces with short mean free paths.
The BRDF describes the surface response of a standard material as a function made of two terms:
- A diffuse component, or $f_d$
- A specular component, or $f_r$
The relationship between a surface, the surface normal, incident light and these terms is shown in figure [frFd] (we ignore subsurface scattering for now):
![Figure [frFd]: Interaction of the light with a surface using BRDF model with a diffuse term $ f_d $ and a specular term $ f_r $](images/diagram_fr_fd.png)
The complete surface response can be expressed as such:
$$\begin{equation}\label{brdf}
f(v,l)=f_d(v,l)+f_r(v,l)
\end{equation}$$
This equation characterizes the surface response for incident light from a single direction. The full rendering equation would require to integrate $l$ over the entire hemisphere.
Commonly encountered surfaces are usually not made of a flat interface so we need a model that can characterize the interaction of light with an irregular interface.
A microfacet BRDF is a good physically plausible BRDF for that purpose. Such BRDF states that surfaces are not smooth at a micro level, but made of a large number of randomly aligned planar surface fragments, called microfacets. Figure [microfacetVsFlat] shows the difference between a flat interface and an irregular interface at a micro level:
![Figure [microfacetVsFlat]: Irregular interface as modeled by a microfacet model (left) and flat interface (right)](images/diagram_microfacet.png)
Only the microfacets whose normal is oriented halfway between the light direction and the view direction will reflect visible light, as shown in figure [microfacets].
![Figure [microfacets]: Microfacets](images/diagram_macrosurface.png)
However, not all microfacets with a properly oriented normal will contribute reflected light as the BRDF takes into account masking and shadowing. This is illustrated in figure [microfacetShadowing].
![Figure [microfacetShadowing]: Masking and shadowing of microfacets](images/diagram_shadowing_masking.png)
A microfacet BRDF is heavily influenced by a _roughness_ parameter which describes how smooth (low roughness) or how rough (high roughness) a surface is at a micro level. The smoother the surface, the more facets are aligned and the more pronounced the reflected light is. The rougher the surface, the fewer facets are oriented towards the camera and incoming light is scattered away from the camera after reflection, giving a blurry aspect to the specular highlights.
Figure [roughness] shows surfaces of different roughness and how light interacts with them.
![Figure [roughness]: Varying roughness (from left to right, rough to smooth) and the resulting BRDF specular component lobe](images/diagram_roughness.png)
!!! Note: About roughness
The roughness parameter as set by the user is called `perceptualRoughness` in the shader snippets throughout this document. The variable called `roughness` is the `perceptualRoughness` with a remapping explained in section [Parameterization].
A microfacet model is described by the following equation (where x stands for the specular or diffuse component):
$$\begin{equation}
\fX(v,l) = \frac{1}{| \NoV | | \NoL |}
\int_\Omega D(m,\alpha) G(v,l,m) f_m(v,l,m) (v \cdot m) (l \cdot m) dm
\end{equation}$$
The term $D$ models the distribution of the microfacets (this term is also referred to as the NDF or Normal Distribution Function). This term plays a primordial role in the appearance of surfaces as shown in figure [roughness].
The term $G$ models the visibility (or occlusion or shadow-masking) of the microfacets.
Since this equation is valid for both the specular and diffuse components, the difference lies in the microfacet BRDF $f_m$.
It is important to note that this equation is used to integrate over the hemisphere at a _micro level_:
![Figure [microLevel]: Modeling the surface response at a single point requires an integration at the micro level](images/diagram_micro_vs_macro.png)
The diagram above shows that at a macro level, the surfaces is considered flat. This helps simplify our equations by assuming that a shaded fragment lit from a single direction corresponds to a single point at the surface.
At a micro level however, the surface is not flat and we cannot assume a single ray of light anymore (we can however assume that the incident rays are parallel). Since the micro facets will scatter the light in different directions given a bundle of parallel incident rays, we must integrate the surface response over a hemisphere, noted m in the above diagram.
It is obviously not practical to compute the full integration over the microfacets hemisphere for each shaded fragment. We will therefore rely on approximations of the integration for both the specular and diffuse components.
## Dielectrics and conductors
To better understand some of the equations and behaviors shown below, we must first clearly understand the difference between metallic (conductor) and non-metallic (dielectric) surfaces.
We saw earlier that when incident light hits a surface governed by a BRDF, the light is reflected as two separate components: the diffuse reflectance and the specular reflectance. The modelization of this behavior is straightforward as shown in figure [bsdfBrdf].
![Figure [bsdfBrdf]: Modelization of the BRDF part of a BSDF](images/diagram_fr_fd.png)
This modelization is a simplification of how the light actually interacts with the surface. In reality, part of the incident light will penetrate the surface, scatter inside, and exit the surface again as diffuse reflectance. This phenomenon is illustrated in figure [diffuseScattering].
![Figure [diffuseScattering]: Scattering of diffuse light](images/diagram_scattering.png)
Here lies the difference between conductors and dielectrics. There is no subsurface scattering occurring with purely metallic materials, which means there is no diffuse component (and we will see later that this has an influence on the perceived color of the specular component). Scattering happens in dielectrics, which means they have both specular and diffuse components.
To properly modelize the BRDF we must therefore distinguish between dielectrics and conductors (scattering not shown for clarity), as shown in figure [dielectricConductor].
![Figure [dielectricConductor]: BRDF modelization for dielectric and conductor surfaces](images/diagram_brdf_dielectric_conductor.png)
## Energy conservation
Energy conservation is one of the key components of a good BRDF for physically based rendering. An energy conservative BRDF states that the total amount of specular and diffuse reflectance energy is less than the total amount of incident energy. Without an energy conservative BRDF, artists must manually ensure that the light reflected off a surface is never more intense than the incident light.
## Specular BRDF
For the specular term, $f_r$ is a mirror BRDF that can be modeled with the Fresnel law, noted $F$ in the Cook-Torrance approximation of the microfacet model integration:
$$\begin{equation}
f_r(v,l) = \frac{D(h, \alpha) G(v, l, \alpha) F(v, h, f0)}{4(\NoV)(\NoL)}
\end{equation}$$
Given our real-time constraints, we must use an approximation for the three terms $D$, $G$ and $F$. [#Karis13a] has compiled a great list of formulations for these three terms that can be used with the Cook-Torrance specular BRDF. The sections that follow describe the equations we picked for these terms.
### Normal distribution function (specular D)
[#Burley12] observed that long-tailed normal distribution functions (NDF) are a good fit for real-world surfaces. The GGX distribution described in [#Walter07] is a distribution with long-tailed falloff and short peak in the highlights, with a simple formulation suitable for real-time implementations. It is also a popular model, equivalent to the Trowbridge-Reitz distribution, in modern physically based renderers.
$$\begin{equation}
D_{GGX}(h,\alpha) = \frac{\aa}{\pi ( (\NoH)^2 (\aa - 1) + 1)^2}
\end{equation}$$
The GLSL implementation of the NDF, shown in listing [specularD], is simple and efficient.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float D_GGX(float NoH, float roughness) {
float a = NoH * roughness;
float k = roughness / (1.0 - NoH * NoH + a * a);
return k * k * (1.0 / PI);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [specularD]: Implementation of the specular D term in GLSL]
We can improve this implementation by using half precision floats. This optimization requires changes to the original equation as there are two problems when computing $1 - (\NoH)^2$ in half-floats. First, this computation suffers from floating point cancellation when $(\NoH)^2$ is close to 1 (highlights). Secondly $\NoH$ does not have enough precision around 1.
The solution involves Lagrange's identity:
$$\begin{equation}
| a \times b |^2 = |a|^2 |b|^2 - (a \cdot b)^2
\end{equation}$$
Since both $n$ and $h$ are unit vectors, $|n \times h|^2 = 1 - (\NoH)^2$. This allows us to compute $1 - (\NoH)^2$ directly with half precision floats by using a simple cross product. Listing [specularDfp16] shows the final optimized implementation.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define MEDIUMP_FLT_MAX 65504.0
#define saturateMediump(x) min(x, MEDIUMP_FLT_MAX)
float D_GGX(float roughness, float NoH, const vec3 n, const vec3 h) {
vec3 NxH = cross(n, h);
float a = NoH * roughness;
float k = roughness / (dot(NxH, NxH) + a * a);
float d = k * k * (1.0 / PI);
return saturateMediump(d);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [specularDfp16]: Implementation of the specular D term in GLSL optimized for fp16]
### Geometric shadowing (specular G)
Eric Heitz showed in [#Heitz14] that the Smith geometric shadowing function is the correct and exact $G$ term to use. The Smith formulation is the following:
$$\begin{equation}
G(v,l,\alpha) = G_1(l,\alpha) G_1(v,\alpha)
\end{equation}$$
$G_1$ can in turn follow several models, and is commonly set to the GGX formulation:
$$\begin{equation}
G_1(v,\alpha) = G_{GGX}(v,\alpha) = \frac{2 (\NoV)}{\NoV + \sqrt{\aa + (1 - \aa) (\NoV)^2}}
\end{equation}$$
The full Smith-GGX formulation thus becomes:
$$\begin{equation}
G(v,l,\alpha) = \frac{2 (\NoL)}{\NoL + \sqrt{\aa + (1 - \aa) (\NoL)^2}} \frac{2 (\NoV)}{\NoV + \sqrt{\aa + (1 - \aa) (\NoV)^2}}
\end{equation}$$
We can observe that the dividends $2 (\NoL)$ and $2 (n \cdot v)$ allow us to simplify the original function $f_r$ by introducing a visibility function $V$:
$$\begin{equation}
f_r(v,l) = D(h, \alpha) V(v, l, \alpha) F(v, h, f_0)
\end{equation}$$
Where:
$$\begin{equation}
V(v,l,\alpha) = \frac{G(v, l, \alpha)}{4 (\NoV) (\NoL)} = V_1(l,\alpha) V_1(v,\alpha)
\end{equation}$$
And:
$$\begin{equation}
V_1(v,\alpha) = \frac{1}{\NoV + \sqrt{\aa + (1 - \aa) (\NoV)^2}}
\end{equation}$$
Heitz notes however that taking the height of the microfacets into account to correlate masking and shadowing leads to more accurate results. He defines the height-correlated Smith function thusly:
$$\begin{equation}
G(v,l,h,\alpha) = \frac{\chi^+(\VoH) \chi^+(\LoH)}{1 + \Lambda(v) + \Lambda(l)}
\end{equation}$$
$$\begin{equation}
\Lambda(m) = \frac{-1 + \sqrt{1 + \aa tan^2(\theta_m)}}{2} = \frac{-1 + \sqrt{1 + \aa \frac{(1 - cos^2(\theta_m))}{cos^2(\theta_m)}}}{2}
\end{equation}$$
Replacing $cos(\theta_m)$ by $\NoV$, we obtain:
$$\begin{equation}
\Lambda(v) = \frac{1}{2} \left( \frac{\sqrt{\aa + (1 - \aa)(\NoV)^2}}{\NoV} - 1 \right)
\end{equation}$$
From which we can derive the visibility function:
$$\begin{equation}
V(v,l,\alpha) = \frac{0.5}{\NoL \sqrt{(\NoV)^2 (1 - \aa) + \aa} + \NoV \sqrt{(\NoL)^2 (1 - \aa) + \aa}}
\end{equation}$$
The GLSL implementation of the visibility term, shown in listing [specularV], is a bit more expensive than we would like since it requires two `sqrt` operations.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float V_SmithGGXCorrelated(float NoV, float NoL, float roughness) {
float a2 = roughness * roughness;
float GGXV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
float GGXL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
return 0.5 / (GGXV + GGXL);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [specularV]: Implementation of the specular V term in GLSL]
We can optimize this visibility function by using an approximation after noticing that all the terms under the square roots are squares and that all the terms are in the $[0..1]$ range:
$$\begin{equation}
V(v,l,\alpha) = \frac{0.5}{\NoL (\NoV (1 - \alpha) + \alpha) + \NoV (\NoL (1 - \alpha) + \alpha)}
\end{equation}$$
This approximation is mathematically wrong but saves two square root operations and is good enough for real-time mobile applications, as shown in listing [approximatedSpecularV].
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float V_SmithGGXCorrelatedFast(float NoV, float NoL, float roughness) {
float a = roughness;
float GGXV = NoL * (NoV * (1.0 - a) + a);
float GGXL = NoV * (NoL * (1.0 - a) + a);
return 0.5 / (GGXV + GGXL);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [approximatedSpecularV]: Implementation of the approximated specular V term in GLSL]
[#Hammon17] proposes the same approximation based on the same observation that the square root can be removed. It does so by rewriting the expressions as _lerps_:
$$\begin{equation}
V(v,l,\alpha) = \frac{0.5}{lerp(2 (\NoL) (\NoV), \NoL + \NoV, \alpha)}
\end{equation}$$
### Fresnel (specular F)
The Fresnel effect plays an important role in the appearance of physically based materials. This effect models the fact that the amount of light the viewer sees reflected from a surface depends on the viewing angle. Large bodies of water are a perfect way to experience this phenomenon, as shown in figure [fresnelLake]. When looking at the water straight down (at normal incidence) you can see through the water. However, when looking further out in the distance (at grazing angle, where perceived light rays are getting parallel to the surface), you will see the specular reflections on the water become more intense.
The amount of light reflected depends not only on the viewing angle, but also on the index of refraction (IOR) of the material. At normal incidence (perpendicular to the surface, or 0 degree angle), the amount of light reflected back is noted $\fNormal$ and can be derived from the IOR as we will see in section [Reflectance remapping]. The amount of light reflected back at grazing angle is noted $\fGrazing$ and approaches 100% for smooth materials.
![Figure [fresnelLake]: The Fresnel effect is particularly evident on large bodies of water](images/photo_fresnel_lake.jpg)
More formally, the Fresnel term defines how light reflects and refracts at the interface between two different media, or the ratio of reflected and transmitted energy. [#Schlick94] describes an inexpensive approximation of the Fresnel term for the Cook-Torrance specular BRDF:
$$\begin{equation}
F_{Schlick}(v,h,\fNormal,\fGrazing) = \fNormal + (\fGrazing - \fNormal)(1 - \VoH)^5
\end{equation}$$
The constant $\fNormal$ represents the specular reflectance at normal incidence and is achromatic for dielectrics, and chromatic for metals. The actual value depends on the index of refraction of the interface. The GLSL implementation of this term requires the use of a `pow`, as shown in listing [specularF], which can be replaced by a few multiplications.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vec3 F_Schlick(float u, vec3 f0, float f90) {
return f0 + (vec3(f90) - f0) * pow(1.0 - u, 5.0);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [specularF]: Implementation of the specular F term in GLSL]
This Fresnel function can be seen as interpolating between the incident specular reflectance and the reflectance at grazing angles, represented here by $\fGrazing$. Observation of real world materials show that both dielectrics and conductors exhibit achromatic specular reflectance at grazing angles and that the Fresnel reflectance is 1.0 at 90 degrees. A more correct $\fGrazing$ is discussed in section [Specular occlusion].
Using $\fGrazing$ set to 1, the Schlick approximation for the Fresnel term can be optimized for scalar operations by refactoring the code slightly. The result is shown in listing [scalarSpecularF].
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vec3 F_Schlick(float u, vec3 f0) {
float f = pow(1.0 - u, 5.0);
return f + f0 * (1.0 - f);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [scalarSpecularF]: Scalar optimization of the specular F term in GLSL]
## Diffuse BRDF
In the diffuse term, $f_m$ is a Lambertian function and the diffuse term of the BRDF becomes:
$$\begin{equation}
\fDiffuse(v,l) = \frac{\sigma}{\pi} \frac{1}{| \NoV | | \NoL |}
\int_\Omega D(m,\alpha) G(v,l,m) (v \cdot m) (l \cdot m) dm
\end{equation}$$
Our implementation will instead use a simple Lambertian BRDF that assumes a uniform diffuse response over the microfacets hemisphere:
$$\begin{equation}
\fDiffuse(v,l) = \frac{\sigma}{\pi}
\end{equation}$$
In practice, the diffuse reflectance $\sigma$ is multiplied later, as shown in listing [diffuseBRDF].
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float Fd_Lambert() {
return 1.0 / PI;
}
vec3 Fd = diffuseColor * Fd_Lambert();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [diffuseBRDF]: Implementation of the diffuse Lambertian BRDF in GLSL]
The Lambertian BRDF is obviously extremely efficient and delivers results close enough to more complex models.
However, the diffuse part would ideally be coherent with the specular term and take into account the surface roughness. Both the Disney diffuse BRDF [#Burley12] and Oren-Nayar model [#Oren94] take the roughness into account and create some retro-reflection at grazing angles. Given our constraints we decided that the extra runtime cost does not justify the slight increase in quality. This sophisticated diffuse model also renders image-based and spherical harmonics more difficult to express and implement.
For completeness, the Disney diffuse BRDF expressed in [#Burley12] is the following:
$$\begin{equation}
\fDiffuse(v,l) = \frac{\sigma}{\pi} \schlick(n,l,1,\fGrazing) \schlick(n,v,1,\fGrazing)
\end{equation}$$
Where:
$$\begin{equation}
\fGrazing=0.5 + 2 \cdot \alpha cos^2(\theta_d)
\end{equation}$$
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float F_Schlick(float u, float f0, float f90) {
return f0 + (f90 - f0) * pow(1.0 - u, 5.0);
}
float Fd_Burley(float NoV, float NoL, float LoH, float roughness) {
float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
float lightScatter = F_Schlick(NoL, 1.0, f90);
float viewScatter = F_Schlick(NoV, 1.0, f90);
return lightScatter * viewScatter * (1.0 / PI);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [diffuseBRDF]: Implementation of the diffuse Disney BRDF in GLSL]
Figure [lambert_vs_disney] shows a comparison between a simple Lambertian diffuse BRDF and the higher quality Disney diffuse BRDF, using a fully rough dielectric material. For comparison purposes, the right sphere was mirrored. The surface response is very similar with both BRDFs but the Disney one exhibits some nice retro-reflections at grazing angles (look closely at the left edge of the spheres).
![Figure [lambert_vs_disney]: Comparison between the Lambertian diffuse BRDF (left) and the Disney diffuse BRDF (right)](images/diagram_lambert_vs_disney.png)
We could allow artists/developers to choose the Disney diffuse BRDF depending on the quality they desire and the performance of the target device. It is important to note however that the Disney diffuse BRDF is not energy conserving as expressed here.
## Standard model summary
**Specular term**: a Cook-Torrance specular microfacet model, with a GGX normal distribution function, a Smith-GGX height-correlated visibility function, and a Schlick Fresnel function.
**Diffuse term**: a Lambertian diffuse model.
The full GLSL implementation of the standard model is shown in listing [glslBRDF].
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float D_GGX(float NoH, float a) {
float a2 = a * a;
float f = (NoH * a2 - NoH) * NoH + 1.0;
return a2 / (PI * f * f);
}
vec3 F_Schlick(float u, vec3 f0) {
return f0 + (vec3(1.0) - f0) * pow(1.0 - u, 5.0);
}
float V_SmithGGXCorrelated(float NoV, float NoL, float a) {
float a2 = a * a;
float GGXL = NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);
float GGXV = NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);
return 0.5 / (GGXV + GGXL);
}
float Fd_Lambert() {
return 1.0 / PI;
}
void BRDF(...) {
vec3 h = normalize(v + l);
float NoV = abs(dot(n, v)) + 1e-5;
float NoL = clamp(dot(n, l), 0.0, 1.0);
float NoH = clamp(dot(n, h), 0.0, 1.0);
float LoH = clamp(dot(l, h), 0.0, 1.0);
// perceptually linear roughness to roughness (see parameterization)
float roughness = perceptualRoughness * perceptualRoughness;
float D = D_GGX(NoH, roughness);
vec3 F = F_Schlick(LoH, f0);
float V = V_SmithGGXCorrelated(NoV, NoL, roughness);
// specular BRDF
vec3 Fr = (D * V) * F;
// diffuse BRDF
vec3 Fd = diffuseColor * Fd_Lambert();
// apply lighting...
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [glslBRDF]: Evaluation of the BRDF in GLSL]
## Improving the BRDFs
We mentioned in section [Energy conservation] that energy conservation is one of the key components of a good BRDF. Unfortunately the BRDFs explored previously suffer from two problems that we will examine below.
### Energy gain in diffuse reflectance
The Lambert diffuse BRDF does not account for the light that reflects at the surface and that is therefore not able to participate in the diffuse scattering event.
[TODO: talk about the issue with fr+fd]
### Energy loss in specular reflectance
The Cook-Torrance BRDF we presented earlier attempts to model several events at the microfacet level but does so by accounting for a single bounce of light. This approximation can cause a loss of energy at high roughness, the surface is not energy preserving. Figure [singleVsMultiBounce] shows why this loss of energy occurs. In the single bounce (or single scattering) model, a ray of light hitting the surface can be reflected back onto another microfacet and thus be discarded because of the masking and shadowing term. If we however account for multiple bounces (multiscattering), the same ray of light might escape the microfacet field and be reflected back towards the viewer.
![Figure [singleVsMultiBounce]: Single scattering (left) vs multiscattering](images/diagram_single_vs_multi_scatter.png)
Based on this simple explanation, we can intuitively deduce that the rougher a surface is, the higher the chances are that energy gets lost because of the failure to account for multiple scattering events. This loss of energy appears to darken rough materials. Metallic surfaces are particularly affected because all of their reflectance is specular. This darkening effect is illustrated in figure [metallicRoughEnergyLoss]. With multiscattering, energy preservation can be achieved, as shown in figure [metallicRoughEnergyPreservation].
![Figure [metallicRoughEnergyLoss]: Darkening increases with roughness due to single scattering](images/material_metallic_energy_loss.png)
![Figure [metallicRoughEnergyPreservation]: Energy preservation with multiscattering](images/material_metallic_energy_preservation.png)
We can use a white furnace, a uniform lighting environment set to pure white, to validate the energy preservation property of a BRDF. When energy preservation is achieved, a purely reflective metallic surface ($\fNormal = 1$) should be indistinguishable from the background, no matter the roughness of said surface. Figure [whiteFurnaceLoss] shows what such a surface looks like with the specular BRDF presented in the previous sections. The loss of energy as the roughness increases is obvious. In contrast, figure [whiteFurnacePreservation] shows that accounting for multiscattering events addresses the energy loss.
![Figure [whiteFurnaceLoss]: Darkening increases with roughness due to single scattering](images/material_furnace_energy_loss.png)
![Figure [whiteFurnacePreservation]: Energy preservation with multiscattering](images/material_furnace_energy_preservation.png)
Multiple-scattering microfacet BRDFs are discussed in depth in [#Heitz16]. Unfortunately this paper only presents a stochastic evaluation of the multiscattering BRDF. This solution is therefore not suitable for real-time rendering. Kulla and Conty present a different approach in [#Kulla17]. Their idea is to add an energy compensation term as an additional BRDF lobe shown in equation $\ref{energyCompensationLobe}$:
$$\begin{equation}\label{energyCompensationLobe}
f_{ms}(l,v) = \frac{(1 - E(l)) (1 - E(v)) F_{avg}^2 E_{avg}}{\pi (1 - E_{avg}) (1 - F_{avg}(1 - E_{avg}))}
\end{equation}$$
Where $E$ is the directional albedo of the specular BRDF $f_r$, with $\fNormal$ set to 1:
$$\begin{equation}
E(l) = \int_{\Omega} f(l,v) (\NoV) dv
\end{equation}$$
The term $E_{avg}$ is the cosine-weighted average of $E$:
$$\begin{equation}
E_{avg} = 2 \int_0^1 E(\mu) \mu d\mu
\end{equation}$$
Similarly, $F_{avg}$ is the cosine-weighted average of the Fresnel term:
$$\begin{equation}
F_{avg} = 2 \int_0^1 F(\mu) \mu d\mu
\end{equation}$$
Both terms $E$ and $E_{avg}$ can be precomputed and stored in lookup tables. while $F_{avg}$ can be greatly simplified when the Schlick approximation is used:
$$\begin{equation}\label{averageFresnel}
F_{avg} = \frac{1 + 20 \fNormal}{21}
\end{equation}$$
This new lobe is combined with the original single scattering lobe, previously noted $f_r$:
$$\begin{equation}
f_{r}(l,v) = f_{ss}(l,v) + f_{ms}(l,v)
\end{equation}$$
In [#Lagarde18], with credit to Emmanuel Turquin, Lagarde and Golubev make the observation that equation $\ref{averageFresnel}$ can be simplified to $\fNormal$. They also propose to apply energy compensation by adding a scaled GGX specular lobe:
$$\begin{equation}\label{energyCompensation}
f_{ms}(l,v) = \fNormal \frac{1 - E(l)}{E(l)} f_{ss}(l,v)
\end{equation}$$
The key insight is that $E(l)$ can not only be precomputed but also shared with image-based lighting pre-integration. The multiscattering energy compensation formula thus becomes:
$$\begin{equation}\label{scaledEnergyCompensationLobe}
f_r(l,v) = f_{ss}(l,v) + \fNormal \left( \frac{1}{r} - 1 \right) f_{ss}(l,v)
\end{equation}$$
Where $r$ is defined as:
$$\begin{equation}
r = \int_{\Omega} D(l,v) V(l,v) \left< \NoL \right> dl
\end{equation}$$
We can implement specular energy compensation at a negligible cost if we store $r$ in the DFG lookup table presented in section [Image based lights]. Listing [energyCompensationImpl] shows that the implementation is a direct conversion of equation $\ref{scaledEnergyCompensationLobe}$.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vec3 energyCompensation = 1.0 + f0 * (1.0 / dfg.y - 1.0);
// Scale the specular lobe to account for multiscattering
Fr *= pixel.energyCompensation;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [energyCompensationImpl]: Implementation of the energy compensation specular lobe]
Please refer to section [Image based lights] and section [Pre-integration for multiscattering] to learn how the DFG lookup table is derived and computed.
## Parameterization
Disney's material model described in [#Burley12] is a good starting point but its numerous parameters makes it impractical for real-time implementations. In addition, we would like our standard material model to be easy to understand and easy to use for both artists and developers.
### Standard parameters
Table [standardParameters] describes the list of parameters that satisfy our constraints.
Parameter | Definition
---------------------:|:---------------------
**BaseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces
**Metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
**Roughness** | Perceived smoothness (0.0) or roughness (1.0) of a surface. Smooth surfaces exhibit sharp reflections
**Reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This replaces an explicit index of refraction
**Emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This parameter is mostly useful in an HDR pipeline with a bloom pass
**Ambient occlusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0. This parameter will be discussed in more details in the lighting section
[Table [standardParameters]: Parameters of the standard model]
Figure [material_parameters] shows how the metallic, roughness and reflectance parameters affect the appearance of a surface.
![Figure [material_parameters]: From top to bottom: varying metallic, varying dielectric roughness, varying metallic roughness, varying reflectance](images/material_parameters.png)
### Types and ranges
It is important to understand the type and range of the different parameters of our material model, described in table [standardParametersTypes].
Parameter | Type and range
---------------------:|:---------------------
**BaseColor** | Linear RGB [0..1]
**Metallic** | Scalar [0..1]
**Roughness** | Scalar [0..1]
**Reflectance** | Scalar [0..1]
**Emissive** | Linear RGB [0..1] + exposure compensation
**Ambient occlusion** | Scalar [0..1]
[Table [standardParametersTypes]: Range and type of the standard model's parameters]
Note that the types and ranges described here are what the shader will expect. The API and/or tools UI could and should allow to specify the parameters using other types and ranges when they are more intuitive for artists.
For instance, the base color could be expressed in sRGB space and converted to linear space before being sent off to the shader. It can also be useful for artists to express the metallic, roughness and reflectance parameters as gray values between 0 and 255 (black to white).
Another example: the emissive parameter could be expressed as a color temperature and an intensity, to simulate the light emitted by a black body.
### Remapping
To make the standard material model easier and more intuitive to use for artists, we must remap the parameters _baseColor_, _roughness_ and _reflectance_.
#### Base color remapping
The base color of a material is affected by the "metallicness" of said material. Dielectrics have achromatic specular reflectance but retain their base color as the diffuse color. Conductors on the other hand use their base color as the specular color and do not have a diffuse component.
The lighting equations must therefore use the diffuse color and $\fNormal$ instead of the base color. The diffuse color can easily be computed from the base color, as show in listing [baseColorToDiffuse].
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [baseColorToDiffuse]: Conversion of base color to diffuse in GLSL]
#### Reflectance remapping
**Dielectrics**
The Fresnel term relies on $\fNormal$, the specular reflectance at normal incidence angle, and is achromatic for dielectrics. We will use the remapping for dielectric surfaces described in [#Lagarde14] :
$$\begin{equation}
\fNormal = 0.16 \cdot reflectance^2
\end{equation}$$
The goal is to map $\fNormal$ onto a range that can represent the Fresnel values of both common dielectric surfaces (4% reflectance) and gemstones (8% to 16%). The mapping function is chosen to yield a 4% Fresnel reflectance value for an input reflectance of 0.5 (or 128 on a linear RGB gray scale). Figure [reflectance] show those common values and how they relate to the mapping function.
![Figure [reflectance]: Common reflectance values](images/diagram_reflectance.png)
If the index of refraction is known (for instance, an air-water interface has an IOR of 1.33), the Fresnel reflectance can be calculated as follows:
$$\begin{equation}\label{fresnelEquation}
\fNormal(n_{ior}) = \frac{(\nior - 1)^2}{(\nior + 1)^2}
\end{equation}$$
And if the reflectance value is known, we can compute the corresponding IOR:
$$\begin{equation}
n_{ior} = \frac{2}{1 - \sqrt{\fNormal}} - 1
\end{equation}$$
Table [commonMatReflectance] describes acceptable Fresnel reflectance values for various types of materials (no real world material has a value under 2%).
Material | Reflectance | IOR | Linear value
--------------------------:|:-----------------|:-----------------|:----------------
Water | 2% | 1.33 | 0.35
Fabric | 4% to 5.6% | 1.5 to 1.62 | 0.5 to 0.59
Common liquids | 2% to 4% | 1.33 to 1.5 | 0.35 to 0.5
Common gemstones | 5% to 16% | 1.58 to 2.33 | 0.56 to 1.0
Plastics, glass | 4% to 5% | 1.5 to 1.58 | 0.5 to 0.56
Other dielectric materials | 2% to 5% | 1.33 to 1.58 | 0.35 to 0.56
Eyes | 2.5% | 1.38 | 0.39
Skin | 2.8% | 1.4 | 0.42
Hair | 4.6% | 1.55 | 0.54
Teeth | 5.8% | 1.63 | 0.6
Default value | 4% | 1.5 | 0.5
[Table [commonMatReflectance]: Reflectance of common materials (source: Real-Time Rendering 4th Edition)]
Table [fNormalMetals] lists the $\fNormal$ values for a few metals. The values are given in sRGB and must be used as the base color in our material model. Please refer to the annex, section [Specular color], for an explanation of how these sRGB colors are computed from measured data.
Metal | $\fNormal$ in sRGB | Hexadecimal | Color
----------:|:-------------------:|:------------:|-------------------------------------------------------
Silver | 0.97, 0.96, 0.91 | #f7f4e8 | <div style="background-color: #f7f4e8; width: 60px"> </div>
Aluminum | 0.91, 0.92, 0.92 | #e8eaea | <div style="background-color: #e8eaea; width: 60px"> </div>
Titanium | 0.76, 0.73, 0.69 | #c1baaf | <div style="background-color: #c1baaf; width: 60px"> </div>
Iron | 0.77, 0.78, 0.78 | #c4c6c6 | <div style="background-color: #c4c6c6; width: 60px"> </div>
Platinum | 0.83, 0.81, 0.78 | #d3cec6 | <div style="background-color: #d3cec6; width: 60px"> </div>
Gold | 1.00, 0.85, 0.57 | #ffd891 | <div style="background-color: #ffd891; width: 60px"> </div>
Brass | 0.98, 0.90, 0.59 | #f9e596 | <div style="background-color: #f9e596; width: 60px"> </div>
Copper | 0.97, 0.74, 0.62 | #f7bc9e | <div style="background-color: #f7bc9e; width: 60px"> </div>
[Table [fNormalMetals]: $\fNormal$ for common metals]
All materials have a Fresnel reflectance of 100% at grazing angles so we will set $\fGrazing$ in the following way when evaluating the specular BRDF $\fSpecular$:
$$\begin{equation}
\fGrazing = 1.0
\end{equation}$$
Figure [grazing_reflectance] shows a red plastic ball. If you look closely at the edges of the sphere, you will be able to notice the achromatic specular reflectance at grazing angles.
![Figure [grazing_reflectance]: The specular reflectance becomes achromatic at grazing angles](images/material_grazing_reflectance.png)
**Conductors**
The specular reflectance of metallic surfaces is chromatic:
$$\begin{equation}
\fNormal = baseColor \cdot metallic
\end{equation}$$
Listing [fNormal] shows how $\fNormal$ is computed for both dielectric and metallic materials. It shows that the color of the specular reflectance is derived from the base color in the metallic case.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vec3 f0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + baseColor * metallic;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [fNormal]: Computing $\fNormal$ for dielectric and metallic materials in GLSL]
#### Roughness remapping and clamping
The roughness set by the user, called `perceptualRoughness` here, is remapped to a perceptually linear range using the following formulation:
$$\begin{equation}
\alpha = perceptualRoughness^2
\end{equation}$$
Figure [roughness_remap] shows a silver metallic surface with increasing roughness (from 0.0 to 1.0), using the unmodified roughness value (bottom) and the remapped value (top).
![Figure [roughness_remap]: Roughness remapping comparison: perceptually linear roughness (top) and roughness (bottom)](images/material_roughness_remap.png)
Using this visual comparison, it is obvious that the remapped roughness is easier to understand by artists and developers. Without this remapping, shiny metallic surfaces would have to be confined to a very small range between 0.0 and 0.05.
Brent Burley made similar observations in his presentation [#Burley12]. After experimenting with other remappings (cubic and quadratic mappings for instance), we have reached the conclusion that this simple square remapping delivers visually pleasing and intuitive results while being cheap for real-time applications.
Last but not least, it is important to note that the roughness parameters is used in various computations at runtime where limited floating point precision can become an issue. For instance, _mediump_ precision floats are often implemented as half-floats (fp16) on mobile GPUs.
This cause problems when computing small values like $\frac{1}{perceptualRoughness^4}$ in our lighting equations (roughness squared in the GGX computation). The smallest value that can be represented as a half-float is $2^{-14}$ or $6.1 \times 10^{-5}$. To avoid divisions by 0 on devices that do not support denormals, the result of $\frac{1}{roughness^4}$ must therefore not be lower than $6.1 \times 10^{-5}$. To do so, we must clamp the roughness to 0.089, which gives us $6.274 \times 10^{-5}$.
Denormals should also be avoided to prevent performance drops. The roughness can also not be set to 0 to avoid obvious divisions by 0.
Since we also want specular highlights to have a minimum size (a roughness close to 0 creates almost invisible highlights), we should clamp the roughness to a safe range in the shader. This clamping has the added benefit of correcting specular aliasing[^frostbiteRoughnessClamp] that can appear for low roughness values.
[^frostbiteRoughnessClamp]: The Frostbite engine clamps the roughness of analytical lights to 0.045 to reduce specular aliasing. This is possible when using single precision floats (fp32).
### Blending and layering
As noted in [#Burley12] and [#Neubelt13], this model allows for robust blending between different materials by simply interpolating the different parameters. In particular, this allows to layer different materials using simple masks.
For instance, figure [materialBlending] shows how the studio Ready at Dawn used material blending and layering in _The Order: 1886_ to create complex appearances from a library of simple materials (gold, copper, wood, rust, etc.).
![Figure [materialBlending]: Material blending and layering. Source: Ready at Dawn Studios](images/material_blending.png)
The blending and layering of materials is effectively an interpolation of the various parameters of the material model. Figure [material_interpolation] show an interpolation between shiny metallic chrome and rough red plastic. While the intermediate blended materials make little physical sense, they look plausible.
![Figure [material_interpolation]: Interpolation from shiny chrome (left) to rough red plastic (right)](images/material_interpolation.png)
### Crafting physically based materials
Designing physically based materials is fairly easy once you understand the nature of the four main parameters: base color, metallic, roughness and reflectance.
We provide a [useful chart/reference guide](./Material%20Properties.pdf) to help artists and developers craft their own physically based materials.
![Crafting physically based materials](images/material_chart.jpg)
In addition, here is a quick summary of how to use our material model:
All materials
: **Base color** should be devoid of lighting information, except for micro-occlusion.
**Metallic** is almost a binary value. Pure conductors have a metallic value of 1 and pure dielectrics have a metallic value of 0. You should try to use values close at or close to 0 and 1. Intermediate values are meant for transitions between surface types (metal to rust for instance).
Non-metallic materials
: **Base color** represents the reflected color and should be an sRGB value in the range 50-240 (strict range) or 30-240 (tolerant range).
**Metallic** should be 0 or close to 0.
**Reflectance** should be set to 127 sRGB (0.5 linear, 4% reflectance) if you cannot find a proper value. Do not use values under 90 sRGB (0.35 linear, 2% reflectance).
Metallic materials
: **Base color** represents both the specular color and reflectance. Use values with a luminosity of 67% to 100% (170-255 sRGB). Oxidized or dirty metals should use a lower luminosity than clean metals to take into account the non-metallic components.
**Metallic** should be 1 or close to 1.
**Reflectance** is ignored (calculated from the base color).
## Clear coat model
The standard material model described previously is a good fit for isotropic surfaces made of a single layer. Multi-layer materials are unfortunately fairly common, particularly materials with a thin translucent layer over a standard layer. Real world examples of such materials include car paints, soda cans, lacquered wood, acrylic, etc.
![Figure [materialClearCoat]: Comparison of a blue metallic surface under the standard material model (left) and the clear coat model (right)](images/material_clear_coat.png)
A clear coat layer can be simulated as an extension of the standard material model by adding a second specular lobe, which implies evaluating a second specular BRDF. To simplify the implementation and parameterization, the clear coat layer will always be isotropic and dielectric. The base layer can be anything allowed by the standard model (dielectric or conductor).
Since incoming light will traverse the clear coat layer, we must also take the loss of energy into account as shown in figure [clearCoatModel]. Our model will however not simulate inter reflection and refraction behaviors.
![Figure [clearCoatModel]: Clear coat surface model](images/diagram_clear_coat.png)
### Clear coat specular BRDF
The clear coat layer will be modeled using the same Cook-Torrance microfacet BRDF used in the standard model. Since the clear coat layer is always isotropic and dielectric, with low roughness values (see section [Clear coat parameterization]), we can choose cheaper DFG terms without notably sacrificing visual quality.
A survey of the terms listed in [#Karis13a] and [#Burley12] shows that the Fresnel and NDF terms we already use in the standard model are not computationally more expensive than other terms. [#Kelemen01] describes a much simpler term that can replace our Smith-GGX visibility term:
$$\begin{equation}
V(l,h) = \frac{1}{4(\LoH)^2}
\end{equation}$$
This masking-shadowing function is not physically based, as shown in [#Heitz14], but its simplicity makes it desirable for real-time rendering.
In summary, our clear coat BRDF is a Cook-Torrance specular microfacet model, with a GGX normal distribution function, a Kelemen visibility function, and a Schlick Fresnel function. Listing [kelemen] shows how trivial the GLSL implementation is.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float V_Kelemen(float LoH) {
return 0.25 / (LoH * LoH);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [kelemen]: Implementation of the Kelemen visibility term in GLSL]
**Note on the Fresnel term**
The Fresnel term of the specular BRDF requires $\fNormal$, the specular reflectance at normal incidence angle. This parameter can be computed from an index of refraction of an interface. We will assume that our clear coat layer is made of polyurethane, a common compound [used in coatings and varnishes](https://en.wikipedia.org/wiki/List_of_polyurethane_applications#Varnish), or similar. An air-polyurethane interface [has an IOR of 1.5](http://www.clearpur.com/transparent-polyurethanes/), from which we can deduce $\fNormal$:
$$\begin{equation}
\fNormal(1.5) = \frac{(1.5 - 1)^2}{(1.5 + 1)^2} = 0.04
\end{equation}$$
This corresponds to a Fresnel reflectance of 4% that we know is associated with common dielectric materials.
### Integration in the surface response
Because we must take into account the loss of energy caused by the addition of the clear coat layer, we can reformulate the BRDF from equation $\ref{brdf}$ thusly:
$$\begin{equation}
f(v,l)=\fDiffuse(v,l) (1 - F_c) + \fSpecular(v,l) (1 - F_c) + f_c(v,l)
\end{equation}$$
Where $F_c$ is the Fresnel term of the clear coat BRDF and $f_c$ the clear coat BRDF
### Clear coat parameterization
The clear coat material model encompasses all the parameters previously defined for the standard material mode, plus two parameters described in table [clearCoatParameters].
Parameter | Definition
----------------------:|:---------------------
**ClearCoat** | Strength of the clear coat layer. Scalar between 0 and 1
**ClearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer. Scalar between 0 and 1
[Table [clearCoatParameters]: Clear coat model parameters]
The clear coat roughness parameter is remapped and clamped in a similar way to the roughness parameter of the standard material.
Figure [clearCoat] and figure [clearCoatRoughness] show how the clear coat parameters affect the appearance of a surface.
![Figure [clearCoat]: Clear coat varying from 0.0 (left) to 1.0 (right) with metallic set to 1.0 and roughness to 0.8](images/material_clear_coat1.png)
![Figure [clearCoatRoughness]: Clear coat roughness varying from 0.0 (left) to 1.0 (right) with metallic set to 1.0, roughness to 0.8 and clear coat to 1.0](images/material_clear_coat2.png)
Listing [clearCoatBRDF] shows the GLSL implementation of the clear coat material model after remapping, parameterization and integration in the standard surface response.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void BRDF(...) {
// compute Fd and Fr from standard model
// remapping and linearization of clear coat roughness
clearCoatPerceptualRoughness = clamp(clearCoatPerceptualRoughness, 0.089, 1.0);
clearCoatRoughness = clearCoatPerceptualRoughness * clearCoatPerceptualRoughness;
// clear coat BRDF
float Dc = D_GGX(clearCoatRoughness, NoH);
float Vc = V_Kelemen(clearCoatRoughness, LoH);
float Fc = F_Schlick(0.04, LoH) * clearCoat; // clear coat strength
float Frc = (Dc * Vc) * Fc;
// account for energy loss in the base layer
return color * ((Fd + Fr * (1.0 - Fc)) * (1.0 - Fc) + Frc);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [clearCoatBRDF]: Implementation of the clear coat BRDF in GLSL]
### Base layer modification
The presence of a clear coat layer means that we should recompute $\fNormal$, since it is normally based on an air-material interface. The base layer thus requires $\fNormal$ to be computed based on a clear coat-material interface instead.
This can be achieved by computing the material's index of refraction (IOR) from $\fNormal$, then computing a new $\fNormal$ based on the newly computed IOR and the IOR of the clear coat layer (1.5).
First, we compute the base layer's IOR:
$$
IOR_{base} = \frac{1 + \sqrt{\fNormal}}{1 - \sqrt{\fNormal}}
$$
Then we compute the new $\fNormal$ from this new index of refraction:
$$
f_{0_{base}} = \left( \frac{IOR_{base} - 1.5}{IOR_{base} + 1.5} \right) ^2
$$
Since the clear coat layer's IOR is fixed, we can combine both steps to simplify:
$$
f_{0_{base}} = \frac{\left( 1 - 5 \sqrt{\fNormal} \right) ^2}{\left( 5 - \sqrt{\fNormal} \right) ^2}
$$
We should also modify the base layer's apparent roughness based on the IOR of the clear coat layer but this is something we have opted to leave out for now.
## Anisotropic model
The standard material model described previously can only describe isotropic surfaces, that is, surfaces whose properties are identical in all directions. Many real-world materials, such as brushed metal, can, however, only be replicated using an anisotropic model.
![Figure [anisotropic]: Comparison of isotropic material (left) and anisotropic material (right)](images/material_anisotropic.png)
### Anisotropic specular BRDF
The isotropic specular BRDF described previously can be modified to handle anisotropic materials. Burley achieves this by using an anisotropic GGX NDF:
$$\begin{equation}
D_{aniso}(h,\alpha) = \frac{1}{\pi \alpha_t \alpha_b} \frac{1}{((\frac{t \cdot h}{\alpha_t})^2 + (\frac{b \cdot h}{\alpha_b})^2 + (\NoH)^2)^2}
\end{equation}$$
This NDF unfortunately relies on two supplemental roughness terms noted $\alpha_b$, the roughness along the bitangent direction, and $\alpha_t$, the roughness along the tangent direction. Neubelt and Pettineo [#Neubelt13] propose a way to derive $\alpha_b$ from $\alpha_t$ by using an _anisotropy_ parameter that describes the relationship between the two roughness values for a material:
$$
\begin{align*}
\alpha_t &= \alpha \\
\alpha_b &= lerp(0, \alpha, 1 - anisotropy)
\end{align*}
$$
The relationship defined in [#Burley12] is different, offers more pleasant and intuitive results, but is slightly more expensive:
$$
\begin{align*}
\alpha_t &= \frac{\alpha}{\sqrt{1 - 0.9 \times anisotropy}} \\
\alpha_b &= \alpha \sqrt{1 - 0.9 \times anisotropy}
\end{align*}
$$
We instead opted to follow the relationship described in [#Kulla17] as it allows creation of sharp highlights:
$$
\begin{align*}
\alpha_t &= \alpha \times (1 + anisotropy) \\
\alpha_b &= \alpha \times (1 - anisotropy)
\end{align*}
$$
Note that this NDF requires the tangent and bitangent directions in addition to the normal direction. Since these directions are already needed for normal mapping, providing them may not be an issue.
The resulting implementation is described in listing [anisotropicBRDF].
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float at = max(roughness * (1.0 + anisotropy), 0.001);
float ab = max(roughness * (1.0 - anisotropy), 0.001);
float D_GGX_Anisotropic(float NoH, const vec3 h,
const vec3 t, const vec3 b, float at, float ab) {
float ToH = dot(t, h);
float BoH = dot(b, h);
float a2 = at * ab;
highp vec3 v = vec3(ab * ToH, at * BoH, a2 * NoH);
highp float v2 = dot(v, v);
float w2 = a2 / v2;
return a2 * w2 * w2 * (1.0 / PI);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [anisotropicBRDF]: Implementation of Burley's anisotropic NDF in GLSL]
In addition, [#Heitz14] presents an anisotropic masking-shadowing function to match the height-correlated GGX distribution. The masking-shadowing term can be greatly simplified by using the visibility function instead:
$$\begin{equation}
G(v,l,h,\alpha) = \frac{\chi^+(\VoH) \chi^+(\LoH)}{1 + \Lambda(v) + \Lambda(l)}
\end{equation}$$
$$\begin{equation}
\Lambda(m) = \frac{-1 + \sqrt{1 + \alpha_0^2 tan^2(\theta_m)}}{2} = \frac{-1 + \sqrt{1 + \alpha_0^2 \frac{(1 - cos^2(\theta_m))}{cos^2(\theta_m)}}}{2}
\end{equation}$$
Where:
$$\begin{equation}