# Implementing GGX VNDF importance sampling with DXR

I am currently implementing the GGX specular BRDF in a DXR raytracer using the importance sampling method describe by Heitz

I am sampling the VNDF with:

//https://hal.archives-ouvertes.fr/hal-01509746/document
inline float3 VNDF_GGX(in float3 wo, float roughness2, float rand1, float rand2)
{
//Strecht view vector
float3 view = normalize(float3(wo.x * roughness2, wo.y, wo.z * roughness2));

//Orthonormal basis
float3 lensq = pow(view.x, 2) + pow(view.z, 2);
float3 t1 = (lensq > 0.0f) ? normalize(cross(view, float3(0.0f, 1.0f, 0.0f))) : float3(1, 0, 0);
float3 t2 = cross(view, t1);

//project
float r = sqrt(rand1);
float phi = 2.0f * pi * rand2;
float p1 = r * cos(phi);
float p2 = r * sin(phi);

float s = 0.5f * (1.0f + view.y);
p2 = (1.0f - s) * sqrt(1.0f - pow(p1, 2.0f)) + s * p2;

float3 n = p1 * t1 + p2 * t2 + sqrt(max(0.0f, 1.0f - pow(p1, 2.0f) - pow(p2, 2.0f))) * view;

//unstrech normal and renormalize
float3 n_out = normalize(float3(roughness2 * n.x, max(0.0f, n.y), roughness2 * n.z));

return n_out;
}


where wo is the view direction in tangent space, roughness2 is the material roughness [0;1] squared and rand1/rand2 are uniform random numbers [0;1] sampled from a texture. I am using the exact algorithm described by Heitz except using y-up instead of z-up.

Using the microfacet normal m calculated like this I then acquire the sample direction wi by reflecting the world ray direction -wo in tangent space about the microfacet normal m.

void ImportanceSampleGGXVNDF(float3 wo, Material material, inout float3 wi, inout float reflectance, float2 randSeed, bool isInVec, inout float fschlick)
{
float roughness2 = pow(material.roughness, 2.0f);
float3 m = VNDF_GGX(wo, roughness2, randSeed.x, randSeed.y);

wi = normalize(reflect(-wo, m));

float3 normalTangentSpace = float3(0.0f, 1.0f, 0.0f);

fschlick = FresnelSchlick(m, wi, isInVec ? material.ior1 : 1.0f, isInVec ? 1.0f : material.ior1);
float G1 = SmithMaskingGGX(wi, wo, normalTangentSpace, pow(roughness2, 2));
float G2 = SmithShdowingGGX(wo, wi, normalTangentSpace, pow(roughness2, 2));

reflectance = fschlick *(G2 / G1);
}


Where wo is the view direction, wi is the sample direction in tangent space, reflectance equaling the BRDF term (everything else cancels out according to Heitz! See here details of the derivation)

This however seems to be producing invalid sampling Directions for ray directions with negative values in y or z component resulting in invalid light probes. Comparing with similar implementations (including: here implementing the exact same method, here also implementing the same method and here using an algorithm described in an older paper also published by Heitz) sadly yielded no solution.

The final output color is computed via

    ImportanceSampleGGXVNDF(tangentSpaceDir, halfVec, l_materialCB, rayDir, reflectance, rand, isInVec, fschlick);

newRayDir = tangentToWorldSpace(rayDir, tangent, bitangent, triangleNormal);

ray.Direction = newRayDir;
ray.TMin = 0.0001f;
ray.TMax = 10000.0; //Maximum travel distance of ray
reflected = TraceReflectiveRay(ray, currNumRecursions);

reflected *= reflectance;
$$$$
`