UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

3 lines (2 loc) 24.1 kB
declare const _default: "\n\n#include \"lightBufferDefinesPS\"\n\n// include this before shadow / cookie code\n#include \"clusteredLightUtilsPS\"\n\n#ifdef CLUSTER_COOKIES\n #include \"clusteredLightCookiesPS\"\n#endif\n\n#ifdef CLUSTER_SHADOWS\n #include \"clusteredLightShadowsPS\"\n#endif\n\nvar clusterWorldTexture: texture_2d<f32>;\nvar lightsTexture: texture_2d<f32>;\n\n#ifdef CLUSTER_SHADOWS\n // TODO: when VSM shadow is supported, it needs to use sampler2D in webgl2\n var shadowAtlasTexture: texture_depth_2d;\n var shadowAtlasTextureSampler: sampler_comparison;\n#endif\n\n#ifdef CLUSTER_COOKIES\n var cookieAtlasTexture: texture_2d<f32>;\n var cookieAtlasTextureSampler: sampler;\n#endif\n\nuniform clusterMaxCells: i32;\n\n// 1.0 if clustered lighting can be skipped (0 lights in the clusters)\nuniform clusterSkip: f32;\n\nuniform clusterCellsCountByBoundsSize: vec3f;\nuniform clusterTextureSize: vec3f;\nuniform clusterBoundsMin: vec3f;\nuniform clusterBoundsDelta: vec3f;\nuniform clusterCellsDot: vec3f;\nuniform clusterCellsMax: vec3f;\nuniform shadowAtlasParams: vec2f;\n\n// structure storing light properties of a clustered light\n// it's sorted to have all vectors aligned to 4 floats to limit padding\nstruct ClusterLightData {\n\n // 32bit of flags\n flags: u32,\n\n // area light sizes / orientation\n halfWidth: vec3f,\n\n isSpot: bool,\n\n // area light sizes / orientation\n halfHeight: vec3f,\n\n // light index\n lightIndex: i32,\n\n // world space position\n position: vec3f,\n\n // area light shape\n shape: u32,\n\n // world space direction (spot light only)\n direction: vec3f,\n\n // light follow mode\n falloffModeLinear: bool,\n\n // color\n color: vec3f,\n\n // 0.0 if the light doesn't cast shadows\n shadowIntensity: f32,\n\n // atlas viewport for omni light shadow and cookie (.xy is offset to the viewport slot, .z is size of the face in the atlas)\n omniAtlasViewport: vec3f,\n\n // range of the light\n range: f32,\n\n // channel mask - one of the channels has 1, the others are 0\n cookieChannelMask: vec4f,\n\n // compressed biases, two haf-floats stored in a float\n biasesData: f32,\n\n // shadow bias values\n shadowBias: f32,\n shadowNormalBias: f32,\n\n // compressed angles, two haf-floats stored in a float\n anglesData: f32,\n\n // spot light inner and outer angle cosine\n innerConeAngleCos: f32,\n outerConeAngleCos: f32,\n\n // intensity of the cookie\n cookieIntensity: f32,\n\n // light mask\n //float mask;\n isDynamic: bool,\n isLightmapped: bool\n}\n\n// Note: on some devices (tested on Pixel 3A XL), this matrix when stored inside the light struct has lower precision compared to\n// when stored outside, so we store it outside to avoid spot shadow flickering. This might need to be done to other / all members\n// of the structure if further similar issues are observed.\n\n// shadow (spot light only) / cookie projection matrix\nvar<private> lightProjectionMatrix: mat4x4f;\n\nfn sampleLightTextureF(lightIndex: i32, index: i32) -> vec4f {\n return textureLoad(lightsTexture, vec2<i32>(index, lightIndex), 0);\n}\n\nfn decodeClusterLightCore(clusterLightData: ptr<function, ClusterLightData>, lightIndex: f32) {\n\n // light index\n clusterLightData.lightIndex = i32(lightIndex);\n\n // sample data encoding half-float values into 32bit uints\n let halfData: vec4f = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_COLOR_ANGLES_BIAS});\n\n // store floats we decode later as needed\n clusterLightData.anglesData = halfData.z;\n clusterLightData.biasesData = halfData.w;\n\n // decompress color half-floats\n let colorRG: vec2f = unpack2x16float(bitcast<u32>(halfData.x));\n let colorB_: vec2f = unpack2x16float(bitcast<u32>(halfData.y));\n clusterLightData.color = vec3f(colorRG, colorB_.x) * {LIGHT_COLOR_DIVIDER};\n\n // position and range, full floats\n let lightPosRange: vec4f = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_POSITION_RANGE});\n clusterLightData.position = lightPosRange.xyz;\n clusterLightData.range = lightPosRange.w;\n\n // spot direction & flags data\n let lightDir_Flags: vec4f = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_DIRECTION_FLAGS});\n\n // spot light direction\n clusterLightData.direction = lightDir_Flags.xyz;\n\n // 32bit flags\n let flags_uint: u32 = bitcast<u32>(lightDir_Flags.w);\n clusterLightData.flags = flags_uint;\n clusterLightData.isSpot = (flags_uint & (1u << 30u)) != 0u;\n clusterLightData.shape = (flags_uint >> 28u) & 0x3u;\n clusterLightData.falloffModeLinear = (flags_uint & (1u << 27u)) == 0u;\n clusterLightData.shadowIntensity = f32((flags_uint >> 0u) & 0xFFu) / 255.0;\n clusterLightData.cookieIntensity = f32((flags_uint >> 8u) & 0xFFu) / 255.0;\n clusterLightData.isDynamic = (flags_uint & (1u << 22u)) != 0u;\n clusterLightData.isLightmapped = (flags_uint & (1u << 21u)) != 0u;\n}\n\nfn decodeClusterLightSpot(clusterLightData: ptr<function, ClusterLightData>) {\n // spot light cos angles\n let angles: vec2f = unpack2x16float(bitcast<u32>(clusterLightData.anglesData));\n clusterLightData.innerConeAngleCos = angles.x;\n clusterLightData.outerConeAngleCos = angles.y;\n}\n\nfn decodeClusterLightOmniAtlasViewport(clusterLightData: ptr<function, ClusterLightData>) {\n clusterLightData.omniAtlasViewport = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_0}).xyz;\n}\n\nfn decodeClusterLightAreaData(clusterLightData: ptr<function, ClusterLightData>) {\n clusterLightData.halfWidth = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_AREA_DATA_WIDTH}).xyz;\n clusterLightData.halfHeight = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_AREA_DATA_HEIGHT}).xyz;\n}\n\nfn decodeClusterLightProjectionMatrixData(clusterLightData: ptr<function, ClusterLightData>) {\n // shadow matrix\n let m0: vec4f = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_0});\n let m1: vec4f = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_1});\n let m2: vec4f = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_2});\n let m3: vec4f = sampleLightTextureF(clusterLightData.lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_3});\n lightProjectionMatrix = mat4x4f(m0, m1, m2, m3);\n}\n\nfn decodeClusterLightShadowData(clusterLightData: ptr<function, ClusterLightData>) {\n // shadow biases\n let biases: vec2f = unpack2x16float(bitcast<u32>(clusterLightData.biasesData));\n clusterLightData.shadowBias = biases.x;\n clusterLightData.shadowNormalBias = biases.y;\n}\n\nfn decodeClusterLightCookieData(clusterLightData: ptr<function, ClusterLightData>) {\n\n // extract channel mask from flags\n let cookieFlags: u32 = (clusterLightData.flags >> 23u) & 0x0Fu; // 4bits, each bit enables a channel\n let mask_uvec: vec4<u32> = vec4<u32>(cookieFlags) & vec4<u32>(1u, 2u, 4u, 8u);\n clusterLightData.cookieChannelMask = step(vec4f(1.0), vec4f(mask_uvec)); // Normalize to 0.0 or 1.0\n}\n\nfn evaluateLight(\n light: ptr<function, ClusterLightData>,\n worldNormal: vec3f,\n viewDir: vec3f,\n reflectionDir: vec3f,\n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir: vec3f,\n#endif\n gloss: f32,\n specularity: vec3f,\n geometricNormal: vec3f,\n tbn: mat3x3f,\n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel: vec3f,\n#endif\n clearcoat_worldNormal: vec3f,\n clearcoat_gloss: f32,\n sheen_gloss: f32,\n iridescence_intensity: f32\n) {\n\n var cookieAttenuation: vec3f = vec3f(1.0);\n var diffuseAttenuation: f32 = 1.0;\n var falloffAttenuation: f32 = 1.0;\n\n // evaluate omni part of the light\n let lightDirW: vec3f = evalOmniLight(light.position);\n let lightDirNormW: vec3f = normalize(lightDirW);\n\n #ifdef CLUSTER_AREALIGHTS\n\n // distance attenuation\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // area lights\n decodeClusterLightAreaData(light);\n\n // handle light shape\n if (light.shape == {LIGHTSHAPE_RECT}) {\n calcRectLightValues(light.position, light.halfWidth, light.halfHeight);\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n calcDiskLightValues(light.position, light.halfWidth, light.halfHeight);\n } else { // sphere\n calcSphereLightValues(light.position, light.halfWidth, light.halfHeight);\n }\n\n falloffAttenuation = getFalloffWindow(light.range, lightDirW);\n\n } else\n\n #endif\n\n { // punctual light\n\n if (light.falloffModeLinear) {\n falloffAttenuation = getFalloffLinear(light.range, lightDirW);\n } else {\n falloffAttenuation = getFalloffInvSquared(light.range, lightDirW);\n }\n }\n\n if (falloffAttenuation > 0.00001) {\n\n #ifdef CLUSTER_AREALIGHTS\n\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // handle light shape\n if (light.shape == {LIGHTSHAPE_RECT}) {\n diffuseAttenuation = getRectLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n diffuseAttenuation = getDiskLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n } else { // sphere\n diffuseAttenuation = getSphereLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n }\n\n } else\n\n #endif\n\n {\n falloffAttenuation = falloffAttenuation * getLightDiffuse(worldNormal, viewDir, lightDirNormW);\n }\n\n // spot light falloff\n if (light.isSpot) {\n decodeClusterLightSpot(light);\n falloffAttenuation = falloffAttenuation * getSpotEffect(light.direction, light.innerConeAngleCos, light.outerConeAngleCos, lightDirNormW);\n }\n\n #if defined(CLUSTER_COOKIES) || defined(CLUSTER_SHADOWS)\n\n if (falloffAttenuation > 0.00001) {\n\n // shadow / cookie\n if (light.shadowIntensity > 0.0 || light.cookieIntensity > 0.0) {\n\n // shared shadow / cookie data depends on light type\n if (light.isSpot) {\n decodeClusterLightProjectionMatrixData(light);\n } else {\n decodeClusterLightOmniAtlasViewport(light);\n }\n\n let shadowTextureResolution: f32 = uniform.shadowAtlasParams.x;\n let shadowEdgePixels: f32 = uniform.shadowAtlasParams.y;\n\n #ifdef CLUSTER_COOKIES\n\n // cookie\n if (light.cookieIntensity > 0.0) {\n decodeClusterLightCookieData(light);\n\n if (light.isSpot) {\n // !!!!!!!!!!! TEXTURE_PASS likely needs sampler. Assuming cookieAtlasTextureSampler exists.\n cookieAttenuation = getCookie2DClustered(cookieAtlasTexture, cookieAtlasTextureSampler, lightProjectionMatrix, vPositionW, light.cookieIntensity, light.cookieChannelMask);\n } else {\n // !!!!!!!!!!! TEXTURE_PASS likely needs sampler. Assuming cookieAtlasTextureSampler exists.\n cookieAttenuation = getCookieCubeClustered(cookieAtlasTexture, cookieAtlasTextureSampler, lightDirW, light.cookieIntensity, light.cookieChannelMask, shadowTextureResolution, shadowEdgePixels, light.omniAtlasViewport);\n }\n }\n\n #endif\n\n #ifdef CLUSTER_SHADOWS\n\n // shadow\n if (light.shadowIntensity > 0.0) {\n decodeClusterLightShadowData(light);\n\n let shadowParams: vec4f = vec4f(shadowTextureResolution, light.shadowNormalBias, light.shadowBias, 1.0 / light.range);\n\n if (light.isSpot) {\n\n // spot shadow\n let shadowCoord: vec3f = getShadowCoordPerspZbufferNormalOffset(lightProjectionMatrix, shadowParams, geometricNormal);\n\n // !!!!!!!!!!! SHADOWMAP_PASS needs texture and sampler_comparison.\n // !!!!!!!!!!! Shadow functions need update for WGSL textureSampleCompare etc. Assuming these are handled in includes.\n #if defined(CLUSTER_SHADOW_TYPE_PCF1)\n let shadow: f32 = getShadowSpotClusteredPCF1(shadowAtlasTexture, shadowAtlasTextureSampler, shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF3)\n let shadow: f32 = getShadowSpotClusteredPCF3(shadowAtlasTexture, shadowAtlasTextureSampler, shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF5)\n let shadow: f32 = getShadowSpotClusteredPCF5(shadowAtlasTexture, shadowAtlasTextureSampler, shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCSS)\n let shadow: f32 = getShadowSpotClusteredPCSS(shadowAtlasTexture, shadowAtlasTextureSampler, shadowCoord, shadowParams);\n #endif\n falloffAttenuation = falloffAttenuation * mix(1.0, shadow, light.shadowIntensity);\n\n } else {\n\n // omni shadow\n let dir: vec3f = normalOffsetPointShadow(shadowParams, light.position, lightDirW, lightDirNormW, geometricNormal); // normalBias adjusted for distance\n\n // !!!!!!!!!!! SHADOWMAP_PASS needs texture and sampler_comparison.\n // !!!!!!!!!!! Shadow functions need update for WGSL textureSampleCompare etc. Assuming these are handled in includes.\n #if defined(CLUSTER_SHADOW_TYPE_PCF1)\n let shadow: f32 = getShadowOmniClusteredPCF1(shadowAtlasTexture, shadowAtlasTextureSampler, shadowParams, light.omniAtlasViewport, shadowEdgePixels, dir);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF3)\n let shadow: f32 = getShadowOmniClusteredPCF3(shadowAtlasTexture, shadowAtlasTextureSampler, shadowParams, light.omniAtlasViewport, shadowEdgePixels, dir);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF5)\n let shadow: f32 = getShadowOmniClusteredPCF5(shadowAtlasTexture, shadowAtlasTextureSampler, shadowParams, light.omniAtlasViewport, shadowEdgePixels, dir);\n #endif\n falloffAttenuation = falloffAttenuation * mix(1.0, shadow, light.shadowIntensity);\n }\n }\n\n #endif\n }\n }\n\n #endif\n\n // diffuse / specular / clearcoat\n #ifdef CLUSTER_AREALIGHTS\n\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // area light diffuse\n {\n var areaDiffuse: vec3f = (diffuseAttenuation * falloffAttenuation) * light.color * cookieAttenuation;\n\n #if defined(LIT_SPECULAR)\n areaDiffuse = mix(areaDiffuse, vec3f(0.0), dLTCSpecFres);\n #endif\n\n // area light diffuse - it does not mix diffuse lighting into specular attenuation\n dDiffuseLight = dDiffuseLight + areaDiffuse;\n }\n\n // specular and clear coat are material settings and get included by a define based on the material\n #ifdef LIT_SPECULAR\n\n // area light specular\n var areaLightSpecular: f32; // Use var because assigned in if/else\n\n if (light.shape == {LIGHTSHAPE_RECT}) {\n areaLightSpecular = getRectLightSpecular(worldNormal, viewDir);\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n areaLightSpecular = getDiskLightSpecular(worldNormal, viewDir);\n } else { // sphere\n areaLightSpecular = getSphereLightSpecular(worldNormal, viewDir);\n }\n\n dSpecularLight = dSpecularLight + dLTCSpecFres * areaLightSpecular * falloffAttenuation * light.color * cookieAttenuation;\n\n #ifdef LIT_CLEARCOAT\n\n // area light specular clear coat\n var areaLightSpecularCC: f32; // Use var because assigned in if/else\n\n if (light.shape == {LIGHTSHAPE_RECT}) {\n areaLightSpecularCC = getRectLightSpecular(clearcoat_worldNormal, viewDir);\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n areaLightSpecularCC = getDiskLightSpecular(clearcoat_worldNormal, viewDir);\n } else { // sphere\n areaLightSpecularCC = getSphereLightSpecular(clearcoat_worldNormal, viewDir);\n }\n\n ccSpecularLight = ccSpecularLight + ccLTCSpecFres * areaLightSpecularCC * falloffAttenuation * light.color * cookieAttenuation;\n\n #endif\n\n #endif\n\n } else\n\n #endif\n\n { // punctual light\n\n // punctual light diffuse\n {\n var punctualDiffuse: vec3f = falloffAttenuation * light.color * cookieAttenuation;\n\n #if defined(CLUSTER_AREALIGHTS)\n #if defined(LIT_SPECULAR)\n punctualDiffuse = mix(punctualDiffuse, vec3f(0.0), specularity);\n #endif\n #endif\n\n dDiffuseLight = dDiffuseLight + punctualDiffuse;\n }\n\n // specular and clear coat are material settings and get included by a define based on the material\n #ifdef LIT_SPECULAR\n\n let halfDir: vec3f = normalize(-lightDirNormW + viewDir);\n\n // specular\n #ifdef LIT_SPECULAR_FRESNEL\n dSpecularLight = dSpecularLight +\n getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation *\n getFresnel(\n dot(viewDir, halfDir),\n gloss,\n specularity\n #if defined(LIT_IRIDESCENCE)\n , iridescenceFresnel,\n iridescence_intensity\n #endif\n );\n #else\n dSpecularLight = dSpecularLight + getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * specularity;\n #endif\n\n #ifdef LIT_CLEARCOAT\n #ifdef LIT_SPECULAR_FRESNEL\n ccSpecularLight = ccSpecularLight + getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * getFresnelCC(dot(viewDir, halfDir));\n #else\n ccSpecularLight = ccSpecularLight + getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation;\n #endif\n #endif\n\n #ifdef LIT_SHEEN\n sSpecularLight = sSpecularLight + getLightSpecularSheen(halfDir, worldNormal, viewDir, lightDirNormW, sheen_gloss) * falloffAttenuation * light.color * cookieAttenuation;\n #endif\n\n #endif\n }\n }\n\n // Write to global attenuation values (for lightmapper)\n dAtten = falloffAttenuation;\n dLightDirNormW = lightDirNormW;\n}\n\n\nfn evaluateClusterLight(\n lightIndex: f32,\n worldNormal: vec3f,\n viewDir: vec3f,\n reflectionDir: vec3f,\n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir: vec3f,\n#endif\n gloss: f32,\n specularity: vec3f,\n geometricNormal: vec3f,\n tbn: mat3x3f,\n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel: vec3f,\n#endif\n clearcoat_worldNormal: vec3f,\n clearcoat_gloss: f32,\n sheen_gloss: f32,\n iridescence_intensity: f32\n) {\n\n // decode core light data from textures\n var clusterLightData: ClusterLightData;\n decodeClusterLightCore(&clusterLightData, lightIndex);\n\n // evaluate light if it uses accepted light mask\n #ifdef CLUSTER_MESH_DYNAMIC_LIGHTS\n let acceptLightMask: bool = clusterLightData.isDynamic;\n #else\n let acceptLightMask: bool = clusterLightData.isLightmapped;\n #endif\n\n if (acceptLightMask) {\n evaluateLight(\n &clusterLightData,\n worldNormal,\n viewDir,\n reflectionDir,\n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir,\n#endif\n gloss,\n specularity,\n geometricNormal,\n tbn,\n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel,\n#endif\n clearcoat_worldNormal,\n clearcoat_gloss,\n sheen_gloss,\n iridescence_intensity\n );\n }\n}\n\n\nfn addClusteredLights(\n worldNormal: vec3f,\n viewDir: vec3f,\n reflectionDir: vec3f,\n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir: vec3f,\n#endif\n gloss: f32,\n specularity: vec3f,\n geometricNormal: vec3f,\n tbn: mat3x3f,\n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel: vec3f,\n#endif\n clearcoat_worldNormal: vec3f,\n clearcoat_gloss: f32,\n sheen_gloss: f32,\n iridescence_intensity: f32\n) {\n\n // skip lights if no lights at all\n if (uniform.clusterSkip > 0.5) {\n return;\n }\n\n // world space position to 3d integer cell cordinates in the cluster structure\n let cellCoords: vec3f = floor((vPositionW - uniform.clusterBoundsMin) * uniform.clusterCellsCountByBoundsSize);\n\n // no lighting when cell coordinate is out of range\n if (!(any(cellCoords < vec3f(0.0)) || any(cellCoords >= uniform.clusterCellsMax))) {\n\n // cell index (mapping from 3d cell coordinates to linear memory)\n let cellIndex: f32 = dot(uniform.clusterCellsDot, cellCoords);\n\n // convert cell index to uv coordinates\n let clusterV: f32 = floor(cellIndex * uniform.clusterTextureSize.y);\n let clusterU: f32 = cellIndex - (clusterV * uniform.clusterTextureSize.x);\n\n // loop over maximum number of light cells\n for (var lightCellIndex: i32 = 0; lightCellIndex < uniform.clusterMaxCells; lightCellIndex = lightCellIndex + 1) {\n\n // using a single channel texture with data in red channel\n let lightIndexPacked: f32 = textureLoad(clusterWorldTexture, vec2<i32>(i32(clusterU) + lightCellIndex, i32(clusterV)), 0).r;\n\n if (lightIndexPacked <= 0.0) {\n break;\n }\n\n evaluateClusterLight(\n lightIndexPacked * 255.0,\n worldNormal,\n viewDir,\n reflectionDir,\n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir,\n#endif\n gloss,\n specularity,\n geometricNormal,\n tbn,\n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel,\n#endif\n clearcoat_worldNormal,\n clearcoat_gloss,\n sheen_gloss,\n iridescence_intensity\n );\n }\n }\n}"; export default _default;