Rewrite Radiance and Reflection probes to use Octahedral maps.

Co-authored-by: clayjohn <claynjohn@gmail.com>
This commit is contained in:
Dario 2025-06-13 16:03:49 -03:00 committed by clayjohn
parent 25203e24c4
commit c78c3ba894
48 changed files with 1513 additions and 1557 deletions

View file

@ -3226,7 +3226,7 @@
<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">
<member name="rendering/reflections/sky_reflections/roughness_layers" type="int" setter="" getter="" default="7">
Limits the number of layers to use in radiance maps when using importance sampling. A lower number will be slightly faster and take up less VRAM.
</member>
<member name="rendering/reflections/sky_reflections/texture_array_reflections" type="bool" setter="" getter="" default="true">

View file

@ -986,6 +986,10 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
return true;
}
bool LightStorage::reflection_probe_instance_end_render(RID p_instance, RID p_reflection_atlas) {
return true;
}
Ref<RenderSceneBuffers> LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) {
ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas);
ERR_FAIL_NULL_V(atlas, Ref<RenderSceneBuffersGLES3>());

View file

@ -687,6 +687,7 @@ public:
virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override;
virtual bool reflection_probe_instance_has_reflection(RID p_instance) override;
virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override;
virtual bool reflection_probe_instance_end_render(RID p_instance, RID p_reflection_atlas) override;
virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override;
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;

View file

@ -165,6 +165,7 @@ public:
virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override { return false; }
virtual bool reflection_probe_instance_has_reflection(RID p_instance) override { return false; }
virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override { return false; }
virtual bool reflection_probe_instance_end_render(RID p_instance, RID p_reflection_atlas) override { return false; }
virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override { return Ref<RenderSceneBuffers>(); }
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override { return true; }

View file

@ -43,11 +43,11 @@ CopyEffects *CopyEffects::get_singleton() {
return singleton;
}
CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
CopyEffects::CopyEffects(BitField<RasterEffects> p_raster_effects) {
singleton = this;
prefer_raster_effects = p_prefer_raster_effects;
raster_effects = p_raster_effects;
if (prefer_raster_effects) {
if (raster_effects.has_flag(RASTER_EFFECT_GAUSSIAN_BLUR)) {
// init blur shader (on compute use copy shader)
Vector<String> blur_modes;
@ -96,8 +96,8 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
copy_modes.push_back("\n#define MODE_SET_COLOR\n#define DST_IMAGE_8BIT\n");
copy_modes.push_back("\n#define MODE_MIPMAP\n");
copy_modes.push_back("\n#define MODE_LINEARIZE_DEPTH_COPY\n");
copy_modes.push_back("\n#define MODE_CUBEMAP_TO_PANORAMA\n");
copy_modes.push_back("\n#define MODE_CUBEMAP_ARRAY_TO_PANORAMA\n");
copy_modes.push_back("\n#define MODE_OCTMAP_TO_PANORAMA\n");
copy_modes.push_back("\n#define MODE_OCTMAP_ARRAY_TO_PANORAMA\n");
copy.shader.initialize(copy_modes);
memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
@ -157,23 +157,27 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
}
{
//Initialize cubemap downsampler
Vector<String> cubemap_downsampler_modes;
cubemap_downsampler_modes.push_back("");
// Initialize cubemap to octmap copier.
cube_to_octmap.shader.initialize({ "" });
cube_to_octmap.shader_version = cube_to_octmap.shader.version_create();
RID shader = cube_to_octmap.shader.version_get_shader(cube_to_octmap.shader_version, 0);
cube_to_octmap.pipeline.setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled());
}
if (prefer_raster_effects) {
cubemap_downsampler.raster_shader.initialize(cubemap_downsampler_modes);
cubemap_downsampler.shader_version = cubemap_downsampler.raster_shader.version_create();
cubemap_downsampler.raster_pipeline.setup(cubemap_downsampler.raster_shader.version_get_shader(cubemap_downsampler.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
{
// Initialize octmap downsampler.
if (raster_effects.has_flag(RASTER_EFFECT_OCTMAP)) {
octmap_downsampler.raster_shader.initialize({ "", "\n#define USE_HIGH_QUALITY\n" });
octmap_downsampler.shader_version = octmap_downsampler.raster_shader.version_create();
for (int i = 0; i < DOWNSAMPLER_MODE_MAX; i++) {
octmap_downsampler.raster_pipelines[i].setup(octmap_downsampler.raster_shader.version_get_shader(octmap_downsampler.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
}
} else {
cubemap_downsampler.compute_shader.initialize(cubemap_downsampler_modes);
cubemap_downsampler.shader_version = cubemap_downsampler.compute_shader.version_create();
cubemap_downsampler.compute_pipeline.create_compute_pipeline(cubemap_downsampler.compute_shader.version_get_shader(cubemap_downsampler.shader_version, 0));
cubemap_downsampler.raster_pipeline.clear();
octmap_downsampler.compute_shader.initialize({ "", "\n#define USE_HIGH_QUALITY\n" });
octmap_downsampler.shader_version = octmap_downsampler.compute_shader.version_create();
for (int i = 0; i < DOWNSAMPLER_MODE_MAX; i++) {
octmap_downsampler.compute_pipelines[i].create_compute_pipeline(octmap_downsampler.compute_shader.version_get_shader(octmap_downsampler.shader_version, i));
}
}
}
@ -195,7 +199,7 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
RD::get_singleton()->buffer_update(filter.coefficient_buffer, 0, sizeof(low_quality_coeffs), &low_quality_coeffs[0]);
}
if (prefer_raster_effects) {
if (raster_effects.has_flag(RASTER_EFFECT_OCTMAP)) {
filter.raster_shader.initialize(cubemap_filter_modes);
// array variants are not supported in raster
@ -247,7 +251,7 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
Vector<String> cubemap_roughness_modes;
cubemap_roughness_modes.push_back("");
if (prefer_raster_effects) {
if (raster_effects.has_flag(RASTER_EFFECT_OCTMAP)) {
roughness.raster_shader.initialize(cubemap_roughness_modes);
roughness.shader_version = roughness.raster_shader.version_create();
@ -324,17 +328,17 @@ CopyEffects::~CopyEffects() {
filter.compute_pipelines[i].free();
}
cubemap_downsampler.compute_pipeline.free();
roughness.compute_pipeline.free();
if (prefer_raster_effects) {
if (raster_effects.has_flag(RASTER_EFFECT_GAUSSIAN_BLUR)) {
blur_raster.shader.version_free(blur_raster.shader_version);
RD::get_singleton()->free_rid(blur_raster.glow_sampler);
cubemap_downsampler.raster_shader.version_free(cubemap_downsampler.shader_version);
}
if (raster_effects.has_flag(RASTER_EFFECT_OCTMAP)) {
octmap_downsampler.raster_shader.version_free(octmap_downsampler.shader_version);
filter.raster_shader.version_free(filter.shader_version);
roughness.raster_shader.version_free(roughness.shader_version);
} else {
cubemap_downsampler.compute_shader.version_free(cubemap_downsampler.shader_version);
octmap_downsampler.compute_shader.version_free(octmap_downsampler.shader_version);
filter.compute_shader.version_free(filter.shader_version);
roughness.compute_shader.version_free(roughness.shader_version);
}
@ -354,6 +358,7 @@ CopyEffects::~CopyEffects() {
copy_to_fb.shader.version_free(copy_to_fb.shader_version);
cube_to_dp.shader.version_free(cube_to_dp.shader_version);
cube_to_octmap.shader.version_free(cube_to_octmap.shader_version);
singleton = nullptr;
}
@ -411,7 +416,7 @@ void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, cons
RD::get_singleton()->compute_list_end();
}
void CopyEffects::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array) {
void CopyEffects::copy_octmap_to_panorama(RID p_source_octmap, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array, const Size2 &p_source_octmap_border_size) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@ -426,22 +431,25 @@ void CopyEffects::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panoram
copy.push_constant.target[0] = 0;
copy.push_constant.target[1] = 0;
copy.push_constant.camera_z_far = p_lod;
copy.push_constant.octmap_border_size[0] = p_source_octmap_border_size.x;
copy.push_constant.octmap_border_size[1] = p_source_octmap_border_size.y;
copy.push_constant.luminance_multiplier = prefer_raster_effects ? 2.0 : 1.0;
// TODO, if this is needed at the copy stage, then we need to pass in the multiplier.
copy.push_constant.luminance_multiplier = raster_effects.has_flag(RASTER_EFFECT_COPY) ? 2.0 : 1.0;
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_cube(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_cube }));
RD::Uniform u_source_octmap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_octmap }));
RD::Uniform u_dest_panorama(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_panorama);
CopyMode mode = p_is_array ? COPY_MODE_CUBE_ARRAY_TO_PANORAMA : COPY_MODE_CUBE_TO_PANORAMA;
CopyMode mode = p_is_array ? COPY_MODE_OCTMAP_ARRAY_TO_PANORAMA : COPY_MODE_OCTMAP_TO_PANORAMA;
RID shader = copy.shader.version_get_shader(copy.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[mode].get_rid());
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_cube), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_octmap), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_panorama), 3);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_panorama_size.width, p_panorama_size.height, 1);
@ -561,7 +569,7 @@ void CopyEffects::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuff
RD::get_singleton()->draw_list_draw(draw_list, true);
}
void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview, bool p_alpha_to_one, bool p_linear, bool p_normal, const Rect2 &p_src_rect) {
void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview, bool p_alpha_to_one, bool p_linear, bool p_normal, const Rect2 &p_src_rect, float p_linear_luminance_multiplier) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@ -586,10 +594,8 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe
copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_ALPHA_TO_ONE;
}
if (p_linear) {
// Used for copying to a linear buffer. In the mobile renderer we divide the contents of the linear buffer
// to allow for a wider effective range.
copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_LINEAR;
copy_to_fb.push_constant.luminance_multiplier = prefer_raster_effects ? 2.0 : 1.0;
copy_to_fb.push_constant.luminance_multiplier = p_linear_luminance_multiplier;
}
if (p_normal) {
@ -633,7 +639,7 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe
RD::get_singleton()->draw_list_end();
}
void CopyEffects::copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFormatID p_fb_format, RID p_source_rd_texture, bool p_linear) {
void CopyEffects::copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFormatID p_fb_format, RID p_source_rd_texture, bool p_linear, float p_linear_luminance_multiplier) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@ -643,10 +649,8 @@ void CopyEffects::copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFo
copy_to_fb.push_constant.luminance_multiplier = 1.0;
if (p_linear) {
// Used for copying to a linear buffer. In the mobile renderer we divide the contents of the linear buffer
// to allow for a wider effective range.
copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_LINEAR;
copy_to_fb.push_constant.luminance_multiplier = prefer_raster_effects ? 2.0 : 1.0;
copy_to_fb.push_constant.luminance_multiplier = p_linear_luminance_multiplier;
}
// setup our uniforms
@ -668,7 +672,7 @@ void CopyEffects::copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFo
}
void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the copy with the clustered renderer.");
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_COPY), "Can't use the raster version of the copy.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -696,7 +700,7 @@ void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) {
}
void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, const Size2i &p_size, bool p_8bit_dst) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian blur with the mobile renderer.");
ERR_FAIL_COND_MSG(raster_effects.has_flag(RASTER_EFFECT_GAUSSIAN_BLUR), "Can't use the compute version of the gaussian blur.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -735,7 +739,7 @@ void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Re
}
void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_region, const Size2i &p_size) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian blur with the clustered renderer.");
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_GAUSSIAN_BLUR), "Can't use the raster version of the gaussian blur.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -770,7 +774,7 @@ void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_textu
}
void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_scale) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian glow with the mobile renderer.");
ERR_FAIL_COND_MSG(raster_effects.has_flag(RASTER_EFFECT_GAUSSIAN_BLUR), "Can't use the compute version of the gaussian glow.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -821,7 +825,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con
}
void CopyEffects::gaussian_glow_downsample_raster(RID p_source_rd_texture, RID p_dest_texture, float p_luminance_multiplier, const Size2i &p_size, float p_strength, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer.");
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_GAUSSIAN_BLUR), "Can't use the raster version of the gaussian glow.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -864,7 +868,7 @@ void CopyEffects::gaussian_glow_downsample_raster(RID p_source_rd_texture, RID p
}
void CopyEffects::gaussian_glow_upsample_raster(RID p_source_rd_texture, RID p_dest_texture, RID p_blend_texture, float p_luminance_multiplier, const Size2i &p_source_size, const Size2i &p_dest_size, float p_level, float p_base_strength, bool p_use_debanding) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer.");
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_GAUSSIAN_BLUR), "Can't use the raster version of the gaussian glow.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -908,7 +912,7 @@ void CopyEffects::gaussian_glow_upsample_raster(RID p_source_rd_texture, RID p_d
}
void CopyEffects::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the make_mipmap shader with the mobile renderer.");
ERR_FAIL_COND_MSG(raster_effects.has_flag(RASTER_EFFECT_COPY), "Can't use the compute version of the make_mipmap shader.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -942,7 +946,7 @@ void CopyEffects::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const
}
void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of mipmap with the clustered renderer.");
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_COPY), "Can't use the raster version of mipmap.");
RID dest_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_dest_texture);
@ -976,7 +980,7 @@ void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture
}
void CopyEffects::set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the set_color shader with the mobile renderer.");
ERR_FAIL_COND_MSG(raster_effects.has_flag(RASTER_EFFECT_COPY), "Can't use the compute version of the set_color shader.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -1010,7 +1014,7 @@ void CopyEffects::set_color(RID p_dest_texture, const Color &p_color, const Rect
}
void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, const Rect2i &p_region) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the set_color shader with the clustered renderer.");
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_COPY), "Can't use the raster version of the set_color shader.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@ -1078,85 +1082,131 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf
RD::get_singleton()->draw_list_end();
}
void CopyEffects::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute based cubemap downsample with the mobile renderer.");
void CopyEffects::copy_cubemap_to_octmap(RID p_source_rd_texture, RID p_dst_framebuffer, float p_border_size) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
RID shader = cube_to_octmap.shader.version_get_shader(cube_to_octmap.shader_version, 0);
ERR_FAIL_COND(shader.is_null());
cube_to_octmap.push_constant.border_size = 1.0f - p_border_size * 2.0f;
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cube_to_octmap.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &cube_to_octmap.push_constant, sizeof(CopyToOctmapPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
RD::get_singleton()->draw_list_end();
}
void CopyEffects::octmap_downsample(RID p_source_octmap, RID p_dest_octmap, const Size2i &p_size, bool p_use_filter_quality, float p_border_size) {
ERR_FAIL_COND_MSG(raster_effects.has_flag(RASTER_EFFECT_OCTMAP), "Can't use compute based octmap downsample.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
cubemap_downsampler.push_constant.face_size = p_size.x;
cubemap_downsampler.push_constant.face_id = 0; // we render all 6 sides to each layer in one call
octmap_downsampler.push_constant.size = p_size.x;
octmap_downsampler.push_constant.border_size = 1.0f - p_border_size * 2.0f;
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_cubemap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_cubemap }));
RD::Uniform u_dest_cubemap(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_dest_cubemap }));
RD::Uniform u_source_octmap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_octmap }));
RD::Uniform u_dest_octmap(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_dest_octmap }));
RID shader = cubemap_downsampler.compute_shader.version_get_shader(cubemap_downsampler.shader_version, 0);
RID shader = octmap_downsampler.compute_shader.version_get_shader(octmap_downsampler.shader_version, 0);
ERR_FAIL_COND(shader.is_null());
int pipeline_index = (!p_use_filter_quality || filter.use_high_quality) ? DOWNSAMPLER_MODE_HIGH_QUALITY : DOWNSAMPLER_MODE_LOW_QUALITY;
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, cubemap_downsampler.compute_pipeline.get_rid());
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_dest_cubemap), 1);
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, octmap_downsampler.compute_pipelines[pipeline_index].get_rid());
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_octmap), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_dest_octmap), 1);
int x_groups = Math::division_round_up(p_size.x, 8);
int y_groups = Math::division_round_up(p_size.y, 8);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &cubemap_downsampler.push_constant, sizeof(CubemapDownsamplerPushConstant));
RD::get_singleton()->compute_list_set_push_constant(compute_list, &octmap_downsampler.push_constant, sizeof(OctmapDownsamplerPushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 6); // one z_group for each face
RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
RD::get_singleton()->compute_list_end();
}
void CopyEffects::cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster based cubemap downsample with the clustered renderer.");
ERR_FAIL_COND_MSG(p_face_id >= 6, "Raster implementation of cubemap downsample must process one side at a time.");
void CopyEffects::octmap_downsample_raster(RID p_source_octmap, RID p_dest_framebuffer, const Size2i &p_size, bool p_use_filter_quality, float p_border_size) {
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_OCTMAP), "Can't use raster based octmap downsample.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
cubemap_downsampler.push_constant.face_size = p_size.x;
cubemap_downsampler.push_constant.face_id = p_face_id;
octmap_downsampler.push_constant.size = p_size.x;
octmap_downsampler.push_constant.border_size = 1.0f - p_border_size * 2.0f;
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_cubemap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_cubemap }));
RD::Uniform u_source_cubemap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_octmap }));
RID shader = cubemap_downsampler.raster_shader.version_get_shader(cubemap_downsampler.shader_version, 0);
RID shader = octmap_downsampler.raster_shader.version_get_shader(octmap_downsampler.shader_version, 0);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cubemap_downsampler.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
int pipeline_index = (!p_use_filter_quality || filter.use_high_quality) ? DOWNSAMPLER_MODE_HIGH_QUALITY : DOWNSAMPLER_MODE_LOW_QUALITY;
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::DRAW_IGNORE_COLOR_ALL);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, octmap_downsampler.raster_pipelines[pipeline_index].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &cubemap_downsampler.push_constant, sizeof(CubemapDownsamplerPushConstant));
RD::get_singleton()->draw_list_set_push_constant(draw_list, &octmap_downsampler.push_constant, sizeof(OctmapDownsamplerPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
void CopyEffects::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute based cubemap filter with the mobile renderer.");
static constexpr int _compute_dispatch_size(bool p_use_array) {
constexpr int SIZE = 320;
constexpr int GROUP = 64;
constexpr int LEVELS = 6; // One less than Sky::REAL_TIME_ROUGHNESS_LAYERS.
int size = 0;
if (p_use_array) {
size = SIZE * SIZE * LEVELS;
} else {
int dim = SIZE;
for (int i = 0; i < LEVELS && dim >= 2; i++) {
size += dim * dim;
dim >>= 1;
}
}
return (size + GROUP - 1) / GROUP;
}
void CopyEffects::octmap_filter(RID p_source_octmap, const Vector<RID> &p_dest_octmap, bool p_use_array, float p_border_size) {
ERR_FAIL_COND_MSG(raster_effects.has_flag(RASTER_EFFECT_OCTMAP), "Can't use compute based octmap filter.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
OctmapFilterPushConstant push_constant;
push_constant.border_size[0] = p_border_size;
push_constant.border_size[1] = 1.0f - p_border_size * 2.0f;
push_constant.size = 320;
Vector<RD::Uniform> uniforms;
for (int i = 0; i < p_dest_cubemap.size(); i++) {
for (int i = 0; i < p_dest_octmap.size(); i++) {
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = i;
u.append_id(p_dest_cubemap[i]);
u.append_id(p_dest_octmap[i]);
uniforms.push_back(u);
}
if (RD::get_singleton()->uniform_set_is_valid(filter.image_uniform_set)) {
@ -1167,7 +1217,7 @@ void CopyEffects::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubema
// setup our uniforms
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_cubemap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_mipmap_sampler, p_source_cubemap }));
RD::Uniform u_source_octmap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_mipmap_sampler, p_source_octmap }));
int mode = p_use_array ? FILTER_MODE_HIGH_QUALITY_ARRAY : FILTER_MODE_HIGH_QUALITY;
mode = filter.use_high_quality ? mode : mode + 1;
@ -1177,68 +1227,69 @@ void CopyEffects::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubema
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, filter.compute_pipelines[mode].get_rid());
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_octmap), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, filter.uniform_set, 1);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, filter.image_uniform_set, 2);
int x_groups = p_use_array ? 1792 : 342; // (128 * 128 * 7) / 64 : (128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4 + 2*2) / 64
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(OctmapFilterPushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, 6, 1); // one y_group for each face
RD::get_singleton()->compute_list_dispatch(compute_list, _compute_dispatch_size(p_use_array), 1, 1);
RD::get_singleton()->compute_list_end();
}
void CopyEffects::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_mip_level) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster based cubemap filter with the clustered renderer.");
ERR_FAIL_COND_MSG(p_face_id >= 6, "Raster implementation of cubemap filter must process one side at a time.");
void CopyEffects::octmap_filter_raster(RID p_source_octmap, RID p_dest_framebuffer, uint32_t p_mip_level, float p_border_size) {
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_OCTMAP), "Can't use raster based octmap filter.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
// TODO implement!
CubemapFilterRasterPushConstant push_constant;
OctmapFilterRasterPushConstant push_constant;
push_constant.border_size[0] = p_border_size;
push_constant.border_size[1] = 1.0f - p_border_size * 2.0f;
push_constant.mip_level = p_mip_level;
push_constant.face_id = p_face_id;
// setup our uniforms
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_cubemap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_mipmap_sampler, p_source_cubemap }));
RD::Uniform u_source_octmap(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_mipmap_sampler, p_source_octmap }));
CubemapFilterMode mode = filter.use_high_quality ? FILTER_MODE_HIGH_QUALITY : FILTER_MODE_LOW_QUALITY;
OctmapFilterMode mode = filter.use_high_quality ? FILTER_MODE_HIGH_QUALITY : FILTER_MODE_LOW_QUALITY;
RID shader = filter.raster_shader.version_get_shader(filter.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::DRAW_IGNORE_COLOR_ALL);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, filter.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_octmap), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, filter.uniform_set, 1);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CubemapFilterRasterPushConstant));
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(OctmapFilterRasterPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
void CopyEffects::cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute based cubemap roughness with the mobile renderer.");
void CopyEffects::octmap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_sample_count, float p_roughness, uint32_t p_source_size, uint32_t p_dest_size, float p_border_size) {
ERR_FAIL_COND_MSG(raster_effects.has_flag(RASTER_EFFECT_OCTMAP), "Can't use compute based octmap roughness.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
memset(&roughness.push_constant, 0, sizeof(CubemapRoughnessPushConstant));
memset(&roughness.push_constant, 0, sizeof(OctmapRoughnessPushConstant));
roughness.push_constant.face_id = p_face_id > 9 ? 0 : p_face_id;
// Remap to perceptual-roughness^2 to create more detail in lower mips and match the mapping of cubemap_filter.
// Remap to perceptual-roughness^2 to create more detail in lower mips and match the mapping of octmap_filter.
roughness.push_constant.roughness = p_roughness * p_roughness;
roughness.push_constant.sample_count = p_sample_count;
roughness.push_constant.source_size = p_source_size;
roughness.push_constant.dest_size = p_dest_size;
roughness.push_constant.use_direct_write = p_roughness == 0.0;
roughness.push_constant.face_size = p_size;
roughness.push_constant.border_size[0] = p_border_size;
roughness.push_constant.border_size[1] = 1.0f - p_border_size * 2.0;
// setup our uniforms
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
@ -1255,32 +1306,33 @@ void CopyEffects::cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture,
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_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(OctmapRoughnessPushConstant));
int x_groups = Math::division_round_up(p_size, 8);
int x_groups = (p_dest_size + 7) / 8;
int y_groups = x_groups;
RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, p_face_id > 9 ? 6 : 1);
RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
RD::get_singleton()->compute_list_end();
}
void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster based cubemap roughness with the clustered renderer.");
ERR_FAIL_COND_MSG(p_face_id >= 6, "Raster implementation of cubemap roughness must process one side at a time.");
void CopyEffects::octmap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_sample_count, float p_roughness, uint32_t p_source_size, uint32_t p_dest_size, float p_border_size) {
ERR_FAIL_COND_MSG(!raster_effects.has_flag(RASTER_EFFECT_OCTMAP), "Can't use raster based octmap roughness.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
memset(&roughness.push_constant, 0, sizeof(CubemapRoughnessPushConstant));
memset(&roughness.push_constant, 0, sizeof(OctmapRoughnessPushConstant));
roughness.push_constant.face_id = p_face_id;
roughness.push_constant.roughness = p_roughness * p_roughness; // Shader expects roughness, not perceptual roughness, so multiply before passing in.
roughness.push_constant.sample_count = p_sample_count;
roughness.push_constant.source_size = p_source_size;
roughness.push_constant.dest_size = p_dest_size;
roughness.push_constant.use_direct_write = p_roughness == 0.0;
roughness.push_constant.face_size = p_size;
roughness.push_constant.border_size[0] = p_border_size;
roughness.push_constant.border_size[1] = 1.0f - p_border_size * 2.0;
// Setup our uniforms.
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
@ -1290,11 +1342,11 @@ void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_f
RID shader = roughness.raster_shader.version_get_shader(roughness.shader_version, 0);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::DRAW_IGNORE_COLOR_ALL);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant));
RD::get_singleton()->draw_list_set_push_constant(draw_list, &roughness.push_constant, sizeof(OctmapRoughnessPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();

View file

@ -36,12 +36,13 @@
#include "servers/rendering/renderer_rd/shaders/effects/copy.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_filter.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_roughness.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cube_to_octmap.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/octmap_downsampler.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/octmap_downsampler_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/octmap_filter.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/octmap_filter_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/octmap_roughness.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/octmap_roughness_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
@ -50,8 +51,15 @@
namespace RendererRD {
class CopyEffects {
public:
enum RasterEffects {
RASTER_EFFECT_COPY = 1 << 0,
RASTER_EFFECT_GAUSSIAN_BLUR = 1 << 1,
RASTER_EFFECT_OCTMAP = 1 << 2,
};
private:
bool prefer_raster_effects;
BitField<RasterEffects> raster_effects;
// Blur raster shader
@ -115,8 +123,8 @@ private:
COPY_MODE_SET_COLOR_8BIT,
COPY_MODE_MIPMAP,
COPY_MODE_LINEARIZE_DEPTH,
COPY_MODE_CUBE_TO_PANORAMA,
COPY_MODE_CUBE_ARRAY_TO_PANORAMA,
COPY_MODE_OCTMAP_TO_PANORAMA,
COPY_MODE_OCTMAP_ARRAY_TO_PANORAMA,
COPY_MODE_MAX,
};
@ -152,7 +160,8 @@ private:
// DOF.
float camera_z_far;
float camera_z_near;
uint32_t pad2[2];
// Octmap.
float octmap_border_size[2];
//SET color
float set_color[4];
};
@ -224,24 +233,44 @@ private:
PipelineCacheRD pipeline;
} cube_to_dp;
// Cubemap effects
// Copy to Octmap
struct CubemapDownsamplerPushConstant {
uint32_t face_size;
uint32_t face_id;
float pad[2];
struct CopyToOctmapPushConstant {
float border_size;
float pad[3];
};
struct CubemapDownsampler {
CubemapDownsamplerPushConstant push_constant;
CubemapDownsamplerShaderRD compute_shader;
CubemapDownsamplerRasterShaderRD raster_shader;
struct CopyToOctmap {
CopyToOctmapPushConstant push_constant;
CubeToOctmapShaderRD shader;
RID shader_version;
PipelineDeferredRD compute_pipeline;
PipelineCacheRD raster_pipeline;
} cubemap_downsampler;
PipelineCacheRD pipeline;
} cube_to_octmap;
enum CubemapFilterMode {
// Octmap effects
struct OctmapDownsamplerPushConstant {
float border_size;
uint32_t size;
uint32_t pad[2];
};
enum OctmapDownsamplerMode {
DOWNSAMPLER_MODE_LOW_QUALITY,
DOWNSAMPLER_MODE_HIGH_QUALITY,
DOWNSAMPLER_MODE_MAX
};
struct OctmapDownsampler {
OctmapDownsamplerPushConstant push_constant;
OctmapDownsamplerShaderRD compute_shader;
OctmapDownsamplerRasterShaderRD raster_shader;
RID shader_version;
PipelineDeferredRD compute_pipelines[DOWNSAMPLER_MODE_MAX];
PipelineCacheRD raster_pipelines[DOWNSAMPLER_MODE_MAX];
} octmap_downsampler;
enum OctmapFilterMode {
FILTER_MODE_HIGH_QUALITY,
FILTER_MODE_LOW_QUALITY,
FILTER_MODE_HIGH_QUALITY_ARRAY,
@ -249,15 +278,21 @@ private:
FILTER_MODE_MAX,
};
struct CubemapFilterRasterPushConstant {
uint32_t mip_level;
uint32_t face_id;
float pad[2];
struct OctmapFilterPushConstant {
float border_size[2];
uint32_t size;
uint32_t pad;
};
struct CubemapFilter {
CubemapFilterShaderRD compute_shader;
CubemapFilterRasterShaderRD raster_shader;
struct OctmapFilterRasterPushConstant {
float border_size[2];
uint32_t mip_level;
uint32_t pad;
};
struct OctmapFilter {
OctmapFilterShaderRD compute_shader;
OctmapFilterRasterShaderRD raster_shader;
RID shader_version;
PipelineDeferredRD compute_pipelines[FILTER_MODE_MAX];
PipelineCacheRD raster_pipelines[FILTER_MODE_MAX];
@ -269,19 +304,21 @@ private:
} filter;
struct CubemapRoughnessPushConstant {
uint32_t face_id;
struct OctmapRoughnessPushConstant {
uint32_t sample_count;
float roughness;
uint32_t source_size;
uint32_t dest_size;
float border_size[2];
uint32_t use_direct_write;
float face_size;
float pad[3];
uint32_t pad;
};
struct CubemapRoughness {
CubemapRoughnessPushConstant push_constant;
CubemapRoughnessShaderRD compute_shader;
CubemapRoughnessRasterShaderRD raster_shader;
struct OctmapRoughness {
OctmapRoughnessPushConstant push_constant;
OctmapRoughnessShaderRD compute_shader;
OctmapRoughnessRasterShaderRD raster_shader;
RID shader_version;
PipelineDeferredRD compute_pipeline;
PipelineCacheRD raster_pipeline;
@ -319,18 +356,18 @@ private:
public:
static CopyEffects *get_singleton();
CopyEffects(bool p_prefer_raster_effects);
CopyEffects(BitField<RasterEffects> p_raster_effects);
~CopyEffects();
bool get_prefer_raster_effects() { return prefer_raster_effects; }
BitField<RasterEffects> get_raster_effects() { return raster_effects; }
void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false, bool p_sanitize_inf_nan = false);
void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
void copy_octmap_to_panorama(RID p_source_octmap, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array, const Size2 &p_source_octmap_border_size);
void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false, bool alpha_to_one = false, bool p_linear = false, bool p_normal = false, const Rect2 &p_src_rect = Rect2());
void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false, bool alpha_to_one = false, bool p_linear = false, bool p_normal = false, const Rect2 &p_src_rect = Rect2(), float p_linear_luminance_multiplier = 1.0);
void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
void copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFormatID p_fb_format, RID p_source_rd_texture, bool p_linear = false);
void copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFormatID p_fb_format, RID p_source_rd_texture, bool p_linear = false, float p_linear_luminance_multiplier = 1.0);
void copy_raster(RID p_source_texture, RID p_dest_framebuffer);
void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, const Size2i &p_size, bool p_8bit_dst = false);
@ -346,13 +383,13 @@ public:
void set_color_raster(RID p_dest_texture, const Color &p_color, const Rect2i &p_region);
void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip);
void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size);
void cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size);
void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array);
void cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_mip_level);
void cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void copy_cubemap_to_octmap(RID p_source_rd_texture, RID p_dst_framebuffer, float p_border_size);
void octmap_downsample(RID p_source_octmap, RID p_dest_octmap, const Size2i &p_size, bool p_use_filter_quality, float p_border_size);
void octmap_downsample_raster(RID p_source_octmap, RID p_dest_framebuffer, const Size2i &p_size, bool p_use_filter_quality, float p_border_size);
void octmap_filter(RID p_source_octmap, const Vector<RID> &p_dest_octmap, bool p_use_array, float p_border_size);
void octmap_filter_raster(RID p_source_octmap, RID p_dest_framebuffer, uint32_t p_mip_level, float p_border_size);
void octmap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_sample_count, float p_roughness, uint32_t p_source_size, uint32_t p_dest_size, float p_border_size);
void octmap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_sample_count, float p_roughness, uint32_t p_source_size, uint32_t p_dest_size, float p_border_size);
void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection, uint32_t p_view_count);
};

View file

@ -211,7 +211,7 @@ void Fog::fog_instance_free(RID p_rid) {
////////////////////////////////////////////////////////////////////////////////
// Volumetric Fog Shader
void Fog::init_fog_shader(uint32_t p_max_directional_lights, int p_roughness_layers, bool p_is_using_radiance_cubemap_array) {
void Fog::init_fog_shader(uint32_t p_max_directional_lights, int p_roughness_layers, bool p_is_using_radiance_octmap_array) {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
{
@ -302,8 +302,8 @@ ALBEDO = vec3(1.0);
{
String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(p_max_directional_lights) + "\n";
defines += "\n#define MAX_SKY_LOD " + itos(p_roughness_layers - 1) + ".0\n";
if (p_is_using_radiance_cubemap_array) {
defines += "\n#define USE_RADIANCE_CUBEMAP_ARRAY \n";
if (p_is_using_radiance_octmap_array) {
defines += "\n#define USE_RADIANCE_OCTMAP_ARRAY \n";
}
Vector<ShaderRD::VariantDefine> volumetric_fog_modes;
int shader_group = 0;
@ -977,7 +977,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 19;
RID radiance_texture = texture_storage->texture_rd_get_default(p_settings.is_using_radiance_cubemap_array ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
RID radiance_texture = texture_storage->texture_rd_get_default(p_settings.is_using_radiance_octmap_array ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
RID sky_texture = RendererSceneRenderRD::get_singleton()->environment_get_sky(p_settings.env).is_valid() ? p_settings.sky->sky_get_radiance_texture_rd(RendererSceneRenderRD::get_singleton()->environment_get_sky(p_settings.env)) : RID();
u.append_id(sky_texture.is_valid() ? sky_texture : radiance_texture);
uniforms.push_back(u);
@ -1118,6 +1118,13 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
params.use_temporal_reprojection = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_temporal_reprojection(p_settings.env);
params.temporal_blend = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_temporal_reprojection_amount(p_settings.env);
RID sky_rid = RendererSceneRenderRD::get_singleton()->environment_get_sky(p_settings.env);
if (sky_rid.is_valid()) {
float uv_border_size = p_settings.sky->sky_get_uv_border_size(sky_rid);
params.sky_border_size[0] = uv_border_size;
params.sky_border_size[1] = 1.0f - uv_border_size * 2.0f;
}
{
uint32_t cluster_size = p_settings.cluster_builder->get_cluster_size();
params.cluster_shift = get_shift_from_power_of_2(cluster_size);

View file

@ -182,6 +182,9 @@ private:
uint32_t temporal_frame;
float temporal_blend;
float sky_border_size[2];
float pad[2];
float cam_rotation[12];
float to_prev_view[16];
float radiance_inverse_xform[12];
@ -340,13 +343,13 @@ public:
~VolumetricFog();
};
void init_fog_shader(uint32_t p_max_directional_lights, int p_roughness_layers, bool p_is_using_radiance_cubemap_array);
void init_fog_shader(uint32_t p_max_directional_lights, int p_roughness_layers, bool p_is_using_radiance_octmap_array);
void free_fog_shader();
struct VolumetricFogSettings {
Vector2i rb_size;
double time;
bool is_using_radiance_cubemap_array;
bool is_using_radiance_octmap_array;
uint32_t max_cluster_elements;
bool volumetric_fog_filter_active;
RID shadow_sampler;

View file

@ -1306,6 +1306,10 @@ void GI::SDFGI::update_probes(RID p_env, SkyRD::Sky *p_sky) {
push_constant.image_size[1] = probe_axis_count;
push_constant.store_ambient_texture = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_enabled(p_env);
const float sky_irradiance_border_size = p_sky != nullptr ? p_sky->uv_border_size : 0.0f;
push_constant.sky_irradiance_border_size[0] = sky_irradiance_border_size;
push_constant.sky_irradiance_border_size[1] = 1.0 - sky_irradiance_border_size * 2.0f;
RID sky_uniform_set = gi->sdfgi_shader.integrate_default_sky_uniform_set;
push_constant.sky_flags = 0;
push_constant.y_mult = y_mult;
@ -3521,8 +3525,8 @@ void GI::init(SkyRD *p_sky) {
//calculate tables
String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
defines += "\n#define SH_SIZE " + itos(SDFGI::SH_SIZE) + "\n";
if (p_sky->sky_use_cubemap_array) {
defines += "\n#define USE_CUBEMAP_ARRAY\n";
if (p_sky->sky_use_octmap_array) {
defines += "\n#define USE_OCTMAP_ARRAY\n";
}
Vector<String> integrate_modes;
@ -3544,10 +3548,10 @@ void GI::init(SkyRD *p_sky) {
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 0;
if (p_sky->sky_use_cubemap_array) {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_WHITE));
if (p_sky->sky_use_octmap_array) {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE));
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE));
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE));
}
uniforms.push_back(u);
}

View file

@ -419,8 +419,9 @@ private:
float sky_color_or_orientation[3];
float y_mult;
float sky_irradiance_border_size[2];
uint32_t store_ambient_texture;
uint32_t pad[3];
uint32_t pad;
};
SdfgiIntegrateShaderRD integrate;

View file

@ -215,7 +215,7 @@ static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_ar
p_array[11] = 0;
}
void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const Projection &p_projection, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier, float p_brightness_multiplier) {
void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const Projection &p_projection, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier, float p_brightness_multiplier, float p_border_size) {
SkyPushConstant sky_push_constant;
memset(&sky_push_constant, 0, sizeof(SkyPushConstant));
@ -230,6 +230,8 @@ void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineC
sky_push_constant.position[1] = p_position.y;
sky_push_constant.position[2] = p_position.z;
sky_push_constant.time = p_time;
sky_push_constant.border_size[0] = p_border_size;
sky_push_constant.border_size[1] = 1.0f - p_border_size * 2.0;
sky_push_constant.luminance_multiplier = p_luminance_multiplier;
sky_push_constant.brightness_multiplier = p_brightness_multiplier;
store_transform_3x3(p_orientation, sky_push_constant.orientation);
@ -265,44 +267,41 @@ void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineC
void SkyRD::ReflectionData::clear_reflection_data() {
layers.clear();
radiance_base_cubemap = RID();
if (downsampled_radiance_cubemap.is_valid()) {
RD::get_singleton()->free_rid(downsampled_radiance_cubemap);
radiance_base_octmap = RID();
if (downsampled_radiance_octmap.is_valid()) {
RD::get_singleton()->free_rid(downsampled_radiance_octmap);
}
downsampled_radiance_cubemap = RID();
downsampled_radiance_octmap = RID();
downsampled_layer.mipmaps.clear();
coefficient_buffer = RID();
}
void SkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format) {
void SkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format, float p_border_size) {
//recreate radiance and all data
int mipmaps = p_mipmaps;
uint32_t w = p_size, h = p_size;
bool render_buffers_can_be_storage = RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage();
bool use_raster_effect = RendererRD::CopyEffects::get_singleton()->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
uv_border_size = p_border_size;
if (p_use_array) {
int num_layers = p_low_quality ? 8 : p_roughness_layers;
int num_layers = p_low_quality ? Sky::REAL_TIME_ROUGHNESS_LAYERS : p_roughness_layers;
for (int i = 0; i < num_layers; i++) {
ReflectionData::Layer layer;
uint32_t mmw = w;
uint32_t mmh = h;
layer.mipmaps.resize(mipmaps);
layer.views.resize(mipmaps);
for (int j = 0; j < mipmaps; j++) {
ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
ReflectionData::Layer::Mipmap &mm = layer.mipmaps[j];
mm.size.width = mmw;
mm.size.height = mmh;
for (int k = 0; k < 6; k++) {
mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6 + k, j);
Vector<RID> fbtex;
fbtex.push_back(mm.views[k]);
mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
}
layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6, j, 1, RD::TEXTURE_SLICE_CUBEMAP);
mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i, j);
Vector<RID> fbtex;
fbtex.append(mm.view);
mm.framebuffer = RD::get_singleton()->framebuffer_create(fbtex);
mmw = MAX(1u, mmw >> 1);
mmh = MAX(1u, mmh >> 1);
@ -310,27 +309,22 @@ void SkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bo
layers.push_back(layer);
}
} else {
mipmaps = p_low_quality ? 8 : mipmaps;
//regular cubemap, lower quality (aliasing, less memory)
mipmaps = p_low_quality ? Sky::REAL_TIME_ROUGHNESS_LAYERS : mipmaps;
ReflectionData::Layer layer;
uint32_t mmw = w;
uint32_t mmh = h;
layer.mipmaps.resize(mipmaps);
layer.views.resize(mipmaps);
for (int j = 0; j < mipmaps; j++) {
ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
ReflectionData::Layer::Mipmap &mm = layer.mipmaps[j];
mm.size.width = mmw;
mm.size.height = mmh;
for (int k = 0; k < 6; k++) {
mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + k, j);
Vector<RID> fbtex;
fbtex.push_back(mm.views[k]);
mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
}
mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, j);
layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, j, 1, RD::TEXTURE_SLICE_CUBEMAP);
Vector<RID> fbtex;
fbtex.push_back(mm.view);
mm.framebuffer = RD::get_singleton()->framebuffer_create(fbtex);
mmw = MAX(1u, mmw >> 1);
mmh = MAX(1u, mmh >> 1);
@ -339,43 +333,36 @@ void SkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bo
layers.push_back(layer);
}
radiance_base_cubemap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, 0, 1, RD::TEXTURE_SLICE_CUBEMAP);
RD::get_singleton()->set_resource_name(radiance_base_cubemap, "radiance base cubemap");
radiance_base_octmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, 0);
RD::get_singleton()->set_resource_name(radiance_base_octmap, "Radiance Base Octmap");
RD::TextureFormat tf;
tf.format = p_texture_format;
tf.width = p_low_quality ? 64 : p_size >> 1; // Always 64x64 when using REALTIME.
tf.height = p_low_quality ? 64 : p_size >> 1;
tf.texture_type = RD::TEXTURE_TYPE_CUBE;
tf.array_layers = 6;
tf.width = p_low_quality ? 160 : p_size >> 1; // Always 160x160 when using REALTIME.
tf.height = p_low_quality ? 160 : p_size >> 1;
tf.mipmaps = p_low_quality ? 7 : mipmaps - 1;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
if (render_buffers_can_be_storage) {
if (!use_raster_effect) {
tf.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT;
}
downsampled_radiance_cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView());
RD::get_singleton()->set_resource_name(downsampled_radiance_cubemap, "downsampled radiance cubemap");
downsampled_radiance_octmap = RD::get_singleton()->texture_create(tf, RD::TextureView());
RD::get_singleton()->set_resource_name(downsampled_radiance_octmap, "Downsampled Radiance Octmap");
{
uint32_t mmw = tf.width;
uint32_t mmh = tf.height;
downsampled_layer.mipmaps.resize(tf.mipmaps);
for (int j = 0; j < downsampled_layer.mipmaps.size(); j++) {
ReflectionData::DownsampleLayer::Mipmap &mm = downsampled_layer.mipmaps.write[j];
for (uint32_t j = 0; j < downsampled_layer.mipmaps.size(); j++) {
ReflectionData::DownsampleLayer::Mipmap &mm = downsampled_layer.mipmaps[j];
mm.size.width = mmw;
mm.size.height = mmh;
mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_cubemap, 0, j, 1, RD::TEXTURE_SLICE_CUBEMAP);
RD::get_singleton()->set_resource_name(mm.view, "Downsampled Radiance Cubemap Mip " + itos(j) + " ");
if (!render_buffers_can_be_storage) {
// we need a framebuffer for each side of our cubemap
for (int k = 0; k < 6; k++) {
mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_cubemap, k, j);
RD::get_singleton()->set_resource_name(mm.view, "Downsampled Radiance Cubemap Mip: " + itos(j) + " Face: " + itos(k) + " ");
Vector<RID> fbtex;
fbtex.push_back(mm.views[k]);
mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
}
mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_octmap, 0, j);
RD::get_singleton()->set_resource_name(mm.view, "Downsampled Radiance Octmap Mip " + itos(j) + " ");
if (use_raster_effect) {
// We need a framebuffer for the octmap.
Vector<RID> fbtex;
fbtex.push_back(mm.view);
mm.framebuffer = RD::get_singleton()->framebuffer_create(fbtex);
}
mmw = MAX(1u, mmw >> 1);
@ -387,125 +374,105 @@ void SkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bo
void SkyRD::ReflectionData::create_reflection_fast_filter(bool p_use_arrays) {
RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialized");
bool prefer_raster_effects = copy_effects->get_prefer_raster_effects();
bool use_raster_effect = copy_effects->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
if (prefer_raster_effects) {
if (use_raster_effect) {
RD::get_singleton()->draw_command_begin_label("Downsample Radiance Map");
for (int k = 0; k < 6; k++) {
copy_effects->cubemap_downsample_raster(radiance_base_cubemap, downsampled_layer.mipmaps[0].framebuffers[k], k, downsampled_layer.mipmaps[0].size);
}
copy_effects->octmap_downsample_raster(radiance_base_octmap, downsampled_layer.mipmaps[0].framebuffer, downsampled_layer.mipmaps[0].size, true, uv_border_size);
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
for (int k = 0; k < 6; k++) {
copy_effects->cubemap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffers[k], k, downsampled_layer.mipmaps[i].size);
}
for (uint32_t i = 1; i < downsampled_layer.mipmaps.size(); i++) {
copy_effects->octmap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffer, downsampled_layer.mipmaps[i].size, true, uv_border_size);
}
RD::get_singleton()->draw_command_end_label(); // Downsample Radiance
if (p_use_arrays) {
RD::get_singleton()->draw_command_begin_label("Filter Radiance Map into Array Heads");
for (int i = 0; i < layers.size(); i++) {
for (int k = 0; k < 6; k++) {
copy_effects->cubemap_filter_raster(downsampled_radiance_cubemap, layers[i].mipmaps[0].framebuffers[k], k, i);
}
for (uint32_t i = 0; i < layers.size(); i++) {
copy_effects->octmap_filter_raster(downsampled_radiance_octmap, layers[i].mipmaps[0].framebuffer, i, uv_border_size);
}
} else {
RD::get_singleton()->draw_command_begin_label("Filter Radiance Map into Mipmaps Directly");
for (int j = 0; j < layers[0].mipmaps.size(); j++) {
for (int k = 0; k < 6; k++) {
copy_effects->cubemap_filter_raster(downsampled_radiance_cubemap, layers[0].mipmaps[j].framebuffers[k], k, j);
}
for (uint32_t j = 0; j < layers[0].mipmaps.size(); j++) {
copy_effects->octmap_filter_raster(downsampled_radiance_octmap, layers[0].mipmaps[j].framebuffer, j, uv_border_size);
}
}
RD::get_singleton()->draw_command_end_label(); // Filter radiance
} else {
RD::get_singleton()->draw_command_begin_label("Downsample Radiance Map");
copy_effects->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size);
copy_effects->octmap_downsample(radiance_base_octmap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size, true, uv_border_size);
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
copy_effects->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size);
for (uint32_t i = 1; i < downsampled_layer.mipmaps.size(); i++) {
copy_effects->octmap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size, true, uv_border_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++) {
views.push_back(layers[i].views[0]);
for (uint32_t i = 1; i < layers.size(); i++) {
views.push_back(layers[i].mipmaps[0].view);
}
} else {
for (int i = 1; i < layers[0].views.size(); i++) {
views.push_back(layers[0].views[i]);
for (uint32_t i = 1; i < layers[0].mipmaps.size(); i++) {
views.push_back(layers[0].mipmaps[i].view);
}
}
RD::get_singleton()->draw_command_begin_label("Fast Filter Radiance");
copy_effects->cubemap_filter(downsampled_radiance_cubemap, views, p_use_arrays);
copy_effects->octmap_filter(downsampled_radiance_octmap, views, p_use_arrays, uv_border_size);
RD::get_singleton()->draw_command_end_label(); // Filter radiance
}
}
void SkyRD::ReflectionData::create_reflection_importance_sample(bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality) {
void SkyRD::ReflectionData::create_reflection_importance_sample(bool p_use_arrays, int p_base_layer, uint32_t p_sky_ggx_samples_quality) {
RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialized");
bool prefer_raster_effects = copy_effects->get_prefer_raster_effects();
bool use_raster_effect = copy_effects->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
if (prefer_raster_effects) {
if (use_raster_effect) {
if (p_base_layer == 1) {
RD::get_singleton()->draw_command_begin_label("Downsample Radiance Map");
for (int k = 0; k < 6; k++) {
copy_effects->cubemap_downsample_raster(radiance_base_cubemap, downsampled_layer.mipmaps[0].framebuffers[k], k, downsampled_layer.mipmaps[0].size);
}
copy_effects->octmap_downsample_raster(radiance_base_octmap, downsampled_layer.mipmaps[0].framebuffer, downsampled_layer.mipmaps[0].size, false, uv_border_size);
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
for (int k = 0; k < 6; k++) {
copy_effects->cubemap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffers[k], k, downsampled_layer.mipmaps[i].size);
}
for (uint32_t i = 1; i < downsampled_layer.mipmaps.size(); i++) {
copy_effects->octmap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffer, downsampled_layer.mipmaps[i].size, false, uv_border_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++) {
copy_effects->cubemap_roughness_raster(
downsampled_radiance_cubemap,
layers[p_base_layer].mipmaps[0].framebuffers[k],
k,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers.size() - 1.0),
layers[p_base_layer].mipmaps[0].size.x);
}
copy_effects->octmap_roughness_raster(
downsampled_radiance_octmap,
layers[p_base_layer].mipmaps[0].framebuffer,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers.size() - 1.0),
downsampled_layer.mipmaps[0].size.x,
layers[p_base_layer].mipmaps[0].size.x,
uv_border_size);
} else {
for (int k = 0; k < 6; k++) {
copy_effects->cubemap_roughness_raster(
downsampled_radiance_cubemap,
layers[0].mipmaps[p_base_layer].framebuffers[k],
k,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers[0].mipmaps.size() - 1.0),
layers[0].mipmaps[p_base_layer].size.x);
}
copy_effects->octmap_roughness_raster(
downsampled_radiance_octmap,
layers[0].mipmaps[p_base_layer].framebuffer,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers[0].mipmaps.size() - 1.0),
downsampled_layer.mipmaps[0].size.x,
layers[0].mipmaps[p_base_layer].size.x,
uv_border_size);
}
} else {
if (p_base_layer == 1) {
RD::get_singleton()->draw_command_begin_label("Downsample Radiance Map");
copy_effects->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size);
copy_effects->octmap_downsample(radiance_base_octmap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size, false, uv_border_size);
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
copy_effects->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size);
for (uint32_t i = 1; i < downsampled_layer.mipmaps.size(); i++) {
copy_effects->octmap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size, false, uv_border_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) {
copy_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);
copy_effects->octmap_roughness(downsampled_radiance_octmap, layers[p_base_layer].mipmaps[0].view, p_sky_ggx_samples_quality, float(p_base_layer) / (layers.size() - 1.0), downsampled_layer.mipmaps[0].size.x, layers[p_base_layer].mipmaps[0].size.x, uv_border_size);
} else {
copy_effects->cubemap_roughness(
downsampled_radiance_cubemap,
layers[0].views[p_base_layer],
p_cube_side,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers[0].mipmaps.size() - 1.0),
layers[0].mipmaps[p_base_layer].size.x);
copy_effects->octmap_roughness(downsampled_radiance_octmap, layers[0].mipmaps[p_base_layer].view, p_sky_ggx_samples_quality, float(p_base_layer) / (layers[0].mipmaps.size() - 1.0), downsampled_layer.mipmaps[0].size.x, layers[0].mipmaps[p_base_layer].size.x, uv_border_size);
}
}
RD::get_singleton()->draw_command_end_label(); // Filter radiance
@ -514,21 +481,19 @@ void SkyRD::ReflectionData::create_reflection_importance_sample(bool p_use_array
void SkyRD::ReflectionData::update_reflection_mipmaps(int p_start, int p_end) {
RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialized");
bool prefer_raster_effects = copy_effects->get_prefer_raster_effects();
bool use_raster_effect = copy_effects->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
RD::get_singleton()->draw_command_begin_label("Update Radiance Cubemap Array Mipmaps");
RD::get_singleton()->draw_command_begin_label("Update Radiance Octmap Array Mipmaps");
for (int i = p_start; i < p_end; i++) {
for (int j = 0; j < layers[i].views.size() - 1; j++) {
RID view = layers[i].views[j];
for (uint32_t j = 0; j < layers[i].mipmaps.size() - 1; j++) {
RID view = layers[i].mipmaps[j].view;
Size2i size = layers[i].mipmaps[j + 1].size;
if (prefer_raster_effects) {
for (int k = 0; k < 6; k++) {
RID framebuffer = layers[i].mipmaps[j + 1].framebuffers[k];
copy_effects->cubemap_downsample_raster(view, framebuffer, k, size);
}
if (use_raster_effect) {
RID framebuffer = layers[i].mipmaps[j + 1].framebuffer;
copy_effects->octmap_downsample_raster(view, framebuffer, size, false, uv_border_size);
} else {
RID texture = layers[i].views[j + 1];
copy_effects->cubemap_downsample(view, texture, size);
RID texture = layers[i].mipmaps[j + 1].view;
copy_effects->octmap_downsample(view, texture, size, false, uv_border_size);
}
}
}
@ -568,7 +533,7 @@ RID SkyRD::Sky::get_textures(SkyTextureSetVersion p_version, RID p_default_shade
if (radiance.is_valid() && p_version <= SKY_TEXTURE_SET_QUARTER_RES) {
u.append_id(radiance);
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK));
}
uniforms.push_back(u);
}
@ -576,11 +541,11 @@ RID SkyRD::Sky::get_textures(SkyTextureSetVersion p_version, RID p_default_shade
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1; // half res
if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
if (reflection.layers.size() && reflection.layers[0].views.size() >= 2 && reflection.layers[0].views[1].is_valid() && p_version != SKY_TEXTURE_SET_CUBEMAP_HALF_RES) {
u.append_id(reflection.layers[0].views[1]);
if (p_version >= SKY_TEXTURE_SET_OCTMAP) {
if (reflection.layers.size() && reflection.layers[0].mipmaps.size() >= 2 && reflection.layers[0].mipmaps[1].view.is_valid() && p_version != SKY_TEXTURE_SET_OCTMAP_HALF_RES) {
u.append_id(reflection.layers[0].mipmaps[1].view);
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK));
}
} else {
RID half_texture = p_render_buffers->has_texture(RB_SCOPE_SKY, RB_HALF_TEXTURE) ? p_render_buffers->get_texture(RB_SCOPE_SKY, RB_HALF_TEXTURE) : RID();
@ -596,11 +561,11 @@ RID SkyRD::Sky::get_textures(SkyTextureSetVersion p_version, RID p_default_shade
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2; // quarter res
if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
if (reflection.layers.size() && reflection.layers[0].views.size() >= 3 && reflection.layers[0].views[2].is_valid() && p_version != SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES) {
u.append_id(reflection.layers[0].views[2]);
if (p_version >= SKY_TEXTURE_SET_OCTMAP) {
if (reflection.layers.size() && reflection.layers[0].mipmaps.size() >= 3 && reflection.layers[0].mipmaps[2].view.is_valid() && p_version != SKY_TEXTURE_SET_OCTMAP_QUARTER_RES) {
u.append_id(reflection.layers[0].mipmaps[2].view);
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK));
}
} else {
RID quarter_texture = p_render_buffers->has_texture(RB_SCOPE_SKY, RB_QUARTER_TEXTURE) ? p_render_buffers->get_texture(RB_SCOPE_SKY, RB_QUARTER_TEXTURE) : RID();
@ -623,9 +588,9 @@ bool SkyRD::Sky::set_radiance_size(int p_radiance_size) {
}
radiance_size = p_radiance_size;
if (mode == RS::SKY_MODE_REALTIME && radiance_size != 256) {
WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
radiance_size = 256;
if (mode == RS::SKY_MODE_REALTIME && radiance_size != REAL_TIME_SIZE) {
WARN_PRINT(vformat("Realtime Skies can only use a radiance size of %d. Radiance size will be set to %d internally.", REAL_TIME_SIZE, REAL_TIME_SIZE));
radiance_size = REAL_TIME_SIZE;
}
if (radiance.is_valid()) {
@ -637,6 +602,10 @@ bool SkyRD::Sky::set_radiance_size(int p_radiance_size) {
return true;
}
int SkyRD::Sky::get_radiance_size() const {
return radiance_size;
}
bool SkyRD::Sky::set_mode(RS::SkyMode p_mode) {
if (mode == p_mode) {
return false;
@ -644,9 +613,9 @@ bool SkyRD::Sky::set_mode(RS::SkyMode p_mode) {
mode = p_mode;
if (mode == RS::SKY_MODE_REALTIME && radiance_size != 256) {
WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
set_radiance_size(256);
if (mode == RS::SKY_MODE_REALTIME && radiance_size != REAL_TIME_SIZE) {
WARN_PRINT(vformat("Realtime Skies can only use a radiance size of %d. Radiance size will be set to %d internally.", REAL_TIME_SIZE, REAL_TIME_SIZE));
set_radiance_size(REAL_TIME_SIZE);
}
if (radiance.is_valid()) {
@ -678,7 +647,7 @@ Ref<Image> SkyRD::Sky::bake_panorama(float p_energy, int p_roughness_layers, con
tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
RID rad_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
copy_effects->copy_cubemap_to_panorama(radiance, rad_tex, p_size, p_roughness_layers, reflection.layers.size() > 1);
copy_effects->copy_octmap_to_panorama(radiance, rad_tex, p_size, p_roughness_layers, reflection.layers.size() > 1, Size2(uv_border_size, 1.0f - uv_border_size * 2.0));
Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0);
RD::get_singleton()->free_rid(rad_tex);
@ -726,13 +695,7 @@ RendererRD::MaterialStorage::MaterialData *SkyRD::_create_sky_material_funcs(Ren
SkyRD::SkyRD() {
roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
sky_ggx_samples_quality = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
sky_use_cubemap_array = GLOBAL_GET("rendering/reflections/sky_reflections/texture_array_reflections");
#if defined(MACOS_ENABLED) && defined(__x86_64__)
if (OS::get_singleton()->get_current_rendering_driver_name() == "vulkan" && RenderingDevice::get_singleton()->get_device_name().contains("Intel")) {
// Disable texture array reflections on macOS on Intel GPUs due to driver bugs.
sky_use_cubemap_array = false;
}
#endif
sky_use_octmap_array = GLOBAL_GET("rendering/reflections/sky_reflections/texture_array_reflections");
}
void SkyRD::init() {
@ -1273,7 +1236,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
RS::SkyMode sky_mode = sky->mode;
if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
if ((shader_data->uses_time || shader_data->uses_position) && sky->radiance_size == 256) {
if ((shader_data->uses_time || shader_data->uses_position) && sky->radiance_size == Sky::REAL_TIME_SIZE) {
update_single_frame = true;
sky_mode = RS::SKY_MODE_REALTIME;
} else if (shader_data->uses_light || shader_data->ubo_size > 0) {
@ -1291,27 +1254,10 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
sky_mode = RS::SKY_MODE_QUALITY;
}
int max_processing_layer = sky_use_cubemap_array ? sky->reflection.layers.size() : sky->reflection.layers[0].mipmaps.size();
int max_processing_layer = sky_use_octmap_array ? sky->reflection.layers.size() : sky->reflection.layers[0].mipmaps.size();
// Update radiance cubemap
// Update radiance octmap
if (sky->reflection.dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) {
static const Vector3 view_normals[6] = {
Vector3(+1, 0, 0),
Vector3(-1, 0, 0),
Vector3(0, +1, 0),
Vector3(0, -1, 0),
Vector3(0, 0, +1),
Vector3(0, 0, -1)
};
static const Vector3 view_up[6] = {
Vector3(0, -1, 0),
Vector3(0, -1, 0),
Vector3(0, 0, +1),
Vector3(0, 0, -1),
Vector3(0, -1, 0),
Vector3(0, -1, 0)
};
Projection cm;
cm.set_perspective(90, 1, 0.01, 10.0);
Projection correction;
@ -1322,20 +1268,18 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
if (shader_data->uses_quarter_res && roughness_layers >= 3) {
RD::get_singleton()->draw_command_begin_label("Render Sky to Quarter-Resolution Cubemap");
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_QUARTER_RES];
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_OCTMAP_QUARTER_RES];
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID cubemap_draw_list;
RD::DrawListID octmap_draw_list;
for (int i = 0; i < 6; i++) {
Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd, p_render_buffers);
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_OCTMAP_QUARTER_RES, sky_shader.default_shader_rd, p_render_buffers);
octmap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffer, RD::DRAW_IGNORE_COLOR_ALL);
_render_sky(octmap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffer, pipeline, material->uniform_set, texture_uniform_set, cm, Basis(), p_global_pos, p_luminance_multiplier, p_brightness_multiplier, sky->uv_border_size);
RD::get_singleton()->draw_list_end();
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i]);
_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier, p_brightness_multiplier);
RD::get_singleton()->draw_list_end();
}
RD::get_singleton()->draw_command_end_label();
} else if (shader_data->uses_quarter_res && roughness_layers < 3) {
ERR_PRINT_ED("Cannot use quarter res buffer in sky shader when roughness layers is less than 3. Please increase rendering/reflections/sky_reflections/roughness_layers.");
@ -1343,54 +1287,51 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
if (shader_data->uses_half_res && roughness_layers >= 2) {
RD::get_singleton()->draw_command_begin_label("Render Sky to Half-Resolution Cubemap");
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_HALF_RES];
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_OCTMAP_HALF_RES];
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID cubemap_draw_list;
for (int i = 0; i < 6; i++) {
Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd, p_render_buffers);
RD::DrawListID octmap_draw_list;
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_OCTMAP_HALF_RES, sky_shader.default_shader_rd, p_render_buffers);
octmap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffer, RD::DRAW_IGNORE_COLOR_ALL);
_render_sky(octmap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffer, pipeline, material->uniform_set, texture_uniform_set, cm, Basis(), p_global_pos, p_luminance_multiplier, p_brightness_multiplier, sky->uv_border_size);
RD::get_singleton()->draw_list_end();
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i]);
_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier, p_brightness_multiplier);
RD::get_singleton()->draw_list_end();
}
RD::get_singleton()->draw_command_end_label();
} else if (shader_data->uses_half_res && roughness_layers < 2) {
ERR_PRINT_ED("Cannot use half res buffer in sky shader when roughness layers is less than 2. Please increase rendering/reflections/sky_reflections/roughness_layers.");
}
RD::DrawListID cubemap_draw_list;
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP];
RD::DrawListID octmap_draw_list;
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_OCTMAP];
RD::get_singleton()->draw_command_begin_label("Render Sky Cubemap");
for (int i = 0; i < 6; i++) {
Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd, p_render_buffers);
RD::get_singleton()->draw_command_begin_label("Render Sky Octmap");
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_OCTMAP, sky_shader.default_shader_rd, p_render_buffers);
octmap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffer, RD::DRAW_IGNORE_COLOR_ALL, Vector<Color>(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::SKY_PASS);
_render_sky(octmap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffer, pipeline, material->uniform_set, texture_uniform_set, cm, Basis(), p_global_pos, p_luminance_multiplier, p_brightness_multiplier, sky->uv_border_size);
RD::get_singleton()->draw_list_end();
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::SKY_PASS | uint32_t(i));
_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier, p_brightness_multiplier);
RD::get_singleton()->draw_list_end();
}
RD::get_singleton()->draw_command_end_label();
if (sky_mode == RS::SKY_MODE_REALTIME) {
sky->reflection.create_reflection_fast_filter(sky_use_cubemap_array);
if (sky_use_cubemap_array) {
sky->reflection.create_reflection_fast_filter(sky_use_octmap_array);
if (sky_use_octmap_array) {
sky->reflection.update_reflection_mipmaps(0, sky->reflection.layers.size());
}
} else {
if (update_single_frame) {
for (int i = 1; i < max_processing_layer; i++) {
sky->reflection.create_reflection_importance_sample(sky_use_cubemap_array, 10, i, sky_ggx_samples_quality);
sky->reflection.create_reflection_importance_sample(sky_use_octmap_array, i, sky_ggx_samples_quality);
}
if (sky_use_cubemap_array) {
if (sky_use_octmap_array) {
sky->reflection.update_reflection_mipmaps(0, sky->reflection.layers.size());
}
} else {
if (sky_use_cubemap_array) {
if (sky_use_octmap_array) {
// Multi-Frame so just update the first array level
sky->reflection.update_reflection_mipmaps(0, 1);
}
@ -1402,9 +1343,9 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
} else {
if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) {
sky->reflection.create_reflection_importance_sample(sky_use_cubemap_array, 10, sky->processing_layer, sky_ggx_samples_quality);
sky->reflection.create_reflection_importance_sample(sky_use_octmap_array, sky->processing_layer, sky_ggx_samples_quality);
if (sky_use_cubemap_array) {
if (sky_use_octmap_array) {
sky->reflection.update_reflection_mipmaps(sky->processing_layer, sky->processing_layer + 1);
}
@ -1480,10 +1421,7 @@ void SkyRD::update_res_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd, p_render_buffers);
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_CLEAR_ALL, clear_colors);
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_IGNORE_COLOR_ALL);
_render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier, p_brightness_multiplier);
RD::get_singleton()->draw_list_end();
}
@ -1499,10 +1437,7 @@ void SkyRD::update_res_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p
RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd, p_render_buffers);
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_CLEAR_ALL, clear_colors);
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_IGNORE_COLOR_ALL);
_render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier, p_brightness_multiplier);
RD::get_singleton()->draw_list_end();
}
@ -1580,6 +1515,7 @@ void SkyRD::invalidate_sky(Sky *p_sky) {
}
void SkyRD::update_dirty_skys() {
bool use_raster_effect = RendererRD::CopyEffects::get_singleton()->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
Sky *sky = dirty_sky_list;
while (sky) {
@ -1591,50 +1527,59 @@ void SkyRD::update_dirty_skys() {
if (sky->radiance.is_null()) {
int mipmaps = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBAH) + 1;
uint32_t w = sky->radiance_size, h = sky->radiance_size;
int layers = roughness_layers;
if (sky->mode == RS::SKY_MODE_REALTIME) {
layers = 8;
if (roughness_layers != 8) {
WARN_PRINT("When using the Real-Time sky update mode (or Automatic with a sky shader using \"TIME\"), \"rendering/reflections/sky_reflections/roughness_layers\" should be set to 8 in the project settings for best quality reflections.");
layers = Sky::REAL_TIME_ROUGHNESS_LAYERS;
if (roughness_layers != layers) {
WARN_PRINT(vformat("When using the Real-Time sky update mode (or Automatic with a sky shader using \"TIME\"), \"rendering/reflections/sky_reflections/roughness_layers\" should be set to %d in the project settings for best quality reflections.", Sky::REAL_TIME_ROUGHNESS_LAYERS));
}
}
if (sky_use_cubemap_array) {
//array (higher quality, 6 times more memory)
if (sky_use_octmap_array) {
mipmaps -= 2; // reduce the number of mipmaps to keep the border size reasonable.
// Double size to approximate texel density of cubemaps + add border for proper filtering/mipmapping.
uint32_t padding_pixels = (1 << (mipmaps - 1));
uint32_t w = sky->radiance_size * 2 + padding_pixels * 2;
uint32_t h = w;
sky->uv_border_size = float(padding_pixels) / float(w);
// Array (higher quality, more memory).
RD::TextureFormat tf;
tf.array_layers = layers * 6;
tf.array_layers = layers;
tf.format = texture_format;
tf.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY;
tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
tf.mipmaps = mipmaps;
tf.width = w;
tf.height = h;
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
if (!use_raster_effect) {
tf.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT;
}
sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
sky->reflection.update_reflection_data(sky->radiance_size, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format);
sky->reflection.update_reflection_data(w, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format, sky->uv_border_size);
} else {
//regular cubemap, lower quality (aliasing, less memory)
// Double size to approximate texel density of cubemaps + add border for proper filtering/mipmapping.
uint32_t padding_pixels = (1 << (MIN(mipmaps, layers) - 1));
uint32_t w = sky->radiance_size * 2 + padding_pixels * 2;
uint32_t h = w;
sky->uv_border_size = float(padding_pixels) / float(w);
// Single texture (lower quality, less memory).
RD::TextureFormat tf;
tf.array_layers = 6;
tf.format = texture_format;
tf.texture_type = RD::TEXTURE_TYPE_CUBE;
tf.mipmaps = MIN(mipmaps, layers);
tf.width = w;
tf.height = h;
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
if (!use_raster_effect) {
tf.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT;
}
sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
sky->reflection.update_reflection_data(sky->radiance_size, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format);
sky->reflection.update_reflection_data(w, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format, sky->uv_border_size);
}
}
@ -1693,6 +1638,13 @@ void SkyRD::sky_set_radiance_size(RID p_sky, int p_radiance_size) {
}
}
int SkyRD::sky_get_radiance_size(RID p_sky) const {
Sky *sky = get_sky(p_sky);
ERR_FAIL_NULL_V(sky, 0);
return sky->get_radiance_size();
}
void SkyRD::sky_set_mode(RID p_sky, RS::SkyMode p_mode) {
Sky *sky = get_sky(p_sky);
ERR_FAIL_NULL(sky);
@ -1726,3 +1678,10 @@ RID SkyRD::sky_get_radiance_texture_rd(RID p_sky) const {
return sky->radiance;
}
float SkyRD::sky_get_uv_border_size(RID p_sky) {
Sky *sky = get_sky(p_sky);
ERR_FAIL_NULL_V(sky, 1.0);
return sky->uv_border_size;
}

View file

@ -74,9 +74,9 @@ private:
SKY_TEXTURE_SET_BACKGROUND,
SKY_TEXTURE_SET_HALF_RES,
SKY_TEXTURE_SET_QUARTER_RES,
SKY_TEXTURE_SET_CUBEMAP,
SKY_TEXTURE_SET_CUBEMAP_HALF_RES,
SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES,
SKY_TEXTURE_SET_OCTMAP,
SKY_TEXTURE_SET_OCTMAP_HALF_RES,
SKY_TEXTURE_SET_OCTMAP_QUARTER_RES,
SKY_TEXTURE_SET_MAX
};
@ -84,9 +84,9 @@ private:
SKY_VERSION_BACKGROUND,
SKY_VERSION_HALF_RES,
SKY_VERSION_QUARTER_RES,
SKY_VERSION_CUBEMAP,
SKY_VERSION_CUBEMAP_HALF_RES,
SKY_VERSION_CUBEMAP_QUARTER_RES,
SKY_VERSION_OCTMAP,
SKY_VERSION_OCTMAP_HALF_RES,
SKY_VERSION_OCTMAP_QUARTER_RES,
SKY_VERSION_BACKGROUND_MULTIVIEW,
SKY_VERSION_HALF_RES_MULTIVIEW,
@ -100,7 +100,7 @@ private:
float projection[4]; // 16 - 64
float position[3]; // 12 - 76
float time; // 4 - 80
float pad[2]; // 8 - 88
float border_size[2]; // 8 - 88
float luminance_multiplier; // 4 - 92
float brightness_multiplier; // 4 - 96
// 128 is the max size of a push constant. We can replace "pad" but we can't add any more.
@ -134,7 +134,7 @@ private:
virtual ~SkyShaderData();
};
void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const Projection &p_projection, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier, float p_brightness_modifier);
void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const Projection &p_projection, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier, float p_brightness_modifier, float p_border_size = 0.0);
public:
struct SkySceneState {
@ -186,12 +186,11 @@ public:
struct ReflectionData {
struct Layer {
struct Mipmap {
RID framebuffers[6];
RID views[6];
RID framebuffer;
RID view;
Size2i size;
};
Vector<Mipmap> mipmaps; //per-face view
Vector<RID> views; // per-cubemap view
LocalVector<Mipmap> mipmaps;
};
struct DownsampleLayer {
@ -199,26 +198,27 @@ public:
RID view;
Size2i size;
// for mobile only
RID views[6];
RID framebuffers[6];
// Used only for the raster version.
RID octmap_view;
RID framebuffer;
};
Vector<Mipmap> mipmaps;
LocalVector<Mipmap> mipmaps;
};
RID radiance_base_cubemap; //cubemap for first layer, first cubemap
RID downsampled_radiance_cubemap;
RID radiance_base_octmap;
RID downsampled_radiance_octmap;
DownsampleLayer downsampled_layer;
RID coefficient_buffer;
bool dirty = true;
float uv_border_size = 0.0; // Border size in UV space.
Vector<Layer> layers;
LocalVector<Layer> layers;
void clear_reflection_data();
void update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format);
void update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_oct, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format, float p_border_size);
void create_reflection_fast_filter(bool p_use_arrays);
void create_reflection_importance_sample(bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality);
void create_reflection_importance_sample(bool p_use_arrays, int p_base_layer, uint32_t p_sky_ggx_samples_quality);
void update_reflection_mipmaps(int p_start, int p_end);
};
@ -245,6 +245,9 @@ public:
};
struct Sky {
static inline const int REAL_TIME_SIZE = 256;
static inline const int REAL_TIME_ROUGHNESS_LAYERS = 7;
RID radiance;
RID quarter_res_pass;
RID quarter_res_framebuffer;
@ -255,7 +258,8 @@ public:
RID material;
RID uniform_buffer;
int radiance_size = 256;
int radiance_size = REAL_TIME_SIZE;
float uv_border_size = 0.0; // Border size in UV space.
RS::SkyMode mode = RS::SKY_MODE_AUTOMATIC;
@ -265,7 +269,7 @@ public:
Sky *dirty_list = nullptr;
float baked_exposure = 1.0;
//State to track when radiance cubemap needs updating
// State to track when radiance octmap needs updating.
SkyMaterialData *prev_material = nullptr;
Vector3 prev_position;
float prev_time;
@ -274,13 +278,14 @@ public:
RID get_textures(SkyTextureSetVersion p_version, RID p_default_shader_rd, Ref<RenderSceneBuffersRD> p_render_buffers);
bool set_radiance_size(int p_radiance_size);
int get_radiance_size() const;
bool set_mode(RS::SkyMode p_mode);
bool set_material(RID p_material);
Ref<Image> bake_panorama(float p_energy, int p_roughness_layers, const Size2i &p_size);
};
uint32_t sky_ggx_samples_quality;
bool sky_use_cubemap_array;
bool sky_use_octmap_array;
Sky *dirty_sky_list = nullptr;
mutable RID_Owner<Sky, true> sky_owner;
@ -314,9 +319,11 @@ public:
Sky *get_sky(RID p_sky) const;
void free_sky(RID p_sky);
void sky_set_radiance_size(RID p_sky, int p_radiance_size);
int sky_get_radiance_size(RID p_sky) const;
void sky_set_mode(RID p_sky, RS::SkyMode p_mode);
void sky_set_material(RID p_sky, RID p_material);
Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size);
float sky_get_uv_border_size(RID p_sky);
};
} // namespace RendererRD

View file

@ -1347,7 +1347,7 @@ void RenderForwardClustered::_update_volumetric_fog(Ref<RenderSceneBuffersRD> p_
RendererRD::Fog::VolumetricFogSettings settings;
settings.rb_size = size;
settings.time = time;
settings.is_using_radiance_cubemap_array = is_using_radiance_cubemap_array();
settings.is_using_radiance_octmap_array = is_using_radiance_octmap_array();
settings.max_cluster_elements = RendererRD::LightStorage::get_singleton()->get_max_cluster_elements();
settings.volumetric_fog_filter_active = get_volumetric_fog_filter_active();
@ -3306,7 +3306,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend
if (p_radiance_texture.is_valid()) {
radiance_texture = p_radiance_texture;
} else {
radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_octmap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
}
RD::Uniform u;
u.binding = 3;
@ -3672,7 +3672,7 @@ RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_te
}
{
// No radiance texture.
RID radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
RID radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_octmap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
RD::Uniform u;
u.binding = 3;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
@ -4436,13 +4436,13 @@ static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::Da
return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes, p_view_count);
}
static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline() {
static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline(bool p_storage) {
RD::AttachmentFormat attachment;
thread_local Vector<RD::AttachmentFormat> attachments;
attachments.clear();
attachment.format = RendererRD::LightStorage::get_reflection_probe_color_format();
attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_color_usage_bits();
attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_color_usage_bits(p_storage);
attachments.push_back(attachment);
attachment.format = RendererRD::LightStorage::get_reflection_probe_depth_format();
@ -4543,6 +4543,7 @@ void RenderForwardClustered::_mesh_compile_pipeline_for_surface(SceneShaderForwa
void RenderForwardClustered::_mesh_compile_pipelines_for_surface(const SurfacePipelineData &p_surface, const GlobalPipelineData &p_global, RS::PipelineSource p_source, Vector<ShaderPipelinePair> *r_pipeline_pairs) {
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
bool octmap_use_storage = !copy_effects->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
// Retrieve from the scene shader which groups are currently enabled.
const bool multiview_enabled = p_global.use_multiview && scene_shader.is_multiview_shader_group_enabled();
@ -4578,7 +4579,7 @@ void RenderForwardClustered::_mesh_compile_pipelines_for_surface(const SurfacePi
pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MULTIVIEW;
} else if (p_global.use_reflection_probes) {
// Reflection probe can't be rendered in multiview.
pipeline_key.framebuffer_format_id = _get_reflection_probe_color_framebuffer_format_for_pipeline();
pipeline_key.framebuffer_format_id = _get_reflection_probe_color_framebuffer_format_for_pipeline(octmap_use_storage);
_mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
}
@ -4978,8 +4979,8 @@ RenderForwardClustered::RenderForwardClustered() {
{
String defines;
defines += "\n#define MAX_ROUGHNESS_LOD " + itos(get_roughness_layers() - 1) + ".0\n";
if (is_using_radiance_cubemap_array()) {
defines += "\n#define USE_RADIANCE_CUBEMAP_ARRAY \n";
if (is_using_radiance_octmap_array()) {
defines += "\n#define USE_RADIANCE_OCTMAP_ARRAY \n";
}
defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";

View file

@ -495,7 +495,7 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_
if (p_radiance_texture.is_valid()) {
radiance_texture = p_radiance_texture;
} else {
radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
radiance_texture = texture_storage->texture_rd_get_default(is_using_radiance_octmap_array() ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
}
RD::Uniform u;
u.binding = 2;
@ -1226,7 +1226,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target());
bool convert_to_linear = !hdr_render_target;
copy_effects->copy_to_drawlist(draw_list, fb_format, texture, convert_to_linear);
copy_effects->copy_to_drawlist(draw_list, fb_format, texture, convert_to_linear, 2.0f);
}
}
@ -3137,13 +3137,13 @@ static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::Da
return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes, p_view_count, vrs_attachment);
}
static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline() {
static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline(bool p_storage) {
RD::AttachmentFormat attachment;
thread_local Vector<RD::AttachmentFormat> attachments;
attachments.clear();
attachment.format = RendererRD::LightStorage::get_reflection_probe_color_format();
attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_color_usage_bits();
attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_color_usage_bits(p_storage);
attachments.push_back(attachment);
attachment.format = RendererRD::LightStorage::get_reflection_probe_depth_format();
@ -3192,6 +3192,7 @@ void RenderForwardMobile::_mesh_compile_pipeline_for_surface(SceneShaderForwardM
void RenderForwardMobile::_mesh_compile_pipelines_for_surface(const SurfacePipelineData &p_surface, const GlobalPipelineData &p_global, RS::PipelineSource p_source, Vector<ShaderPipelinePair> *r_pipeline_pairs) {
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
bool octmap_use_storage = !copy_effects->get_raster_effects().has_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
// Set the attributes common to all pipelines.
SceneShaderForwardMobile::ShaderData::PipelineKey pipeline_key;
@ -3241,7 +3242,7 @@ void RenderForwardMobile::_mesh_compile_pipelines_for_surface(const SurfacePipel
if (p_global.use_reflection_probes) {
pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS;
pipeline_key.framebuffer_format_id = _get_reflection_probe_color_framebuffer_format_for_pipeline();
pipeline_key.framebuffer_format_id = _get_reflection_probe_color_framebuffer_format_for_pipeline(octmap_use_storage);
_mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
}
@ -3404,8 +3405,8 @@ RenderForwardMobile::RenderForwardMobile() {
String defines;
defines += "\n#define MAX_ROUGHNESS_LOD " + itos(get_roughness_layers() - 1) + ".0\n";
if (is_using_radiance_cubemap_array()) {
defines += "\n#define USE_RADIANCE_CUBEMAP_ARRAY \n";
if (is_using_radiance_octmap_array()) {
defines += "\n#define USE_RADIANCE_OCTMAP_ARRAY \n";
}
// defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";

View file

@ -113,14 +113,14 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba
RS::EnvironmentAmbientSource ambient_source = environment_get_ambient_source(p_env);
bool use_ambient_light = false;
bool use_cube_map = false;
bool use_octmap = false;
if (ambient_source == RS::ENV_AMBIENT_SOURCE_BG && (environment_background == RS::ENV_BG_CLEAR_COLOR || environment_background == RS::ENV_BG_COLOR)) {
use_ambient_light = true;
} else {
use_cube_map = (ambient_source == RS::ENV_AMBIENT_SOURCE_BG && environment_background == RS::ENV_BG_SKY) || ambient_source == RS::ENV_AMBIENT_SOURCE_SKY;
use_ambient_light = use_cube_map || ambient_source == RS::ENV_AMBIENT_SOURCE_COLOR;
use_octmap = (ambient_source == RS::ENV_AMBIENT_SOURCE_BG && environment_background == RS::ENV_BG_SKY) || ambient_source == RS::ENV_AMBIENT_SOURCE_SKY;
use_ambient_light = use_octmap || ambient_source == RS::ENV_AMBIENT_SOURCE_COLOR;
}
use_cube_map = use_cube_map || (environment_background == RS::ENV_BG_SKY && environment_get_sky(p_env).is_valid());
use_octmap = use_octmap || (environment_background == RS::ENV_BG_SKY && environment_get_sky(p_env).is_valid());
Color ambient_color;
float ambient_color_sky_mix = 0.0;
@ -134,7 +134,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba
ambient_color.b *= ambient_energy;
}
if (use_cube_map) {
if (use_octmap) {
Ref<Image> panorama = sky_bake_panorama(environment_get_sky(p_env), environment_get_bg_energy_multiplier(p_env), p_bake_irradiance, p_size);
if (use_ambient_light && panorama.is_valid()) {
for (int x = 0; x < p_size.width; x++) {
@ -1234,8 +1234,8 @@ int RendererSceneRenderRD::get_roughness_layers() const {
return sky.roughness_layers;
}
bool RendererSceneRenderRD::is_using_radiance_cubemap_array() const {
return sky.sky_use_cubemap_array;
bool RendererSceneRenderRD::is_using_radiance_octmap_array() const {
return sky.sky_use_octmap_array;
}
void RendererSceneRenderRD::_update_vrs(Ref<RenderSceneBuffersRD> p_render_buffers) {
@ -1360,6 +1360,22 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render
scene_data.directional_shadow_pixel_size.y = 1.0 / directional_shadow_size;
}
if (p_environment.is_valid()) {
RID sky_rid = environment_get_sky(p_environment);
if (sky_rid.is_valid()) {
int radiance_size = sky.sky_get_radiance_size(sky_rid);
scene_data.radiance_pixel_size = 1.0f / radiance_size;
float uv_border_size = sky.sky_get_uv_border_size(sky_rid);
scene_data.radiance_border_size = uv_border_size;
}
}
if (p_reflection_atlas.is_valid()) {
float border_size = light_storage->reflection_atlas_get_border_size(p_reflection_atlas);
scene_data.reflection_atlas_border_size.x = border_size;
scene_data.reflection_atlas_border_size.y = 1.0f - border_size * 2.0;
}
scene_data.time = time;
scene_data.time_step = time_step;
}
@ -1659,7 +1675,7 @@ void RendererSceneRenderRD::init() {
}
if (is_volumetric_supported()) {
RendererRD::Fog::get_singleton()->init_fog_shader(RendererRD::LightStorage::get_singleton()->get_max_directional_lights(), get_roughness_layers(), is_using_radiance_cubemap_array());
RendererRD::Fog::get_singleton()->init_fog_shader(RendererRD::LightStorage::get_singleton()->get_max_directional_lights(), get_roughness_layers(), is_using_radiance_octmap_array());
}
RSG::camera_attributes->camera_attributes_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape"))));
@ -1690,8 +1706,20 @@ void RendererSceneRenderRD::init() {
bool can_use_storage = _render_buffers_can_be_storage();
bool can_use_vrs = is_vrs_supported();
BitField<RendererRD::CopyEffects::RasterEffects> raster_effects = {};
if (!can_use_storage) {
raster_effects.set_flag(RendererRD::CopyEffects::RASTER_EFFECT_COPY);
raster_effects.set_flag(RendererRD::CopyEffects::RASTER_EFFECT_GAUSSIAN_BLUR);
// This path can be used in the future to redirect certain devices to use the raster version of the effect, either due to performance or driver errors.
bool use_raster_for_octmaps = false;
if (use_raster_for_octmaps) {
raster_effects.set_flag(RendererRD::CopyEffects::RASTER_EFFECT_OCTMAP);
}
}
bokeh_dof = memnew(RendererRD::BokehDOF(!can_use_storage));
copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage));
copy_effects = memnew(RendererRD::CopyEffects(raster_effects));
debug_effects = memnew(RendererRD::DebugEffects);
luminance = memnew(RendererRD::Luminance(!can_use_storage));
smaa = memnew(RendererRD::SMAA);

View file

@ -325,7 +325,7 @@ public:
}
int get_roughness_layers() const;
bool is_using_radiance_cubemap_array() const;
bool is_using_radiance_octmap_array() const;
virtual TypedArray<Image> bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size) override;

View file

@ -4,6 +4,8 @@
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define FLAG_HORIZONTAL (1 << 0)
@ -35,16 +37,17 @@ layout(push_constant, std430) uniform Params {
// DOF.
float camera_z_far;
float camera_z_near;
uint pad2[2];
// Octmap.
vec2 octmap_border_size;
vec4 set_color;
}
params;
#ifdef MODE_CUBEMAP_ARRAY_TO_PANORAMA
layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
#elif defined(MODE_CUBEMAP_TO_PANORAMA)
layout(set = 0, binding = 0) uniform samplerCube source_color;
#ifdef MODE_OCTMAP_ARRAY_TO_PANORAMA
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
#elif defined(MODE_OCTMAP_TO_PANORAMA)
layout(set = 0, binding = 0) uniform sampler2D source_color;
#elif !defined(MODE_SET_COLOR)
layout(set = 0, binding = 0) uniform sampler2D source_color;
#endif
@ -262,7 +265,7 @@ void main() {
imageStore(dest_buffer, pos + params.target, color);
#endif // MODE_LINEARIZE_DEPTH_COPY
#if defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
#if defined(MODE_OCTMAP_TO_PANORAMA) || defined(MODE_OCTMAP_ARRAY_TO_PANORAMA)
const float PI = 3.14159265359;
vec2 uv = vec2(pos) / vec2(params.section.zw);
@ -277,13 +280,13 @@ void main() {
normal.y = cos(theta);
normal.z = cos(phi) * sin(theta) * -1.0;
#ifdef MODE_CUBEMAP_TO_PANORAMA
vec4 color = textureLod(source_color, normal, params.camera_z_far); //the biggest the lod the least the acne
#ifdef MODE_OCTMAP_TO_PANORAMA
vec4 color = textureLod(source_color, vec3_to_oct_with_border(normal, params.octmap_border_size), params.camera_z_far); //the biggest the lod the least the acne
#else
vec4 color = textureLod(source_color, vec4(normal, params.camera_z_far), 0.0); //the biggest the lod the least the acne
vec4 color = textureLod(source_color, vec3(vec3_to_oct_with_border(normal, params.octmap_border_size), params.camera_z_far), 0.0); //the biggest the lod the least the acne
#endif
imageStore(dest_buffer, pos + params.target, color * params.luminance_multiplier);
#endif // defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
#endif // defined(MODE_OCTMAP_TO_PANORAMA) || defined(MODE_OCTMAP_ARRAY_TO_PANORAMA)
#ifdef MODE_SET_COLOR
imageStore(dest_buffer, pos + params.target, params.set_color);

View file

@ -0,0 +1,36 @@
#[vertex]
#version 450
#VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp, 0.0, 1.0);
}
#[fragment]
#version 450
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(push_constant, std430) uniform Params {
float border_size;
}
params;
void main() {
vec3 dir = oct_to_vec3_with_border(uv_interp * 0.5 + 0.5, params.border_size);
frag_color = vec4(texture(source_cube, dir).rgb, 1.0);
}

View file

@ -1,145 +0,0 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
#include "cubemap_downsampler_inc.glsl"
void main() {
uvec3 id = gl_GlobalInvocationID;
uint face_size = params.face_size;
if (id.x < face_size && id.y < face_size) {
float inv_face_size = 1.0 / float(face_size);
float u0 = (float(id.x) * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
float u1 = (float(id.x) * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
float v0 = (float(id.y) * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
float v1 = (float(id.y) * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + .125;
}
vec3 dir;
vec4 color;
switch (id.z) {
case 0:
get_dir_0(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_0(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_0(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_0(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 1:
get_dir_1(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_1(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_1(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_1(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 2:
get_dir_2(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_2(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_2(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_2(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 3:
get_dir_3(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_3(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_3(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_3(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 4:
get_dir_4(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_4(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_4(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_4(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
default:
get_dir_5(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_5(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_5(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_5(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
}
imageStore(dest_cubemap, ivec3(id), color);
}
}

View file

@ -1,48 +0,0 @@
layout(push_constant, std430) uniform Params {
uint face_size;
uint face_id; // only used in raster shader
}
params;
#define M_PI 3.14159265359
void get_dir_0(out vec3 dir, in float u, in float v) {
dir[0] = 1.0;
dir[1] = v;
dir[2] = -u;
}
void get_dir_1(out vec3 dir, in float u, in float v) {
dir[0] = -1.0;
dir[1] = v;
dir[2] = u;
}
void get_dir_2(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = 1.0;
dir[2] = -v;
}
void get_dir_3(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = -1.0;
dir[2] = v;
}
void get_dir_4(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = v;
dir[2] = 1.0;
}
void get_dir_5(out vec3 dir, in float u, in float v) {
dir[0] = -u;
dir[1] = v;
dir[2] = -1.0;
}
float calcWeight(float u, float v) {
float val = u * u + v * v + 1.0;
return val * sqrt(val);
}

View file

@ -1,161 +0,0 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/* clang-format off */
#[vertex]
#version 450
#VERSION_DEFINES
#include "cubemap_downsampler_inc.glsl"
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0 * float(params.face_size); // saturate(x) * 2.0
}
/* clang-format off */
#[fragment]
#version 450
#VERSION_DEFINES
#include "cubemap_downsampler_inc.glsl"
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
/* clang-format on */
void main() {
// Converted from compute shader which uses absolute coordinates.
// Could possibly simplify this
float face_size = float(params.face_size);
float inv_face_size = 1.0 / face_size;
vec2 id = floor(uv_interp);
float u1 = (id.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
float u0 = (id.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
float v0 = (id.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
float v1 = (id.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + .125;
}
vec3 dir;
vec4 color;
switch (params.face_id) {
case 0:
get_dir_0(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_0(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_0(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_0(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 1:
get_dir_1(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_1(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_1(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_1(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 2:
get_dir_2(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_2(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_2(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_2(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 3:
get_dir_3(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_3(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_3(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_3(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 4:
get_dir_4(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_4(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_4(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_4(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
default:
get_dir_5(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_5(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_5(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_5(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
}
frag_color = color;
}

View file

@ -1,329 +0,0 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 64
layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly imageCube dest_cubemap0;
layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly imageCube dest_cubemap1;
layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly imageCube dest_cubemap2;
layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly imageCube dest_cubemap3;
layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly imageCube dest_cubemap4;
layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly imageCube dest_cubemap5;
layout(rgba16f, set = 2, binding = 6) uniform restrict writeonly imageCube dest_cubemap6;
#ifdef USE_HIGH_QUALITY
#define NUM_TAPS 32
#else
#define NUM_TAPS 8
#endif
#define BASE_RESOLUTION 128
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
}
data;
#else
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][6] coeffs;
}
data;
#endif
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
switch (face) {
case 0:
dir = vec3(1.0, uv[1], -uv[0]);
break;
case 1:
dir = vec3(-1.0, uv[1], uv[0]);
break;
case 2:
dir = vec3(uv[0], 1.0, -uv[1]);
break;
case 3:
dir = vec3(uv[0], -1.0, uv[1]);
break;
case 4:
dir = vec3(uv[0], uv[1], 1.0);
break;
default:
dir = vec3(-uv[0], uv[1], -1.0);
break;
}
}
void main() {
// INPUT:
// id.x = the linear address of the texel (ignoring face)
// id.y = the face
// -> use to index output texture
// id.x = texel x
// id.y = texel y
// id.z = face
uvec3 id = gl_GlobalInvocationID;
// determine which texel this is
#ifndef USE_TEXTURE_ARRAY
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
int mip_level = 0;
if (id.x < (128 * 128)) {
mip_level = 0;
} else if (id.x < (128 * 128 + 64 * 64)) {
mip_level = 1;
id.x -= (128 * 128);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32)) {
mip_level = 2;
id.x -= (128 * 128 + 64 * 64);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16)) {
mip_level = 3;
id.x -= (128 * 128 + 64 * 64 + 32 * 32);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8)) {
mip_level = 4;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4)) {
mip_level = 5;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2)) {
mip_level = 6;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4);
} else {
return;
}
int res = BASE_RESOLUTION >> mip_level;
#else // Using Texture Arrays so all levels are the same resolution
int res = BASE_RESOLUTION;
int mip_level = int(id.x / (BASE_RESOLUTION * BASE_RESOLUTION));
id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION;
#endif
// determine dir / pos for the texel
vec3 dir, adir, frameZ;
{
id.z = id.y;
id.y = id.x / res;
id.x -= id.y * res;
vec2 uv;
uv.x = (float(id.x) * 2.0 + 1.0) / float(res) - 1.0;
uv.y = -(float(id.y) * 2.0 + 1.0) / float(res) + 1.0;
get_dir(dir, uv, id.z);
frameZ = normalize(dir);
adir = abs(dir);
}
// GGX gather colors
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
switch (axis) {
case 0:
UpVector = vec3(1, 0, 0);
break;
case 1:
UpVector = vec3(0, 1, 0);
break;
default:
UpVector = vec3(0, 0, 1);
break;
}
vec3 frameX = normalize(cross(UpVector, frameZ));
vec3 frameY = cross(frameZ, frameX);
// calculate parametrization for polynomial
float Nx = dir[otherAxis0];
float Ny = dir[otherAxis1];
float Nz = adir[axis];
float NmaxXY = max(abs(Ny), abs(Nx));
Nx /= NmaxXY;
Ny /= NmaxXY;
float theta;
if (Ny < Nx) {
if (Ny <= -0.999) {
theta = Nx;
} else {
theta = Ny;
}
} else {
if (Ny >= 0.999) {
theta = -Nx;
} else {
theta = -Ny;
}
}
float phi;
if (Nz <= -0.999) {
phi = -NmaxXY;
} else if (Nz >= 0.999) {
phi = NmaxXY;
} else {
phi = Nz;
}
float theta2 = theta * theta;
float phi2 = phi * phi;
// sample
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
#ifdef USE_HIGH_QUALITY
vec4 coeffsDir0[3];
vec4 coeffsDir1[3];
vec4 coeffsDir2[3];
vec4 coeffsLevel[3];
vec4 coeffsWeight[3];
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index];
coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index];
coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index];
coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index];
coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index];
}
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
#else
vec4 coeffsDir0 = data.coeffs[mip_level][0][index];
vec4 coeffsDir1 = data.coeffs[mip_level][1][index];
vec4 coeffsDir2 = data.coeffs[mip_level][2][index];
vec4 coeffsLevel = data.coeffs[mip_level][3][index];
vec4 coeffsWeight = data.coeffs[mip_level][4][index];
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
float sample_level = coeffsLevel[iSubTap];
float sample_weight = coeffsWeight[iSubTap];
#endif
sample_weight *= frameweight;
// adjust for jacobian
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
#ifndef USE_TEXTURE_ARRAY
sample_level += float(mip_level) / 6.0; // Hack to increase the perceived roughness and reduce upscaling artifacts
#endif
// sample cubemap
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
color.w += sample_weight;
}
}
}
}
color /= color.w;
// write color
color.xyz = max(vec3(0.0), color.xyz);
color.w = 1.0;
#ifdef USE_TEXTURE_ARRAY
id.xy *= uvec2(2, 2);
#endif
switch (mip_level) {
case 0:
imageStore(dest_cubemap0, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 1:
imageStore(dest_cubemap1, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 2:
imageStore(dest_cubemap2, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 3:
imageStore(dest_cubemap3, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 4:
imageStore(dest_cubemap4, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 5:
imageStore(dest_cubemap5, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
default:
imageStore(dest_cubemap6, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
}
}

View file

@ -1,63 +0,0 @@
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 8
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
#include "cubemap_roughness_inc.glsl"
void main() {
uvec3 id = gl_GlobalInvocationID;
id.z += params.face_id;
vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
vec3 N = texelCoordToVec(uv, id.z);
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 = 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) {
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 /= sum.a;
imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0));
}
}

View file

@ -1,84 +0,0 @@
#define M_PI 3.14159265359
layout(push_constant, std430) uniform Params {
uint face_id;
uint sample_count;
float roughness;
bool use_direct_write;
float face_size;
}
params;
vec3 texelCoordToVec(vec2 uv, uint faceID) {
mat3 faceUvVectors[6];
// -x
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
// +x
faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face
// -y
faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
// +y
faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z
faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face
// -z
faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
// +z
faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
return normalize(result);
}
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 + (roughness4 - 1.0) * xi.y));
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
// Convert to spherical direction
vec3 H;
H.x = SinTheta * cos(Phi);
H.y = SinTheta * sin(Phi);
H.z = CosTheta;
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;
return roughness4 / denom;
}
float radicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N) {
return vec2(float(i) / float(N), radicalInverse_VdC(i));
}

View file

@ -0,0 +1,83 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define BLOCK_SIZE 8
#include "../oct_inc.glsl"
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D dest_octmap;
layout(push_constant, std430) uniform Params {
float border_size;
uint size;
uint pad;
uint pad2;
}
params;
// Use an approximation of the Jacobian.
float calcWeight(float u, float v) {
vec3 d = oct_to_vec3_with_border(vec2(u, v), params.border_size);
return 1.0 / pow(abs(d.z) + 1.0, 3.0);
}
void main() {
uvec2 id = gl_GlobalInvocationID.xy;
if (id.x < params.size && id.y < params.size) {
float inv_size = 1.0 / float(params.size);
uvec2 sample_id = id;
#ifdef USE_HIGH_QUALITY
float u0 = (float(sample_id.x) * 2.0f + 1.0f - 0.75f) * inv_size - 1.0f;
float u1 = (float(sample_id.x) * 2.0f + 1.0f + 0.75f) * inv_size - 1.0f;
float v0 = (float(sample_id.y) * 2.0f + 1.0f - 0.75f) * inv_size - 1.0f;
float v1 = (float(sample_id.y) * 2.0f + 1.0f + 0.75f) * inv_size - 1.0f;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + 0.125;
}
vec4 color = textureLod(source_octmap, vec2(u0, v0) * 0.5f + 0.5f, 0.0) * weights[0];
color += textureLod(source_octmap, vec2(u1, v0) * 0.5f + 0.5f, 0.0) * weights[1];
color += textureLod(source_octmap, vec2(u0, v1) * 0.5f + 0.5f, 0.0) * weights[2];
color += textureLod(source_octmap, vec2(u1, v1) * 0.5f + 0.5f, 0.0) * weights[3];
imageStore(dest_octmap, ivec2(id), color);
#else
vec2 uv = (vec2(sample_id.xy) + 0.5) * inv_size;
imageStore(dest_octmap, ivec2(id), textureLod(source_octmap, uv, 0.0));
#endif
}
}

View file

@ -0,0 +1,94 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/* clang-format off */
#[vertex]
#version 450
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(push_constant, std430) uniform Params {
uint size;
}
params;
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
#[fragment]
#version 450
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(push_constant, std430) uniform Params {
uint size;
}
params;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
/* clang-format on */
float calcWeight(float u, float v) {
float val = u * u + v * v + 1.0;
return val * sqrt(val);
}
void main() {
#ifdef USE_HIGH_QUALITY
float inv_size = 1.0 / float(params.size);
float u0 = (uv_interp.x * 2.0f - 0.75) * inv_size - 1.0f;
float u1 = (uv_interp.x * 2.0f + 0.75) * inv_size - 1.0f;
float v0 = (uv_interp.y * 2.0f - 0.75) * inv_size - 1.0f;
float v1 = (uv_interp.y * 2.0f + 0.75) * inv_size - 1.0f;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + 0.125;
}
frag_color = textureLod(source_octmap, vec2(u0, v0) * 0.5f + 0.5f, 0.0) * weights[0];
frag_color += textureLod(source_octmap, vec2(u1, v0) * 0.5f + 0.5f, 0.0) * weights[1];
frag_color += textureLod(source_octmap, vec2(u0, v1) * 0.5f + 0.5f, 0.0) * weights[2];
frag_color += textureLod(source_octmap, vec2(u1, v1) * 0.5f + 0.5f, 0.0) * weights[3];
#else
frag_color = textureLod(source_octmap, uv_interp, 0.0);
#endif
}

View file

@ -0,0 +1,258 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 64
#include "../oct_inc.glsl"
layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly image2D dest_octmap0;
layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly image2D dest_octmap1;
layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly image2D dest_octmap2;
layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly image2D dest_octmap3;
layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly image2D dest_octmap4;
layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly image2D dest_octmap5;
#ifdef USE_HIGH_QUALITY
#define NUM_TAPS 32
#else
#define NUM_TAPS 8
#endif
layout(push_constant, std430) uniform Params {
vec2 border_size;
vec2 pad;
}
params;
#define BASE_RESOLUTION 320
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
}
data;
#else
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][6] coeffs;
}
data;
#endif
void main() {
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
uvec2 id = gl_GlobalInvocationID.xy;
uint mip_level = 0;
#ifndef USE_TEXTURE_ARRAY
uint res = BASE_RESOLUTION;
while ((id.x >= (res * res)) && (res > 1)) {
id.x -= res * res;
res = res >> 1;
mip_level++;
}
#else // Using Texture Arrays so all levels are the same resolution
uint res = BASE_RESOLUTION;
mip_level = id.x / (BASE_RESOLUTION * BASE_RESOLUTION);
id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION;
#endif
// Determine the direction from the texel's position.
id.y = id.x / res;
id.x -= id.y * res;
vec2 inv_res = 1.0 / vec2(res);
vec3 dir = oct_to_vec3_with_border((vec2(id.xy) + vec2(0.5)) * inv_res, params.border_size.y);
vec3 adir = abs(dir);
vec3 frameZ = dir;
// Gather colors using GGX.
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
const float lowerBound = 0.57735; // 1 / sqrt(3), magnitude for each component on a vector where all the components are equal.
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - lowerBound) / (1.0 - lowerBound);
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
switch (axis) {
case 0:
UpVector = vec3(1, 0, 0);
break;
case 1:
UpVector = vec3(0, 1, 0);
break;
default:
UpVector = vec3(0, 0, 1);
break;
}
vec3 frameX = normalize(cross(UpVector, frameZ));
vec3 frameY = cross(frameZ, frameX);
// Calculate parametrization for polynomial.
float Nx = dir[otherAxis0];
float Ny = dir[otherAxis1];
float Nz = adir[axis];
float NmaxXY = max(abs(Ny), abs(Nx));
Nx /= NmaxXY;
Ny /= NmaxXY;
float theta;
if (Ny < Nx) {
if (Ny <= -0.999) {
theta = Nx;
} else {
theta = Ny;
}
} else {
if (Ny >= 0.999) {
theta = -Nx;
} else {
theta = -Ny;
}
}
float phi;
if (Nz <= -0.999) {
phi = -NmaxXY;
} else if (Nz >= 0.999) {
phi = NmaxXY;
} else {
phi = Nz;
}
float theta2 = theta * theta;
float phi2 = phi * phi;
// Sample. The coefficient table was computed with less mip levels than required, so we clamp the maximum level.
uint coeff_mip_level = min(mip_level, 5);
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
#ifdef USE_HIGH_QUALITY
vec4 coeffsDir0[3];
vec4 coeffsDir1[3];
vec4 coeffsDir2[3];
vec4 coeffsLevel[3];
vec4 coeffsWeight[3];
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
coeffsDir0[iCoeff] = data.coeffs[coeff_mip_level][0][iCoeff][index];
coeffsDir1[iCoeff] = data.coeffs[coeff_mip_level][1][iCoeff][index];
coeffsDir2[iCoeff] = data.coeffs[coeff_mip_level][2][iCoeff][index];
coeffsLevel[iCoeff] = data.coeffs[coeff_mip_level][3][iCoeff][index];
coeffsWeight[iCoeff] = data.coeffs[coeff_mip_level][4][iCoeff][index];
}
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// Determine sample attributes (dir, weight, coeff_mip_level)
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
#else
vec4 coeffsDir0 = data.coeffs[coeff_mip_level][0][index];
vec4 coeffsDir1 = data.coeffs[coeff_mip_level][1][index];
vec4 coeffsDir2 = data.coeffs[coeff_mip_level][2][index];
vec4 coeffsLevel = data.coeffs[coeff_mip_level][3][index];
vec4 coeffsWeight = data.coeffs[coeff_mip_level][4][index];
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, coeff_mip_level)
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
float sample_level = coeffsLevel[iSubTap];
float sample_weight = coeffsWeight[iSubTap];
#endif
sample_weight *= frameweight;
#ifdef USE_HIGH_QUALITY
// Adjust for Jacobian.
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
#endif
#ifndef USE_TEXTURE_ARRAY
sample_level += float(mip_level) / 5.0; // Hack to increase the perceived roughness and reduce upscaling artifacts
#endif
// Sample Octmap.
vec2 sample_uv = vec3_to_oct_with_border(normalize(sample_dir), params.border_size);
color.rgb += textureLod(source_octmap, sample_uv, sample_level).rgb * sample_weight;
color.a += sample_weight;
}
}
}
}
// Write out the result.
color = vec4(max(vec3(0.0), color.rgb / color.a), 1.0);
#ifdef USE_TEXTURE_ARRAY
id.xy *= uvec2(2, 2);
#endif
if (mip_level > 5) {
return;
}
#ifdef USE_TEXTURE_ARRAY
#define IMAGE_STORE(x) \
imageStore(x, ivec2(id), color); \
imageStore(x, ivec2(id) + ivec2(1, 0), color); \
imageStore(x, ivec2(id) + ivec2(0, 1), color); \
imageStore(x, ivec2(id) + ivec2(1, 1), color)
#else
#define IMAGE_STORE(x) imageStore(x, ivec2(id), color)
#endif
switch (mip_level) {
case 0:
IMAGE_STORE(dest_octmap0);
break;
case 1:
IMAGE_STORE(dest_octmap1);
break;
case 2:
IMAGE_STORE(dest_octmap2);
break;
case 3:
IMAGE_STORE(dest_octmap3);
break;
case 4:
IMAGE_STORE(dest_octmap4);
break;
case 5:
default:
IMAGE_STORE(dest_octmap5);
break;
}
}

View file

@ -26,8 +26,9 @@
#VERSION_DEFINES
layout(push_constant, std430) uniform Params {
vec2 border_size;
int mip_level;
uint face_id;
int pad;
}
params;
@ -35,9 +36,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp, 0.0, 1.0);
}
/* clang-format off */
@ -47,13 +48,16 @@ void main() {
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(push_constant, std430) uniform Params {
vec2 border_size;
int mip_level;
uint face_id;
int pad;
}
params;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
@ -66,8 +70,6 @@ layout(location = 0) out vec4 frag_color;
#define NUM_TAPS 8
#endif
#define BASE_RESOLUTION 128
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
@ -80,67 +82,35 @@ layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
data;
#endif
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
switch (face) {
case 0:
dir = vec3(1.0, uv[1], -uv[0]);
break;
case 1:
dir = vec3(-1.0, uv[1], uv[0]);
break;
case 2:
dir = vec3(uv[0], 1.0, -uv[1]);
break;
case 3:
dir = vec3(uv[0], -1.0, uv[1]);
break;
case 4:
dir = vec3(uv[0], uv[1], 1.0);
break;
default:
dir = vec3(-uv[0], uv[1], -1.0);
break;
}
}
void main() {
// determine dir / pos for the texel
vec3 dir, adir, frameZ;
{
vec2 uv;
uv.x = uv_interp.x;
uv.y = 1.0 - uv_interp.y;
uv = uv * 2.0 - 1.0;
get_dir(dir, uv, params.face_id);
frameZ = normalize(dir);
adir = abs(dir);
}
// determine which texel this is
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
int mip_level = 0;
vec2 uv = uv_interp * 0.5 + 0.5;
if (params.mip_level < 0) {
// return as is
frag_color.rgb = textureLod(source_cubemap, frameZ, 0.0).rgb;
// Just copy the octmap directly.
frag_color.rgb = textureLod(source_octmap, uv, 0.0).rgb;
frag_color.a = 1.0;
return;
} else if (params.mip_level > 6) {
// maximum level
mip_level = 6;
} else if (params.mip_level > 5) {
// Limit the level.
mip_level = 5;
} else {
// Use the level from the push constant.
mip_level = params.mip_level;
}
// GGX gather colors
// Determine the direction from the texel's position.
vec3 dir = oct_to_vec3_with_border(uv, params.border_size.y);
vec3 adir = abs(dir);
vec3 frameZ = dir;
// Gather colors using GGX.
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
const float lowerBound = 0.57735; // 1 / sqrt(3), magnitude for each component on a vector where all the components are equal.
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - lowerBound) / (1.0 - lowerBound);
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
@ -195,7 +165,7 @@ void main() {
float theta2 = theta * theta;
float phi2 = phi * phi;
// sample
// Sample.
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
@ -239,21 +209,15 @@ void main() {
sample_weight *= frameweight;
// adjust for jacobian
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
// sample cubemap
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
color.w += sample_weight;
// Sample Octmap.
vec2 sample_uv = vec3_to_oct_with_border(normalize(sample_dir), params.border_size);
color.rgb += textureLod(source_octmap, sample_uv, sample_level).rgb * sample_weight;
color.a += sample_weight;
}
}
}
}
color /= color.w;
// write color
color.xyz = max(vec3(0.0), color.xyz);
color.w = 1.0;
frag_color = color;
// Write out the result.
frag_color = vec4(max(vec3(0.0), color.rgb / color.a), 1.0);
}

View file

@ -0,0 +1,64 @@
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 8
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_oct;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D dest_octmap;
#include "../oct_inc.glsl"
#include "octmap_roughness_inc.glsl"
void main() {
uvec2 id = gl_GlobalInvocationID.xy;
if (id.x < params.dest_size && id.y < params.dest_size) {
vec2 inv_source_size = 1.0 / vec2(params.source_size);
vec2 inv_dest_size = 1.0 / vec2(params.dest_size);
vec2 uv = (vec2(id.xy) + 0.5) * inv_dest_size;
if (params.use_direct_write) {
imageStore(dest_octmap, ivec2(id), vec4(texture(source_oct, uv).rgb, 1.0));
} else {
vec3 N = oct_to_vec3_with_border(uv, params.border_size.y);
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
float solid_angle_texel = 4.0 * M_PI / float(params.dest_size * params.dest_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 = 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) {
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);
vec2 sample_uv = vec3_to_oct_with_border(L, params.border_size);
sum.rgb += textureLod(source_oct, sample_uv, mipLevel).rgb * ndotl;
sum.a += ndotl;
}
}
imageStore(dest_octmap, ivec2(id), vec4(sum.rgb / sum.a, 1.0));
}
}
}

View file

@ -0,0 +1,49 @@
#define M_PI 3.14159265359
layout(push_constant, std430) uniform Params {
uint sample_count;
float roughness;
uint source_size;
uint dest_size;
vec2 border_size;
bool use_direct_write;
uint pad;
}
params;
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 + (roughness4 - 1.0) * xi.y));
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
// Convert to spherical direction
vec3 H;
H.x = SinTheta * cos(Phi);
H.y = SinTheta * sin(Phi);
H.z = CosTheta;
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;
return roughness4 / denom;
}
float radicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N) {
return vec2(float(i) / float(N), radicalInverse_VdC(i));
}

View file

@ -5,7 +5,7 @@
#VERSION_DEFINES
#include "cubemap_roughness_inc.glsl"
#include "octmap_roughness_inc.glsl"
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
@ -23,26 +23,24 @@ void main() {
#VERSION_DEFINES
#include "cubemap_roughness_inc.glsl"
#include "../oct_inc.glsl"
#include "octmap_roughness_inc.glsl"
layout(location = 0) in vec2 uv_interp;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(set = 0, binding = 0) uniform sampler2D source_oct;
layout(location = 0) out vec4 frag_color;
/* clang-format on */
void main() {
vec3 N = texelCoordToVec(uv_interp * 2.0 - 1.0, params.face_id);
//vec4 color = color_interp;
if (params.use_direct_write) {
frag_color = vec4(texture(source_cube, N).rgb, 1.0);
frag_color = vec4(texture(source_oct, uv_interp).rgb, 1.0);
} else {
vec2 inv_size = 1.0 / vec2(params.size);
vec3 N = oct_to_vec3_with_border(uv_interp, params.border_size.y);
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 solid_angle_texel = 4.0 * M_PI / (params.size * params.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);
@ -68,12 +66,12 @@ void main() {
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;
vec2 sample_uv = vec3_to_oct_with_border(L, params.border_size);
sum.rgb += textureLod(source_oct, sample_uv, mipLevel).rgb * ndotl;
sum.a += ndotl;
}
}
sum /= sum.a;
frag_color = vec4(sum.rgb, 1.0);
frag_color = vec4(sum.rgb / sum.a, 1.0);
}
}

View file

@ -4,6 +4,8 @@
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define MAX_CASCADES 8
@ -40,10 +42,10 @@ layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_aver
layout(rgba16f, set = 0, binding = 14) uniform restrict writeonly image2DArray lightprobe_ambient_texture;
#ifdef USE_CUBEMAP_ARRAY
layout(set = 1, binding = 0) uniform textureCubeArray sky_irradiance;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 1, binding = 0) uniform texture2DArray sky_irradiance;
#else
layout(set = 1, binding = 0) uniform textureCube sky_irradiance;
layout(set = 1, binding = 0) uniform texture2D sky_irradiance;
#endif
layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps;
@ -75,8 +77,9 @@ layout(push_constant, std430) uniform Params {
vec3 sky_color_or_orientation;
float y_mult;
vec2 sky_irradiance_border_size;
bool store_ambient_texture;
uint pad[3];
uint pad;
}
params;
@ -271,10 +274,10 @@ void main() {
vec4 sky_quat = vec4(params.sky_color_or_orientation, sky_sign * sqrt(1.0 - dot(params.sky_color_or_orientation, params.sky_color_or_orientation)));
vec3 sky_dir = cross(sky_quat.xyz, ray_dir);
sky_dir = ray_dir + ((sky_dir * sky_quat.w) + cross(sky_quat.xyz, sky_dir)) * 2.0;
#ifdef USE_CUBEMAP_ARRAY
light.rgb = textureLod(samplerCubeArray(sky_irradiance, linear_sampler_mipmaps), vec4(sky_dir, 0.0), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
#ifdef USE_RADIANCE_OCTMAP_ARRAY
light.rgb = textureLod(sampler2DArray(sky_irradiance, linear_sampler_mipmaps), vec3(vec3_to_oct_with_border(sky_dir, params.sky_irradiance_border_size), 0.0), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
#else
light.rgb = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), sky_dir, 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
light.rgb = textureLod(sampler2D(sky_irradiance, linear_sampler_mipmaps), vec3_to_oct_with_border(sky_dir, params.sky_irradiance_border_size), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
#endif
light.rgb *= params.sky_energy;
light.a = 0.0;

View file

@ -29,6 +29,8 @@ void main() {
#VERSION_DEFINES
#include "../oct_inc.glsl"
#ifdef USE_MULTIVIEW
#extension GL_EXT_multiview : enable
#define ViewIndex gl_ViewIndex
@ -43,7 +45,7 @@ layout(push_constant, std430) uniform Params {
vec4 projection; // only applicable if not multiview
vec3 position;
float time;
vec2 pad;
vec2 border_size;
float luminance_multiplier;
float brightness_multiplier;
}
@ -100,10 +102,10 @@ layout(set = 1, binding = 0, std140) uniform MaterialUniforms {
/* clang-format on */
#endif
layout(set = 2, binding = 0) uniform textureCube radiance;
layout(set = 2, binding = 0) uniform texture2D radiance;
#ifdef USE_CUBEMAP_PASS
layout(set = 2, binding = 1) uniform textureCube half_res;
layout(set = 2, binding = 2) uniform textureCube quarter_res;
layout(set = 2, binding = 1) uniform texture2D half_res;
layout(set = 2, binding = 2) uniform texture2D quarter_res;
#elif defined(USE_MULTIVIEW)
layout(set = 2, binding = 1) uniform texture2DArray half_res;
layout(set = 2, binding = 2) uniform texture2DArray quarter_res;
@ -199,7 +201,11 @@ float atan2_approx(float y, float x) {
}
void main() {
vec2 uv = uv_interp * 0.5 + 0.5;
vec3 cube_normal;
#ifdef USE_CUBEMAP_PASS
cube_normal = oct_to_vec3_with_border(uv, params.border_size.y);
#else
#ifdef USE_MULTIVIEW
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
vec4 unproject = vec4(uv_interp.x, uv_interp.y, 0.0, 1.0); // unproject at the far plane
@ -215,8 +221,7 @@ void main() {
#endif
cube_normal = mat3(params.orientation) * cube_normal;
cube_normal = normalize(cube_normal);
vec2 uv = uv_interp * 0.5 + 0.5;
#endif
vec2 panorama_coords = vec2(atan2_approx(cube_normal.x, -cube_normal.z), acos_approx(cube_normal.y));
@ -235,10 +240,10 @@ void main() {
#ifdef USE_CUBEMAP_PASS
#ifdef USES_HALF_RES_COLOR
half_res_color = texture(samplerCube(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
half_res_color = texture(sampler2D(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3_to_oct_with_border(cube_normal, params.border_size)) / params.luminance_multiplier;
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = texture(samplerCube(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
quarter_res_color = texture(sampler2D(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3_to_oct_with_border(cube_normal, params.border_size)) / params.luminance_multiplier;
#endif
#else

View file

@ -16,6 +16,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "../cluster_data_inc.glsl"
#include "../light_data_inc.glsl"
#include "../oct_inc.glsl"
#define M_PI 3.14159265359
@ -176,6 +177,9 @@ layout(set = 0, binding = 14, std140) uniform Params {
uint temporal_frame;
float temporal_blend;
vec2 sky_border_size;
vec2 pad;
mat3x4 cam_rotation;
mat4 to_prev_view;
@ -201,10 +205,10 @@ layout(r32ui, set = 0, binding = 17) uniform uimage3D light_only_map;
layout(r32ui, set = 0, binding = 18) uniform uimage3D emissive_only_map;
#endif
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
layout(set = 0, binding = 19) uniform textureCubeArray sky_texture;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 0, binding = 19) uniform texture2DArray sky_texture;
#else
layout(set = 0, binding = 19) uniform textureCube sky_texture;
layout(set = 0, binding = 19) uniform texture2D sky_texture;
#endif
#endif // MODE_COPY
@ -429,13 +433,13 @@ void main() {
if (params.sky_contribution > 0.0) {
float mip_bias = 2.0 + total_density * (MAX_SKY_LOD - 2.0); // Not physically based, but looks nice
vec3 scatter_direction = (params.radiance_inverse_xform * normalize(view_pos)) * sign(params.phase_g);
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
isotropic = texture(samplerCubeArray(sky_texture, linear_sampler_with_mipmaps), vec4(0.0, 1.0, 0.0, mip_bias)).rgb;
anisotropic = texture(samplerCubeArray(sky_texture, linear_sampler_with_mipmaps), vec4(scatter_direction, mip_bias)).rgb;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
isotropic = texture(sampler2DArray(sky_texture, linear_sampler_with_mipmaps), vec3(vec3_to_oct_with_border(vec3(0.0, 1.0, 0.0), params.sky_border_size), mip_bias)).rgb;
anisotropic = texture(sampler2DArray(sky_texture, linear_sampler_with_mipmaps), vec3(vec3_to_oct_with_border(scatter_direction, params.sky_border_size), mip_bias)).rgb;
#else
isotropic = textureLod(samplerCube(sky_texture, linear_sampler_with_mipmaps), vec3(0.0, 1.0, 0.0), mip_bias).rgb;
anisotropic = textureLod(samplerCube(sky_texture, linear_sampler_with_mipmaps), vec3(scatter_direction), mip_bias).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
isotropic = textureLod(sampler2D(sky_texture, linear_sampler_with_mipmaps), vec3_to_oct_with_border(vec3(0.0, 1.0, 0.0), params.sky_border_size), mip_bias).rgb;
anisotropic = textureLod(sampler2D(sky_texture, linear_sampler_with_mipmaps), vec3_to_oct_with_border(scatter_direction, params.sky_border_size), mip_bias).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
}
total_light += mix(params.ambient_color, mix(isotropic, anisotropic, abs(params.phase_g)), params.sky_contribution) * params.ambient_inject;

View file

@ -71,13 +71,6 @@ layout(location = 13) in vec4 previous_normal_attrib;
#endif // MOTION_VECTORS
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
void axis_angle_to_tbn(vec3 axis, float angle, out vec3 tangent, out vec3 binormal, out vec3 normal) {
float c = cos(angle);
float s = sin(angle);
@ -1102,14 +1095,19 @@ vec4 fog_process(vec3 vertex) {
vec3 cube_view = scene_data_block.data.radiance_inverse_xform * vertex;
// mip_level always reads from the second mipmap and higher so the fog is always slightly blurred
float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data_block.data.z_near) / (scene_data_block.data.z_far - scene_data_block.data.z_near));
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
float lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod);
sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb;
sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend);
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float roughness_lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, roughness_lod);
float cube_lod = vec3_to_oct_lod(dFdx(cube_view), dFdy(cube_view), scene_data_block.data.radiance_pixel_size);
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 sky_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod), cube_lod).rgb;
vec3 sky_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod + 1), cube_lod).rgb;
sky_fog_color = mix(sky_sample_a, sky_sample_b, blend);
#else
sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
float roughness_lod = mip_level * MAX_ROUGHNESS_LOD;
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
sky_fog_color = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective);
}
@ -1693,18 +1691,24 @@ void fragment_shader(in SceneData scene_data) {
float horizon = min(1.0 + dot(ref_vec, normal), 1.0);
ref_vec = scene_data.radiance_inverse_xform * ref_vec;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod, blend;
float roughness_lod, blend;
blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
indirect_specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
indirect_specular_light = mix(indirect_specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, roughness_lod);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 indirect_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, roughness_lod), ref_lod).rgb;
vec3 indirect_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, roughness_lod + 1), ref_lod).rgb;
indirect_specular_light = mix(indirect_sample_a, indirect_sample_b, blend);
#else
indirect_specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
float roughness_lod = sqrt(roughness) * MAX_ROUGHNESS_LOD;
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
indirect_specular_light = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light *= scene_data.IBL_exposure_normalization;
indirect_specular_light *= horizon * horizon;
indirect_specular_light *= scene_data.ambient_light_color_energy.a;
@ -1721,11 +1725,15 @@ void fragment_shader(in SceneData scene_data) {
if (bool(scene_data.flags & SCENE_DATA_FLAGS_USE_AMBIENT_CUBEMAP)) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * indirect_normal;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float ambient_lod = vec3_to_oct_lod(dFdx(ambient_dir), dFdy(ambient_dir), scene_data_block.data.radiance_pixel_size);
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 cubemap_ambient = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ambient_uv, MAX_ROUGHNESS_LOD), ambient_lod).rgb;
#else
vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
float roughness_lod = MAX_ROUGHNESS_LOD;
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 cubemap_ambient = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
cubemap_ambient *= scene_data.IBL_exposure_normalization;
ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
}
@ -1750,17 +1758,22 @@ void fragment_shader(in SceneData scene_data) {
float horizon = min(1.0 + dot(ref_vec, indirect_normal), 1.0);
ref_vec = scene_data.radiance_inverse_xform * ref_vec;
float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod, blend;
blend = modf(roughness_lod, lod);
vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 clearcoat_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, lod), ref_lod).rgb;
vec3 clearcoat_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, lod + 1), ref_lod).rgb;
vec3 clearcoat_light = mix(clearcoat_sample_a, clearcoat_sample_b, blend);
#else
vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 clearcoat_light = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
}
#endif // LIGHT_CLEARCOAT_USED

View file

@ -10,6 +10,7 @@
#include "../cluster_data_inc.glsl"
#include "../decal_data_inc.glsl"
#include "../oct_inc.glsl"
#include "../scene_data_inc.glsl"
#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(MODE_RENDER_SDF) || defined(MODE_RENDER_NORMAL_ROUGHNESS) || defined(MODE_RENDER_VOXEL_GI) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
@ -352,17 +353,17 @@ layout(set = 1, binding = 2, std430) buffer restrict readonly InstanceDataBuffer
}
instances;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 1, binding = 3) uniform textureCubeArray radiance_cubemap;
layout(set = 1, binding = 3) uniform texture2DArray radiance_octmap;
#else
layout(set = 1, binding = 3) uniform textureCube radiance_cubemap;
layout(set = 1, binding = 3) uniform texture2D radiance_octmap;
#endif
layout(set = 1, binding = 4) uniform textureCubeArray reflection_atlas;
layout(set = 1, binding = 4) uniform texture2DArray reflection_atlas;
layout(set = 1, binding = 5) uniform texture2D shadow_atlas;

View file

@ -78,13 +78,6 @@ layout(location = 13) in vec4 previous_normal_attrib;
#endif // MODE_RENDER_MOTION_VECTORS
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
void axis_angle_to_tbn(vec3 axis, float angle, out vec3 tangent, out vec3 binormal, out vec3 normal) {
float c = cos(angle);
float s = sin(angle);
@ -1048,14 +1041,19 @@ hvec4 fog_process(vec3 vertex) {
vec3 cube_view = scene_data_block.data.radiance_inverse_xform * vertex;
// mip_level always reads from the second mipmap and higher so the fog is always slightly blurred
float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data_block.data.z_near) / (scene_data_block.data.z_far - scene_data_block.data.z_near));
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
float lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod);
sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb;
sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend);
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float roughness_lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, roughness_lod);
float cube_lod = vec3_to_oct_lod(dFdx(cube_view), dFdy(cube_view), scene_data_block.data.radiance_pixel_size);
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 sky_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod), cube_lod).rgb;
vec3 sky_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod + 1), cube_lod).rgb;
sky_fog_color = mix(sky_sample_a, sky_sample_b, blend);
#else
sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
float roughness_lod = mip_level * MAX_ROUGHNESS_LOD;
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
sky_fog_color = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective);
}
@ -1595,20 +1593,20 @@ void main() {
#endif
half horizon = min(half(1.0) + dot(ref_vec, indirect_normal), half(1.0));
ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(ref_vec));
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod;
half blend = half(modf(float(sqrt(roughness) * half(MAX_ROUGHNESS_LOD)), lod));
half blend = half(modf(float(sqrt(roughness) * MAX_ROUGHNESS_LOD), lod));
hvec3 indirect_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(vec3(ref_vec), float(lod))).rgb);
hvec3 indirect_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(vec3(ref_vec), float(lod) + 1.0)).rgb);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 indirect_sample_a = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, float(lod)), ref_lod).rgb);
hvec3 indirect_sample_b = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, float(lod) + 1.0), ref_lod).rgb);
indirect_specular_light = mix(indirect_sample_a, indirect_sample_b, blend);
#else // USE_RADIANCE_CUBEMAP_ARRAY
float lod = sqrt(roughness) * half(MAX_ROUGHNESS_LOD);
indirect_specular_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_vec), lod).rgb);
#endif //USE_RADIANCE_CUBEMAP_ARRAY
#else // USE_RADIANCE_OCTMAP_ARRAY
float lod = sqrt(roughness) * MAX_ROUGHNESS_LOD;
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
indirect_specular_light = hvec3(textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, lod).rgb);
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light *= REFLECTION_MULTIPLIER;
indirect_specular_light *= half(scene_data.IBL_exposure_normalization);
indirect_specular_light *= horizon * horizon;
@ -1626,14 +1624,18 @@ void main() {
if (sc_scene_use_ambient_cubemap()) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * indirect_normal;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
hvec3 cubemap_ambient = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb);
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float ambient_lod = vec3_to_oct_lod(dFdx(ambient_dir), dFdy(ambient_dir), scene_data_block.data.radiance_pixel_size);
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 octmap_ambient = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ambient_uv, MAX_ROUGHNESS_LOD), ambient_lod).rgb);
#else
hvec3 cubemap_ambient = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb);
#endif //USE_RADIANCE_CUBEMAP_ARRAY
cubemap_ambient *= REFLECTION_MULTIPLIER;
cubemap_ambient *= half(scene_data.IBL_exposure_normalization);
ambient_light = mix(ambient_light, cubemap_ambient * half(scene_data.ambient_light_color_energy.a), half(scene_data.ambient_color_sky_mix));
float roughness_lod = MAX_ROUGHNESS_LOD;
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 octmap_ambient = hvec3(textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_uv, roughness_lod).rgb);
#endif //USE_RADIANCE_OCTMAP_ARRAY
octmap_ambient *= REFLECTION_MULTIPLIER;
octmap_ambient *= half(scene_data.IBL_exposure_normalization);
ambient_light = mix(ambient_light, octmap_ambient * half(scene_data.ambient_light_color_energy.a), half(scene_data.ambient_color_sky_mix));
}
}
#endif // !USE_LIGHTMAP
@ -1656,18 +1658,19 @@ void main() {
half horizon = min(half(1.0) + dot(ref_vec, indirect_normal), half(1.0));
ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(ref_vec));
float roughness_lod = mix(0.001, 0.1, sqrt(float(clearcoat_roughness))) * MAX_ROUGHNESS_LOD;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod;
half blend = half(modf(roughness_lod, lod));
hvec3 clearcoat_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb);
hvec3 clearcoat_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 clearcoat_sample_a = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, lod), ref_lod).rgb);
hvec3 clearcoat_sample_b = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, lod + 1), ref_lod).rgb);
hvec3 clearcoat_light = mix(clearcoat_sample_a, clearcoat_sample_b, blend);
#else
hvec3 clearcoat_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_vec), roughness_lod).rgb);
#endif //USE_RADIANCE_CUBEMAP_ARRAY
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 clearcoat_light = hvec3(textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, roughness_lod).rgb);
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light += clearcoat_light * horizon * horizon * Fc * half(scene_data.ambient_light_color_energy.a);
}
#endif // LIGHT_CLEARCOAT_USED

View file

@ -2,6 +2,7 @@
#define MAX_VIEWS 2
#include "../decal_data_inc.glsl"
#include "../oct_inc.glsl"
#include "../scene_data_inc.glsl"
#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
@ -351,17 +352,17 @@ layout(set = 1, binding = 1, std430) buffer restrict readonly InstanceDataBuffer
}
instances;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 1, binding = 2) uniform textureCubeArray radiance_cubemap;
layout(set = 1, binding = 2) uniform texture2DArray radiance_octmap;
#else
layout(set = 1, binding = 2) uniform textureCube radiance_cubemap;
layout(set = 1, binding = 2) uniform texture2D radiance_octmap;
#endif
layout(set = 1, binding = 3) uniform textureCubeArray reflection_atlas;
layout(set = 1, binding = 3) uniform texture2DArray reflection_atlas;
layout(set = 1, binding = 4) uniform texture2D shadow_atlas;

View file

@ -0,0 +1,51 @@
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
// border_size: 1.0 - padding_in_uv_space * 2.0.
vec3 oct_to_vec3_with_border(vec2 uv, float border_size) {
// Convert into [-1,1] space and add border which extends beyond [-1,1].
uv = (uv - 0.5) * (2.0 / border_size);
// Calculate octahedral mirroring for values outside of [-1,1].
// Inspired by Timothy Lottes' code here: https://gpuopen.com/learn/fetching-from-cubes-and-octahedrons/
vec2 mask = step(vec2(1.0), abs(uv));
uv = 2.0 * clamp(uv, -1.0, 1.0) - uv;
uv = mix(uv, -uv, mask.yx);
return oct_to_vec3(uv);
}
vec2 oct_wrap(vec2 v) {
vec2 signVal;
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
return (1.0 - abs(v.yx)) * signVal;
}
vec2 vec3_to_oct(vec3 n) {
// Reference: https://twitter.com/Stubbesaurus/status/937994790553227264
n /= (abs(n.x) + abs(n.y) + abs(n.z));
n.xy = (n.z >= 0.0) ? n.xy : oct_wrap(n.xy);
n.xy = n.xy * 0.5 + 0.5;
return n.xy;
}
// border_size.x: padding_in_uv_space
// border_size.y: 1.0 - padding_in_uv_space * 2.0
vec2 vec3_to_oct_with_border(vec3 n, vec2 border_size) {
vec2 uv = vec3_to_oct(n);
return uv * border_size.y + border_size.x;
}
float vec3_to_oct_lod(vec3 n_ddx, vec3 n_ddy, float pixel_size) {
// Approximate UV space derivatives by a factor of 0.5 because
// vec3_to_oct maps from [-1,1] to [0,1].
float pixel_size_sqr = 4.0 * pixel_size * pixel_size;
float ddx = dot(n_ddx, n_ddx) / pixel_size_sqr;
float ddy = dot(n_ddy, n_ddy) / pixel_size_sqr;
float dd_sqr = max(ddx, ddy);
return 0.25 * log2(dd_sqr + 1e-6f);
}

View file

@ -42,6 +42,10 @@ struct SceneData {
vec2 shadow_atlas_pixel_size;
vec2 directional_shadow_pixel_size;
float radiance_pixel_size;
float radiance_border_size;
vec2 reflection_atlas_border_size;
uint directional_light_count;
float dual_paraboloid_side;
float z_far;

View file

@ -937,6 +937,7 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal
blend = pow(blend_axes.x * blend_axes.y * blend_axes.z, half(2.0));
}
vec2 border_size = scene_data_block.data.reflection_atlas_border_size;
if (reflections.data[ref_index].intensity > 0.0 && reflection_accum.a < half(1.0)) { // compute reflection
vec3 local_ref_vec = (reflections.data[ref_index].local_matrix * vec4(ref_vec, 0.0)).xyz;
@ -957,7 +958,9 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal
hvec4 reflection;
half reflection_blend = max(half(0.0), blend - reflection_accum.a);
reflection.rgb = hvec3(textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb) * REFLECTION_MULTIPLIER;
float roughness_lod = sqrt(roughness) * MAX_ROUGHNESS_LOD;
vec2 reflection_uv = vec3_to_oct_with_border(local_ref_vec, border_size);
reflection.rgb = hvec3(textureLod(sampler2DArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(reflection_uv, reflections.data[ref_index].index), roughness_lod).rgb) * REFLECTION_MULTIPLIER;
reflection.rgb *= half(reflections.data[ref_index].exposure_normalization);
reflection.a = reflection_blend;
@ -980,7 +983,9 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal
hvec4 ambient_out;
half ambient_blend = max(half(0.0), blend - ambient_accum.a);
ambient_out.rgb = hvec3(textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb);
float roughness_lod = MAX_ROUGHNESS_LOD;
vec2 ambient_uv = vec3_to_oct_with_border(local_amb_vec, border_size);
ambient_out.rgb = hvec3(textureLod(sampler2DArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ambient_uv, reflections.data[ref_index].index), roughness_lod).rgb);
ambient_out.rgb *= half(reflections.data[ref_index].exposure_normalization);
ambient_out.a = ambient_blend;
ambient_out.rgb *= ambient_out.a;

View file

@ -1452,7 +1452,6 @@ void LightStorage::reflection_probe_release_atlas_index(RID p_instance) {
rpi->rendering = false;
rpi->dirty = true;
rpi->processing_layer = 1;
rpi->processing_side = 0;
}
rpi->atlas_index = -1;
@ -1518,20 +1517,41 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
_reflection_atlas_clear(atlas);
}
const int required_real_time_mipmaps = 7;
if (atlas->reflection.is_null()) {
RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
ERR_FAIL_NULL_V_MSG(copy_effects, false, "Effects haven't been initialized");
int mipmaps = MIN(RendererSceneRenderRD::get_singleton()->get_sky()->roughness_layers, Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) + 1);
mipmaps = update_always ? 8 : mipmaps; // always use 8 mipmaps with real time filtering
mipmaps = update_always ? required_real_time_mipmaps : mipmaps;
// Double size to approximate texel density of cubemaps + add border for proper filtering/mipmapping.
uint32_t padding_pixels = (1 << (mipmaps - 1));
atlas->reflection_texture_size = atlas->size * 2 + padding_pixels * 2;
atlas->uv_border_size = float(padding_pixels) / float(atlas->reflection_texture_size);
bool use_storage = !copy_effects->get_raster_effects().has_flag(CopyEffects::RASTER_EFFECT_OCTMAP);
{
//reflection atlas was unused, create:
RD::TextureFormat tf;
tf.array_layers = 6 * atlas->count;
tf.array_layers = atlas->count;
tf.format = get_reflection_probe_color_format();
tf.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY;
tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
tf.mipmaps = mipmaps;
tf.width = atlas->reflection_texture_size;
tf.height = atlas->reflection_texture_size;
tf.usage_bits = get_reflection_probe_color_usage_bits(use_storage);
atlas->reflection = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
{
RD::TextureFormat tf;
tf.array_layers = 6;
tf.format = get_reflection_probe_color_format();
tf.texture_type = RD::TEXTURE_TYPE_CUBE;
tf.width = atlas->size;
tf.height = atlas->size;
tf.usage_bits = get_reflection_probe_color_usage_bits();
atlas->reflection = RD::get_singleton()->texture_create(tf, RD::TextureView());
tf.usage_bits = get_reflection_probe_color_usage_bits(use_storage);
atlas->color_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
{
RD::TextureFormat tf;
@ -1543,15 +1563,17 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
}
atlas->reflections.resize(atlas->count);
for (int i = 0; i < atlas->count; i++) {
atlas->reflections.write[i].data.update_reflection_data(atlas->size, mipmaps, false, atlas->reflection, i * 6, update_always, RendererSceneRenderRD::get_singleton()->get_sky()->roughness_layers, RendererSceneRenderRD::get_singleton()->_render_buffers_get_preferred_color_format());
for (int j = 0; j < 6; j++) {
atlas->reflections.write[i].fbs[j] = RendererSceneRenderRD::get_singleton()->reflection_probe_create_framebuffer(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j], atlas->depth_buffer);
}
atlas->reflections.write[i].data.update_reflection_data(atlas->reflection_texture_size, mipmaps, false, atlas->reflection, i, update_always, RendererSceneRenderRD::get_singleton()->get_sky()->roughness_layers, RendererSceneRenderRD::get_singleton()->_render_buffers_get_preferred_color_format(), atlas->uv_border_size);
}
Vector<RID> fb;
fb.push_back(atlas->depth_buffer);
atlas->depth_fb = RD::get_singleton()->framebuffer_create(fb);
for (int i = 0; i < 6; i++) {
atlas->color_views[i] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), atlas->color_buffer, i, 0);
atlas->color_fbs[i] = RendererSceneRenderRD::get_singleton()->reflection_probe_create_framebuffer(atlas->color_views[i], atlas->depth_buffer);
}
Vector<RID> fbs;
fbs.push_back(atlas->depth_buffer);
atlas->depth_fb = RD::get_singleton()->framebuffer_create(fbs);
atlas->render_buffers->configure_for_reflections(Size2i(atlas->size, atlas->size));
atlas->update_always = update_always;
@ -1587,13 +1609,29 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
rpi->rendering = true;
rpi->dirty = false;
rpi->processing_layer = 1;
rpi->processing_side = 0;
RD::get_singleton()->draw_command_end_label();
return true;
}
bool LightStorage::reflection_probe_instance_end_render(RID p_instance, RID p_reflection_atlas) {
RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
ERR_FAIL_NULL_V_MSG(copy_effects, false, "Effects haven't been initialized");
ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas);
ERR_FAIL_NULL_V(atlas, false);
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, false);
RD::get_singleton()->draw_command_begin_label("Convert reflection probe to octahedral");
copy_effects->copy_cubemap_to_octmap(atlas->color_buffer, atlas->reflections.write[rpi->atlas_index].data.layers[0].mipmaps[0].framebuffer, atlas->uv_border_size);
RD::get_singleton()->draw_command_end_label();
return true;
}
Ref<RenderSceneBuffers> LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) {
ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas);
ERR_FAIL_NULL_V(atlas, Ref<RenderSceneBuffersRD>());
@ -1617,35 +1655,17 @@ bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) {
// Using real time reflections, all roughness is done in one step
atlas->reflections.write[rpi->atlas_index].data.create_reflection_fast_filter(false);
rpi->rendering = false;
rpi->processing_side = 0;
rpi->processing_layer = 1;
return true;
}
if (rpi->processing_layer > 1) {
atlas->reflections.write[rpi->atlas_index].data.create_reflection_importance_sample(false, 10, rpi->processing_layer, RendererSceneRenderRD::get_singleton()->get_sky()->sky_ggx_samples_quality);
rpi->processing_layer++;
if (rpi->processing_layer == atlas->reflections[rpi->atlas_index].data.layers[0].mipmaps.size()) {
rpi->rendering = false;
rpi->processing_side = 0;
rpi->processing_layer = 1;
return true;
}
return false;
atlas->reflections.write[rpi->atlas_index].data.create_reflection_importance_sample(false, rpi->processing_layer, RendererSceneRenderRD::get_singleton()->get_sky()->sky_ggx_samples_quality);
} else {
atlas->reflections.write[rpi->atlas_index].data.create_reflection_importance_sample(false, rpi->processing_side, rpi->processing_layer, RendererSceneRenderRD::get_singleton()->get_sky()->sky_ggx_samples_quality);
}
rpi->processing_side++;
if (rpi->processing_side == 6) {
rpi->processing_side = 0;
rpi->processing_layer++;
if (rpi->processing_layer == atlas->reflections[rpi->atlas_index].data.layers[0].mipmaps.size()) {
rpi->rendering = false;
rpi->processing_layer = 1;
return true;
}
rpi->processing_layer++;
if (rpi->processing_layer == (int)atlas->reflections[rpi->atlas_index].data.layers[0].mipmaps.size()) {
rpi->rendering = false;
rpi->processing_layer = 1;
return true;
}
return false;
@ -1669,7 +1689,7 @@ RID LightStorage::reflection_probe_instance_get_framebuffer(RID p_instance, int
ERR_FAIL_NULL_V(atlas, RID());
ERR_FAIL_COND_V_MSG(rpi->atlas_index < 0, RID(), "Reflection probe atlas index invalid. Maximum amount of reflection probes in use (" + itos(atlas->count) + ") may have been exceeded, reflections will not display properly. Consider increasing Rendering > Reflections > Reflection Atlas > Reflection Count in the Project Settings.");
return atlas->reflections[rpi->atlas_index].fbs[p_index];
return atlas->color_fbs[p_index];
}
RID LightStorage::reflection_probe_instance_get_depth_framebuffer(RID p_instance, int p_index) {
@ -1818,8 +1838,8 @@ RD::DataFormat LightStorage::get_reflection_probe_color_format() {
return RendererSceneRenderRD::get_singleton()->_render_buffers_get_preferred_color_format();
}
uint32_t LightStorage::get_reflection_probe_color_usage_bits() {
return RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
uint32_t LightStorage::get_reflection_probe_color_usage_bits(bool p_storage) {
return RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | (p_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
}
RD::DataFormat LightStorage::get_reflection_probe_depth_format() {

View file

@ -245,16 +245,20 @@ private:
struct ReflectionAtlas {
int count = 0;
int size = 0;
int reflection_texture_size = 0;
float uv_border_size = 0.0;
bool update_always = false;
RID reflection;
RID color_buffer;
RID color_views[6];
RID color_fbs[6];
RID depth_buffer;
RID depth_fb;
struct Reflection {
RID owner;
RendererRD::SkyRD::ReflectionData data;
RID fbs[6];
};
Vector<Reflection> reflections;
@ -278,7 +282,6 @@ private:
bool dirty = true;
bool rendering = false;
int processing_layer = 1;
int processing_side = 0;
uint64_t last_pass = 0;
uint32_t cull_mask = 0;
@ -893,6 +896,18 @@ public:
return atlas->reflection;
}
_FORCE_INLINE_ int reflection_atlas_get_texture_size(RID p_ref_atlas) {
ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_ref_atlas);
ERR_FAIL_NULL_V(atlas, 0);
return atlas->reflection_texture_size;
}
_FORCE_INLINE_ float reflection_atlas_get_border_size(RID p_ref_atlas) {
ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_ref_atlas);
ERR_FAIL_NULL_V(atlas, 0.0);
return atlas->uv_border_size;
}
/* REFLECTION PROBE INSTANCE */
bool owns_reflection_probe_instance(RID p_rid) { return reflection_probe_instance_owner.owns(p_rid); }
@ -905,6 +920,7 @@ public:
virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override;
virtual bool reflection_probe_instance_has_reflection(RID p_instance) override;
virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override;
virtual bool reflection_probe_instance_end_render(RID p_instance, RID p_reflection_atlas) override;
virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override;
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;
@ -968,7 +984,7 @@ public:
RID get_reflection_probe_buffer() { return reflection_buffer; }
void update_reflection_probe_buffer(RenderDataRD *p_render_data, const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment);
static RD::DataFormat get_reflection_probe_color_format();
static uint32_t get_reflection_probe_color_usage_bits();
static uint32_t get_reflection_probe_color_usage_bits(bool p_storage);
static RD::DataFormat get_reflection_probe_depth_format();
static uint32_t get_reflection_probe_depth_usage_bits();

View file

@ -140,6 +140,12 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p
ubo.directional_shadow_pixel_size[0] = directional_shadow_pixel_size.x;
ubo.directional_shadow_pixel_size[1] = directional_shadow_pixel_size.y;
ubo.radiance_pixel_size = radiance_pixel_size;
ubo.radiance_border_size = radiance_border_size;
ubo.reflection_atlas_border_size[0] = reflection_atlas_border_size.x;
ubo.reflection_atlas_border_size[1] = reflection_atlas_border_size.y;
ubo.time = time;
ubo.directional_light_count = directional_light_count;

View file

@ -80,6 +80,10 @@ public:
Size2 shadow_atlas_pixel_size;
Size2 directional_shadow_pixel_size;
float radiance_pixel_size;
float radiance_border_size;
Size2 reflection_atlas_border_size;
float time;
float time_step;
@ -139,6 +143,10 @@ private:
float shadow_atlas_pixel_size[2];
float directional_shadow_pixel_size[2];
float radiance_pixel_size;
float radiance_border_size;
float reflection_atlas_border_size[2];
uint32_t directional_light_count;
float dual_paraboloid_side;
float z_far;

View file

@ -3573,7 +3573,7 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int
return true;
}
if (p_step >= 0 && p_step < 6) {
if (p_step == 0) {
static const Vector3 view_normals[6] = {
Vector3(+1, 0, 0),
Vector3(-1, 0, 0),
@ -3596,44 +3596,34 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int
float max_distance = RSG::light_storage->reflection_probe_get_origin_max_distance(p_instance->base);
float atlas_size = RSG::light_storage->reflection_atlas_get_size(scenario->reflection_atlas);
float mesh_lod_threshold = RSG::light_storage->reflection_probe_get_mesh_lod_threshold(p_instance->base) / atlas_size;
Vector3 edge = view_normals[p_step] * probe_size / 2;
float distance = Math::abs(view_normals[p_step].dot(edge) - view_normals[p_step].dot(origin_offset)); //distance from origin offset to actual view distance limit
max_distance = MAX(max_distance, distance);
//render cubemap side
Projection cm;
cm.set_perspective(90, 1, 0.01, max_distance);
Transform3D local_view;
local_view.set_look_at(origin_offset, origin_offset + view_normals[p_step], view_up[p_step]);
Transform3D xform = p_instance->transform * local_view;
RID shadow_atlas;
bool use_shadows = RSG::light_storage->reflection_probe_renders_shadows(p_instance->base);
if (use_shadows) {
shadow_atlas = scenario->reflection_probe_shadow_atlas;
}
RID environment;
if (scenario->environment.is_valid()) {
environment = scenario->environment;
} else {
environment = scenario->fallback_environment;
}
RENDER_TIMESTAMP("Render ReflectionProbe, Step " + itos(p_step));
RendererSceneRender::CameraData camera_data;
camera_data.set_camera(xform, cm, false, false, false);
RID shadow_atlas = use_shadows ? scenario->reflection_probe_shadow_atlas : RID();
RID environment = scenario->environment.is_valid() ? scenario->environment : scenario->fallback_environment;
Ref<RenderSceneBuffers> render_buffers = RSG::light_storage->reflection_probe_atlas_get_render_buffers(scenario->reflection_atlas);
_render_scene(&camera_data, render_buffers, environment, RID(), RID(), RSG::light_storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, mesh_lod_threshold, use_shadows);
for (uint32_t face = 0; face < 6; face++) {
// Compute distance from origin offset to the actual view distance limit.
Vector3 edge = view_normals[face] * probe_size / 2;
float distance = Math::abs(view_normals[face].dot(edge) - view_normals[face].dot(origin_offset));
max_distance = MAX(max_distance, distance);
// Render cubemap side.
Projection cm;
cm.set_perspective(90, 1, 0.01, max_distance);
Transform3D local_view;
local_view.set_look_at(origin_offset, origin_offset + view_normals[face], view_up[face]);
RendererSceneRender::CameraData camera_data;
Transform3D xform = p_instance->transform * local_view;
camera_data.set_camera(xform, cm, false, false, false);
RENDER_TIMESTAMP("Render ReflectionProbe, Face " + itos(face));
_render_scene(&camera_data, render_buffers, environment, RID(), RID(), RSG::light_storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, face, mesh_lod_threshold, use_shadows);
}
RSG::light_storage->reflection_probe_instance_end_render(reflection_probe->instance, scenario->reflection_atlas);
} else {
//do roughness postprocess step until it believes it's done
// Do roughness postprocess step until it believes it's done.
RENDER_TIMESTAMP("Post-Process ReflectionProbe, Step " + itos(p_step));
return RSG::light_storage->reflection_probe_instance_postprocess_step(reflection_probe->instance);
}

View file

@ -3723,7 +3723,7 @@ void RenderingServer::init() {
GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug", false);
GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug.release", true);
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/reflections/sky_reflections/roughness_layers", PROPERTY_HINT_RANGE, "1,32,1"), 8); // Assumes a 256x256 cubemap
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/reflections/sky_reflections/roughness_layers", PROPERTY_HINT_RANGE, "1,32,1"), 7);
GLOBAL_DEF_RST("rendering/reflections/sky_reflections/texture_array_reflections", true);
GLOBAL_DEF("rendering/reflections/sky_reflections/texture_array_reflections.mobile", false);
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/reflections/sky_reflections/ggx_samples", PROPERTY_HINT_RANGE, "0,256,1"), 32);

View file

@ -151,6 +151,7 @@ public:
virtual bool reflection_probe_instance_needs_redraw(RID p_instance) = 0;
virtual bool reflection_probe_instance_has_reflection(RID p_instance) = 0;
virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) = 0;
virtual bool reflection_probe_instance_end_render(RID p_instance, RID p_reflection_atlas) = 0;
virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) = 0;
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) = 0;