Importance sampling is a key ingredient of path tracing: the faster the sampling, the faster the rendering.
In this blog post, you will find an implementation for importance sampling a VNDF (GGX-Smith) isotropic distribution that is 15% faster than the current state of the art and doesn’t require building a local basis.
Here is the HLSL implementation:
float3 sample_vndf_isotropic(float2 u, float3 wi, float alpha, float3 n)
{
// decompose the floattor in parallel and perpendicular components
float3 wi_z = -n * dot(wi, n);
float3 wi_xy = wi + wi_z;
// warp to the hemisphere configuration
float3 wiStd = -normalize(alpha * wi_xy + wi_z);
// sample a spherical cap in (-wiStd.z, 1]
float wiStd_z = dot(wiStd, n);
float z = 1.0 - u.y * (1.0 + wiStd_z);
float sinTheta = sqrt(saturate(1.0f - z * z));
float phi = TWO_PI * u.x - PI;
float x = sinTheta * cos(phi);
float y = sinTheta * sin(phi);
float3 cStd = float3(x, y, z);
// reflect sample to align with normal
float3 up = float3(0, 0, 1.000001); // Used for the singularity
float3 wr = n + up;
float3 c = dot(wr, cStd) * wr / wr.z - cStd;
// compute halfway direction as standard normal
float3 wmStd = c + wiStd;
float3 wmStd_z = n * dot(n, wmStd);
float3 wmStd_xy = wmStd_z - wmStd;
// return final normal
return normalize(alpha * wmStd_xy + wmStd_z);
}
Where wi is the view vector in world space, n the normal in world space, alpha the isotropic roughness and u a pair of random values (usually they are stratified and dithered).
Here is the GLSL implementation:
vec3 SampleVndf_GGX(vec2 u, vec3 wi, float alpha, vec3 n)
{
// decompose the vector in parallel and perpendicular components
vec3 wi_z = n * dot(wi, n);
vec3 wi_xy = wi - wi_z;
// warp to the hemisphere configuration
vec3 wiStd = normalize(wi_z - alpha * wi_xy);
// sample a spherical cap in (-wiStd.z, 1]
float wiStd_z = dot(wiStd, n);
float phi = (2.0f * u.x - 1.0f) * M_PI;
float z = (1.0f - u.y) * (1.0f + wiStd_z) - wiStd_z;
float sinTheta = sqrt(clamp(1.0f - z * z, 0.0f, 1.0f));
float x = sinTheta * cos(phi);
float y = sinTheta * sin(phi);
vec3 cStd = vec3(x, y, z);
// reflect sample to align with normal
vec3 up = vec3(0, 0, 1);
vec3 wr = n + up;
vec3 c = dot(wr, cStd) * wr / wr.z - cStd;
// compute halfway direction as standard normal
vec3 wmStd = c + wiStd;
vec3 wmStd_z = n * dot(n, wmStd);
vec3 wmStd_xy = wmStd_z - wmStd;
// warp back to the ellipsoid configuration
vec3 wm = normalize(wmStd_z + alpha * wmStd_xy);
// return final normal
return wm;
}
Note that our method is an improvement of our publication from last year (see here: https://www.intel.com/content/www/us/en/developer/articles/technical/sampling-visible-ggx-normals.html).
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.