xeolabs bio photo


Lindsay Kay

3D software engineer / WebGL developer

Twitter LinkedIn Github

My chapter on SceneJS for OpenGL Insights is now free to download!

Adding Physically Based Rendering to xeogl


This month I extended xeogl to support Physically Based Rendering (PBR), which is an approach to rendering that uses real world values to provide results that are more accurate and consistent under all lighting conditions.

xeogl’s PBR is fairly standard so far:

  • Cook-Torrance BRDF
  • GGX microfacet distribution
  • Shlick’s approximation for specular Fresnel
  • Image-base lighting

I also extended xeogl to load glTF models with PBR materials, for both metallic/roughness and specular/glossiness work flows, using the FRAUNHOFER_materials_pbr extension.

This is still a work in progress, as I’m following along behind the current development of the glTF 2.0 spec. Also, I’m learning PBR as I go here, and still need to implement some of those little things in the shaders that really make it look good, like HDR lighting, gamma correction and tone mapping.

For an introduction to PBR concepts, check out the Allegorithmic PBR Guide.

API additions

To support PBR, I added two new material component types to xeogl, which closely follow glTF’s PBR spec:

The code snippet below, which creates the yellow fire hydrant example shown above, gives an example of how these materials are used within xeogl’s entity-component API.

// Entity with ObjGeometry and MetallicMaterial components
var fireHydrant = new xeogl.Entity({

   geometry: new xeogl.OBJGeometry({
       src: "models/obj/FireHydrantMesh.obj"

   material: new xeogl.MetallicMaterial({        
       baseColorMap : new xeogl.Texture({  // RGB is baseColor
           src: "textures/diffuse/fire_hydrant_Base_Color.png"
       metallicMap : new xeogl.Texture({   // R component is metallic
           src: "textures/metallic/fire_hydrant_Metallic.png"
       roughnessMap : new xeogl.Texture({  // R component is roughness
           src: "textures/roughness/fire_hydrant_Roughness.png"
       occlusionMap : new xeogl.Texture({  // R component is ambient occlusion
           src: "textures/occlusion/fire_hydrant_Mixed_AO.png"
       normalMap : new xeogl.Texture({
           src: "textures/normal/fire_hydrant_Normal_OpenGL.png"

Combining textures

Note that each channel on the MetallicMaterial in our hydrant example has its own individual texture. This happens to allow us to switch each texture on and off with a GUI, so that we can see its effect.

To reduce download times, however, glTF combines some material channels within the same textures. Therefore, for compatibility with glTF, I also made xeogl support these multichannel textures. For instance, instead of having a metallicMap and a roughnessMap the MetallicMaterial from our example could instead have a combined metallicRoughnessMap, which would have metalness in its texture’s R component and roughness in its G component.

Work remaining

  • Proper accreditation on all models.
  • Organize glTF examples better. Organize them into 1.0, 1.1 and 2.0 categories so that xeogl support can be checked at a glance.
  • HDR for IBL (looks so rough without this).
  • Gamma correction - not sure yet at what scope to do this in the API - per material, per model or entire scene?
  • Tone mapping - will probably need to build up a postprocessing framework and make this an effect that plugs into it.