declare const _default: "\n// Real-Time Polygonal-Light Shading with Linearly Transformed Cosines\n// by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt\n// code: https://github.com/selfshadow/ltc_code/\n\nfn LTC_Uv(N: vec3f, V: vec3f, roughness: f32) -> vec2f {\n    const LUT_SIZE: f32 = 64.0;\n    const LUT_SCALE: f32 = (LUT_SIZE - 1.0) / LUT_SIZE;\n    const LUT_BIAS: f32 = 0.5 / LUT_SIZE;\n    let dotNV: f32 = saturate(dot( N, V ));\n    // texture parameterized by sqrt( GGX alpha ) and sqrt( 1 - cos( theta ) )\n    let uv: vec2f = vec2f( roughness, sqrt( 1.0 - dotNV ) );\n    return uv * LUT_SCALE + LUT_BIAS;\n}\n\nfn LTC_ClippedSphereFormFactor( f: vec3f ) -> f32 {\n    // Real-Time Area Lighting: a Journey from Research to Production (p.102)\n    // An approximation of the form factor of a horizon-clipped rectangle.\n    let l: f32 = length( f );\n    return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\n\nfn LTC_EdgeVectorFormFactor( v1: vec3f, v2: vec3f ) -> vec3f {\n    let x: f32 = dot( v1, v2 );\n    let y: f32 = abs( x );\n    // rational polynomial approximation to theta / sin( theta ) / 2PI\n    let a: f32 = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n    let b: f32 = 3.4175940 + ( 4.1616724 + y ) * y;\n    let v: f32 = a / b;\n    let inv_sqrt_term = inverseSqrt( max( 1.0 - x * x, 1e-7f ) );\n    let theta_sintheta: f32 = select( (0.5 * inv_sqrt_term - v), v, x > 0.0 );\n    return cross( v1, v2 ) * theta_sintheta;\n}\n\nstruct Coords {\n    coord0: vec3f,\n    coord1: vec3f,\n    coord2: vec3f,\n    coord3: vec3f,\n}\n\nfn LTC_EvaluateRect( N: vec3f, V: vec3f, P: vec3f, mInv: mat3x3f, rectCoords: Coords) -> f32 {\n    // bail if point is on back side of plane of light\n    // assumes ccw winding order of light vertices\n    let v1: vec3f = rectCoords.coord1 - rectCoords.coord0;\n    let v2: vec3f = rectCoords.coord3 - rectCoords.coord0;\n\n    let lightNormal: vec3f = cross( v1, v2 );\n    let factor: f32 = sign(-dot( lightNormal, P - rectCoords.coord0 ));\n\n    // construct orthonormal basis around N\n    let T1: vec3f = normalize( V - N * dot( V, N ) );\n    let T2: vec3f = factor * cross( N, T1 ); // negated from paper; possibly due to a different handedness of world coordinate system\n    // compute transform\n    let mat: mat3x3f = mInv * transpose( mat3x3f( T1, T2, N ) );\n    // transform rect\n    var coords: array<vec3f, 4>;\n    coords[0] = mat * ( rectCoords.coord0 - P );\n    coords[1] = mat * ( rectCoords.coord1 - P );\n    coords[2] = mat * ( rectCoords.coord2 - P );\n    coords[3] = mat * ( rectCoords.coord3 - P );\n    // project rect onto sphere\n    coords[0] = normalize( coords[0] );\n    coords[1] = normalize( coords[1] );\n    coords[2] = normalize( coords[2] );\n    coords[3] = normalize( coords[3] );\n    // calculate vector form factor\n    var vectorFormFactor: vec3f = vec3f( 0.0 );\n    vectorFormFactor = vectorFormFactor + LTC_EdgeVectorFormFactor( coords[0], coords[1] );\n    vectorFormFactor = vectorFormFactor + LTC_EdgeVectorFormFactor( coords[1], coords[2] );\n    vectorFormFactor = vectorFormFactor + LTC_EdgeVectorFormFactor( coords[2], coords[3] );\n    vectorFormFactor = vectorFormFactor + LTC_EdgeVectorFormFactor( coords[3], coords[0] );\n    // adjust for horizon clipping\n    let result: f32 = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\n    return result;\n}\n\nvar<private> dLTCCoords: Coords;\nfn getLTCLightCoords(lightPos: vec3f, halfWidth: vec3f, halfHeight: vec3f) -> Coords {\n    var coords: Coords;\n    coords.coord0 = lightPos + halfWidth - halfHeight;\n    coords.coord1 = lightPos - halfWidth - halfHeight;\n    coords.coord2 = lightPos - halfWidth + halfHeight;\n    coords.coord3 = lightPos + halfWidth + halfHeight;\n    return coords;\n}\n\nvar<private> dSphereRadius: f32;\nfn getSphereLightCoords(lightPos: vec3f, halfWidth: vec3f, halfHeight: vec3f) -> Coords {\n    // used for simple sphere light falloff\n    // also, the code only handles a spherical light, it cannot be non-uniformly scaled in world space, and so we enforce it here\n    dSphereRadius = max(length(halfWidth), length(halfHeight));\n\n    // Billboard the 2d light quad to reflection vector, as it's used for specular. This allows us to use disk math for the sphere.\n    let f: vec3f = reflect(normalize(lightPos - uniform.view_position), vNormalW);\n    let w: vec3f = normalize(cross(f, halfHeight));\n    let h: vec3f = normalize(cross(f, w));\n\n    return getLTCLightCoords(lightPos, w * dSphereRadius, h * dSphereRadius);\n}\n\n// used for LTC LUT texture lookup\nvar<private> dLTCUV: vec2f;\n#ifdef LIT_CLEARCOAT\n    var<private> ccLTCUV: vec2f;\n#endif\n\nfn getLTCLightUV(gloss: f32, worldNormal: vec3f, viewDir: vec3f) -> vec2f {\n    let roughness: f32 = max((1.0 - gloss) * (1.0 - gloss), 0.001);\n    return LTC_Uv( worldNormal, viewDir, roughness );\n}\n\n// used for energy conservation and to modulate specular\nvar<private> dLTCSpecFres: vec3f;\n#ifdef LIT_CLEARCOAT\n    var<private> ccLTCSpecFres: vec3f;\n#endif\n\nfn getLTCLightSpecFres(uv: vec2f, specularity: vec3f) -> vec3f {\n    let t2: vec4f = textureSampleLevel(areaLightsLutTex2, areaLightsLutTex2Sampler, uv, 0.0);\n    return specularity * t2.x + ( vec3f( 1.0 ) - specularity) * t2.y;\n}\n\nfn calcLTCLightValues(gloss: f32, worldNormal: vec3f, viewDir: vec3f, specularity: vec3f, clearcoatGloss: f32, clearcoatWorldNormal: vec3f, clearcoatSpecularity: f32) {\n    dLTCUV = getLTCLightUV(gloss, worldNormal, viewDir);\n    dLTCSpecFres = getLTCLightSpecFres(dLTCUV, specularity);\n\n    #ifdef LIT_CLEARCOAT\n        ccLTCUV = getLTCLightUV(clearcoatGloss, clearcoatWorldNormal, viewDir);\n        ccLTCSpecFres = getLTCLightSpecFres(ccLTCUV, vec3f(clearcoatSpecularity));\n    #endif\n}\n\nfn calcRectLightValues(lightPos: vec3f, halfWidth: vec3f, halfHeight: vec3f) {\n    dLTCCoords = getLTCLightCoords(lightPos, halfWidth, halfHeight);\n}\nfn calcDiskLightValues(lightPos: vec3f, halfWidth: vec3f, halfHeight: vec3f) {\n    calcRectLightValues(lightPos, halfWidth, halfHeight);\n}\nfn calcSphereLightValues(lightPos: vec3f, halfWidth: vec3f, halfHeight: vec3f) {\n    dLTCCoords = getSphereLightCoords(lightPos, halfWidth, halfHeight);\n}\n\n// An extended version of the implementation from \"How to solve a cubic equation, revisited\"\n// http://momentsingraphics.de/?p=105\nfn SolveCubic(Coefficient_in: vec4f) -> vec3f {\n    let pi: f32 = 3.14159;\n    var Coefficient = Coefficient_in;\n\n    // Normalize the polynomial\n    Coefficient = vec4f(Coefficient.xyz / Coefficient.w, Coefficient.w);\n\n    // Divide middle coefficients by three\n    let new_yz: vec2f = Coefficient.yz / 3.0;\n    Coefficient = vec4f(Coefficient.x, new_yz.x, new_yz.y, Coefficient.w);\n    \n    let A: f32 = Coefficient.w;\n    let B: f32 = Coefficient.z;\n    let C: f32 = Coefficient.y;\n    let D: f32 = Coefficient.x;\n\n    // Compute the Hessian and the discriminant\n    let Delta: vec3f = vec3f(\n        -Coefficient.z * Coefficient.z + Coefficient.y,\n        -Coefficient.y * Coefficient.z + Coefficient.x,\n        dot(vec2f(Coefficient.z, -Coefficient.y), Coefficient.xy)\n    );\n\n    let Discriminant: f32 = dot(vec2f(4.0 * Delta.x, -Delta.y), Delta.zy);\n\n    var xlc: vec2f;\n    var xsc: vec2f;\n\n    // Algorithm A\n    {\n        let A_a: f32 = 1.0;\n        let C_a: f32 = Delta.x;\n        let D_a: f32 = -2.0 * B * Delta.x + Delta.y;\n\n        // Take the cubic root of a normalized complex number\n        let Theta: f32 = atan2(sqrt(Discriminant), -D_a) / 3.0;\n\n        let sqrt_neg_Ca = sqrt(-C_a);\n        let x_1a: f32 = 2.0 * sqrt_neg_Ca * cos(Theta);\n        let x_3a: f32 = 2.0 * sqrt_neg_Ca * cos(Theta + (2.0 / 3.0) * pi);\n\n        let xl: f32 = select(x_3a, x_1a, (x_1a + x_3a) > 2.0 * B);\n        xlc = vec2f(xl - B, A);\n    }\n\n    // Algorithm D\n    {\n        let A_d: f32 = D;\n        let C_d: f32 = Delta.z;\n        let D_d: f32 = -D * Delta.y + 2.0 * C * Delta.z;\n\n        // Take the cubic root of a normalized complex number\n        let Theta: f32 = atan2(D * sqrt(Discriminant), -D_d) / 3.0;\n\n        let sqrt_neg_Cd = sqrt(-C_d);\n        let x_1d: f32 = 2.0 * sqrt_neg_Cd * cos(Theta);\n        let x_3d: f32 = 2.0 * sqrt_neg_Cd * cos(Theta + (2.0 / 3.0) * pi);\n\n        let xs: f32 = select(x_3d, x_1d, x_1d + x_3d < 2.0 * C);\n        xsc = vec2f(-D, xs + C);\n    }\n\n    let E: f32 =  xlc.y * xsc.y;\n    let F: f32 = -xlc.x * xsc.y - xlc.y * xsc.x;\n    let G: f32 =  xlc.x * xsc.x;\n\n    let xmc: vec2f = vec2f(C * F - B * G, -B * F + C * E);\n\n    var Root: vec3f = vec3f(xsc.x / xsc.y, xmc.x / xmc.y, xlc.x / xlc.y);\n\n    if (Root.x < Root.y && Root.x < Root.z) {\n        Root = Root.yxz;\n    } else if (Root.z < Root.x && Root.z < Root.y) {\n        Root = Root.xzy;\n    }\n    return Root;\n}\n\nfn LTC_EvaluateDisk(N: vec3f, V: vec3f, P: vec3f, Minv: mat3x3f, points: Coords) -> f32 {\n    // construct orthonormal basis around N\n    let T1: vec3f = normalize(V - N * dot(V, N));\n    let T2: vec3f = cross(N, T1);\n\n    // rotate area light in (T1, T2, N) basis\n    let R: mat3x3f = transpose( mat3x3f( T1, T2, N ) );\n    // polygon (allocate 5 vertices for clipping\n    var L_: array<vec3f, 3>;\n    L_[0] = R * ( points.coord0 - P );\n    L_[1] = R * ( points.coord1 - P );\n    L_[2] = R * ( points.coord2 - P );\n\n    // init ellipse\n    let C: vec3f  = 0.5 * (L_[0] + L_[2]);\n    var V1: vec3f = 0.5 * (L_[1] - L_[2]);\n    var V2: vec3f = 0.5 * (L_[1] - L_[0]);\n\n    let C_Minv: vec3f  = Minv * C;\n    let V1_Minv: vec3f = Minv * V1;\n    let V2_Minv: vec3f = Minv * V2;\n\n    // compute eigenvectors of ellipse\n    var a: f32;\n    var b: f32;\n    let d11: f32 = dot(V1_Minv, V1_Minv);\n    let d22: f32 = dot(V2_Minv, V2_Minv);\n    let d12: f32 = dot(V1_Minv, V2_Minv);\n    if (abs(d12) / sqrt(d11 * d22) > 0.0001) {\n        let tr: f32 = d11 + d22;\n        let det_inner: f32 = -d12 * d12 + d11 * d22;\n        let det: f32 = sqrt(det_inner);\n        let u: f32 = 0.5 * sqrt(tr - 2.0 * det);\n        let v: f32 = 0.5 * sqrt(tr + 2.0 * det);\n        let e_max: f32 = (u + v) * (u + v);\n        let e_min: f32 = (u - v) * (u - v);\n\n        var V1_: vec3f;\n        var V2_: vec3f;\n\n        if (d11 > d22) {\n            V1_ = d12 * V1_Minv + (e_max - d11) * V2_Minv;\n            V2_ = d12 * V1_Minv + (e_min - d11) * V2_Minv;\n        } else {\n            V1_ = d12*V2_Minv + (e_max - d22)*V1_Minv;\n            V2_ = d12*V2_Minv + (e_min - d22)*V1_Minv;\n        }\n\n        a = 1.0 / e_max;\n        b = 1.0 / e_min;\n        V1 = normalize(V1_);\n        V2 = normalize(V2_);\n    } else {\n        a = 1.0 / dot(V1_Minv, V1_Minv);\n        b = 1.0 / dot(V2_Minv, V2_Minv);\n        V1 = V1_Minv * sqrt(a);\n        V2 = V2_Minv * sqrt(b);\n    }\n\n    var V3: vec3f = normalize(cross(V1, V2));\n    if (dot(C_Minv, V3) < 0.0) {\n        V3 = V3 * -1.0;\n    }\n\n    let L: f32  = dot(V3, C_Minv);\n    let x0: f32 = dot(V1, C_Minv) / L;\n    let y0: f32 = dot(V2, C_Minv) / L;\n\n    let E1: f32 = inverseSqrt(a);\n    let E2: f32 = inverseSqrt(b);\n\n    let a_scaled = a * L * L;\n    let b_scaled = b * L * L;\n\n    let c0: f32 = a_scaled * b_scaled;\n    let c1: f32 = a_scaled * b_scaled * (1.0 + x0 * x0 + y0 * y0) - a_scaled - b_scaled;\n    let c2: f32 = 1.0 - a_scaled * (1.0 + x0 * x0) - b_scaled * (1.0 + y0 * y0);\n    let c3: f32 = 1.0;\n\n    let roots: vec3f = SolveCubic(vec4f(c0, c1, c2, c3));\n    let e1: f32 = roots.x;\n    let e2: f32 = roots.y;\n    let e3: f32 = roots.z;\n\n    var avgDir: vec3f = vec3f(a_scaled * x0 / (a_scaled - e2), b_scaled * y0 / (b_scaled - e2), 1.0);\n\n    let rotate: mat3x3f = mat3x3f(V1, V2, V3);\n\n    avgDir = rotate * avgDir;\n    avgDir = normalize(avgDir);\n\n    let L1: f32 = sqrt(-e2 / e3);\n    let L2: f32 = sqrt(-e2 / e1);\n\n    let formFactor: f32 = max(0.0, L1 * L2 * inverseSqrt((1.0 + L1 * L1) * (1.0 + L2 * L2)));\n\n    const LUT_SIZE_disk: f32 = 64.0;\n    const LUT_SCALE_disk: f32 = ( LUT_SIZE_disk - 1.0 ) / LUT_SIZE_disk;\n    const LUT_BIAS_disk: f32 = 0.5 / LUT_SIZE_disk;\n\n    // use tabulated horizon-clipped sphere\n    var uv: vec2f = vec2f(avgDir.z * 0.5 + 0.5, formFactor);\n    uv = uv * LUT_SCALE_disk + LUT_BIAS_disk;\n\n    let scale: f32 = textureSampleLevel(areaLightsLutTex2, areaLightsLutTex2Sampler, uv, 0.0).w;\n\n    return formFactor * scale;\n}\n\n// LTC_EvaluateDisk in some rare cases genereates NaN values in a or b, just before 'float c0 = a * b;'\n// Get rid of those Nan values before they propagate further, as in case of bloom / DOF blurs they\n// propagate to large areas. I didn't find the actual reason where those come from, so that is still TODO.\n// Note that only disk/sphere lights are causing it, so only handle those.\nfn FixNan(value: f32) -> f32 {\n    // use value != value check for NaN as isnan() is not available in WGSL\n    return select(value, 0.0, value != value);\n}\n\nfn getRectLightDiffuse(worldNormal: vec3f, viewDir: vec3f, lightDir: vec3f, lightDirNorm: vec3f) -> f32 {\n    let identityMat = mat3x3f(vec3f(1.0, 0.0, 0.0), vec3f(0.0, 1.0, 0.0), vec3f(0.0, 0.0, 1.0));\n    return LTC_EvaluateRect( worldNormal, viewDir, vPositionW, identityMat, dLTCCoords );\n}\n\nfn getDiskLightDiffuse(worldNormal: vec3f, viewDir: vec3f, lightDir: vec3f, lightDirNorm: vec3f) -> f32 {\n    let identityMat = mat3x3f(vec3f(1.0, 0.0, 0.0), vec3f(0.0, 1.0, 0.0), vec3f(0.0, 0.0, 1.0));\n    return FixNan(LTC_EvaluateDisk( worldNormal, viewDir, vPositionW, identityMat, dLTCCoords ));\n}\n\nfn getSphereLightDiffuse(worldNormal: vec3f, viewDir: vec3f, lightDir: vec3f, lightDirNorm: vec3f) -> f32 {\n    // NB: this could be improved further with distance based wrap lighting\n    let falloff: f32 = dSphereRadius / (dot(lightDir, lightDir) + dSphereRadius);\n    return FixNan(getLightDiffuse(worldNormal, viewDir, lightDirNorm) * falloff);\n}\n\nfn getLTCLightInvMat(uv: vec2f) -> mat3x3f {\n    let t1: vec4f = textureSampleLevel(areaLightsLutTex1, areaLightsLutTex1Sampler, uv, 0.0);\n\n    return mat3x3f(\n        vec3f( t1.x, 0.0, t1.y ),\n        vec3f( 0.0, 1.0, 0.0 ),\n        vec3f( t1.z, 0.0, t1.w )\n    );\n}\n\nfn calcRectLightSpecular(worldNormal: vec3f, viewDir: vec3f, uv: vec2f) -> f32 {\n    let mInv: mat3x3f = getLTCLightInvMat(uv);\n    return LTC_EvaluateRect( worldNormal, viewDir, vPositionW, mInv, dLTCCoords );\n}\n\nfn getRectLightSpecular(worldNormal: vec3f, viewDir: vec3f) -> f32 {\n    return calcRectLightSpecular(worldNormal, viewDir, dLTCUV);\n}\n\nfn calcDiskLightSpecular(worldNormal: vec3f, viewDir: vec3f, uv: vec2f) -> f32 {\n    let mInv: mat3x3f = getLTCLightInvMat(uv);\n    return LTC_EvaluateDisk( worldNormal, viewDir, vPositionW, mInv, dLTCCoords );\n}\n\nfn getDiskLightSpecular(worldNormal: vec3f, viewDir: vec3f) -> f32 {\n    return calcDiskLightSpecular(worldNormal, viewDir, dLTCUV);\n}\n\nfn getSphereLightSpecular(worldNormal: vec3f, viewDir: vec3f) -> f32 {\n    return calcDiskLightSpecular(worldNormal, viewDir, dLTCUV);\n}\n";
export default _default;
