From 48728a79b6a70a9515ea2c291936bb0ad4e9cb47 Mon Sep 17 00:00:00 2001 From: clayjohn Date: Wed, 16 Feb 2022 00:54:08 -0800 Subject: [PATCH] Use prefiltered radiance --- doc/classes/ProjectSettings.xml | 4 +- servers/rendering/renderer_rd/effects_rd.cpp | 2 +- .../renderer_rd/renderer_scene_sky_rd.cpp | 44 +++++++++++++++---- .../shaders/cubemap_roughness.glsl | 26 ++++++++--- .../shaders/cubemap_roughness_inc.glsl | 21 ++++----- .../shaders/cubemap_roughness_raster.glsl | 24 ++++++++-- servers/rendering_server.cpp | 6 +-- 7 files changed, 92 insertions(+), 35 deletions(-) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 805f897b631..0a695414ee6 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1776,10 +1776,10 @@ Use a higher quality variant of the fast filtering algorithm. Significantly slower than using default quality, but results in smoother reflections. Should only be used when the scene is especially detailed. - + Sets the number of samples to take when using importance sampling for [Sky]s and [ReflectionProbe]s. A higher value will result in smoother, higher quality reflections, but increases time to calculate radiance maps. In general, fewer samples are needed for simpler, low dynamic range environments while more samples are needed for HDR environments and environments with a high level of detail. - + Lower-end override for [member rendering/reflections/sky_reflections/ggx_samples] on mobile devices, due to performance concerns or driver support. diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 02a0b6f184b..6c28cfd1343 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -2040,7 +2040,7 @@ void EffectsRD::cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, u RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, roughness.compute_pipeline); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture, true), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 1); RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index 354516ae87b..b44ae6cf8db 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -473,12 +473,13 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_fast_filter(RendererS } RD::get_singleton()->draw_command_end_label(); // Filter radiance } else { + RD::get_singleton()->draw_command_begin_label("Downsample radiance map"); effects->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size); for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) { effects->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size); } - + RD::get_singleton()->draw_command_end_label(); // Downsample Radiance Vector views; if (p_use_arrays) { for (int i = 1; i < layers.size(); i++) { @@ -489,8 +490,9 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_fast_filter(RendererS views.push_back(layers[0].views[i]); } } - + RD::get_singleton()->draw_command_begin_label("Fast filter radiance"); effects->cubemap_filter(downsampled_radiance_cubemap, views, p_use_arrays); + RD::get_singleton()->draw_command_end_label(); // Filter radiance } } @@ -500,12 +502,25 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(Ren bool prefer_raster_effects = effects->get_prefer_raster_effects(); if (prefer_raster_effects) { - // Need to ask clayjohn but p_cube_side is set to 10, looks like in the compute shader we're doing all 6 sides in one call - // here we need to do them one by one so ignoring p_cube_side + if (p_base_layer == 1) { + RD::get_singleton()->draw_command_begin_label("Downsample radiance map"); + for (int k = 0; k < 6; k++) { + effects->cubemap_downsample_raster(radiance_base_cubemap, downsampled_layer.mipmaps[0].framebuffers[k], k, downsampled_layer.mipmaps[0].size); + } + + for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) { + for (int k = 0; k < 6; k++) { + effects->cubemap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffers[k], k, downsampled_layer.mipmaps[i].size); + } + } + RD::get_singleton()->draw_command_end_label(); // Downsample Radiance + } + + RD::get_singleton()->draw_command_begin_label("High Quality filter radiance"); if (p_use_arrays) { for (int k = 0; k < 6; k++) { effects->cubemap_roughness_raster( - radiance_base_cubemap, + downsampled_radiance_cubemap, layers[p_base_layer].mipmaps[0].framebuffers[k], k, p_sky_ggx_samples_quality, @@ -515,7 +530,7 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(Ren } else { for (int k = 0; k < 6; k++) { effects->cubemap_roughness_raster( - layers[0].views[p_base_layer - 1], + downsampled_radiance_cubemap, layers[0].mipmaps[p_base_layer].framebuffers[k], k, p_sky_ggx_samples_quality, @@ -524,12 +539,22 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(Ren } } } else { + if (p_base_layer == 1) { + RD::get_singleton()->draw_command_begin_label("Downsample radiance map"); + effects->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size); + + for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) { + effects->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size); + } + RD::get_singleton()->draw_command_end_label(); // Downsample Radiance + } + + RD::get_singleton()->draw_command_begin_label("High Quality filter radiance"); if (p_use_arrays) { - //render directly to the layers - effects->cubemap_roughness(radiance_base_cubemap, layers[p_base_layer].views[0], p_cube_side, p_sky_ggx_samples_quality, float(p_base_layer) / (layers.size() - 1.0), layers[p_base_layer].mipmaps[0].size.x); + effects->cubemap_roughness(downsampled_radiance_cubemap, layers[p_base_layer].views[0], p_cube_side, p_sky_ggx_samples_quality, float(p_base_layer) / (layers.size() - 1.0), layers[p_base_layer].mipmaps[0].size.x); } else { effects->cubemap_roughness( - layers[0].views[p_base_layer - 1], + downsampled_radiance_cubemap, layers[0].views[p_base_layer], p_cube_side, p_sky_ggx_samples_quality, @@ -537,6 +562,7 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(Ren layers[0].mipmaps[p_base_layer].size.x); } } + RD::get_singleton()->draw_command_end_label(); // Filter radiance } void RendererSceneSkyRD::ReflectionData::update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end) { diff --git a/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl b/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl index 28f4dc59ecf..1d46f594085 100644 --- a/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl +++ b/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl @@ -21,24 +21,38 @@ void main() { vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0); vec3 N = texelCoordToVec(uv, id.z); - //vec4 color = color_interp; - if (params.use_direct_write) { imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0)); } else { vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size); + float roughness2 = params.roughness * params.roughness; + float roughness4 = roughness2 * roughness2; + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + mat3 T; + T[0] = normalize(cross(UpVector, N)); + T[1] = cross(N, T[0]); + T[2] = N; + for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) { vec2 xi = Hammersley(sampleNum, params.sample_count); - vec3 H = ImportanceSampleGGX(xi, params.roughness, N); - vec3 V = N; - vec3 L = (2.0 * dot(V, H) * H - V); + vec3 H = T * ImportanceSampleGGX(xi, roughness4); + float NdotH = dot(N, H); + vec3 L = (2.0 * NdotH * H - N); float ndotl = clamp(dot(N, L), 0.0, 1.0); if (ndotl > 0.0) { - sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl; + float D = DistributionGGX(NdotH, roughness4); + float pdf = D * NdotH / (4.0 * NdotH) + 0.0001; + + float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001); + + float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel); + + sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl; sum.a += ndotl; } } diff --git a/servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl b/servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl index ce0a25e12f6..1bee428a6f4 100644 --- a/servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl @@ -47,12 +47,10 @@ vec3 texelCoordToVec(vec2 uv, uint faceID) { return normalize(result); } -vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { - float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] - +vec3 ImportanceSampleGGX(vec2 xi, float roughness4) { // Compute distribution direction - float Phi = 2.0 * M_PI * Xi.x; - float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); + float Phi = 2.0 * M_PI * xi.x; + float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y)); float SinTheta = sqrt(1.0 - CosTheta * CosTheta); // Convert to spherical direction @@ -61,12 +59,15 @@ vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { H.y = SinTheta * sin(Phi); H.z = CosTheta; - vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 TangentX = normalize(cross(UpVector, N)); - vec3 TangentY = cross(N, TangentX); + return H; +} - // Tangent to world space - return TangentX * H.x + TangentY * H.y + N * H.z; +float DistributionGGX(float NdotH, float roughness4) { + float NdotH2 = NdotH * NdotH; + float denom = (NdotH2 * (roughness4 - 1.0) + 1.0); + denom = M_PI * denom * denom; + + return roughness4 / denom; } // https://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html diff --git a/servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl b/servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl index 25703088167..c29accd8a77 100644 --- a/servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl @@ -42,17 +42,33 @@ void main() { } else { vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size); + float roughness2 = params.roughness * params.roughness; + float roughness4 = roughness2 * roughness2; + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + mat3 T; + T[0] = normalize(cross(UpVector, N)); + T[1] = cross(N, T[0]); + T[2] = N; + for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) { vec2 xi = Hammersley(sampleNum, params.sample_count); - vec3 H = ImportanceSampleGGX(xi, params.roughness, N); - vec3 V = N; - vec3 L = (2.0 * dot(V, H) * H - V); + vec3 H = T * ImportanceSampleGGX(xi, roughness4); + float NdotH = dot(N, H); + vec3 L = (2.0 * NdotH * H - N); float ndotl = clamp(dot(N, L), 0.0, 1.0); if (ndotl > 0.0) { - sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl; + float D = DistributionGGX(NdotH, roughness4); + float pdf = D * NdotH / (4.0 * NdotH) + 0.0001; + + float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001); + + float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel); + + sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl; sum.a += ndotl; } } diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 20372681348..13521d35493 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2860,11 +2860,11 @@ RenderingServer::RenderingServer() { GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug", false); GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug.release", true); - GLOBAL_DEF("rendering/reflections/sky_reflections/roughness_layers", 8); + GLOBAL_DEF_RST("rendering/reflections/sky_reflections/roughness_layers", 8); // Assumes a 256x256 cubemap GLOBAL_DEF_RST("rendering/reflections/sky_reflections/texture_array_reflections", true); GLOBAL_DEF("rendering/reflections/sky_reflections/texture_array_reflections.mobile", false); - GLOBAL_DEF("rendering/reflections/sky_reflections/ggx_samples", 1024); - GLOBAL_DEF("rendering/reflections/sky_reflections/ggx_samples.mobile", 128); + GLOBAL_DEF_RST("rendering/reflections/sky_reflections/ggx_samples", 32); + GLOBAL_DEF("rendering/reflections/sky_reflections/ggx_samples.mobile", 16); GLOBAL_DEF("rendering/reflections/sky_reflections/fast_filter_high_quality", false); GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size", 256); GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size.mobile", 128);