diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 92a29ebcfba..f41f4fe3a5f 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -895,6 +895,22 @@ vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) { return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) + (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z)))); } + +// https://github.com/kayru/Probulator/blob/4a97a2b021eb2ca7ef696f4ddf36ba9a9432cbb6/Source/Probulator/SphericalHarmonics.h#L136-L151 +// http://www.geomerics.com/wp-content/uploads/2015/08/CEDEC_Geomerics_ReconstructingDiffuseLighting1.pdf +float shEvaluateDiffuseL1Geomerics(vec4 sh, vec3 n) { + float R0 = sh.x; + + vec3 R1 = 0.5f * sh.wyz; + float lenR1 = length(R1); + + float q = 0.5f * (1.0f + dot(R1 / lenR1, n)); + + float p = 1.0f + 2.0f * lenR1 / R0; + float a = (1.0f - lenR1 / R0) / (1.0f + lenR1 / R0); + + return R0 * (a + (1.0f - a) * (p + 1.0f) * pow(q, p)); +} #endif //USE_LIGHTMAP #ifdef USE_MULTIVIEW @@ -1675,31 +1691,32 @@ void fragment_shader(in SceneData scene_data) { uvw.z = float(slice); if (uses_sh) { - uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0; - vec3 lm_light_l1n1; - vec3 lm_light_l1_0; - vec3 lm_light_l1p1; + uvw.z *= 4.0; // SH textures use 4 times more data. + vec3 lm_sh[4]; if (sc_use_lightmap_bicubic_filter()) { - lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; - lm_light_l1n1 = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0; - lm_light_l1_0 = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0; - lm_light_l1p1 = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0; + lm_sh[0] = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_sh[1] = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0; + lm_sh[2] = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0; + lm_sh[3] = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0; } else { - lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - lm_light_l1n1 = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb - vec3(0.5)) * 2.0; - lm_light_l1_0 = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb - vec3(0.5)) * 2.0; - lm_light_l1p1 = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb - vec3(0.5)) * 2.0; + lm_sh[0] = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + lm_sh[1] = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb - vec3(0.5)) * 2.0; + lm_sh[2] = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb - vec3(0.5)) * 2.0; + lm_sh[3] = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb - vec3(0.5)) * 2.0; } - vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); - float en = lightmaps.data[ofs].exposure_normalization; + lm_sh[1] *= lm_sh[0] * 4.0; + lm_sh[2] *= lm_sh[0] * 4.0; + lm_sh[3] *= lm_sh[0] * 4.0; - ambient_light += lm_light_l0 * en; - ambient_light += lm_light_l1n1 * n.y * (lm_light_l0 * en * 4.0); - ambient_light += lm_light_l1_0 * n.z * (lm_light_l0 * en * 4.0); - ambient_light += lm_light_l1p1 * n.x * (lm_light_l0 * en * 4.0); + vec3 sampleIrradianceSh; + + for (uint i = 0; i < 3; i++) { + sampleIrradianceSh[i] = shEvaluateDiffuseL1Geomerics(vec4(lm_sh[0][i], lm_sh[1][i], lm_sh[2][i], lm_sh[3][i]), normalize(lightmaps.data[ofs].normal_xform * normal)); + } + + ambient_light += sampleIrradianceSh * lightmaps.data[ofs].exposure_normalization; } else { if (sc_use_lightmap_bicubic_filter()) {