From 732f1bf716e22f09bf49d500358a1291117a5a12 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Sun, 28 Jan 2024 22:14:12 +0100 Subject: [PATCH] volumetric: start gl implementation --- data/base/shaders/pointlights.frag | 131 +++++++++++++++++-- data/base/shaders/tcmask_instanced.frag | 13 +- data/base/shaders/terrain_combined_high.frag | 13 +- 3 files changed, 145 insertions(+), 12 deletions(-) diff --git a/data/base/shaders/pointlights.frag b/data/base/shaders/pointlights.frag index 90b99bdb873..4c08bdfb248 100644 --- a/data/base/shaders/pointlights.frag +++ b/data/base/shaders/pointlights.frag @@ -1,6 +1,7 @@ #define WZ_MAX_POINT_LIGHTS 0 #define WZ_MAX_INDEXED_POINT_LIGHTS 0 #define WZ_BUCKET_DIMENSION 0 +#define WZ_VOLUMETRIC_LIGHTING_ENABLED 1 uniform vec4 PointLightsPosition[WZ_MAX_POINT_LIGHTS]; uniform vec4 PointLightsColorAndEnergy[WZ_MAX_POINT_LIGHTS]; @@ -8,6 +9,7 @@ uniform ivec4 bucketOffsetAndSize[WZ_BUCKET_DIMENSION * WZ_BUCKET_DIMENSION]; uniform ivec4 PointLightsIndex[WZ_MAX_INDEXED_POINT_LIGHTS]; uniform int viewportWidth; uniform int viewportHeight; +uniform vec4 cameraPos; // in modelSpace // See https://lisyarus.github.io/blog/graphics/2022/07/30/point-light-attenuation.html for explanation // we want something that looks somewhat physically correct, but must absolutely be 0 past range @@ -21,22 +23,29 @@ float pointLightEnergyAtPosition(vec3 position, vec3 pointLightWorldPosition, fl return numerator * numerator / ( 1.f + 2.f * sqNormDist); } -vec4 processPointLight(vec3 WorldFragPos, vec3 fragNormal, vec3 viewVector, vec4 albedo, float gloss, vec3 pointLightWorldPosition, float pointLightEnergy, vec3 pointLightColor, mat3 normalWorldSpaceToLocalSpace) +struct MaterialInfo +{ + vec4 albedo; + float gloss; +}; + +vec4 processPointLight(vec3 WorldFragPos, vec3 fragNormal, vec3 viewVector, MaterialInfo material, vec3 pointLightWorldPosition, float pointLightEnergy, vec3 pointLightColor, mat3 normalWorldSpaceToLocalSpace) { vec3 pointLightVector = WorldFragPos - pointLightWorldPosition; vec3 pointLightDir = -normalize(pointLightVector * normalWorldSpaceToLocalSpace); float energy = pointLightEnergyAtPosition(WorldFragPos, pointLightWorldPosition, pointLightEnergy); - vec4 lightColor = vec4(pointLightColor * energy, 1.f); + vec4 lightColor = vec4(pointLightColor * energy, 1); - float pointLightLambert = max(dot(fragNormal, pointLightDir), 0.f); + float pointLightLambert = max(dot(fragNormal, pointLightDir), 0.0); vec3 pointLightHalfVec = normalize(viewVector + pointLightDir); float pointLightBlinn = pow(clamp(dot(fragNormal, pointLightHalfVec), 0.f, 1.f), 16.f); - return lightColor * pointLightLambert * (albedo + pointLightBlinn * (gloss * gloss)); + return lightColor * pointLightLambert * (material.albedo + pointLightBlinn * (material.gloss * material.gloss)); } + // This function expects that we have : // - a uniforms named bucketOffsetAndSize of ivec4[] // - a uniforms named PointLightsPosition of vec4[] @@ -48,8 +57,7 @@ vec4 iterateOverAllPointLights( vec3 WorldFragPos, vec3 fragNormal, vec3 viewVector, - vec4 albedo, - float gloss, + MaterialInfo material, mat3 normalWorldSpaceToLocalSpace ) { vec4 light = vec4(0.f); @@ -62,8 +70,115 @@ vec4 iterateOverAllPointLights( int lightIndex = PointLightsIndex[entryInLightList / 4][entryInLightList % 4]; vec4 position = PointLightsPosition[lightIndex]; vec4 colorAndEnergy = PointLightsColorAndEnergy[lightIndex]; - vec3 tmp = position.xyz * vec3(1.f, 1.f, -1.f); - light += processPointLight(WorldFragPos, fragNormal, viewVector, albedo, gloss, tmp, colorAndEnergy.w, colorAndEnergy.xyz, normalWorldSpaceToLocalSpace); + light += processPointLight(WorldFragPos, fragNormal, viewVector, material, position.xyz, colorAndEnergy.w, colorAndEnergy.xyz, normalWorldSpaceToLocalSpace); } return light; } + + + +float getShadowVisibilityWithoutPCF(vec3 fragPos) +{ + if (WZ_SHADOW_MODE == 0 || WZ_SHADOW_FILTER_SIZE == 0) + { + // no shadow-mapping + return 1.0; + } + else + { + // Shadow Mapping + + vec4 fragPosViewSpace = ViewMatrix * vec4(fragPos, 1.0); + float depthValue = abs(fragPosViewSpace.z); + + int cascadeIndex = 0; + + // unrolled loop, using vec4 swizzles + if (WZ_SHADOW_CASCADES_COUNT > 1) + { + if (depthValue >= ShadowMapCascadeSplits.x) + { + cascadeIndex = 1; + } + } + if (WZ_SHADOW_CASCADES_COUNT > 2) + { + if (depthValue >= ShadowMapCascadeSplits.y) + { + cascadeIndex = 2; + } + } + if (WZ_SHADOW_CASCADES_COUNT > 3) + { + if (depthValue >= ShadowMapCascadeSplits.z) + { + cascadeIndex = 3; + } + } + + vec4 shadowPos = ShadowMapMVPMatrix[cascadeIndex] * vec4(fragPos, 1.0); + vec3 pos = shadowPos.xyz / shadowPos.w; + + if (pos.z > 1.0f) + { + return 1.0; + } + + float bias = 0.0002f; + + return texture( shadowMap, vec4(pos.xy, cascadeIndex, (pos.z+bias)) ); + } +} + + +// based on equations found here : https://www.shadertoy.com/view/lstfR7 +vec4 volumetricLights( + vec2 clipSpaceCoord, + vec3 cameraPosition, + vec3 WorldFragPos, + vec3 sunLightColor +) { + vec3 result = vec3(0); + ivec2 bucket = ivec2(WZ_BUCKET_DIMENSION * clipSpaceCoord); + int bucketId = min(bucket.y + bucket.x * WZ_BUCKET_DIMENSION, WZ_BUCKET_DIMENSION * WZ_BUCKET_DIMENSION - 1); + + + vec3 viewLine = cameraPosition.xyz - WorldFragPos; + vec3 currentTransmittence = vec3(1); + vec3 transMittance = vec3(1); + + +#define STEPS 64 + for (int i = 0; i < STEPS; i++) + { + + vec3 posOnViewLine = WorldFragPos + viewLine * i / STEPS; + // fog is thicker near 0 + float thickness = exp(-posOnViewLine.y / 300); + + vec3 od = fogColor.xyz * thickness * length(viewLine / STEPS) / 1000; + + float sunLightEnergy = getShadowVisibilityWithoutPCF(posOnViewLine) ; + vec3 scatteredLight = vec3(sunLightEnergy) * sunLightColor * od; + + for (int i = 0; i < bucketOffsetAndSize[bucketId].y; i++) + { + int entryInLightList = bucketOffsetAndSize[bucketId].x + i; + int lightIndex = PointLightsIndex[entryInLightList / 4][entryInLightList % 4]; + vec4 position = PointLightsPosition[lightIndex]; + vec4 colorAndEnergy = PointLightsColorAndEnergy[lightIndex]; + scatteredLight += colorAndEnergy.xyz * pointLightEnergyAtPosition(posOnViewLine, position.xyz, colorAndEnergy.w) * od; + } + + result += scatteredLight * currentTransmittence; + + currentTransmittence *= exp2(od); + transMittance *= exp2(-od); + } + return vec4(result * transMittance, transMittance); +} + +vec3 toneMap(vec3 x) +{ + return x; +} \ No newline at end of file diff --git a/data/base/shaders/tcmask_instanced.frag b/data/base/shaders/tcmask_instanced.frag index 873941f5d96..615e19d100b 100644 --- a/data/base/shaders/tcmask_instanced.frag +++ b/data/base/shaders/tcmask_instanced.frag @@ -376,9 +376,13 @@ void main() 0., 1., 0., 0., 0., 1. ); + MaterialInfo materialInfo; + materialInfo.albedo = diffuse; + materialInfo.gloss = specularMapValue; // Normals are in view space, we need to get back to world space vec3 worldSpaceNormal = -(inverse(ViewMatrix) * vec4(N, 0.f)).xyz; - light += iterateOverAllPointLights(clipSpaceCoord, fragPos, worldSpaceNormal, normalize(halfVec - lightDir), diffuse, specularMapValue, identityMat); + + light += iterateOverAllPointLights(clipSpaceCoord, fragPos, worldSpaceNormal, normalize(halfVec - lightDir), materialInfo, identityMat); #endif light.rgb *= visibility; @@ -403,7 +407,12 @@ void main() fragColour.a = 0.66 + 0.66 * graphicsCycle; } - if (fogEnabled > 0) + if (WZ_VOLUMETRIC_LIGHTING_ENABLED == 1) { + vec2 clipSpaceCoord = gl_FragCoord.xy / vec2(viewportWidth, viewportHeight); + vec4 volumetric = volumetricLights(clipSpaceCoord, cameraPos.xyz, fragPos, diffuse.xyz); + fragColour.xyz = toneMap(fragColour.xyz * volumetric.a + volumetric.xyz); + } + else if (fogEnabled > 0) { // Calculate linear fog float fogFactor = (fogEnd - vertexDistance) / (fogEnd - fogStart); diff --git a/data/base/shaders/terrain_combined_high.frag b/data/base/shaders/terrain_combined_high.frag index 4e7c0a22193..15706d91fd9 100644 --- a/data/base/shaders/terrain_combined_high.frag +++ b/data/base/shaders/terrain_combined_high.frag @@ -148,10 +148,14 @@ vec4 doBumpMapping(BumpData b, vec3 lightDir, vec3 halfVec) { vec4 res = (b.color*light) + light_spec; + MaterialInfo materialInfo; + materialInfo.albedo = b.color; + materialInfo.gloss = b.gloss; + #if WZ_POINT_LIGHT_ENABLED == 1 // point lights vec2 clipSpaceCoord = gl_FragCoord.xy / vec2(float(viewportWidth), float(viewportHeight)); - res += iterateOverAllPointLights(clipSpaceCoord, fragPos, b.N, normalize(halfVec - lightDir), b.color, b.gloss, ModelTangentMatrix); + res += iterateOverAllPointLights(clipSpaceCoord, fragPos, b.N, normalize(halfVec - lightDir), materialInfo, ModelTangentMatrix); #endif return vec4(res.rgb, b.color.a); @@ -186,7 +190,12 @@ void main() { vec4 fragColor = main_bumpMapping(); - if (fogEnabled > 0) + if (WZ_VOLUMETRIC_LIGHTING_ENABLED == 1) { + vec2 clipSpaceCoord = gl_FragCoord.xy / vec2(viewportWidth, viewportHeight); + vec4 volumetric = volumetricLights(clipSpaceCoord, cameraPos.xyz, frag.fragPos, diffuseLight.xyz); + fragColor.xyz = toneMap(fragColor.xyz * volumetric.a + volumetric.xyz); + } + else if (fogEnabled > 0) { // Calculate linear fog float fogFactor = (fogEnd - vertexDistance) / (fogEnd - fogStart);