Shaders used in the PBR Demo

One of the most important components of the PBR demo is the fragment shader for the model which handles all of the lighting calculations

The majority of the remaining work occurs in the shaders, after any offline asset processing.

The shaders will encode almost all of the physics required by the lighting model. The two sets of shaders used in this example are the shaders for the skybox and the model.

Skybox Shaders

The skybox shaders are very simple:

  • A full-screen-quad vertex shader.
  • A fragment shader that samples the skybox, exposes and tonemaps the value, and then writes out the fragment.

Model Shaders

The fragment shader for the model is much more complicated.

The calculations which occur in this shader are summarised in very broad terms below:

  1. Calculate the basic vectors and material properties

    These include the normal, (from the normal map and brought into the view space), view direction of the camera, , reflection vector , albedo, roughness, metallicity, and so on.

  2. Select the metallicity path

    The metallicity of the material determines how the specular and diffuse lighting components will be combined.

    • For metals (metallicity = 0), the diffuse component is ignored and only the specular contributes, however for very rough metals the specular contribution begins to approximate diffuse light anyway. In this case the specular component is modulated by the albedo of the material, in other words the higher the albedo, the greater the amount of light is reflected from the surface of the material.
    • For non-metals (metallicity = 1), the diffuse and specular components are combined based on roughness. In this path, the specular contribution is modulated by a fixed factor, 0.04 or 4%.
    • Finally, for materials with metallicity between 0 and 1, for example layered materials, these two paths are combined. In this case, the specular component is modulated by a factor between 0.04 and the albedo. This modulating factor is calculated by interpolating between this two values based on the metallicity.
  3. Calculate the lighting

    This step uses a lot of maths derived from lots of equations from a wide variety of papers, to calculate the lighting parameters. There is a lot of material explaining this process in great detail, and there are as many variations as there are implementations.

    The shader follows slightly different paths based on whether an image or dynamic lights are used. Dynamic lights are not used in this demo because the image-based approach is already realistic. The code for dynamic lights is commented out in the current version.

    The basic steps of the image-based approach are summarised below:

    1. Sample the precalculated BRDF texture (get the reflectivity parameters).
    2. Sample the prefiltered reflection map (get the environment's contribution to specular). This reflection map gets more blurred as the roughness of the material increases.
    3. Sample the irradiance map (get the diffuse contribution). The irradiance map is calculated using the ubiquitous , also known as the Lambertian diffuse, NdotL lighting, or "what everybody does for diffuse lighting".
    4. Use these to calculate the various shading parameters of the lighting model (F0, F, D, G, and so on)
    5. Combine these parameters based on the maths of the model.
    6. Factor in Ambient Occlusion (IBL only).
  4. Add emissive

    This is as simple as it sounds, sample the emissive map and add it to the final colour.

  5. Tonemap

    Finally, the linear, high dynamic range space needs to be mapped back into colours that can be displayed on the screen. As with the lighting calculation, there are a multitude of operators that can be applied. Some operators are more involved than others, some looking more vibrant, more realistic, some requiring gamma correction, and some not.

Note: The model also has a vertex shader but it simply calculates the fragment position and outputs a varyings into the fragment shader.

Optimising the Shaders will go into much more detail about optimising the model fragment shader.