RealTime Physically Based Rendering and BRDFs
The topic of BRDFs can often be confusing, so I’m going to attempt to break down what they are and how they relate to physically based rendering as simply as possible. We will be writing GLSL for realtime PBR rendering as we go along, the above image was rendered using the shader.
For this guide I’ve gathered together information and images from disparate sources including Wikipedia, jMonkeyEngine, Marmoset, UnrealEngine and nVidia. I’ve referenced each source as carefully as I can, but if any of the content owners are unhappy with their usage then I will be happy to replace them right away.
Bidirectional Reflectance Distribution Functions
When a process is bidirectional it is taking place in two, usually opposite directions. A BRDF is a function that gives the reflectance of a point on a surface given two directions, the direction of the viewer (Wr) and of the light (Wi).
 source: ^{1}
A BRDF returns the ratio of reflected light from the surface a viewer receives, this is known as the radiance. BRDF inputs Wr and Wi are usually defined as a pair of angles, azimuth (Φ) and zenith (θ), so the BRDF is considered a 4 dimensional function.
Measuring and recording the reflectance of a real surface is a difficult and time consuming process that uses a piece of equipment knows as a gonioreflectometer. The MERL database records discretely sampled data in a BRDF lookup table for 100 different surfaces. Each MERL BRDF contains around 33mb of data.
 source: ^{2}
Instead of having to create large amounts of data for each surface we want to render, analytical models have been developed which, given some more parameters can approximate reflectance for a range of surfaces. Some well known ones are:
 Lambert
 Phong
 BlinnPhong
 CookTorrance
For our purposes we will treat Wr as 3dimentional vector. And rather than single point lighting we want to sample an integral of many vectors over a hemisphere or cone, essentially blurring incoming light, called the irradiance value.
 source: ^{1}
Physically Based Rendering
PBR can be defined in many different ways, but in this article I will stick to the terms used in Epic MegaGames Unreal Engine. PBR combines analytical BRDFs to more closely model real BRDF sampled reflection data, but still retain enough approximation to work in realtime. A PBR shader can represent a range of materials and physical effects, in this case there are two main parameters to control these effects, the microsurface roughness and metalness. These are usually ratios stored in textures.
This image shows Fresnel reflectivity on a car bonnet and and microsurface diffusion on plastics.
 source: ^{3}
Radiance
Two anayltical BRDFs are required for different types interaction of light with the surface, reflection and diffusion. Instead of using them to calculate final radiance values, ours will give a radiance ratio, which then needs to be multiplied by irradiance to give our final values.
 source: ^{4}
Diffusion Radiance Ratio:
Light which penetrates the surface and is diffused. The well known Lambert BRDF can do this and diffuses light uniformly. After computation the reflectance is multiplied by the light color and also the surface colour, otherwise known as the albedo, because it has penetrated the surface. Because Lambertian radiance does not change with view angle, the ratio is only a constant of 1.
Reflection Radiance Ratio:
Light which is reflected in a mirror like way. A BRDF suitable for this task is CookTorrance which can mimic physical effects such as Fresnel reflectivity and microsurface diffusion (blurry reflections) defined by the roughness parameter.
 source: ^{4}
The CookTorrance radiance ratio is difficult to compute, but the good people at Epic MegaGames have open sourced a cheap approximation, how it was arrived at is a topic in itself but this version dispenses even with lookuptables, and so is particularly suitable for mobile.
 source: ^{5}
Irradiance
Calculating irradiance could be difficult because it means evaluating an integral of many rays. A more efficient way is to approximate the integral by preconvolving an HDR cubemap so values can be looked up by direction.
Diffuse (Lambertian) Irradiance Map.
 source: ^{6}
Microsurface Diffusion (CookTorrance) Irradiance Map.
The complete CookTorrance approximation requires splitting calculation between radiance function (EnvBRDFApprox), and an environment cubemap. Convolution is done in such a way to encode the CookTorrance BRDF in to it. This is called the “Split Sum Approximation”.
 source: ^{6}
Correctly convolving a cubemap is computationally expensive, so in this case we will trade off accuracy for performance by using a cubemap with standard mipmaps created with glGenerateMipmap.
Final Radiance Calculation
The two radiance ratio values output by the BRDFs now need to be multiplied by incoming irradiance.
Diffusion Radiance
Simpily look up the irradiance by sampling a cubemap using the surface normal. Multiplying by 1 drops out of the equation.
Reflection Radiance
The heuristic to look up a mipmap level from roughness comes from UnrealEngine.
Energy Conservation
Diffuse and reflective radiance can not just be added them because the surface would be emitting more light than arrived on it, so instead diffuse and albedo are blended using the metalness ratio.
 source: ^{4}
A Word on Gamma
Gamma colour space is designed to be displayed on monitors which have nonlinear brightness curves, most file formats store data in gamma colour space. When sampling the albedo colour it much be changed from gamma to linear space, then it can be used in any computation. After the final pixel value has been evaluated, it then must be translated back to gamma space to be displayed on a monitor. The article The Importance of Being Linear from nVidia gives an indepth guide on this process.
 source: ^{7}
In the first image lighting is computed in the correct colour space, in the second no correction is performed.
Partial GLSL
 environment_sampler: Seamless, HDR, environment cubemap with mipmaps generated.
 roughness_sampler: Roughness Ratio texture map
 metalness_sampler: Metalness ratio texture map
 albedo_sampler: Material colour texture map
Final Result
References

https://en.wikipedia.org/wiki/Bidirectional_reflectance_distribution_function ↩ ↩^{2}

https://www.marmoset.co/posts/basictheoryofphysicallybasedrendering ↩ ↩^{2} ↩^{3}

https://www.unrealengine.com/enUS/blog/physicallybasedshadingonmobile ↩

https://jmonkeyengine.github.io/wiki/jme3/advanced/pbr_part3.html ↩ ↩^{2}

https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch24.html ↩