diff --git a/Shaders/clustered-include.frag b/Shaders/clustered-include.frag index 9b042033a..c6338800b 100644 --- a/Shaders/clustered-include.frag +++ b/Shaders/clustered-include.frag @@ -1,18 +1,22 @@ #version 120 uniform sampler3D fg_Clusters; +uniform sampler2D fg_ClusteredIndices; uniform sampler2D fg_ClusteredPointLights; uniform sampler2D fg_ClusteredSpotLights; +uniform bool fg_ClusteredEnabled; +uniform int fg_ClusteredMaxPointLights; +uniform int fg_ClusteredMaxSpotLights; +uniform int fg_ClusteredMaxLightIndices; uniform int fg_ClusteredTileSize; +uniform int fg_ClusteredDepthSlices; uniform float fg_ClusteredSliceScale; uniform float fg_ClusteredSliceBias; uniform int fg_ClusteredHorizontalTiles; uniform int fg_ClusteredVerticalTiles; -const int MAX_POINTLIGHTS = 1024; -const int MAX_SPOTLIGHTS = 1024; -const int MAX_LIGHT_GROUPS_PER_CLUSTER = 255; +const bool DEBUG = false; struct PointLight { vec4 position; @@ -37,7 +41,7 @@ struct SpotLight { PointLight unpackPointLight(int index) { PointLight light; - float v = (float(index) + 0.5) / float(MAX_POINTLIGHTS); + float v = (float(index) + 0.5) / float(fg_ClusteredMaxPointLights); light.position = texture2D(fg_ClusteredPointLights, vec2(0.1, v)); light.ambient = texture2D(fg_ClusteredPointLights, vec2(0.3, v)); light.diffuse = texture2D(fg_ClusteredPointLights, vec2(0.5, v)); @@ -49,195 +53,129 @@ PointLight unpackPointLight(int index) SpotLight unpackSpotLight(int index) { SpotLight light; - float v = (float(index) + 0.5) / float(MAX_SPOTLIGHTS); + float v = (float(index) + 0.5) / float(fg_ClusteredMaxSpotLights); light.position = texture2D(fg_ClusteredSpotLights, vec2(0.0714, v)); light.direction = texture2D(fg_ClusteredSpotLights, vec2(0.2143, v)); light.ambient = texture2D(fg_ClusteredSpotLights, vec2(0.3571, v)); light.diffuse = texture2D(fg_ClusteredSpotLights, vec2(0.5, v)); light.specular = texture2D(fg_ClusteredSpotLights, vec2(0.6429, v)); light.attenuation = texture2D(fg_ClusteredSpotLights, vec2(0.7857, v)); - vec2 reminder = texture2D(fg_ClusteredSpotLights, vec2(0.9286, v)).xy; - light.cos_cutoff = reminder.x; - light.exponent = reminder.y; + vec2 remainder = texture2D(fg_ClusteredSpotLights, vec2(0.9286, v)).xy; + light.cos_cutoff = remainder.x; + light.exponent = remainder.y; return light; } +int getIndex(int counter) +{ + vec2 coords = vec2(mod(float(counter), float(fg_ClusteredMaxLightIndices)) + 0.5, + float(counter / fg_ClusteredMaxLightIndices) + 0.5); + // Normalize + coords /= vec2(fg_ClusteredMaxLightIndices); + return int(texture2D(fg_ClusteredIndices, coords).r); +} + // @param p Fragment position in view space. // @param n Fragment normal in view space. +// @param texel The diffuse (or albedo) color of the surface. It's usually just +// the one on texture unit 0. +// @return The total color contribution of every light affecting the fragment. +// This result should be added to the fragment color before applying +// any haze, fog or post-processing. vec3 getClusteredLightsContribution(vec3 p, vec3 n, vec3 texel) { - int zSlice = int(max(log2(-p.z) * fg_ClusteredSliceScale - + fg_ClusteredSliceBias, 0.0)); - int ySlice = int(gl_FragCoord.y) / fg_ClusteredTileSize * zSlice; - int xSlice = int(gl_FragCoord.x) / fg_ClusteredTileSize; + if (!fg_ClusteredEnabled) + return vec3(0.0); - vec2 clusterCoords = vec2( - (float(xSlice) + 0.5) / fg_ClusteredHorizontalTiles, - (float(ySlice) * float(zSlice) + 0.5) / fg_ClusteredVerticalTiles); + int slice = int(max(log2(-p.z) * fg_ClusteredSliceScale + + fg_ClusteredSliceBias, 0.0)); + vec3 clusterCoords = vec3(floor(gl_FragCoord.xy / fg_ClusteredTileSize), + slice) + vec3(0.5); // Pixel center + // Normalize + clusterCoords /= vec3(fg_ClusteredHorizontalTiles, + fg_ClusteredVerticalTiles, + fg_ClusteredDepthSlices); - int pointCount = int(texture3D(fg_Clusters, vec3(clusterCoords, 0.0)).r); - int spotCount = int(texture3D(fg_Clusters, vec3(clusterCoords, 0.0)).g); + vec3 cluster = texture3D(fg_Clusters, clusterCoords).rgb; + int lightIndex = int(cluster.r); + int pointCount = int(cluster.g); + int spotCount = int(cluster.b); - int lightGroupCount = int(ceil(float(pointCount + spotCount) / 4.0)); + if (DEBUG) { + vec2 margin = step(1.0, mod(gl_FragCoord.xy, vec2(fg_ClusteredTileSize))); + return mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), + float(pointCount) / 5.0) * margin.x * margin.y; + } vec3 color = vec3(0.0); - for (int i = 0; i < lightGroupCount; ++i) { - float r = (float(i + 1) + 0.5) / float(MAX_LIGHT_GROUPS_PER_CLUSTER + 1); - vec4 packedIndices = texture3D(fg_Clusters, vec3(clusterCoords, r)); + for (int i = 0; i < pointCount; ++i) { + int index = getIndex(lightIndex++); + PointLight light = unpackPointLight(index); - for (int j = 0; j < 4; ++j) { - int index; - if (j == 0) index = int(packedIndices.x); - else if (j == 1) index = int(packedIndices.y); - else if (j == 2) index = int(packedIndices.z); - else if (j == 3) index = int(packedIndices.w); - else break; + float range = light.attenuation.w; + vec3 toLight = light.position.xyz - p; + // Ignore fragments outside the light volume + if (dot(toLight, toLight) > (range * range)) + continue; - int currentLight = i * 4 + j; - if (currentLight < pointCount) { - // This is a point light - PointLight light = unpackPointLight(index); + float d = length(toLight); + float att = 1.0 / (light.attenuation.x // constant + + light.attenuation.y * d // linear + + light.attenuation.z * d * d); // quadratic + vec3 lightDir = normalize(toLight); + float NdotL = max(dot(n, lightDir), 0.0); - float range = light.attenuation.w; - vec3 toLight = light.position.xyz - p; - // Ignore fragments outside the light volume - if (dot(toLight, toLight) > (range * range)) - continue; + vec3 Iamb = light.ambient.rgb; + vec3 Idiff = gl_FrontMaterial.diffuse.rgb * light.diffuse.rgb * NdotL; + vec3 Ispec = vec3(0.0); - float d = length(toLight); - float att = 1.0 / (light.attenuation.x // constant - + light.attenuation.y * d // linear - + light.attenuation.z * d * d); // quadratic - vec3 lightDir = normalize(toLight); - float NdotL = max(dot(n, lightDir), 0.0); - - vec3 Iamb = light.ambient.rgb; - vec3 Idiff = gl_FrontMaterial.diffuse.rgb * light.diffuse.rgb * NdotL; - vec3 Ispec = vec3(0.0); - - if (NdotL > 0.0) { - vec3 halfVector = normalize(lightDir + normalize(-p)); - float NdotHV = max(dot(n, halfVector), 0.0); - Ispec = gl_FrontMaterial.specular.rgb - * light.specular.rgb - * pow(NdotHV, gl_FrontMaterial.shininess); - } - - color += ((Iamb + Idiff) * texel + Ispec) * att; - } else if (currentLight < (pointCount + spotCount)) { - // This is a spot light - SpotLight light = unpackSpotLight(index); - - vec3 toLight = light.position.xyz - p; - - float d = length(toLight); - float att = 1.0 / (light.attenuation.x // constant - + light.attenuation.y * d // linear - + light.attenuation.z * d * d); // quadratic - - vec3 lightDir = normalize(toLight); - - float spotDot = dot(-lightDir, light.direction.xyz); - if (spotDot < light.cos_cutoff) - continue; - - att *= pow(spotDot, light.exponent); - - float NdotL = max(dot(n, lightDir), 0.0); - - vec3 Iamb = light.ambient.rgb; - vec3 Idiff = gl_FrontMaterial.diffuse.rgb * light.diffuse.rgb * NdotL; - vec3 Ispec = vec3(0.0); - - if (NdotL > 0.0) { - vec3 halfVector = normalize(lightDir + normalize(-p)); - float NdotHV = max(dot(n, halfVector), 0.0); - Ispec = gl_FrontMaterial.specular.rgb - * light.specular.rgb - * pow(NdotHV, gl_FrontMaterial.shininess); - } - - color += ((Iamb + Idiff) * texel + Ispec) * att; - } else { - break; - } + if (NdotL > 0.0) { + vec3 halfVector = normalize(lightDir + normalize(-p)); + float NdotHV = max(dot(n, halfVector), 0.0); + Ispec = gl_FrontMaterial.specular.rgb + * light.specular.rgb + * pow(NdotHV, gl_FrontMaterial.shininess); } + + color += ((Iamb + Idiff) * texel + Ispec) * att; + } + + for (int i = 0; i < spotCount; ++i) { + int index = getIndex(lightIndex++); + SpotLight light = unpackSpotLight(index); + + vec3 toLight = light.position.xyz - p; + + float d = length(toLight); + float att = 1.0 / (light.attenuation.x // constant + + light.attenuation.y * d // linear + + light.attenuation.z * d * d); // quadratic + + vec3 lightDir = normalize(toLight); + + float spotDot = dot(-lightDir, light.direction.xyz); + if (spotDot < light.cos_cutoff) + continue; + + att *= pow(spotDot, light.exponent); + + float NdotL = max(dot(n, lightDir), 0.0); + + vec3 Iamb = light.ambient.rgb; + vec3 Idiff = gl_FrontMaterial.diffuse.rgb * light.diffuse.rgb * NdotL; + vec3 Ispec = vec3(0.0); + + if (NdotL > 0.0) { + vec3 halfVector = normalize(lightDir + normalize(-p)); + float NdotHV = max(dot(n, halfVector), 0.0); + Ispec = gl_FrontMaterial.specular.rgb + * light.specular.rgb + * pow(NdotHV, gl_FrontMaterial.shininess); + } + + color += ((Iamb + Idiff) * texel + Ispec) * att; } return clamp(color, 0.0, 1.0); - - // for (int i = 0; i < pointCount; ++i) { - // vec3 lightCoords = clusterCoords; - - // int pointCount = int(texture2D(fg_Clusters, clusterCoords).r); - // PointLight light = pointLights[lightListIndex]; - - // float range = light.attenuation.w; - // vec3 toLight = light.position.xyz - p; - // // Ignore fragments outside the light volume - // if (dot(toLight, toLight) > (range * range)) - // continue; - - // //////////////////////////////////////////////////////////////////////// - // // Actual lighting - - // float d = length(toLight); - // float att = 1.0 / (light.attenuation.x // constant - // + light.attenuation.y * d // linear - // + light.attenuation.z * d * d); // quadratic - // vec3 lightDir = normalize(toLight); - // float NdotL = max(dot(n, lightDir), 0.0); - - // vec3 Iamb = light.ambient.rgb; - // vec3 Idiff = light.diffuse.rgb * NdotL; - // vec3 Ispec = vec3(0.0); - - // if (NdotL > 0.0) { - // vec3 halfVector = normalize(lightDir + normalize(-p)); - // float NdotHV = max(dot(n, halfVector), 0.0); - // Ispec = light.specular.rgb * att * pow(NdotHV, shininess); - // } - - // color += addColors(color, (Iamb + Idiff + Ispec) * att); - // } - - // for (uint i = uint(0); i < spotCount; ++i) { - // uint lightListIndex = texelFetch(fg_ClusteredLightIndices, - // int(startIndex + i)).r; - // SpotLight light = spotLights[lightListIndex]; - - // vec3 toLight = light.position.xyz - p; - - // //////////////////////////////////////////////////////////////////////// - // // Actual lighting - - // float d = length(toLight); - // float att = 1.0 / (light.attenuation.x // constant - // + light.attenuation.y * d // linear - // + light.attenuation.z * d * d); // quadratic - - // vec3 lightDir = normalize(toLight); - - // float spotDot = dot(-lightDir, light.direction.xyz); - // if (spotDot < light.cos_cutoff) - // continue; - - // att *= pow(spotDot, light.exponent); - - // float NdotL = max(dot(n, lightDir), 0.0); - - // vec3 Iamb = light.ambient.rgb; - // vec3 Idiff = light.diffuse.rgb * NdotL; - // vec3 Ispec = vec3(0.0); - - // if (NdotL > 0.0) { - // vec3 halfVector = normalize(lightDir + normalize(-p)); - // float NdotHV = max(dot(n, halfVector), 0.0); - // Ispec = light.specular.rgb * att * pow(NdotHV, shininess); - // } - - // color += (Iamb + Idiff + Ispec) * att; - // } - }