The standard approach proposed by Walter et al. [1] for importance sampling of GGX BRDF is to use the PDF proportional to distribution of microfacet normal D(ω). However, D(ω) is not view-dependent, thus it would waste lots of samples when the incoming direction is near grazing angles. In addition, the small value of D(ω)∣m⋅n∣ at grazing angle would make the sample value as high as hundreds to millions in some situations and cause fireflies in render results.
In order to tackle these problems, Heitz and d’Eon [3] proposed a new method by sampling from the distribution of visible normals. Due to the consideration of view direction, their approach can significantly reduce the amount of wasted samples and make the sample weight in proper range to avoid the fireflies.
Revisit the Distribution of Normals D(ω)
Microsurface configuration. Image from Heitz [2]
Suppose the area of macro surface is 1m2 and its normal is ωg. The normal of each microfacet point pm on the micro-surface M is ωm(pm) and the distribution of normals is:
D(ω)=∫Mδω(ωm(pm))dpm
The unit stated in Heitz [2] is srm2 instead of sr1, it’s different from the one stated in Walter et al. [1]
D(ω) describes the area of points M′⊂M whose normals lie in the given solid angle ω. Additionaly, it relates the integral from microsurface space to the space the normals which is the unit sphere ω:
microsurfacearea=∫Mdpm=∫ΩD(ωm)dωm
and the projected area along the geometry normal ωg is:
∫Ω(ωm⋅ωg)D(ωm)dωm=∫M(ωm(pm)⋅ωg)dpm=1
The Distribution of Visible Normals Dωo(ω)
Distribution of visible normals. Image from Heitz [2]
When we consider the projected area along the outgoing direction ωo, we have to add a masking function G1(ωo,ωm) to exclude the occlusion part (i.e. the black parts in the figure above):
∫ωG1(ωo,ωm)⟨ωo,ωm⟩D(ωm)dωm=cosθo
With these information, the outgoing radiance from the microsurface can be formulated as:
Since it’s not easy to perform importance sampling with Dωo(ωm,αx,αy) directly, Heitz and d’Eon [3] proposed a novel technique based on Heitz’s discovery [2] of stretch invariance property in slope space to accomplish the task.
Normal Vector in Slope Space
Suppose the microsurface is a heightfield, given a normal ωm(xm,ym,zm), its coordinate in slope space can be defined as:
m~(ωm)=(xm~,ym~)=(−zmxm,−zmym)
and the distribution of slopes P22(m~) is necessarily normalized (all the possibilities must sum to 1):
∫−∞∞∫−∞∞P22(m~)dxm~dym~=1
Recall that ∫ω(ωm⋅ωg)D(ωm)dωm=1, we get the following relationship:
Likewise, the relationship involved with masking is (cf. Heitz [2] p.82):
Dωi(ωm)=∂ωm∂m~Pωi22(m~)
where Pωi22(m~) is the distribution of visible slopes, and it shows that if we sample the slopes from Pωi22(m~) and transform them to normal ωm, the resulting PDF is Dωi(ωm).
Stretch Invariance
Suppose the distribution of slopes P22 depends on the roughness αx,αy, Heitz calls P22 is stretch invariant if changing its roughness would only stretch the distribution but keep its shape identical:
Illustration of stretch invariance. Image from Heitz [2]
In 1D case, stretch x-axis by a factor of 2 would compress the distribution by 2 and also notice that the height of distribution has been scaled by 2. Suppose the distribution is D(x,α), the stretch invariance can be described as D(x,α)=λ1D(λx,λα).
With stretch invariance property, any anisotropic distribution can be stretched into an isotropic one whose masking probability G is the same. Then the samples can be generated with the help of the stretched isotropic distribution in following steps:
Apply stretch invariant property to make Pωi22isotropic
Sample the slope coordinate xm~ according to the marginal PDF Pωi2−(xm~,1,1)
Sample ym~ with given xm~ and conditional PDF Pωi2∣2(ym~∣xm~,1,1)
Rotate along the z axis by ϕ in canonical frame
Inversely stretch the slope coordinate (xm,ym)
Compute normal from slope coordinate ωm=xm2+ym2+1(−xm,−ym,1)
The implementation details can be found in the supplemental document of the Heitz and d’Eon [3].
Experimental Results
When cosθo=1.5(θo≈85.94∘), the new method saves 310 wasted samples! The remaining 61 back-facing samples are about 1∼3° below the macrosurface. However, the mysterious situation happens at cosθo=π/4. In current implementation, there still are 52 invalid samples whose maximum θi is about 3π/4!! From the debugging traces, I found all those invalid samples come with a random number rx that is near to 0 or 1. To my best guess, this weird result may be caused by numerical errors and needs further investigation.
As usual, the implementation can be found on my github.