Use prefiltered radiance
This commit is contained in:
parent
7989149b91
commit
48728a79b6
|
@ -1776,10 +1776,10 @@
|
||||||
<member name="rendering/reflections/sky_reflections/fast_filter_high_quality" type="bool" setter="" getter="" default="false">
|
<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.
|
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>
|
||||||
<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.
|
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>
|
||||||
<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.
|
Lower-end override for [member rendering/reflections/sky_reflections/ggx_samples] on mobile devices, due to performance concerns or driver support.
|
||||||
</member>
|
</member>
|
||||||
<member name="rendering/reflections/sky_reflections/roughness_layers" type="int" setter="" getter="" default="8">
|
<member name="rendering/reflections/sky_reflections/roughness_layers" type="int" setter="" getter="" default="8">
|
||||||
|
|
|
@ -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::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_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_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));
|
RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant));
|
||||||
|
|
|
@ -473,12 +473,13 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_fast_filter(RendererS
|
||||||
}
|
}
|
||||||
RD::get_singleton()->draw_command_end_label(); // Filter radiance
|
RD::get_singleton()->draw_command_end_label(); // Filter radiance
|
||||||
} else {
|
} 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);
|
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++) {
|
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);
|
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;
|
Vector<RID> views;
|
||||||
if (p_use_arrays) {
|
if (p_use_arrays) {
|
||||||
for (int i = 1; i < layers.size(); i++) {
|
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]);
|
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);
|
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();
|
bool prefer_raster_effects = effects->get_prefer_raster_effects();
|
||||||
|
|
||||||
if (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
|
if (p_base_layer == 1) {
|
||||||
// here we need to do them one by one so ignoring p_cube_side
|
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) {
|
if (p_use_arrays) {
|
||||||
for (int k = 0; k < 6; k++) {
|
for (int k = 0; k < 6; k++) {
|
||||||
effects->cubemap_roughness_raster(
|
effects->cubemap_roughness_raster(
|
||||||
radiance_base_cubemap,
|
downsampled_radiance_cubemap,
|
||||||
layers[p_base_layer].mipmaps[0].framebuffers[k],
|
layers[p_base_layer].mipmaps[0].framebuffers[k],
|
||||||
k,
|
k,
|
||||||
p_sky_ggx_samples_quality,
|
p_sky_ggx_samples_quality,
|
||||||
|
@ -515,7 +530,7 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(Ren
|
||||||
} else {
|
} else {
|
||||||
for (int k = 0; k < 6; k++) {
|
for (int k = 0; k < 6; k++) {
|
||||||
effects->cubemap_roughness_raster(
|
effects->cubemap_roughness_raster(
|
||||||
layers[0].views[p_base_layer - 1],
|
downsampled_radiance_cubemap,
|
||||||
layers[0].mipmaps[p_base_layer].framebuffers[k],
|
layers[0].mipmaps[p_base_layer].framebuffers[k],
|
||||||
k,
|
k,
|
||||||
p_sky_ggx_samples_quality,
|
p_sky_ggx_samples_quality,
|
||||||
|
@ -524,12 +539,22 @@ void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(Ren
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (p_use_arrays) {
|
||||||
//render directly to the layers
|
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);
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
effects->cubemap_roughness(
|
effects->cubemap_roughness(
|
||||||
layers[0].views[p_base_layer - 1],
|
downsampled_radiance_cubemap,
|
||||||
layers[0].views[p_base_layer],
|
layers[0].views[p_base_layer],
|
||||||
p_cube_side,
|
p_cube_side,
|
||||||
p_sky_ggx_samples_quality,
|
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);
|
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) {
|
void RendererSceneSkyRD::ReflectionData::update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end) {
|
||||||
|
|
|
@ -21,24 +21,38 @@ void main() {
|
||||||
vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
|
vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
|
||||||
vec3 N = texelCoordToVec(uv, id.z);
|
vec3 N = texelCoordToVec(uv, id.z);
|
||||||
|
|
||||||
//vec4 color = color_interp;
|
|
||||||
|
|
||||||
if (params.use_direct_write) {
|
if (params.use_direct_write) {
|
||||||
imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0));
|
imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0));
|
||||||
} else {
|
} else {
|
||||||
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
|
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++) {
|
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
|
||||||
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
||||||
|
|
||||||
vec3 H = ImportanceSampleGGX(xi, params.roughness, N);
|
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
|
||||||
vec3 V = N;
|
float NdotH = dot(N, H);
|
||||||
vec3 L = (2.0 * dot(V, H) * H - V);
|
vec3 L = (2.0 * NdotH * H - N);
|
||||||
|
|
||||||
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
||||||
|
|
||||||
if (ndotl > 0.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;
|
sum.a += ndotl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,12 +47,10 @@ vec3 texelCoordToVec(vec2 uv, uint faceID) {
|
||||||
return normalize(result);
|
return normalize(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) {
|
vec3 ImportanceSampleGGX(vec2 xi, float roughness4) {
|
||||||
float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph]
|
|
||||||
|
|
||||||
// Compute distribution direction
|
// Compute distribution direction
|
||||||
float Phi = 2.0 * M_PI * Xi.x;
|
float Phi = 2.0 * M_PI * xi.x;
|
||||||
float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));
|
float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
|
||||||
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
|
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
|
||||||
|
|
||||||
// Convert to spherical direction
|
// Convert to spherical direction
|
||||||
|
@ -61,12 +59,15 @@ vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) {
|
||||||
H.y = SinTheta * sin(Phi);
|
H.y = SinTheta * sin(Phi);
|
||||||
H.z = CosTheta;
|
H.z = CosTheta;
|
||||||
|
|
||||||
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
return H;
|
||||||
vec3 TangentX = normalize(cross(UpVector, N));
|
}
|
||||||
vec3 TangentY = cross(N, TangentX);
|
|
||||||
|
|
||||||
// Tangent to world space
|
float DistributionGGX(float NdotH, float roughness4) {
|
||||||
return TangentX * H.x + TangentY * H.y + N * H.z;
|
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
|
// https://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
|
||||||
|
|
|
@ -42,17 +42,33 @@ void main() {
|
||||||
} else {
|
} else {
|
||||||
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
|
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++) {
|
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
|
||||||
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
||||||
|
|
||||||
vec3 H = ImportanceSampleGGX(xi, params.roughness, N);
|
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
|
||||||
vec3 V = N;
|
float NdotH = dot(N, H);
|
||||||
vec3 L = (2.0 * dot(V, H) * H - V);
|
vec3 L = (2.0 * NdotH * H - N);
|
||||||
|
|
||||||
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
||||||
|
|
||||||
if (ndotl > 0.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;
|
sum.a += ndotl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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", false);
|
||||||
GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug.release", true);
|
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_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/texture_array_reflections.mobile", false);
|
||||||
GLOBAL_DEF("rendering/reflections/sky_reflections/ggx_samples", 1024);
|
GLOBAL_DEF_RST("rendering/reflections/sky_reflections/ggx_samples", 32);
|
||||||
GLOBAL_DEF("rendering/reflections/sky_reflections/ggx_samples.mobile", 128);
|
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/sky_reflections/fast_filter_high_quality", false);
|
||||||
GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size", 256);
|
GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size", 256);
|
||||||
GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size.mobile", 128);
|
GLOBAL_DEF("rendering/reflections/reflection_atlas/reflection_size.mobile", 128);
|
||||||
|
|
Loading…
Reference in New Issue