Skip to content

Masking Geometry

Jiayin Cao edited this page Jan 15, 2020 · 7 revisions

Geometry masking, or transparent material, is a commonly available feature in real-time rendering, which could easily be implemented by discarding pixels in pixel shader. It greatly reduces the requirement of the complexity of geometries in certain scenes, like forests.

Given the extra simplicity gained in geometry detail, offline renderer also provides similar features. In order to support this feature, the followings are questions of my interest.

  • How to make part of geometry transparent?
  • Is there support for semi-transparent by blending the 'masking' feature with other materials if the feature is implemented in the form of a material?
  • If semi-transparent is supported, how is ray attenuated when traveling through transparent materials? How expensive is it?
  • Is it possible to use this feature as the wrapping geometry of volumes?
  • How does supporting this feature affect SIMD intersection algorithm?

I investigated the solution of this feature in two different renderers, PBRT, and Cycles.

PBRT's implementation

PBRT(3rd)'s implementation is a bit simpler comparing with Cycles. It only supports transparency without semi-transparency support, which is not quite a limitation to me since semi-transparent material can be simulated with other material types. The way it solves the problem is by adding another filtering in the intersection algorithm so that the rest of the system doesn't even know there could be an intersection if it is transparent. The obvious advantage of this implementation is that the way it works is transparent to the rest of the system, neither the lighting or the shadowing algorithm needs to be changed to support masking. The cache is two folds

  • There is no semi-transparent support.
  • It can't be used as geometric boundaries of volumes. PBRT uses a special material, which is nullptr, as an indication of geometric boundaries of volumes. It does introduce a bit of extra complexity here since this could also be handled similarly by the transparent solution too. But not in the way PBRT solves it.

There is no SIMD implementation in PBRT third edition so far. However, if there is such an extension, which I won't be surprised to see in the future, the above solution will inevitably introduce some corner case handling during resolving intersections. That said, every time we detect a nearest intersection among four/eight intersections, we need to check whether it is transparent or not. If it is transparent, we need to keep finding the next nearest one and keep doing it until we find a valid intersection. This will drag down the performance in case of transparent materials. But what is more important is whether it will do the same if there is no transparent material. I think the answer would be negative since modern CPU architecture is really good at branch prediction. If there is no execution of the other branch that handles transparent, the extra cost could be very minimal.

Cycles' implementation

Cycles' implementation is slightly more sophisticated in a way that it is done through its material system instead of changing the intersection algorithm. Basically, there is a material node called 'Transparent BSDF', which will make geometry disappear, by mixing the BSDF with other 'valid' BSDF, it can mask out certain parts of the geometry in a very flexible way.

The transparent BSDF is essentially a Dirac Delta function. The only difference between the BSDF and the Mirror BSDF implementation in PBRT is that the out-going direction is different. In this case, the out-going direction should be exactly the same as the incoming direction. And the system will avoid evaluating direct light contribution if this is the only BSDF in the material. This way, Cycles system can easily add transparent support in the renderer.

What goes beyond it is that since it is just a material node like others, it can be blended with other BSDF material nodes. With the transparent BSDF node being generic like others, it can easily extend the transparent feature to semi-transparent, though it is not strictly physically-based.

Due to the support of semi-transparency, shadow evaluation is a bit complex than before. There are several static code branches solving the problem.

  • Find exactly one intersection at a time. If the attenuation goes to zero, stop moving forward. Otherwise, keep finding the next one. If the shadow ray hits an opaque material, it will simply return fully occluded. If at the end of the day, it doesn't get attenuated to zero, it will return a value ranging from 0 to 1 indicating how much light gets attenuated.
    The cache of this algorithm is the potential inefficiency of duplicated intersection test. If there are multiple primitives touching the shadow ray in the same node, it will do the intersection multiple times.
  • Find all intersection one time and sort them afterward. This will easily avoid the deficiency mentioned above. But It may end up the other kind of inefficiency in the case that the first intersection is opaque, rendering the rest of the intersection algorithm totally useless.

Regardless efficiency of the algorithm, it does solve the problem. There might be more complex details implemented in Cycles not mentioned. Since it is good enough for me to get the ball rolling, it should be good enough for me until it becomes the performance bottleneck.

Solution in SORT

In order to be better aligned with Cycles since it also works in Blender, I decided to support Semi-transparency the same way Cycles works. This gives me the benefit of potential converting from Cycles scene in the future.

With the introduction of transparency, there is a few things done

  • Visibility no longer returns a binary value, but a value goes from 0 to 1 per color channel. Future volumetric rendering code could potentially use the same code infrastructure to evaluate the volume attenuation.
  • Shadow calculation is a lot more complex than before. Because shadow ray needs to be traced repeatedly until a solid opaque primitive is hit. In case of transparent material, this does introduce a bit of extra cost, which is innevitable to support this feature. But there is a separate branch that handles most opaque intersection case that brings the gap in term of performance of these two to literally nothing.