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:

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);

    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.

invalid samples for negative z direction

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[0], 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;


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.