Atlassian uses cookies to improve your browsing experience, perform analytics and research, and conduct advertising. Accept all cookies to indicate that you agree to our use of cookies on your device. Atlassian cookies and tracking notice, (opens new window)

Enlighten SDK 3.10 Documentation
Results will update as you type.
  • Welcome to Enlighten
  • How Enlighten works
  • Artist workflow
  • Install Enlighten
  • Libraries
  • Implementation guide
  • Technical reference
    • Output formats
    • Albedo handling
    • Lightmap lighting models
    • Light probe evaluation
    • Local IBL reflections
    • Light visibility data
    • Custom direct lights
    • Precompute pipeline
    • Low level precompute API
    • Debugging the precompute
    • The low level runtime
    • Baked lighting
    • Performance tuning
    • Technical troubleshooting
    • Terrain LOD
    • Probe LOD
    • Lightmap LOD
  • Advanced techniques
  • Tools
  • Enlighten Mobile
  • White papers
  • Third-party licences
  • Release notes
    Calendars

You‘re viewing this with anonymous access, so some content might be blocked.
/
Light probe evaluation

    This is the documentation for Enlighten.

    Light probe evaluation

    Nov 21, 2019


    Overview

    A good reference for rendering irradiance lighting from SH coefficients is Ramamoorthi & Hanrahan's paper: "An Efficient Representation for Irradiance Environment Maps" (http://graphics.stanford.edu/papers/envmap/envmap.pdf). Note that compared to the paper, Enlighten swaps the y and z axes. For the L2 coefficients, we also bake in the required constants to convert from radiance to irradiance, to make evaluation in shader code as simple as possible.

    This is how L1 and L2 probes are rendered in our sample application GeoRadiosity. For details, see the sample code.

    L1 spherical harmonics

    Enlighten L1 output has four coefficients for each colour channel, corresponding to the spherical basis functions 1, x, y, z (that is, one ambient term and three directional terms, one along each coordinate axis). In the notation of Ramamoorthi & Hanrahan, the output order is L00, L11, L10, L1-1. For L1, the Enlighten output is radiance, and we need to convert to irradiance for a given normal direction in shader code. The standard linear conversion can result in negative values when the lighting environment is highly directional. To avoid this problem we compute irradiance using a non-linear model that avoids negative values while still preserving the correct energy and dynamic range. This needs to be applied individually for each colour channel. The shader code is given below.

    For more information and a derivation of the non-linear model, see the Geomerics CEDEC talk about L1 irradiance reconstruction. There are also more details about how Enlighten uses Spherical Harmonics in this blog post.

    float NonlinearL1ComputeChannel(float3 normal, float4 coeffs)
    {
        float L0 = coeffs.x;
        float3 L1 = coeffs.yzw;
        float modL1 = length(L1);
        if (modL1 == 0.0f)
        {
            return L0;
        }
    
        float q = 0.5f + 0.5f * dot(normal, normalize(L1));
        float r = modL1 / L0;
        float p = 1.0f + 2.0f * r;
        float a = (1.0f - r) / (1.0f + r);
    
        return L0 * lerp((1.0f + p) * pow(q, p), 1.0f, a);
    }
    
    //-------------------------------------------------------------------------------------------------
    float3 NonlinearL1(float3 normal)
    {
        float3 output;
    
        output.r = NonlinearL1ComputeChannel(normal, g_L1CoeffR);
        output.g = NonlinearL1ComputeChannel(normal, g_L1CoeffG);
        output.b = NonlinearL1ComputeChannel(normal, g_L1CoeffB);
    
        return max(float3(0.f, 0.f, 0.f), output);
    }
    

    Compressed L1 spherical harmonics

    Enlighten L1 output can optionally be written as compressed 8-bit-per-channel data. To improve the dynamic range which can be captured, instead of a linear quantisation, the square-root of the L0 (ambient) term is encoded in the first channel, with the range [0,1] mapped to [0...255]. The three L1 terms are quantised linearly: they are first divided by L0, and then the range [-1,1] is mapped to [0...254]. The decode looks like this in C++ code:

    float L0sqrt = (static_cast<float>(outputU8[0])) / 255.0f;         // here outputU8 is a pointer to 8-bit encoded SH data
    float L0 = L0sqrt * L0sqrt;
    float L1_x = ((static_cast<float>(outputU8[1]) - 127.0f) * L0 / 127.0f);
    float L1_y = ((static_cast<float>(outputU8[2]) - 127.0f) * L0 / 127.0f);
    float L1_z = ((static_cast<float>(outputU8[3]) - 127.0f) * L0 / 127.0f);
    

    In shader code, assuming the data is read from a texture:

    float3 RamaCompressedL1(float3 normal, ...)
    {
        float3 output;
        float4 n = float4(1.0f, normalize(normal.xyz));   // extend the normal vector with 1.0 in the ambient channel
     
        float4 RCoeff = tex2D( <read the compressed Red L1 coefficients from an 8-bit-per-channel texture> );
    
        RCoeff.x *= RCoeff.x;
        RCoeff.yzw -= 0.5f;
        RCoeff.yzw *= RCoeff.yzw * 2.0f;
    
            output.r = RamaL1ComputeChannel(normal, RCoeff); // compute the Red output from the decoded Red L1 coefficients
    
        <repeat twice more, Green and Blue>
    
        return max(float3(0.0f, 0.0f, 0.0f), output);     // clamp the result to 0.0
    }
    

    When encoding compressed probe output, the Enlighten probe solver clamps data to [-1,1]. If you wish to capture a different range, use the m_U8OutputScale parameter of RadProbeTask or EntireProbeSetTask. This scale is applied to the raw output data before the encoding and quantisation to 8-bit data. For example, to capture data in the range [-4,4], use a m_U8OutputScale of 0.25, and then apply an extra scale factor of 4.0 during the decode process.

    L2 spherical harmonics

    Enlighten L2 output has nine coefficients for each colour channel, corresponding to the spherical basis functions 1, x, y, z, 3y2 - 1, xy, yz, xz, x2 - z2. In the notation of Ramamoorthi & Hanrahan, the output order is L00, L11, L10, L1-1, L20, L21, L2-1, L2-2, L22. To sample the Enlighten output to give irradiance for a particular surface normal direction, you need to compute a nine-component dot product of these coefficients with the basis functions, evaluated for the normal direction (x, y, z).

    One way of performing this evaluation is to rewrite the dot product as nt M n, where n is the extended normal vector (1, x, y, z) and M is a 4x4 symmetric matrix:

    ( L00 - L20, ½L11, ½L10, ½L1-1 )
    ( ½L11, L22, ½L21, ½L2-2 )
    ( ½L10, ½L21, 3L20, ½L2-1 )
    ( ½L1-1, ½L2-2, ½L2-1, -L22 )

    The weights in this matrix are different to the weights of the original paper by Ramamoorthi & Hanrahan. The output of Enlighten is optimized for the custom weights listed here and your shading result will not look as good if you use the weights of the original paper.

    The evaluation shader code then looks like this:

    float3 RamaL2(float3 normal)
    {
        float3 output;
        float4 n = float4(1.0f, normalize(normal.xyz));  // extend the normal vector with 1.0 in the ambient channel
    
        output.r = dot(n, mul(g_L2MatrixR, n));          // compute the Red output via matrix and dot product with the Red L2 coefficients
        output.g = dot(n, mul(g_L2MatrixG, n));          // compute the Green output via matrix and dot product with the Green L2 coefficients
        output.b = dot(n, mul(g_L2MatrixB, n));          // compute the Blue output via matrix and dot product with the Blue L2 coefficients
    
        return max(float3(0.0f, 0.0f, 0.0f), output);    // clamp the result to 0.0
    }
    

    Environment visibility SH evaluation

    The Environment visibility SH data optionally generated by the Enlighten precompute is normalised so that the same "RamaL2" SH evaluation function as shown above works as expected, generating visibility results in the range [0,1]. This is the case irrespective of whether you use L1 or L2 probes. In other words, the environment visibility SH data for L1 is always pre-scaled by [1,2,2,2], the same scaling factors that the L1 subset of the L2 SH data would have if you used L2 probes.

    , multiple selections available,
    {"serverDuration": 10, "requestCorrelationId": "f16a9a77328b44a7b7839c318aefb04e"}