summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRĂ©mi Verschelde <remi@verschelde.fr>2022-02-17 17:53:42 +0100
committerGitHub <noreply@github.com>2022-02-17 17:53:42 +0100
commite1b8a86b2b107d4a538d7a091ae14c59d8d99e48 (patch)
tree05116379317eb4c9f189659a59744d1c29291d5c
parentb6f3af4698d1d4fa7d2ba73b460e1f4d76505d3f (diff)
parent48728a79b6a70a9515ea2c291936bb0ad4e9cb47 (diff)
Merge pull request #58177 from clayjohn/VULKAN-prefiltered-radiance
-rw-r--r--doc/classes/ProjectSettings.xml4
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp44
-rw-r--r--servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl26
-rw-r--r--servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl21
-rw-r--r--servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl24
-rw-r--r--servers/rendering_server.cpp6
7 files changed, 92 insertions, 35 deletions
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 805f897b63..0a695414ee 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1776,10 +1776,10 @@
<member name="rendering/reflections/sky_reflections/fast_filter_high_quality" type="bool" setter="" getter="" default="false">
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.
</member>
- <member name="rendering/reflections/sky_reflections/ggx_samples" type="int" setter="" getter="" default="1024">
+ <member name="rendering/reflections/sky_reflections/ggx_samples" type="int" setter="" getter="" default="32">
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.
</member>
- <member name="rendering/reflections/sky_reflections/ggx_samples.mobile" type="int" setter="" getter="" default="128">
+ <member name="rendering/reflections/sky_reflections/ggx_samples.mobile" type="int" setter="" getter="" default="16">
Lower-end override for [member rendering/reflections/sky_reflections/ggx_samples] on mobile devices, due to performance concerns or driver support.
</member>
<member name="rendering/reflections/sky_reflections/roughness_layers" type="int" setter="" getter="" default="8">
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index 02a0b6f184..6c28cfd134 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 354516ae87..b44ae6cf8d 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<RID> 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 28f4dc59ec..1d46f59408 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 ce0a25e12f..1bee428a6f 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;
+}
+
+float DistributionGGX(float NdotH, float roughness4) {
+ float NdotH2 = NdotH * NdotH;
+ float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
+ denom = M_PI * denom * denom;
- // Tangent to world space
- return TangentX * H.x + TangentY * H.y + N * H.z;
+ 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 2570308816..c29accd8a7 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 a7fcbcce42..820aeab5b7 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);