UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

408 lines (401 loc) 15.3 kB
var clusteredLightPS = ` #include "lightBufferDefinesPS" #include "clusteredLightUtilsPS" #ifdef CLUSTER_COOKIES #include "clusteredLightCookiesPS" #endif #ifdef CLUSTER_SHADOWS #include "clusteredLightShadowsPS" #endif uniform highp sampler2D clusterWorldTexture; uniform highp sampler2D lightsTexture; #ifdef CLUSTER_SHADOWS uniform sampler2DShadow shadowAtlasTexture; #endif #ifdef CLUSTER_COOKIES uniform sampler2D cookieAtlasTexture; #endif uniform int clusterMaxCells; uniform float clusterSkip; uniform vec3 clusterCellsCountByBoundsSize; uniform vec3 clusterTextureSize; uniform vec3 clusterBoundsMin; uniform vec3 clusterBoundsDelta; uniform vec3 clusterCellsDot; uniform vec3 clusterCellsMax; uniform vec2 shadowAtlasParams; struct ClusterLightData { uint flags; vec3 halfWidth; bool isSpot; vec3 halfHeight; int lightIndex; vec3 position; uint shape; vec3 direction; bool falloffModeLinear; vec3 color; float shadowIntensity; vec3 omniAtlasViewport; float range; vec4 cookieChannelMask; float biasesData; float shadowBias; float shadowNormalBias; float anglesData; float innerConeAngleCos; float outerConeAngleCos; float cookieIntensity; bool isDynamic; bool isLightmapped; }; mat4 lightProjectionMatrix; vec4 sampleLightTextureF(const ClusterLightData clusterLightData, int index) { return texelFetch(lightsTexture, ivec2(index, clusterLightData.lightIndex), 0); } void decodeClusterLightCore(inout ClusterLightData clusterLightData, float lightIndex) { clusterLightData.lightIndex = int(lightIndex); vec4 halfData = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_COLOR_ANGLES_BIAS}); clusterLightData.anglesData = halfData.z; clusterLightData.biasesData = halfData.w; vec2 colorRG = unpackHalf2x16(floatBitsToUint(halfData.x)); vec2 colorB_ = unpackHalf2x16(floatBitsToUint(halfData.y)); clusterLightData.color = vec3(colorRG, colorB_.x) * {LIGHT_COLOR_DIVIDER}; vec4 lightPosRange = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_POSITION_RANGE}); clusterLightData.position = lightPosRange.xyz; clusterLightData.range = lightPosRange.w; vec4 lightDir_Flags = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_DIRECTION_FLAGS}); clusterLightData.direction = lightDir_Flags.xyz; clusterLightData.flags = floatBitsToUint(lightDir_Flags.w); clusterLightData.isSpot = (clusterLightData.flags & (1u << 30u)) != 0u; clusterLightData.shape = (clusterLightData.flags >> 28u) & 0x3u; clusterLightData.falloffModeLinear = (clusterLightData.flags & (1u << 27u)) == 0u; clusterLightData.shadowIntensity = float((clusterLightData.flags >> 0u) & 0xFFu) / 255.0; clusterLightData.cookieIntensity = float((clusterLightData.flags >> 8u) & 0xFFu) / 255.0; clusterLightData.isDynamic = (clusterLightData.flags & (1u << 22u)) != 0u; clusterLightData.isLightmapped = (clusterLightData.flags & (1u << 21u)) != 0u; } void decodeClusterLightSpot(inout ClusterLightData clusterLightData) { vec2 angles = unpackHalf2x16(floatBitsToUint(clusterLightData.anglesData)); clusterLightData.innerConeAngleCos = angles.x; clusterLightData.outerConeAngleCos = angles.y; } void decodeClusterLightOmniAtlasViewport(inout ClusterLightData clusterLightData) { clusterLightData.omniAtlasViewport = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_PROJ_MAT_0}).xyz; } void decodeClusterLightAreaData(inout ClusterLightData clusterLightData) { clusterLightData.halfWidth = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_AREA_DATA_WIDTH}).xyz; clusterLightData.halfHeight = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_AREA_DATA_HEIGHT}).xyz; } void decodeClusterLightProjectionMatrixData(inout ClusterLightData clusterLightData) { vec4 m0 = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_PROJ_MAT_0}); vec4 m1 = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_PROJ_MAT_1}); vec4 m2 = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_PROJ_MAT_2}); vec4 m3 = sampleLightTextureF(clusterLightData, {CLUSTER_TEXTURE_PROJ_MAT_3}); lightProjectionMatrix = mat4(m0, m1, m2, m3); } void decodeClusterLightShadowData(inout ClusterLightData clusterLightData) { vec2 biases = unpackHalf2x16(floatBitsToUint(clusterLightData.biasesData)); clusterLightData.shadowBias = biases.x; clusterLightData.shadowNormalBias = biases.y; } void decodeClusterLightCookieData(inout ClusterLightData clusterLightData) { uint cookieFlags = (clusterLightData.flags >> 23u) & 0x0Fu; clusterLightData.cookieChannelMask = vec4(uvec4(cookieFlags) & uvec4(1u, 2u, 4u, 8u)); clusterLightData.cookieChannelMask = step(1.0, clusterLightData.cookieChannelMask); } void evaluateLight( ClusterLightData light, vec3 worldNormal, vec3 viewDir, vec3 reflectionDir, #if defined(LIT_CLEARCOAT) vec3 clearcoatReflectionDir, #endif float gloss, vec3 specularity, vec3 geometricNormal, mat3 tbn, #if defined(LIT_IRIDESCENCE) vec3 iridescenceFresnel, #endif vec3 clearcoat_worldNormal, float clearcoat_gloss, float sheen_gloss, float iridescence_intensity ) { vec3 cookieAttenuation = vec3(1.0); float diffuseAttenuation = 1.0; float falloffAttenuation = 1.0; vec3 lightDirW = evalOmniLight(light.position); vec3 lightDirNormW = normalize(lightDirW); #ifdef CLUSTER_AREALIGHTS if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { decodeClusterLightAreaData(light); if (light.shape == {LIGHTSHAPE_RECT}) { calcRectLightValues(light.position, light.halfWidth, light.halfHeight); } else if (light.shape == {LIGHTSHAPE_DISK}) { calcDiskLightValues(light.position, light.halfWidth, light.halfHeight); } else { calcSphereLightValues(light.position, light.halfWidth, light.halfHeight); } falloffAttenuation = getFalloffWindow(light.range, lightDirW); } else #endif { if (light.falloffModeLinear) falloffAttenuation = getFalloffLinear(light.range, lightDirW); else falloffAttenuation = getFalloffInvSquared(light.range, lightDirW); } if (falloffAttenuation > 0.00001) { #ifdef CLUSTER_AREALIGHTS if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { if (light.shape == {LIGHTSHAPE_RECT}) { diffuseAttenuation = getRectLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0; } else if (light.shape == {LIGHTSHAPE_DISK}) { diffuseAttenuation = getDiskLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0; } else { diffuseAttenuation = getSphereLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0; } } else #endif { falloffAttenuation *= getLightDiffuse(worldNormal, viewDir, lightDirNormW); } if (light.isSpot) { decodeClusterLightSpot(light); falloffAttenuation *= getSpotEffect(light.direction, light.innerConeAngleCos, light.outerConeAngleCos, lightDirNormW); } #if defined(CLUSTER_COOKIES) || defined(CLUSTER_SHADOWS) if (falloffAttenuation > 0.00001) { if (light.shadowIntensity > 0.0 || light.cookieIntensity > 0.0) { if (light.isSpot) { decodeClusterLightProjectionMatrixData(light); } else { decodeClusterLightOmniAtlasViewport(light); } float shadowTextureResolution = shadowAtlasParams.x; float shadowEdgePixels = shadowAtlasParams.y; #ifdef CLUSTER_COOKIES if (light.cookieIntensity > 0.0) { decodeClusterLightCookieData(light); if (light.isSpot) { cookieAttenuation = getCookie2DClustered(TEXTURE_PASS(cookieAtlasTexture), lightProjectionMatrix, vPositionW, light.cookieIntensity, light.cookieChannelMask); } else { cookieAttenuation = getCookieCubeClustered(TEXTURE_PASS(cookieAtlasTexture), lightDirW, light.cookieIntensity, light.cookieChannelMask, shadowTextureResolution, shadowEdgePixels, light.omniAtlasViewport); } } #endif #ifdef CLUSTER_SHADOWS if (light.shadowIntensity > 0.0) { decodeClusterLightShadowData(light); vec4 shadowParams = vec4(shadowTextureResolution, light.shadowNormalBias, light.shadowBias, 1.0 / light.range); if (light.isSpot) { vec3 shadowCoord = getShadowCoordPerspZbufferNormalOffset(lightProjectionMatrix, shadowParams, geometricNormal); #if defined(CLUSTER_SHADOW_TYPE_PCF1) float shadow = getShadowSpotClusteredPCF1(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams); #elif defined(CLUSTER_SHADOW_TYPE_PCF3) float shadow = getShadowSpotClusteredPCF3(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams); #elif defined(CLUSTER_SHADOW_TYPE_PCF5) float shadow = getShadowSpotClusteredPCF5(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams); #elif defined(CLUSTER_SHADOW_TYPE_PCSS) float shadow = getShadowSpotClusteredPCSS(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams); #endif falloffAttenuation *= mix(1.0, shadow, light.shadowIntensity); } else { vec3 dir = normalOffsetPointShadow(shadowParams, light.position, lightDirW, lightDirNormW, geometricNormal); #if defined(CLUSTER_SHADOW_TYPE_PCF1) float shadow = getShadowOmniClusteredPCF1(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, light.omniAtlasViewport, shadowEdgePixels, dir); #elif defined(CLUSTER_SHADOW_TYPE_PCF3) float shadow = getShadowOmniClusteredPCF3(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, light.omniAtlasViewport, shadowEdgePixels, dir); #elif defined(CLUSTER_SHADOW_TYPE_PCF5) float shadow = getShadowOmniClusteredPCF5(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, light.omniAtlasViewport, shadowEdgePixels, dir); #endif falloffAttenuation *= mix(1.0, shadow, light.shadowIntensity); } } #endif } } #endif #ifdef CLUSTER_AREALIGHTS if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { { vec3 areaDiffuse = (diffuseAttenuation * falloffAttenuation) * light.color * cookieAttenuation; #if defined(LIT_SPECULAR) areaDiffuse = mix(areaDiffuse, vec3(0), dLTCSpecFres); #endif dDiffuseLight += areaDiffuse; } #ifdef LIT_SPECULAR float areaLightSpecular; if (light.shape == {LIGHTSHAPE_RECT}) { areaLightSpecular = getRectLightSpecular(worldNormal, viewDir); } else if (light.shape == {LIGHTSHAPE_DISK}) { areaLightSpecular = getDiskLightSpecular(worldNormal, viewDir); } else { areaLightSpecular = getSphereLightSpecular(worldNormal, viewDir); } dSpecularLight += dLTCSpecFres * areaLightSpecular * falloffAttenuation * light.color * cookieAttenuation; #ifdef LIT_CLEARCOAT float areaLightSpecularCC; if (light.shape == {LIGHTSHAPE_RECT}) { areaLightSpecularCC = getRectLightSpecular(clearcoat_worldNormal, viewDir); } else if (light.shape == {LIGHTSHAPE_DISK}) { areaLightSpecularCC = getDiskLightSpecular(clearcoat_worldNormal, viewDir); } else { areaLightSpecularCC = getSphereLightSpecular(clearcoat_worldNormal, viewDir); } ccSpecularLight += ccLTCSpecFres * areaLightSpecularCC * falloffAttenuation * light.color * cookieAttenuation; #endif #endif } else #endif { { vec3 punctualDiffuse = falloffAttenuation * light.color * cookieAttenuation; #if defined(CLUSTER_AREALIGHTS) #if defined(LIT_SPECULAR) punctualDiffuse = mix(punctualDiffuse, vec3(0), specularity); #endif #endif dDiffuseLight += punctualDiffuse; } #ifdef LIT_SPECULAR vec3 halfDir = normalize(-lightDirNormW + viewDir); #ifdef LIT_SPECULAR_FRESNEL dSpecularLight += getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * getFresnel( dot(viewDir, halfDir), gloss, specularity #if defined(LIT_IRIDESCENCE) , iridescenceFresnel, iridescence_intensity #endif ); #else dSpecularLight += getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * specularity; #endif #ifdef LIT_CLEARCOAT #ifdef LIT_SPECULAR_FRESNEL ccSpecularLight += getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * getFresnelCC(dot(viewDir, halfDir)); #else ccSpecularLight += getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation; #endif #endif #ifdef LIT_SHEEN sSpecularLight += getLightSpecularSheen(halfDir, worldNormal, viewDir, lightDirNormW, sheen_gloss) * falloffAttenuation * light.color * cookieAttenuation; #endif #endif } } dAtten = falloffAttenuation; dLightDirNormW = lightDirNormW; } void evaluateClusterLight( float lightIndex, vec3 worldNormal, vec3 viewDir, vec3 reflectionDir, #if defined(LIT_CLEARCOAT) vec3 clearcoatReflectionDir, #endif float gloss, vec3 specularity, vec3 geometricNormal, mat3 tbn, #if defined(LIT_IRIDESCENCE) vec3 iridescenceFresnel, #endif vec3 clearcoat_worldNormal, float clearcoat_gloss, float sheen_gloss, float iridescence_intensity ) { ClusterLightData clusterLightData; decodeClusterLightCore(clusterLightData, lightIndex); #ifdef CLUSTER_MESH_DYNAMIC_LIGHTS bool acceptLightMask = clusterLightData.isDynamic; #else bool acceptLightMask = clusterLightData.isLightmapped; #endif if (acceptLightMask) evaluateLight( clusterLightData, worldNormal, viewDir, reflectionDir, #if defined(LIT_CLEARCOAT) clearcoatReflectionDir, #endif gloss, specularity, geometricNormal, tbn, #if defined(LIT_IRIDESCENCE) iridescenceFresnel, #endif clearcoat_worldNormal, clearcoat_gloss, sheen_gloss, iridescence_intensity ); } void addClusteredLights( vec3 worldNormal, vec3 viewDir, vec3 reflectionDir, #if defined(LIT_CLEARCOAT) vec3 clearcoatReflectionDir, #endif float gloss, vec3 specularity, vec3 geometricNormal, mat3 tbn, #if defined(LIT_IRIDESCENCE) vec3 iridescenceFresnel, #endif vec3 clearcoat_worldNormal, float clearcoat_gloss, float sheen_gloss, float iridescence_intensity ) { if (clusterSkip > 0.5) return; vec3 cellCoords = floor((vPositionW - clusterBoundsMin) * clusterCellsCountByBoundsSize); if (!(any(lessThan(cellCoords, vec3(0.0))) || any(greaterThanEqual(cellCoords, clusterCellsMax)))) { float cellIndex = dot(clusterCellsDot, cellCoords); float clusterV = floor(cellIndex * clusterTextureSize.y); float clusterU = cellIndex - (clusterV * clusterTextureSize.x); for (int lightCellIndex = 0; lightCellIndex < clusterMaxCells; lightCellIndex++) { float lightIndex = texelFetch(clusterWorldTexture, ivec2(int(clusterU) + lightCellIndex, clusterV), 0).x; if (lightIndex <= 0.0) break; evaluateClusterLight( lightIndex * 255.0, worldNormal, viewDir, reflectionDir, #if defined(LIT_CLEARCOAT) clearcoatReflectionDir, #endif gloss, specularity, geometricNormal, tbn, #if defined(LIT_IRIDESCENCE) iridescenceFresnel, #endif clearcoat_worldNormal, clearcoat_gloss, sheen_gloss, iridescence_intensity ); } } } `; export { clusteredLightPS as default };