GGX in Unity 5.3

January 25, 2016 in Technology

In Unity 5.3 Standard Shader, we have switched to GGX as the BRDF of choice for both analytical lights, such as point/directional light, but also for image based lighting. Furthermore, a complete overhaul has been performed on our implementation for convolution of cube maps to achieve both accurate and noiseless results at low execution time (latter part is in Unity 5.4). The most characteristic difference between GGX and normalized Phong is that the microfacet distribution profiles associated with GGX has a higher and more narrow spike, followed by a prevailing tail as we see here.

Profiles for GGX and Normalized Phong.

The impact of this on the final lit result is that GGX has a brighter highlight, followed by a trailing halo as shown below, which gives a more realistic appearance.

Comparison between GGX and conventional normalized phong.

Cross-industry Compatible Materials

In academics, physically based BRDFs use roughness as the parameter to control the microfacet distribution function. Academic roughness is defined as the root mean square slope of the profile. A common misunderstanding is that roughness maps in CG are the same as academic roughness, which is not the case. The reason academic roughness is not used for texture maps or sliders is because the “blur levels” are not evenly distributed, which is both very difficult to work with, but also leverages the limited bit precision of a texture map poorly. To avoid confusion, Unity uses smoothness instead of roughness maps, where smoothness is converted into academic roughness in the shader, using the formula (1-smoothness)^2. Distribution wise this is equivalent to Burley’s roughness, but reversed such that the most blurry response maps to 0.0 and perfect mirror reflection maps to 1.0, which we find more intuitive.

The significance to such a standardized distribution is that it allows you to import content into Unity made with external tools and achieve similar results. Most CG painting tools today support smoothness maps. To be clear, an identical match is not guaranteed, but proportionality between diffuse, specular brightness and overall blurriness of the specular reflection should be close. The following comparison shot between Unity 5 and Substance Painter, was kindly provided by Wes McDermott from Allegorithmic.

As we see the visuals are very similar. I would also like to thank Wes and Allegorithmic for their collaboration and helpful iteration on this. For more details on the subject people are encouraged to check out their detailed course on PBR and Unity 5.

Coming in Unity 5.4

In Unity 5.4 we have focused on improving the speed of cube map convolution and getting exceptionally clean visuals for the image based lighting (IBL). Below we see a comparison between a sphere lit in Unity 5.4 vs. a conventional path tracer at 50000 rays per pixel.

As we see, there is a significant amount of noise using the conventional path tracer on the right, even at 50000 rays per pixel. The reason for this is because a basic path tracer (BRDF importance sampling) struggles with environment maps, which contain hot singularities such as a sun at physically proportional intensity. Unity 5.4 is now resilient to this problem and the off-line cube map convolution is roughly 2 times faster than Unity 5.2.

Comments (9)

Subscribe to comments
  1. ellon

    February 4, 2016 at 5:36 am / 

    The image links are broken (dropbox contents are not avaialable)

    1. Kristyna Paskova

      February 5, 2016 at 11:14 am / 

      Thanks for noticing, just fixed it!

  2. theprotonfactor

    January 27, 2016 at 7:00 pm / 

    I’m quite curious as to what technique you guys are using in 5.4 for the convolution? As far as I know importance sampling is the “default” way of convolution with GGX.
    If the new method trades speed for accuracy then I’d prefer the option to select the convolution method.

    1. Renaldas Zioma

      January 27, 2016 at 10:34 pm / 

      Our convolution is done with Importance Sampling for higher resolution mipmap levels, but fallbacks to brute-force for low resolution mipmap levels – which was found to be the fastest approach.

      Morten just optimized the heck out of it (including version using AVX) :)

      1. theprotonfactor

        January 28, 2016 at 11:08 am / 

        Ah, that’s so cool! It never occurred to me that lower detail mips probably do not need importance sampling. Very clever way to optimize it. I was a little apprehensive when I read it was going to look better and be more performant, but it makes sense now. Excellent work guys!

  3. Jes

    January 27, 2016 at 5:23 pm / 

    > To avoid confusion, Unity uses smoothness instead of roughness maps, where smoothness is converted into academic roughness in the shader, using the formula (1-smoothness)^2

    Whay not postprocess texture to eliminate shader calculation of (1-smoothness) or even entire (1-smoothness)^2 ?

    May be want to have different texture postprocessors to have different realtime in editor corrections of input textures.

    1. Aras Pranckevičius

      January 27, 2016 at 8:43 pm / 

      Not enough precision to store it in an 8 bit/channel texture. This is very similar to why textures typically store sRGB instead of linear color values — to give more precision where it’s needed.

  4. Asger Hoedt

    January 26, 2016 at 4:28 pm / 

    Thanks for the blog post. I love that you’ve found a formal description of roughness. I see the approximation everywhere, but never how it’s defined. Finally I have a well-defined parameter that can be used to switch between multiple glossy BRDFs. :)

    I do want to correct one thing though. That image has not been rendered by a ‘conventional path tracer’, more like a ‘bare minimum path tracer’, with only BRDF sampling. Most conventional path tracers today support multiple importance sampling and an importance sampled environment, which would have that simple scene converging in 500 samples or less.

  5. matt radford

    January 25, 2016 at 10:34 pm / 

    awesome! Will this speed up the runtime execution for DynamicGI.UpdateEnvironment() ?

    Currently i notice a pretty big hit when calling this function when i specify a custom runtime cubemap:

    terrain / sky is done via raytracer and integrated with unity’s lighting
    https://vimeo.com/152037674

    but i get a little stall when extracting lighting directions. Simple solution is to call it extremely infrequently. But im thinking of just writing a faster way of sampling dominant directions. But if i didn’t have to, awesome!

Comments are closed.