You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Optimize glow and tonemap gather step in the mobile renderer
Mobile devices are typically bandwidth bound which means we need to do as few texture samples as possible. They typically use TBDR GPUs which means that all rendering takes place on special optimized tiles. As a side effect, reading back memory from tile to VRAM is really slow, especially on Mali devices. This commit uses a technique where you do a small blur while downsampling, and then another small blur while upsampling to get really high quality glow. While this doesn't reduce the renderpass count very much, it does reduce the texture read bandwidth by almost 10 times. Overall glow was more texture-read bound than memory write, bound, so this was a huge win. A side effect of this new technique is that we can gather the glow as we upsample instead of gathering the glow in the final tonemap pass. Doing so allows us to significantly reduce the cost of the tonemap pass as well.
This commit is contained in:
@@ -53,8 +53,9 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
|
||||
Vector<String> blur_modes;
|
||||
blur_modes.push_back("\n#define MODE_MIPMAP\n"); // BLUR_MIPMAP
|
||||
blur_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); // BLUR_MODE_GAUSSIAN_BLUR
|
||||
blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n"); // BLUR_MODE_GAUSSIAN_GLOW
|
||||
blur_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n"); // BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE
|
||||
blur_modes.push_back("\n#define MODE_GLOW_GATHER\n"); // BLUR_MODE_GAUSSIAN_GLOW_GATHER
|
||||
blur_modes.push_back("\n#define MODE_GLOW_DOWNSAMPLE\n"); // BLUR_MODE_GAUSSIAN_GLOW_DOWNSAMPLE
|
||||
blur_modes.push_back("\n#define MODE_GLOW_UPSAMPLE\n"); // BLUR_MODE_GAUSSIAN_GLOW_UPSAMPLE
|
||||
blur_modes.push_back("\n#define MODE_COPY\n"); // BLUR_MODE_COPY
|
||||
blur_modes.push_back("\n#define MODE_SET_COLOR\n"); // BLUR_MODE_SET_COLOR
|
||||
|
||||
@@ -66,6 +67,15 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
|
||||
blur_raster.pipelines[i].setup(blur_raster.shader.version_get_shader(blur_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
|
||||
}
|
||||
|
||||
RD::SamplerState sampler_state;
|
||||
sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER;
|
||||
sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER;
|
||||
sampler_state.border_color = RD::SAMPLER_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||
|
||||
blur_raster.glow_sampler = RD::get_singleton()->sampler_create(sampler_state);
|
||||
|
||||
} else {
|
||||
// not used in clustered
|
||||
for (int i = 0; i < BLUR_MODE_MAX; i++) {
|
||||
@@ -319,6 +329,7 @@ CopyEffects::~CopyEffects() {
|
||||
|
||||
if (prefer_raster_effects) {
|
||||
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);
|
||||
filter.raster_shader.version_free(filter.shader_version);
|
||||
roughness.raster_shader.version_free(roughness.shader_version);
|
||||
@@ -733,8 +744,8 @@ void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_textu
|
||||
|
||||
BlurRasterMode blur_mode = BLUR_MODE_GAUSSIAN_BLUR;
|
||||
|
||||
blur_raster.push_constant.pixel_size[0] = 1.0 / float(p_size.x);
|
||||
blur_raster.push_constant.pixel_size[1] = 1.0 / float(p_size.y);
|
||||
blur_raster.push_constant.dest_pixel_size[0] = 1.0 / float(p_size.x);
|
||||
blur_raster.push_constant.dest_pixel_size[1] = 1.0 / float(p_size.y);
|
||||
|
||||
// setup our uniforms
|
||||
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
@@ -805,7 +816,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
|
||||
void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_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, RID p_auto_exposure, float p_auto_exposure_scale) {
|
||||
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.");
|
||||
|
||||
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
|
||||
@@ -813,16 +824,14 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu
|
||||
MaterialStorage *material_storage = MaterialStorage::get_singleton();
|
||||
ERR_FAIL_NULL(material_storage);
|
||||
|
||||
RID half_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_half_texture);
|
||||
RID dest_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_dest_texture);
|
||||
|
||||
memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
|
||||
|
||||
BlurRasterMode blur_mode = p_first_pass && p_auto_exposure.is_valid() ? BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : BLUR_MODE_GAUSSIAN_GLOW;
|
||||
uint32_t base_flags = 0;
|
||||
BlurRasterMode blur_mode = p_first_pass ? BLUR_MODE_GAUSSIAN_GLOW_GATHER : BLUR_MODE_GAUSSIAN_GLOW_DOWNSAMPLE;
|
||||
|
||||
blur_raster.push_constant.pixel_size[0] = 1.0 / float(p_size.x);
|
||||
blur_raster.push_constant.pixel_size[1] = 1.0 / float(p_size.y);
|
||||
blur_raster.push_constant.source_pixel_size[0] = 1.0 / float(p_size.x);
|
||||
blur_raster.push_constant.source_pixel_size[1] = 1.0 / float(p_size.y);
|
||||
|
||||
blur_raster.push_constant.glow_strength = p_strength;
|
||||
blur_raster.push_constant.glow_bloom = p_bloom;
|
||||
@@ -832,45 +841,62 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu
|
||||
blur_raster.push_constant.glow_white = 0; //actually unused
|
||||
blur_raster.push_constant.glow_luminance_cap = p_luminance_cap;
|
||||
|
||||
blur_raster.push_constant.glow_auto_exposure_scale = p_auto_exposure_scale; //unused also
|
||||
|
||||
blur_raster.push_constant.luminance_multiplier = p_luminance_multiplier;
|
||||
|
||||
// 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_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
|
||||
RD::Uniform u_half_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_half_texture }));
|
||||
RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ blur_raster.glow_sampler, p_source_rd_texture }));
|
||||
|
||||
RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode);
|
||||
ERR_FAIL_COND(shader.is_null());
|
||||
|
||||
//HORIZONTAL
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer);
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(half_framebuffer)));
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer);
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
|
||||
if (p_auto_exposure.is_valid() && p_first_pass) {
|
||||
RD::Uniform u_auto_exposure(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_auto_exposure }));
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_auto_exposure), 1);
|
||||
}
|
||||
|
||||
blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL | (p_first_pass ? BLUR_FLAG_GLOW_FIRST_PASS : 0);
|
||||
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
|
||||
|
||||
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
|
||||
blur_mode = BLUR_MODE_GAUSSIAN_GLOW;
|
||||
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.");
|
||||
|
||||
shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode);
|
||||
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 dest_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_dest_texture);
|
||||
|
||||
memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
|
||||
|
||||
BlurRasterMode blur_mode = BLUR_MODE_GAUSSIAN_GLOW_UPSAMPLE;
|
||||
|
||||
blur_raster.push_constant.source_pixel_size[0] = 1.0 / float(p_source_size.x);
|
||||
blur_raster.push_constant.source_pixel_size[1] = 1.0 / float(p_source_size.y);
|
||||
blur_raster.push_constant.dest_pixel_size[0] = 1.0 / float(p_dest_size.x);
|
||||
blur_raster.push_constant.dest_pixel_size[1] = 1.0 / float(p_dest_size.y);
|
||||
blur_raster.push_constant.luminance_multiplier = p_luminance_multiplier;
|
||||
blur_raster.push_constant.level = p_level * 0.5;
|
||||
blur_raster.push_constant.glow_strength = p_base_strength;
|
||||
|
||||
uint32_t spec_constant = p_use_debanding ? 1 : 0;
|
||||
spec_constant |= p_level > 0.01 ? 2 : 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_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
|
||||
RD::Uniform u_blend_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_blend_texture }));
|
||||
|
||||
RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode);
|
||||
ERR_FAIL_COND(shader.is_null());
|
||||
|
||||
//VERTICAL
|
||||
draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer);
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0);
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer);
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer), false, 0, spec_constant));
|
||||
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_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_blend_rd_texture), 1);
|
||||
|
||||
blur_raster.push_constant.flags = base_flags;
|
||||
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
|
||||
|
||||
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
|
||||
@@ -925,8 +951,8 @@ void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture
|
||||
|
||||
BlurRasterMode mode = BLUR_MIPMAP;
|
||||
|
||||
blur_raster.push_constant.pixel_size[0] = 1.0 / float(p_size.x);
|
||||
blur_raster.push_constant.pixel_size[1] = 1.0 / float(p_size.y);
|
||||
blur_raster.push_constant.dest_pixel_size[0] = 1.0 / float(p_size.x);
|
||||
blur_raster.push_constant.dest_pixel_size[1] = 1.0 / float(p_size.y);
|
||||
|
||||
// setup our uniforms
|
||||
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
|
||||
@@ -59,8 +59,9 @@ private:
|
||||
BLUR_MIPMAP,
|
||||
|
||||
BLUR_MODE_GAUSSIAN_BLUR,
|
||||
BLUR_MODE_GAUSSIAN_GLOW,
|
||||
BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
|
||||
BLUR_MODE_GAUSSIAN_GLOW_GATHER,
|
||||
BLUR_MODE_GAUSSIAN_GLOW_DOWNSAMPLE,
|
||||
BLUR_MODE_GAUSSIAN_GLOW_UPSAMPLE,
|
||||
BLUR_MODE_COPY,
|
||||
|
||||
BLUR_MODE_SET_COLOR,
|
||||
@@ -69,15 +70,16 @@ private:
|
||||
};
|
||||
|
||||
enum {
|
||||
BLUR_FLAG_HORIZONTAL = (1 << 0),
|
||||
BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 1),
|
||||
BLUR_FLAG_GLOW_FIRST_PASS = (1 << 2),
|
||||
};
|
||||
|
||||
struct BlurRasterPushConstant {
|
||||
float pixel_size[2];
|
||||
float dest_pixel_size[2];
|
||||
float source_pixel_size[2];
|
||||
|
||||
float pad[2];
|
||||
uint32_t flags;
|
||||
uint32_t pad;
|
||||
float level;
|
||||
|
||||
//glow
|
||||
float glow_strength;
|
||||
@@ -88,12 +90,7 @@ private:
|
||||
float glow_exposure;
|
||||
float glow_white;
|
||||
float glow_luminance_cap;
|
||||
float glow_auto_exposure_scale;
|
||||
|
||||
float luminance_multiplier;
|
||||
float res1;
|
||||
float res2;
|
||||
float res3;
|
||||
};
|
||||
|
||||
struct BlurRaster {
|
||||
@@ -101,6 +98,7 @@ private:
|
||||
BlurRasterShaderRD shader;
|
||||
RID shader_version;
|
||||
PipelineCacheRD pipelines[BLUR_MODE_MAX];
|
||||
RID glow_sampler;
|
||||
} blur_raster;
|
||||
|
||||
// Copy shader
|
||||
@@ -337,7 +335,8 @@ public:
|
||||
void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, const Size2i &p_size, bool p_8bit_dst = false);
|
||||
void gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_region, const Size2i &p_size);
|
||||
void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0);
|
||||
void gaussian_glow_raster(RID p_source_rd_texture, RID p_half_texture, RID p_dest_texture, float p_luminance_multiplier, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0);
|
||||
void gaussian_glow_downsample_raster(RID p_source_rd_texture, RID p_dest_texture, float p_luminance_multiplier, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0);
|
||||
void 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);
|
||||
|
||||
void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
|
||||
void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
|
||||
|
||||
@@ -157,7 +157,7 @@ void SMAA::allocate_render_targets(Ref<RenderSceneBuffersRD> p_render_buffers) {
|
||||
p_render_buffers->create_texture(RB_SCOPE_SMAA, RB_STENCIL, smaa.stencil_format, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, full_size, 1, 1, true, true);
|
||||
}
|
||||
|
||||
void SMAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer) {
|
||||
void SMAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer, bool p_use_debanding) {
|
||||
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
|
||||
ERR_FAIL_NULL(uniform_set_cache);
|
||||
MaterialStorage *material_storage = MaterialStorage::get_singleton();
|
||||
@@ -181,11 +181,7 @@ void SMAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_colo
|
||||
|
||||
smaa.blend_push_constant.inv_size[0] = inv_size.x;
|
||||
smaa.blend_push_constant.inv_size[1] = inv_size.y;
|
||||
if (debanding_mode == DEBANDING_MODE_8_BIT) {
|
||||
smaa.blend_push_constant.flags |= SMAA_BLEND_FLAG_USE_8_BIT_DEBANDING;
|
||||
} else if (debanding_mode == DEBANDING_MODE_10_BIT) {
|
||||
smaa.blend_push_constant.flags |= SMAA_BLEND_FLAG_USE_10_BIT_DEBANDING;
|
||||
}
|
||||
smaa.blend_push_constant.use_debanding = p_use_debanding;
|
||||
|
||||
RID linear_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ private:
|
||||
|
||||
struct SMAABlendPushConstant {
|
||||
float inv_size[2];
|
||||
uint32_t flags;
|
||||
uint32_t use_debanding;
|
||||
float pad;
|
||||
};
|
||||
|
||||
@@ -108,14 +108,7 @@ public:
|
||||
~SMAA();
|
||||
|
||||
void allocate_render_targets(Ref<RenderSceneBuffersRD> p_render_buffers);
|
||||
void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer);
|
||||
|
||||
enum DebandingMode {
|
||||
DEBANDING_MODE_DISABLED,
|
||||
DEBANDING_MODE_8_BIT,
|
||||
DEBANDING_MODE_10_BIT,
|
||||
};
|
||||
DebandingMode debanding_mode = DEBANDING_MODE_DISABLED;
|
||||
void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer, bool p_use_debanding);
|
||||
};
|
||||
|
||||
} // namespace RendererRD
|
||||
|
||||
@@ -35,24 +35,54 @@
|
||||
|
||||
using namespace RendererRD;
|
||||
|
||||
ToneMapper::ToneMapper() {
|
||||
{
|
||||
ToneMapper::ToneMapper(bool p_use_mobile_version) {
|
||||
using_mobile_version = p_use_mobile_version;
|
||||
if (using_mobile_version) {
|
||||
// Initialize tonemapper
|
||||
Vector<String> tonemap_modes;
|
||||
tonemap_modes.push_back("\n");
|
||||
tonemap_modes.push_back("\n#define USE_1D_LUT\n");
|
||||
tonemap_modes.push_back("\n#define SUBPASS\n");
|
||||
tonemap_modes.push_back("\n#define SUBPASS\n#define USE_1D_LUT\n");
|
||||
|
||||
// multiview versions of our shaders
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define USE_1D_LUT\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define SUBPASS\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define SUBPASS\n#define USE_1D_LUT\n");
|
||||
|
||||
tonemap_mobile.shader.initialize(tonemap_modes);
|
||||
|
||||
if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) {
|
||||
tonemap_mobile.shader.set_variant_enabled(TONEMAP_MOBILE_MODE_NORMAL_MULTIVIEW, false);
|
||||
tonemap_mobile.shader.set_variant_enabled(TONEMAP_MOBILE_MODE_1D_LUT_MULTIVIEW, false);
|
||||
tonemap_mobile.shader.set_variant_enabled(TONEMAP_MOBILE_MODE_SUBPASS_MULTIVIEW, false);
|
||||
tonemap_mobile.shader.set_variant_enabled(TONEMAP_MOBILE_MODE_SUBPASS_1D_LUT_MULTIVIEW, false);
|
||||
}
|
||||
|
||||
tonemap_mobile.shader_version = tonemap_mobile.shader.version_create();
|
||||
|
||||
for (int i = 0; i < TONEMAP_MODE_MAX; i++) {
|
||||
if (tonemap_mobile.shader.is_variant_enabled(i)) {
|
||||
tonemap_mobile.pipelines[i].setup(tonemap_mobile.shader.version_get_shader(tonemap_mobile.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
|
||||
} else {
|
||||
tonemap_mobile.pipelines[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Initialize tonemapper
|
||||
Vector<String> tonemap_modes;
|
||||
tonemap_modes.push_back("\n");
|
||||
tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n");
|
||||
tonemap_modes.push_back("\n#define USE_1D_LUT\n");
|
||||
tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
|
||||
tonemap_modes.push_back("\n#define SUBPASS\n");
|
||||
tonemap_modes.push_back("\n#define SUBPASS\n#define USE_1D_LUT\n");
|
||||
|
||||
// multiview versions of our shaders
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define USE_1D_LUT\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define SUBPASS\n");
|
||||
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define SUBPASS\n#define USE_1D_LUT\n");
|
||||
|
||||
tonemap.shader.initialize(tonemap_modes);
|
||||
|
||||
@@ -61,8 +91,6 @@ ToneMapper::ToneMapper() {
|
||||
tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, false);
|
||||
tonemap.shader.set_variant_enabled(TONEMAP_MODE_1D_LUT_MULTIVIEW, false);
|
||||
tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, false);
|
||||
tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_MULTIVIEW, false);
|
||||
tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW, false);
|
||||
}
|
||||
|
||||
tonemap.shader_version = tonemap.shader.version_create();
|
||||
@@ -78,10 +106,15 @@ ToneMapper::ToneMapper() {
|
||||
}
|
||||
|
||||
ToneMapper::~ToneMapper() {
|
||||
tonemap.shader.version_free(tonemap.shader_version);
|
||||
if (using_mobile_version) {
|
||||
tonemap_mobile.shader.version_free(tonemap_mobile.shader_version);
|
||||
} else {
|
||||
tonemap.shader.version_free(tonemap.shader_version);
|
||||
}
|
||||
}
|
||||
|
||||
void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings) {
|
||||
ERR_FAIL_COND_MSG(using_mobile_version, "Can't use the non mobile version of the tonemapper with the Mobile renderer.");
|
||||
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
|
||||
ERR_FAIL_NULL(uniform_set_cache);
|
||||
MaterialStorage *material_storage = MaterialStorage::get_singleton();
|
||||
@@ -125,8 +158,6 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
|
||||
tonemap.push_constant.flags |= p_settings.use_fxaa ? TONEMAP_FLAG_USE_FXAA : 0;
|
||||
if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT) {
|
||||
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_8_BIT_DEBANDING;
|
||||
} else if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_10_BIT) {
|
||||
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_10_BIT_DEBANDING;
|
||||
}
|
||||
tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x;
|
||||
tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y;
|
||||
@@ -135,7 +166,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
|
||||
|
||||
if (p_settings.view_count > 1) {
|
||||
// Use USE_MULTIVIEW versions
|
||||
mode += 6;
|
||||
mode += 4;
|
||||
}
|
||||
|
||||
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
@@ -182,57 +213,61 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
|
||||
void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings) {
|
||||
void ToneMapper::tonemapper_mobile(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings) {
|
||||
ERR_FAIL_COND_MSG(!using_mobile_version, "Can't use the mobile version of the tonemapper with the clustered renderer.");
|
||||
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(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
|
||||
memset(&tonemap_mobile.push_constant, 0, sizeof(TonemapPushConstantMobile));
|
||||
|
||||
tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0;
|
||||
tonemap.push_constant.bcs[0] = p_settings.brightness;
|
||||
tonemap.push_constant.bcs[1] = p_settings.contrast;
|
||||
tonemap.push_constant.bcs[2] = p_settings.saturation;
|
||||
tonemap_mobile.push_constant.bcs[0] = p_settings.brightness;
|
||||
tonemap_mobile.push_constant.bcs[1] = p_settings.contrast;
|
||||
tonemap_mobile.push_constant.bcs[2] = p_settings.saturation;
|
||||
|
||||
ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses.");
|
||||
tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0;
|
||||
tonemap_mobile.push_constant.src_pixel_size[0] = 1.0 / p_settings.texture_size.x;
|
||||
tonemap_mobile.push_constant.src_pixel_size[1] = 1.0 / p_settings.texture_size.y;
|
||||
tonemap_mobile.push_constant.dest_pixel_size[0] = 1.0 / p_settings.dest_texture_size.x;
|
||||
tonemap_mobile.push_constant.dest_pixel_size[1] = 1.0 / p_settings.dest_texture_size.y;
|
||||
tonemap_mobile.push_constant.glow_intensity = p_settings.glow_intensity;
|
||||
tonemap_mobile.push_constant.glow_map_strength = p_settings.glow_map_strength;
|
||||
|
||||
tonemap_mobile.push_constant.exposure = p_settings.exposure;
|
||||
tonemap_mobile.push_constant.white = p_settings.white;
|
||||
tonemap_mobile.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
|
||||
|
||||
uint32_t spec_constant = 0;
|
||||
spec_constant |= p_settings.use_bcs ? TONEMAP_MOBILE_FLAG_USE_BCS : 0;
|
||||
spec_constant |= p_settings.use_glow ? TONEMAP_MOBILE_FLAG_USE_GLOW : 0;
|
||||
spec_constant |= p_settings.glow_map_strength > 0.01 ? TONEMAP_MOBILE_FLAG_USE_GLOW_MAP : 0;
|
||||
spec_constant |= p_settings.use_color_correction ? TONEMAP_MOBILE_FLAG_USE_COLOR_CORRECTION : 0;
|
||||
spec_constant |= p_settings.use_fxaa ? TONEMAP_MOBILE_FLAG_USE_FXAA : 0;
|
||||
spec_constant |= p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT ? TONEMAP_MOBILE_FLAG_USE_8_BIT_DEBANDING : 0;
|
||||
spec_constant |= p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_10_BIT ? TONEMAP_MOBILE_FLAG_USE_10_BIT_DEBANDING : 0;
|
||||
spec_constant |= p_settings.convert_to_srgb ? TONEMAP_MOBILE_FLAG_CONVERT_TO_SRGB : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_LINEAR ? TONEMAP_MOBILE_FLAG_TONEMAPPER_LINEAR : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_REINHARD ? TONEMAP_MOBILE_FLAG_TONEMAPPER_REINHARD : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_FILMIC ? TONEMAP_MOBILE_FLAG_TONEMAPPER_FILMIC : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_ACES ? TONEMAP_MOBILE_FLAG_TONEMAPPER_ACES : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_AGX ? TONEMAP_MOBILE_FLAG_TONEMAPPER_AGX : 0;
|
||||
spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_ADDITIVE ? TONEMAP_MOBILE_FLAG_GLOW_MODE_ADD : 0;
|
||||
spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_SCREEN ? TONEMAP_MOBILE_FLAG_GLOW_MODE_SCREEN : 0;
|
||||
spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT ? TONEMAP_MOBILE_FLAG_GLOW_MODE_SOFTLIGHT : 0;
|
||||
spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_REPLACE ? TONEMAP_MOBILE_FLAG_GLOW_MODE_REPLACE : 0;
|
||||
spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_MIX ? TONEMAP_MOBILE_FLAG_GLOW_MODE_MIX : 0;
|
||||
|
||||
int mode = p_settings.use_1d_color_correction ? TONEMAP_MOBILE_MODE_1D_LUT : TONEMAP_MOBILE_MODE_NORMAL;
|
||||
|
||||
int mode = p_settings.use_1d_color_correction ? TONEMAP_MODE_SUBPASS_1D_LUT : TONEMAP_MODE_SUBPASS;
|
||||
if (p_settings.view_count > 1) {
|
||||
// Use USE_MULTIVIEW versions
|
||||
mode += 6;
|
||||
mode += 4;
|
||||
}
|
||||
|
||||
tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
|
||||
tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0;
|
||||
tonemap.push_constant.exposure = p_settings.exposure;
|
||||
tonemap.push_constant.white = p_settings.white;
|
||||
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
|
||||
|
||||
tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
|
||||
if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT) {
|
||||
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_8_BIT_DEBANDING;
|
||||
} else if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_10_BIT) {
|
||||
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_10_BIT_DEBANDING;
|
||||
}
|
||||
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
|
||||
|
||||
tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0;
|
||||
|
||||
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
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_color;
|
||||
u_source_color.uniform_type = RD::UNIFORM_TYPE_INPUT_ATTACHMENT;
|
||||
u_source_color.binding = 0;
|
||||
u_source_color.append_id(p_source_color);
|
||||
|
||||
RD::Uniform u_exposure_texture;
|
||||
u_exposure_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
|
||||
u_exposure_texture.binding = 0;
|
||||
u_exposure_texture.append_id(default_sampler);
|
||||
u_exposure_texture.append_id(p_settings.exposure_texture);
|
||||
RD::Uniform u_source_color(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_color }));
|
||||
|
||||
RD::Uniform u_glow_texture;
|
||||
u_glow_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
|
||||
@@ -252,15 +287,102 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
|
||||
u_color_correction_texture.append_id(default_sampler);
|
||||
u_color_correction_texture.append_id(p_settings.color_correction_texture);
|
||||
|
||||
RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode);
|
||||
RID shader = tonemap_mobile.shader.version_get_shader(tonemap_mobile.shader_version, mode);
|
||||
ERR_FAIL_COND(shader.is_null());
|
||||
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(p_subpass_draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_dst_format_id, false, RD::get_singleton()->draw_list_get_current_pass()));
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); // should be set to a default texture, it's ignored
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); // should be set to a default texture, it's ignored
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer);
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap_mobile.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass(), spec_constant));
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_glow_texture, u_glow_map), 1);
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_color_correction_texture), 2);
|
||||
|
||||
RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
|
||||
RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap_mobile.push_constant, sizeof(TonemapPushConstantMobile));
|
||||
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
|
||||
void ToneMapper::tonemapper_subpass(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings) {
|
||||
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
|
||||
ERR_FAIL_NULL(uniform_set_cache);
|
||||
MaterialStorage *material_storage = MaterialStorage::get_singleton();
|
||||
ERR_FAIL_NULL(material_storage);
|
||||
|
||||
ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses.");
|
||||
|
||||
memset(&tonemap_mobile.push_constant, 0, sizeof(TonemapPushConstantMobile));
|
||||
|
||||
tonemap_mobile.push_constant.bcs[0] = p_settings.brightness;
|
||||
tonemap_mobile.push_constant.bcs[1] = p_settings.contrast;
|
||||
tonemap_mobile.push_constant.bcs[2] = p_settings.saturation;
|
||||
|
||||
tonemap_mobile.push_constant.src_pixel_size[0] = 1.0 / p_settings.texture_size.x;
|
||||
tonemap_mobile.push_constant.src_pixel_size[1] = 1.0 / p_settings.texture_size.y;
|
||||
tonemap_mobile.push_constant.glow_intensity = p_settings.glow_intensity;
|
||||
tonemap_mobile.push_constant.glow_map_strength = p_settings.glow_map_strength;
|
||||
|
||||
tonemap_mobile.push_constant.exposure = p_settings.exposure;
|
||||
tonemap_mobile.push_constant.white = p_settings.white;
|
||||
tonemap_mobile.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
|
||||
|
||||
uint32_t spec_constant = 0;
|
||||
spec_constant |= p_settings.use_bcs ? TONEMAP_MOBILE_FLAG_USE_BCS : 0;
|
||||
//spec_constant |= p_settings.use_glow ? TONEMAP_MOBILE_FLAG_USE_GLOW : 0;
|
||||
//spec_constant |= p_settings.glow_map_strength > 0.01 ? TONEMAP_MOBILE_FLAG_USE_GLOW_MAP : 0;
|
||||
//spec_constant |= p_settings.use_color_correction ? TONEMAP_MOBILE_FLAG_USE_COLOR_CORRECTION : 0;
|
||||
//spec_constant |= p_settings.use_fxaa ? TONEMAP_MOBILE_FLAG_USE_FXAA : 0;
|
||||
spec_constant |= p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT ? TONEMAP_MOBILE_FLAG_USE_8_BIT_DEBANDING : 0;
|
||||
spec_constant |= p_settings.convert_to_srgb ? TONEMAP_MOBILE_FLAG_CONVERT_TO_SRGB : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_LINEAR ? TONEMAP_MOBILE_FLAG_TONEMAPPER_LINEAR : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_REINHARD ? TONEMAP_MOBILE_FLAG_TONEMAPPER_REINHARD : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_FILMIC ? TONEMAP_MOBILE_FLAG_TONEMAPPER_FILMIC : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_ACES ? TONEMAP_MOBILE_FLAG_TONEMAPPER_ACES : 0;
|
||||
spec_constant |= p_settings.tonemap_mode == RS::ENV_TONE_MAPPER_AGX ? TONEMAP_MOBILE_FLAG_TONEMAPPER_AGX : 0;
|
||||
//spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_ADDITIVE ? TONEMAP_MOBILE_FLAG_GLOW_MODE_ADD : 0;
|
||||
//spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_SCREEN ? TONEMAP_MOBILE_FLAG_GLOW_MODE_SCREEN : 0;
|
||||
//spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT ? TONEMAP_MOBILE_FLAG_GLOW_MODE_SOFTLIGHT : 0;
|
||||
//spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_REPLACE ? TONEMAP_MOBILE_FLAG_GLOW_MODE_REPLACE : 0;
|
||||
//spec_constant |= p_settings.glow_mode == RS::ENV_GLOW_BLEND_MODE_MIX ? TONEMAP_MOBILE_FLAG_GLOW_MODE_MIX : 0;
|
||||
|
||||
int mode = p_settings.use_1d_color_correction ? TONEMAP_MOBILE_MODE_SUBPASS_1D_LUT : TONEMAP_MOBILE_MODE_SUBPASS;
|
||||
if (p_settings.view_count > 1) {
|
||||
// Use USE_MULTIVIEW versions
|
||||
mode += 4;
|
||||
}
|
||||
|
||||
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
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_color;
|
||||
u_source_color.uniform_type = RD::UNIFORM_TYPE_INPUT_ATTACHMENT;
|
||||
u_source_color.binding = 0;
|
||||
u_source_color.append_id(p_source_color);
|
||||
|
||||
RD::Uniform u_glow_texture;
|
||||
u_glow_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
|
||||
u_glow_texture.binding = 0;
|
||||
u_glow_texture.append_id(default_mipmap_sampler);
|
||||
u_glow_texture.append_id(p_settings.glow_texture);
|
||||
|
||||
RD::Uniform u_glow_map;
|
||||
u_glow_map.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
|
||||
u_glow_map.binding = 1;
|
||||
u_glow_map.append_id(default_mipmap_sampler);
|
||||
u_glow_map.append_id(p_settings.glow_map);
|
||||
|
||||
RD::Uniform u_color_correction_texture;
|
||||
u_color_correction_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
|
||||
u_color_correction_texture.binding = 0;
|
||||
u_color_correction_texture.append_id(default_sampler);
|
||||
u_color_correction_texture.append_id(p_settings.color_correction_texture);
|
||||
|
||||
RID shader = tonemap_mobile.shader.version_get_shader(tonemap_mobile.shader_version, mode);
|
||||
ERR_FAIL_COND(shader.is_null());
|
||||
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(p_subpass_draw_list, tonemap_mobile.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_dst_format_id, false, RD::get_singleton()->draw_list_get_current_pass(), spec_constant));
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_glow_texture, u_glow_map), 1); // should be set to a default texture, it's ignored
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_color_correction_texture), 2);
|
||||
|
||||
RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap_mobile.push_constant, sizeof(TonemapPushConstantMobile));
|
||||
RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 3u);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/tonemap.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/tonemap_mobile.glsl.gen.h"
|
||||
|
||||
#include "servers/rendering/rendering_server.h"
|
||||
|
||||
@@ -39,35 +40,68 @@ namespace RendererRD {
|
||||
|
||||
class ToneMapper {
|
||||
private:
|
||||
bool using_mobile_version = false;
|
||||
enum TonemapMode {
|
||||
TONEMAP_MODE_NORMAL,
|
||||
TONEMAP_MODE_BICUBIC_GLOW_FILTER,
|
||||
TONEMAP_MODE_1D_LUT,
|
||||
TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT,
|
||||
TONEMAP_MODE_SUBPASS,
|
||||
TONEMAP_MODE_SUBPASS_1D_LUT,
|
||||
|
||||
TONEMAP_MODE_NORMAL_MULTIVIEW,
|
||||
TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW,
|
||||
TONEMAP_MODE_1D_LUT_MULTIVIEW,
|
||||
TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW,
|
||||
TONEMAP_MODE_SUBPASS_MULTIVIEW,
|
||||
TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW,
|
||||
|
||||
TONEMAP_MODE_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
enum TonemapModeMobile {
|
||||
TONEMAP_MOBILE_MODE_NORMAL,
|
||||
TONEMAP_MOBILE_MODE_1D_LUT,
|
||||
TONEMAP_MOBILE_MODE_SUBPASS,
|
||||
TONEMAP_MOBILE_MODE_SUBPASS_1D_LUT,
|
||||
|
||||
TONEMAP_MOBILE_MODE_NORMAL_MULTIVIEW,
|
||||
TONEMAP_MOBILE_MODE_1D_LUT_MULTIVIEW,
|
||||
TONEMAP_MOBILE_MODE_SUBPASS_MULTIVIEW,
|
||||
TONEMAP_MOBILE_MODE_SUBPASS_1D_LUT_MULTIVIEW,
|
||||
|
||||
TONEMAP_MOBILE_MODE_MAX
|
||||
};
|
||||
|
||||
enum Flags {
|
||||
TONEMAP_FLAG_USE_BCS = (1 << 0),
|
||||
TONEMAP_FLAG_USE_GLOW = (1 << 1),
|
||||
TONEMAP_FLAG_USE_AUTO_EXPOSURE = (1 << 2),
|
||||
TONEMAP_FLAG_USE_COLOR_CORRECTION = (1 << 3),
|
||||
TONEMAP_FLAG_USE_FXAA = (1 << 4),
|
||||
TONEMAP_FLAG_USE_8_BIT_DEBANDING = (1 << 5),
|
||||
TONEMAP_FLAG_USE_10_BIT_DEBANDING = (1 << 6),
|
||||
TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 7),
|
||||
};
|
||||
|
||||
enum FlagsMobile {
|
||||
TONEMAP_MOBILE_FLAG_USE_BCS = (1 << 0),
|
||||
TONEMAP_MOBILE_FLAG_USE_GLOW = (1 << 1),
|
||||
TONEMAP_MOBILE_FLAG_USE_GLOW_MAP = (1 << 2),
|
||||
TONEMAP_MOBILE_FLAG_USE_COLOR_CORRECTION = (1 << 3),
|
||||
TONEMAP_MOBILE_FLAG_USE_FXAA = (1 << 4),
|
||||
TONEMAP_MOBILE_FLAG_USE_8_BIT_DEBANDING = (1 << 5),
|
||||
TONEMAP_MOBILE_FLAG_USE_10_BIT_DEBANDING = (1 << 6),
|
||||
TONEMAP_MOBILE_FLAG_CONVERT_TO_SRGB = (1 << 7),
|
||||
|
||||
TONEMAP_MOBILE_FLAG_TONEMAPPER_LINEAR = (1 << 8),
|
||||
TONEMAP_MOBILE_FLAG_TONEMAPPER_REINHARD = (1 << 9),
|
||||
TONEMAP_MOBILE_FLAG_TONEMAPPER_FILMIC = (1 << 10),
|
||||
TONEMAP_MOBILE_FLAG_TONEMAPPER_ACES = (1 << 11),
|
||||
TONEMAP_MOBILE_FLAG_TONEMAPPER_AGX = (1 << 12),
|
||||
|
||||
TONEMAP_MOBILE_FLAG_GLOW_MODE_ADD = (1 << 13),
|
||||
TONEMAP_MOBILE_FLAG_GLOW_MODE_SCREEN = (1 << 14),
|
||||
TONEMAP_MOBILE_FLAG_GLOW_MODE_SOFTLIGHT = (1 << 15),
|
||||
TONEMAP_MOBILE_FLAG_GLOW_MODE_REPLACE = (1 << 16),
|
||||
TONEMAP_MOBILE_FLAG_GLOW_MODE_MIX = (1 << 17),
|
||||
};
|
||||
|
||||
struct TonemapPushConstant {
|
||||
float bcs[3]; // 12 - 12
|
||||
uint32_t flags; // 4 - 16
|
||||
@@ -89,6 +123,19 @@ private:
|
||||
float luminance_multiplier; // 4 - 96
|
||||
};
|
||||
|
||||
struct TonemapPushConstantMobile {
|
||||
float bcs[3]; // 12 - 12
|
||||
float luminance_multiplier; // 4 - 16
|
||||
|
||||
float src_pixel_size[2]; // 8 - 24
|
||||
float dest_pixel_size[2]; // 8 - 32
|
||||
|
||||
float glow_intensity; // 4 - 36
|
||||
float glow_map_strength; // 4 - 40
|
||||
float exposure; // 4 - 44
|
||||
float white; // 4 - 48
|
||||
};
|
||||
|
||||
/* tonemap actually writes to a framebuffer, which is
|
||||
* better to do using the raster pipeline rather than
|
||||
* compute, as that framebuffer might be in different formats
|
||||
@@ -100,21 +147,20 @@ private:
|
||||
PipelineCacheRD pipelines[TONEMAP_MODE_MAX];
|
||||
} tonemap;
|
||||
|
||||
struct TonemapMobile {
|
||||
TonemapPushConstantMobile push_constant;
|
||||
TonemapMobileShaderRD shader;
|
||||
RID shader_version;
|
||||
PipelineCacheRD pipelines[TONEMAP_MOBILE_MODE_MAX];
|
||||
} tonemap_mobile;
|
||||
|
||||
public:
|
||||
ToneMapper();
|
||||
ToneMapper(bool p_use_mobile_version);
|
||||
~ToneMapper();
|
||||
|
||||
struct TonemapSettings {
|
||||
bool use_glow = false;
|
||||
enum GlowMode {
|
||||
GLOW_MODE_ADD,
|
||||
GLOW_MODE_SCREEN,
|
||||
GLOW_MODE_SOFTLIGHT,
|
||||
GLOW_MODE_REPLACE,
|
||||
GLOW_MODE_MIX
|
||||
};
|
||||
|
||||
GlowMode glow_mode = GLOW_MODE_SCREEN;
|
||||
RS::EnvironmentGlowBlendMode glow_mode = RS::ENV_GLOW_BLEND_MODE_SCREEN;
|
||||
float glow_intensity = 0.3;
|
||||
float glow_map_strength = 0.0f;
|
||||
float glow_levels[7] = { 1.0, 0.8, 0.4, 0.1, 0.0, 0.0, 0.0 };
|
||||
@@ -149,13 +195,15 @@ public:
|
||||
};
|
||||
DebandingMode debanding_mode = DEBANDING_MODE_DISABLED;
|
||||
Vector2i texture_size;
|
||||
Vector2i dest_texture_size;
|
||||
uint32_t view_count = 1;
|
||||
|
||||
bool convert_to_srgb = false;
|
||||
};
|
||||
|
||||
void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
|
||||
void tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings);
|
||||
void tonemapper_mobile(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
|
||||
void tonemapper_subpass(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings);
|
||||
};
|
||||
|
||||
} // namespace RendererRD
|
||||
|
||||
@@ -568,58 +568,99 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
|
||||
int max_glow_level = -1;
|
||||
|
||||
if (can_use_effects && p_render_data->environment.is_valid() && environment_get_glow_enabled(p_render_data->environment)) {
|
||||
RENDER_TIMESTAMP("Glow");
|
||||
RD::get_singleton()->draw_command_begin_label("Gaussian Glow");
|
||||
|
||||
rb->allocate_blur_textures();
|
||||
|
||||
int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1).mipmaps);
|
||||
Vector<float> glow_levels = environment_get_glow_levels(p_render_data->environment);
|
||||
bool use_debanding = rb->get_use_debanding() && !texture_storage->render_target_is_using_hdr(render_target);
|
||||
|
||||
int max_glow_index = -1;
|
||||
int min_glow_level = RS::MAX_GLOW_LEVELS;
|
||||
for (int i = 0; i < RS::MAX_GLOW_LEVELS; i++) {
|
||||
if (environment_get_glow_levels(p_render_data->environment)[i] > 0.0) {
|
||||
int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1).mipmaps);
|
||||
if (i >= mipmaps) {
|
||||
max_glow_level = mipmaps - 1;
|
||||
} else {
|
||||
max_glow_level = i;
|
||||
}
|
||||
if (glow_levels[i] > 0.01) {
|
||||
max_glow_index = MAX(max_glow_index, i);
|
||||
min_glow_level = MIN(min_glow_level, i);
|
||||
}
|
||||
}
|
||||
|
||||
max_glow_index = MIN(max_glow_index, mipmaps - 1);
|
||||
|
||||
float luminance_multiplier = rb->get_luminance_multiplier();
|
||||
for (uint32_t l = 0; l < rb->get_view_count(); l++) {
|
||||
for (int i = 0; i < (max_glow_level + 1); i++) {
|
||||
Size2i vp_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, i);
|
||||
if (can_use_storage) {
|
||||
RD::get_singleton()->draw_command_begin_label("Gaussian Glow");
|
||||
RID luminance_texture;
|
||||
if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) {
|
||||
luminance_texture = luminance->get_current_luminance_buffer(rb); // this will return and empty RID if we don't have an auto exposure buffer
|
||||
}
|
||||
for (uint32_t l = 0; l < rb->get_view_count(); l++) {
|
||||
Size2i vp_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, 0);
|
||||
RID source = rb->get_internal_texture(l);
|
||||
RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, 0);
|
||||
copy_effects->gaussian_glow(source, dest, vp_size, environment_get_glow_strength(p_render_data->environment), true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, auto_exposure_scale);
|
||||
|
||||
if (i == 0) {
|
||||
RID luminance_texture;
|
||||
if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) {
|
||||
luminance_texture = luminance->get_current_luminance_buffer(rb); // this will return and empty RID if we don't have an auto exposure buffer
|
||||
}
|
||||
RID source = rb->get_internal_texture(l);
|
||||
RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i);
|
||||
if (can_use_storage) {
|
||||
copy_effects->gaussian_glow(source, dest, vp_size, environment_get_glow_strength(p_render_data->environment), true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, auto_exposure_scale);
|
||||
} else {
|
||||
RID half = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_HALF_BLUR, 0, i); // we can reuse this for each view
|
||||
copy_effects->gaussian_glow_raster(source, half, dest, luminance_multiplier, vp_size, environment_get_glow_strength(p_render_data->environment), true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, auto_exposure_scale);
|
||||
}
|
||||
} else {
|
||||
RID source = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i - 1);
|
||||
RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i);
|
||||
|
||||
if (can_use_storage) {
|
||||
copy_effects->gaussian_glow(source, dest, vp_size, environment_get_glow_strength(p_render_data->environment));
|
||||
} else {
|
||||
RID half = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_HALF_BLUR, 0, i); // we can reuse this for each view
|
||||
copy_effects->gaussian_glow_raster(source, half, dest, luminance_multiplier, vp_size, environment_get_glow_strength(p_render_data->environment));
|
||||
}
|
||||
for (int i = 1; i < (max_glow_index + 1); i++) {
|
||||
source = dest;
|
||||
vp_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, i);
|
||||
dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i);
|
||||
copy_effects->gaussian_glow(source, dest, vp_size, environment_get_glow_strength(p_render_data->environment));
|
||||
}
|
||||
}
|
||||
}
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
} else {
|
||||
// For the mobile renderer we blur down and up the mip chain. Which works out to (2*level-1) passes. This
|
||||
// allows us to gather our levels at low resolutions and ultimately save a lot of texture read bandwidth.
|
||||
// The tradeoff is that we need to use single-pass blur to minimize the number of render passes.
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
RID source;
|
||||
RID dest;
|
||||
|
||||
for (uint32_t l = 0; l < rb->get_view_count(); l++) {
|
||||
RD::get_singleton()->draw_command_begin_label("Gaussian Glow downsample");
|
||||
|
||||
Size2i source_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_COLOR, 0);
|
||||
|
||||
source = rb->get_internal_texture(l);
|
||||
dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, 1); // Level 1 is quarter res.
|
||||
|
||||
copy_effects->gaussian_glow_downsample_raster(source, dest, luminance_multiplier, source_size, environment_get_glow_strength(p_render_data->environment), true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment));
|
||||
|
||||
Size2i vp_size;
|
||||
for (int i = 1; i < (max_glow_index + 1); i++) {
|
||||
source = dest;
|
||||
vp_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, i);
|
||||
dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i + 1);
|
||||
|
||||
copy_effects->gaussian_glow_downsample_raster(source, dest, luminance_multiplier, vp_size, environment_get_glow_strength(p_render_data->environment));
|
||||
}
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
RD::get_singleton()->draw_command_begin_label("Gaussian Glow upsample");
|
||||
|
||||
if (max_glow_index <= 0) {
|
||||
// Only layer 1 is visible, just copy over.
|
||||
source = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); // Technically a waste, but oh well. I'm not optimizing for the case of only level 1.
|
||||
vp_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, 2); // RB_TEX_BLUR_0 is double the size of RB_TEX_BLUR_1, so go up a mip level.
|
||||
dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, l, 2);
|
||||
RID blend_tex = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, 1);
|
||||
source_size = vp_size;
|
||||
|
||||
copy_effects->gaussian_glow_upsample_raster(source, dest, blend_tex, luminance_multiplier, source_size, vp_size, glow_levels[0], 0.0, use_debanding);
|
||||
}
|
||||
|
||||
for (int i = max_glow_index - 1; i >= 0; i--) {
|
||||
source = dest;
|
||||
source_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, i + 3);
|
||||
vp_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, i + 2); // RB_TEX_BLUR_0 is double the size of RB_TEX_BLUR_1, so go up a mip level.
|
||||
dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, l, i + 2);
|
||||
RID blend_tex = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i + 1);
|
||||
|
||||
copy_effects->gaussian_glow_upsample_raster(source, dest, blend_tex, luminance_multiplier, source_size, vp_size, glow_levels[i], i == (max_glow_index - 1) ? glow_levels[i + 1] : 1.0, use_debanding);
|
||||
}
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -640,8 +681,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
|
||||
|
||||
if (can_use_effects && p_render_data->environment.is_valid() && environment_get_glow_enabled(p_render_data->environment)) {
|
||||
tonemap.use_glow = true;
|
||||
tonemap.glow_mode = RendererRD::ToneMapper::TonemapSettings::GlowMode(environment_get_glow_blend_mode(p_render_data->environment));
|
||||
tonemap.glow_intensity = environment_get_glow_blend_mode(p_render_data->environment) == RS::ENV_GLOW_BLEND_MODE_MIX ? environment_get_glow_mix(p_render_data->environment) : environment_get_glow_intensity(p_render_data->environment);
|
||||
tonemap.glow_mode = environment_get_glow_blend_mode(p_render_data->environment);
|
||||
tonemap.glow_intensity = tonemap.glow_mode == RS::ENV_GLOW_BLEND_MODE_MIX ? environment_get_glow_mix(p_render_data->environment) : environment_get_glow_intensity(p_render_data->environment);
|
||||
for (int i = 0; i < RS::MAX_GLOW_LEVELS; i++) {
|
||||
tonemap.glow_levels[i] = environment_get_glow_levels(p_render_data->environment)[i];
|
||||
}
|
||||
@@ -650,7 +691,13 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
|
||||
tonemap.glow_texture_size.x = msize.width;
|
||||
tonemap.glow_texture_size.y = msize.height;
|
||||
tonemap.glow_use_bicubic_upscale = glow_bicubic_upscale;
|
||||
tonemap.glow_texture = rb->get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1);
|
||||
|
||||
if (can_use_storage) {
|
||||
tonemap.glow_texture = rb->get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1);
|
||||
} else {
|
||||
tonemap.glow_texture = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, 0, 2, rb->get_view_count());
|
||||
}
|
||||
|
||||
if (environment_get_glow_map(p_render_data->environment).is_valid()) {
|
||||
tonemap.glow_map_strength = environment_get_glow_map_strength(p_render_data->environment);
|
||||
tonemap.glow_map = texture_storage->texture_get_rd_texture(environment_get_glow_map(p_render_data->environment));
|
||||
@@ -698,24 +745,13 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
|
||||
|
||||
RID dest_fb;
|
||||
RD::DataFormat dest_fb_format;
|
||||
RD::DataFormat format_for_debanding;
|
||||
if (spatial_upscaler != nullptr || use_smaa) {
|
||||
// If we use a spatial upscaler to upscale or SMAA to antialias we need to write our result into an intermediate buffer.
|
||||
// Note that this is cached so we only create the texture the first time.
|
||||
dest_fb_format = rb->get_base_data_format();
|
||||
RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), dest_fb_format, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
|
||||
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
|
||||
if (use_smaa) {
|
||||
format_for_debanding = dest_fb_format;
|
||||
} else {
|
||||
// Debanding is currently not supported when using spatial upscaling, so apply it before scaling.
|
||||
// This produces suboptimal results because the image will be modified by spatial upscaling after
|
||||
// debanding has been applied. Ideally, debanding should be applied as the final step before quantization
|
||||
// to integer values, but in the case of MetalFX, it may not be worth the performance cost of creating a new
|
||||
// intermediate buffer. In the case of FSR 1.0, the work of adding debanding support hasn't been done yet.
|
||||
// Assume that the DataFormat that will be used by spatial_upscaler is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
|
||||
}
|
||||
tonemap.dest_texture_size = rb->get_internal_size();
|
||||
} else {
|
||||
// If we do a bilinear upscale we just render into our render target and our shader will upscale automatically.
|
||||
// Target size in this case is lying as we never get our real target size communicated.
|
||||
@@ -723,30 +759,27 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
|
||||
|
||||
if (dest_is_msaa_2d) {
|
||||
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target));
|
||||
// Assume that the DataFormat of render_target_get_rd_texture_msaa is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
|
||||
texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved.
|
||||
} else {
|
||||
dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
|
||||
// Assume that the DataFormat of render_target_get_rd_framebuffer is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
|
||||
}
|
||||
tonemap.dest_texture_size = texture_storage->render_target_get_size(render_target);
|
||||
}
|
||||
|
||||
if (rb->get_use_debanding()) {
|
||||
if (_is_8bit_data_format(format_for_debanding)) {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_8_BIT;
|
||||
} else if (_is_10bit_data_format(format_for_debanding)) {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
if (rb->get_use_debanding() && !using_hdr) {
|
||||
if (!can_use_storage && (use_smaa || spatial_upscaler)) {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_10_BIT;
|
||||
} else {
|
||||
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit or SMAA, for example.)
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
} else if (!(use_smaa || spatial_upscaler)) {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_8_BIT;
|
||||
}
|
||||
} else {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
}
|
||||
|
||||
tone_mapper->tonemapper(color_texture, dest_fb, tonemap);
|
||||
if (can_use_storage) {
|
||||
tone_mapper->tonemapper(color_texture, dest_fb, tonemap);
|
||||
} else {
|
||||
tone_mapper->tonemapper_mobile(color_texture, dest_fb, tonemap);
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
@@ -756,6 +789,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
|
||||
RD::get_singleton()->draw_command_begin_label("SMAA");
|
||||
|
||||
bool using_hdr = texture_storage->render_target_is_using_hdr(render_target);
|
||||
|
||||
RID dest_fb;
|
||||
if (spatial_upscaler) {
|
||||
rb->create_texture(SNAME("SMAA"), SNAME("destination"), rb->get_base_data_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
|
||||
@@ -765,79 +799,31 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
|
||||
RID source_texture = rb->get_texture_slice(SNAME("Tonemapper"), SNAME("destination"), v, 0);
|
||||
|
||||
RID dest_texture;
|
||||
RD::DataFormat format_for_debanding;
|
||||
if (spatial_upscaler) {
|
||||
dest_texture = rb->get_texture_slice(SNAME("SMAA"), SNAME("destination"), v, 0);
|
||||
// Debanding is currently not supported when using spatial upscaling, so apply it before scaling.
|
||||
// This produces suboptimal results because the image will be modified by spatial upscaling after
|
||||
// debanding has been applied. Ideally, debanding should be applied as the final step before quantization
|
||||
// to integer values, but in the case of MetalFX, it may not be worth the performance cost of creating a new
|
||||
// intermediate buffer. In the case of FSR 1.0, the work of adding debanding support hasn't been done yet.
|
||||
// Assume that the DataFormat that will be used by spatial_upscaler is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
|
||||
} else {
|
||||
dest_texture = texture_storage->render_target_get_rd_texture_slice(render_target, v);
|
||||
// Assume that the DataFormat is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
|
||||
}
|
||||
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
|
||||
|
||||
if (rb->get_use_debanding()) {
|
||||
if (_is_8bit_data_format(format_for_debanding)) {
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_8_BIT;
|
||||
} else if (_is_10bit_data_format(format_for_debanding)) {
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_10_BIT;
|
||||
} else {
|
||||
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
}
|
||||
} else {
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
}
|
||||
|
||||
smaa->process(rb, source_texture, dest_fb);
|
||||
smaa->process(rb, source_texture, dest_fb, rb->get_use_debanding() && !using_hdr);
|
||||
}
|
||||
} else {
|
||||
RID source_texture = rb->get_texture(SNAME("Tonemapper"), SNAME("destination"));
|
||||
RD::DataFormat format_for_debanding;
|
||||
|
||||
if (spatial_upscaler) {
|
||||
RID dest_texture = rb->create_texture(SNAME("SMAA"), SNAME("destination"), rb->get_base_data_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
|
||||
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
|
||||
// Debanding is currently not supported when using spatial upscaling, so apply it before scaling.
|
||||
// This produces suboptimal results because the image will be modified by spatial upscaling after
|
||||
// debanding has been applied. Ideally, debanding should be applied as the final step before quantization
|
||||
// to integer values, but in the case of MetalFX, it may not be worth the performance cost of creating a new
|
||||
// intermediate buffer. In the case of FSR 1.0, the work of adding debanding support hasn't been done yet.
|
||||
// Assume that the DataFormat that will be used by spatial_upscaler is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
|
||||
} else {
|
||||
if (dest_is_msaa_2d) {
|
||||
dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target));
|
||||
// Assume that the DataFormat of render_target_get_rd_texture_msaa is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
|
||||
texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved.
|
||||
} else {
|
||||
dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
|
||||
// Assume that the DataFormat of render_target_get_rd_framebuffer is the same as render_target_get_color_format.
|
||||
format_for_debanding = texture_storage->render_target_get_color_format(using_hdr, !using_hdr);
|
||||
}
|
||||
}
|
||||
|
||||
if (rb->get_use_debanding()) {
|
||||
if (_is_8bit_data_format(format_for_debanding)) {
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_8_BIT;
|
||||
} else if (_is_10bit_data_format(format_for_debanding)) {
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_10_BIT;
|
||||
} else {
|
||||
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
}
|
||||
} else {
|
||||
smaa->debanding_mode = RendererRD::SMAA::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
}
|
||||
|
||||
smaa->process(rb, source_texture, dest_fb);
|
||||
smaa->process(rb, source_texture, dest_fb, rb->get_use_debanding() && !using_hdr);
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
@@ -939,21 +925,12 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
|
||||
tonemap.view_count = rb->get_view_count();
|
||||
|
||||
if (rb->get_use_debanding()) {
|
||||
// Assume that the DataFormat of p_framebuffer is the same as render_target_get_color_format.
|
||||
RD::DataFormat dest_fb_format = texture_storage->render_target_get_color_format(using_hdr, tonemap.convert_to_srgb);
|
||||
if (dest_fb_format >= RD::DATA_FORMAT_R8_UNORM && dest_fb_format <= RD::DATA_FORMAT_A8B8G8R8_SRGB_PACK32) {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_8_BIT;
|
||||
} else if (dest_fb_format >= RD::DATA_FORMAT_A2R10G10B10_UNORM_PACK32 && dest_fb_format <= RD::DATA_FORMAT_A2B10G10R10_SINT_PACK32) {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_10_BIT;
|
||||
} else {
|
||||
// In this case, debanding will be handled later when quantizing to an integer data format. (During blit, for example.)
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
}
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_8_BIT;
|
||||
} else {
|
||||
tonemap.debanding_mode = RendererRD::ToneMapper::TonemapSettings::DebandingMode::DEBANDING_MODE_DISABLED;
|
||||
}
|
||||
|
||||
tone_mapper->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap);
|
||||
tone_mapper->tonemapper_subpass(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap);
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
@@ -1714,7 +1691,7 @@ void RendererSceneRenderRD::init() {
|
||||
debug_effects = memnew(RendererRD::DebugEffects);
|
||||
luminance = memnew(RendererRD::Luminance(!can_use_storage));
|
||||
smaa = memnew(RendererRD::SMAA);
|
||||
tone_mapper = memnew(RendererRD::ToneMapper);
|
||||
tone_mapper = memnew(RendererRD::ToneMapper(!can_use_storage));
|
||||
if (can_use_vrs) {
|
||||
vrs = memnew(RendererRD::VRS);
|
||||
}
|
||||
|
||||
@@ -115,14 +115,6 @@ protected:
|
||||
void _post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data);
|
||||
void _disable_clear_request(const RenderDataRD *p_render_data);
|
||||
|
||||
_FORCE_INLINE_ bool _is_8bit_data_format(RD::DataFormat p_data_format) {
|
||||
return p_data_format >= RD::DATA_FORMAT_R8_UNORM && p_data_format <= RD::DATA_FORMAT_A8B8G8R8_SRGB_PACK32;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool _is_10bit_data_format(RD::DataFormat p_data_format) {
|
||||
return p_data_format >= RD::DATA_FORMAT_A2R10G10B10_UNORM_PACK32 && p_data_format <= RD::DATA_FORMAT_A2B10G10R10_SINT_PACK32;
|
||||
}
|
||||
|
||||
// needed for a single argument calls (material and uv2)
|
||||
PagedArrayPool<RenderGeometryInstance *> cull_argument_pool;
|
||||
PagedArray<RenderGeometryInstance *> cull_argument; //need this to exist
|
||||
|
||||
@@ -43,18 +43,103 @@ layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
|
||||
#ifdef MODE_GLOW_UPSAMPLE
|
||||
// When upsampling this is original downsampled texture, not the blended upsampled texture.
|
||||
layout(set = 1, binding = 0) uniform sampler2D blend_color;
|
||||
layout(constant_id = 0) const bool use_debanding = false;
|
||||
layout(constant_id = 1) const bool use_blend_color = false;
|
||||
|
||||
// From https://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
|
||||
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
|
||||
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
|
||||
// This dithering must be applied after encoding changes (linear/nonlinear) have been applied
|
||||
// as the final step before quantization from floating point to integer values.
|
||||
vec3 screen_space_dither(vec2 frag_coord, float bit_alignment_diviser) {
|
||||
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
|
||||
// Removed the time component to avoid passing time into this shader.
|
||||
vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
|
||||
dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0));
|
||||
|
||||
// Subtract 0.5 to avoid slightly brightening the whole viewport.
|
||||
// Use a dither strength of 100% rather than the 37.5% suggested by the original source.
|
||||
return (dither.rgb - 0.5) / bit_alignment_diviser;
|
||||
}
|
||||
#endif
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
#ifdef MODE_GLOW_DOWNSAMPLE
|
||||
|
||||
// https://www.shadertoy.com/view/mdsyDf
|
||||
vec4 BloomDownKernel4(sampler2D Tex, vec2 uv0) {
|
||||
vec2 RcpSrcTexRes = blur.source_pixel_size;
|
||||
|
||||
vec2 tc = (uv0 * 2.0 + 1.0) * RcpSrcTexRes;
|
||||
|
||||
float la = 1.0 / 4.0;
|
||||
|
||||
vec2 o = (0.5 + la) * RcpSrcTexRes;
|
||||
|
||||
vec4 c = vec4(0.0);
|
||||
c += textureLod(Tex, tc + vec2(-1.0, -1.0) * o, 0.0) * 0.25;
|
||||
c += textureLod(Tex, tc + vec2(1.0, -1.0) * o, 0.0) * 0.25;
|
||||
c += textureLod(Tex, tc + vec2(-1.0, 1.0) * o, 0.0) * 0.25;
|
||||
c += textureLod(Tex, tc + vec2(1.0, 1.0) * o, 0.0) * 0.25;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GLOW_UPSAMPLE
|
||||
|
||||
// https://www.shadertoy.com/view/mdsyDf
|
||||
vec4 BloomUpKernel4(sampler2D Tex, vec2 uv0) {
|
||||
vec2 RcpSrcTexRes = blur.source_pixel_size;
|
||||
|
||||
vec2 uv = uv0 * 0.5 + 0.5;
|
||||
|
||||
vec2 uvI = floor(uv);
|
||||
vec2 uvF = uv - uvI;
|
||||
|
||||
vec2 tc = uvI * RcpSrcTexRes.xy;
|
||||
|
||||
// optimal stop-band
|
||||
float lw = 0.357386;
|
||||
float la = 25.0 / 32.0; // 0.78125 ~ 0.779627;
|
||||
float lb = 3.0 / 64.0; // 0.046875 ~ 0.0493871;
|
||||
|
||||
vec2 l = vec2(-1.5 + la, 0.5 + lb);
|
||||
|
||||
vec2 lx = uvF.x == 0.0 ? l.xy : -l.yx;
|
||||
vec2 ly = uvF.y == 0.0 ? l.xy : -l.yx;
|
||||
|
||||
lx *= RcpSrcTexRes.xx;
|
||||
ly *= RcpSrcTexRes.yy;
|
||||
|
||||
vec4 c00 = textureLod(Tex, tc + vec2(lx.x, ly.x), 0.0);
|
||||
vec4 c10 = textureLod(Tex, tc + vec2(lx.y, ly.x), 0.0);
|
||||
vec4 c01 = textureLod(Tex, tc + vec2(lx.x, ly.y), 0.0);
|
||||
vec4 c11 = textureLod(Tex, tc + vec2(lx.y, ly.y), 0.0);
|
||||
|
||||
vec2 w = abs(uvF * 2.0 - lw);
|
||||
|
||||
vec4 cx0 = c00 * (1.0 - w.x) + (c10 * w.x);
|
||||
vec4 cx1 = c01 * (1.0 - w.x) + (c11 * w.x);
|
||||
|
||||
vec4 cxy = cx0 * (1.0 - w.y) + (cx1 * w.y);
|
||||
|
||||
return cxy;
|
||||
}
|
||||
|
||||
#endif // MODE_GLOW_UPSAMPLE
|
||||
|
||||
void main() {
|
||||
// We do not apply our color scale for our mobile renderer here, we'll leave our colors at half brightness and apply scale in the tonemap raster.
|
||||
|
||||
#ifdef MODE_MIPMAP
|
||||
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
vec2 pix_size = blur.dest_pixel_size;
|
||||
vec4 color = texture(source_color, uv_interp + vec2(-0.5, -0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(0.5, -0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(0.5, 0.5) * pix_size);
|
||||
@@ -68,19 +153,19 @@ void main() {
|
||||
// For Gaussian Blur we use 13 taps in a single pass instead of 12 taps over 2 passes.
|
||||
// This minimizes the number of times we change framebuffers which is very important for mobile.
|
||||
// Source: http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
|
||||
vec4 A = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, -1.0));
|
||||
vec4 B = texture(source_color, uv_interp + blur.pixel_size * vec2(0.0, -1.0));
|
||||
vec4 C = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, -1.0));
|
||||
vec4 D = texture(source_color, uv_interp + blur.pixel_size * vec2(-0.5, -0.5));
|
||||
vec4 E = texture(source_color, uv_interp + blur.pixel_size * vec2(0.5, -0.5));
|
||||
vec4 F = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, 0.0));
|
||||
vec4 A = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(-1.0, -1.0));
|
||||
vec4 B = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(0.0, -1.0));
|
||||
vec4 C = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(1.0, -1.0));
|
||||
vec4 D = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(-0.5, -0.5));
|
||||
vec4 E = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(0.5, -0.5));
|
||||
vec4 F = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(-1.0, 0.0));
|
||||
vec4 G = texture(source_color, uv_interp);
|
||||
vec4 H = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, 0.0));
|
||||
vec4 I = texture(source_color, uv_interp + blur.pixel_size * vec2(-0.5, 0.5));
|
||||
vec4 J = texture(source_color, uv_interp + blur.pixel_size * vec2(0.5, 0.5));
|
||||
vec4 K = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, 1.0));
|
||||
vec4 L = texture(source_color, uv_interp + blur.pixel_size * vec2(0.0, 1.0));
|
||||
vec4 M = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, 1.0));
|
||||
vec4 H = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(1.0, 0.0));
|
||||
vec4 I = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(-0.5, 0.5));
|
||||
vec4 J = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(0.5, 0.5));
|
||||
vec4 K = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(-1.0, 1.0));
|
||||
vec4 L = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(0.0, 1.0));
|
||||
vec4 M = texture(source_color, uv_interp + blur.dest_pixel_size * vec2(1.0, 1.0));
|
||||
|
||||
float base_weight = 0.5 / 4.0;
|
||||
float lesser_weight = 0.125 / 4.0;
|
||||
@@ -92,67 +177,55 @@ void main() {
|
||||
frag_color += (G + H + M + L) * lesser_weight;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_GLOW
|
||||
#ifdef MODE_GLOW_GATHER
|
||||
// First step, go straight to quarter resolution.
|
||||
// Don't apply blur, but include thresholding.
|
||||
|
||||
//Glow uses larger sigma 1 for a more rounded blur effect
|
||||
vec2 block_pos = floor(gl_FragCoord.xy) * 4.0;
|
||||
vec2 end = max(1.0 / blur.source_pixel_size - vec2(4.0), vec2(0.0));
|
||||
block_pos = clamp(block_pos, vec2(0.0), end);
|
||||
|
||||
#define GLOW_ADD(m_ofs, m_mult) \
|
||||
{ \
|
||||
vec2 ofs = uv_interp + m_ofs * pix_size; \
|
||||
vec4 c = texture(source_color, ofs) * m_mult; \
|
||||
if (any(lessThan(ofs, vec2(0.0))) || any(greaterThan(ofs, vec2(1.0)))) { \
|
||||
c *= 0.0; \
|
||||
} \
|
||||
color += c; \
|
||||
// We skipped a level, so gather 16 closest samples now.
|
||||
|
||||
vec4 color = textureLod(source_color, (block_pos + vec2(1.0, 1.0)) * blur.source_pixel_size, 0.0);
|
||||
color += textureLod(source_color, (block_pos + vec2(1.0, 3.0)) * blur.source_pixel_size, 0.0);
|
||||
color += textureLod(source_color, (block_pos + vec2(3.0, 1.0)) * blur.source_pixel_size, 0.0);
|
||||
color += textureLod(source_color, (block_pos + vec2(3.0, 3.0)) * blur.source_pixel_size, 0.0);
|
||||
frag_color = color * 0.25;
|
||||
|
||||
// Apply strength a second time since it usually gets added at each level.
|
||||
frag_color *= blur.glow_strength;
|
||||
frag_color *= blur.glow_strength;
|
||||
|
||||
// In the first pass bring back to correct color range else we're applying the wrong threshold
|
||||
// in subsequent passes we can use it as is as we'd just be undoing it right after.
|
||||
frag_color *= blur.luminance_multiplier;
|
||||
frag_color *= blur.glow_exposure;
|
||||
|
||||
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
|
||||
float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom);
|
||||
|
||||
frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap)) / blur.luminance_multiplier;
|
||||
#endif // MODE_GLOW_GATHER_WIDE
|
||||
|
||||
#ifdef MODE_GLOW_DOWNSAMPLE
|
||||
// Regular downsample, apply a simple blur.
|
||||
frag_color = BloomDownKernel4(source_color, floor(gl_FragCoord.xy));
|
||||
frag_color *= blur.glow_strength;
|
||||
#endif // MODE_GLOW_DOWNSAMPLE
|
||||
|
||||
#ifdef MODE_GLOW_UPSAMPLE
|
||||
|
||||
frag_color = BloomUpKernel4(source_color, floor(gl_FragCoord.xy)) * blur.glow_strength; // "glow_strength" here is actually the glow level. It is always 1.0, except for the first upsample where we need to apply the level to two textures at once.
|
||||
if (use_blend_color) {
|
||||
vec2 uv = floor(gl_FragCoord.xy) + 0.5;
|
||||
frag_color += textureLod(blend_color, uv * blur.dest_pixel_size, 0.0) * blur.glow_level;
|
||||
}
|
||||
|
||||
if (bool(blur.flags & FLAG_HORIZONTAL)) {
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
pix_size *= 0.5; //reading from larger buffer, so use more samples
|
||||
|
||||
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.174938;
|
||||
GLOW_ADD(vec2(1.0, 0.0), 0.165569);
|
||||
GLOW_ADD(vec2(2.0, 0.0), 0.140367);
|
||||
GLOW_ADD(vec2(3.0, 0.0), 0.106595);
|
||||
GLOW_ADD(vec2(-1.0, 0.0), 0.165569);
|
||||
GLOW_ADD(vec2(-2.0, 0.0), 0.140367);
|
||||
GLOW_ADD(vec2(-3.0, 0.0), 0.106595);
|
||||
|
||||
// only do this in the horizontal pass, if we also do this in the vertical pass we're doubling up.
|
||||
color *= blur.glow_strength;
|
||||
|
||||
frag_color = color;
|
||||
} else {
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.288713;
|
||||
GLOW_ADD(vec2(0.0, 1.0), 0.233062);
|
||||
GLOW_ADD(vec2(0.0, 2.0), 0.122581);
|
||||
GLOW_ADD(vec2(0.0, -1.0), 0.233062);
|
||||
GLOW_ADD(vec2(0.0, -2.0), 0.122581);
|
||||
|
||||
frag_color = color;
|
||||
if (use_debanding) {
|
||||
frag_color.rgb += screen_space_dither(gl_FragCoord.xy, 1023.0);
|
||||
}
|
||||
|
||||
#undef GLOW_ADD
|
||||
|
||||
if (bool(blur.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
// In the first pass bring back to correct color range else we're applying the wrong threshold
|
||||
// in subsequent passes we can use it as is as we'd just be undoing it right after.
|
||||
frag_color *= blur.luminance_multiplier;
|
||||
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
|
||||
frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_scale;
|
||||
#endif
|
||||
frag_color *= blur.glow_exposure;
|
||||
|
||||
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
|
||||
float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom);
|
||||
|
||||
frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap)) / blur.luminance_multiplier;
|
||||
}
|
||||
|
||||
#endif // MODE_GAUSSIAN_GLOW
|
||||
#endif // MODE_GLOW_UPSAMPLE
|
||||
|
||||
#ifdef MODE_COPY
|
||||
vec4 color = textureLod(source_color, uv_interp, 0.0);
|
||||
|
||||
@@ -3,24 +3,22 @@
|
||||
#define FLAG_GLOW_FIRST_PASS (1 << 2)
|
||||
|
||||
layout(push_constant, std430) uniform Blur {
|
||||
vec2 pixel_size; // 08 - 08
|
||||
uint flags; // 04 - 12
|
||||
uint pad; // 04 - 16
|
||||
vec2 dest_pixel_size; // 08 - 08
|
||||
vec2 source_pixel_size; // 08 - 16
|
||||
|
||||
vec2 pad; // 08 - 24
|
||||
uint flags; // 04 - 28
|
||||
float glow_level; // 04 - 32
|
||||
|
||||
// Glow.
|
||||
float glow_strength; // 04 - 20
|
||||
float glow_bloom; // 04 - 24
|
||||
float glow_hdr_threshold; // 04 - 28
|
||||
float glow_hdr_scale; // 04 - 32
|
||||
float glow_strength; // 04 - 36
|
||||
float glow_bloom; // 04 - 40
|
||||
float glow_hdr_threshold; // 04 - 44
|
||||
float glow_hdr_scale; // 04 - 48
|
||||
|
||||
float glow_exposure; // 04 - 36
|
||||
float glow_white; // 04 - 40
|
||||
float glow_luminance_cap; // 04 - 44
|
||||
float glow_auto_exposure_scale; // 04 - 48
|
||||
|
||||
float luminance_multiplier; // 04 - 52
|
||||
float res1; // 04 - 56
|
||||
float res2; // 04 - 60
|
||||
float res3; // 04 - 64
|
||||
float glow_exposure; // 04 - 52
|
||||
float glow_white; // 04 - 56
|
||||
float glow_luminance_cap; // 04 - 60
|
||||
float luminance_multiplier; // 04 - 64
|
||||
}
|
||||
blur;
|
||||
|
||||
@@ -67,7 +67,7 @@ layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec2 inv_size;
|
||||
uint flags;
|
||||
uint use_debanding;
|
||||
float pad;
|
||||
}
|
||||
params;
|
||||
@@ -140,11 +140,8 @@ void main() {
|
||||
out_color.rgb = linear_to_srgb(out_color.rgb);
|
||||
out_color.a = texture(color_tex, tex_coord).a;
|
||||
}
|
||||
if (bool(params.flags & FLAG_USE_8_BIT_DEBANDING)) {
|
||||
if (bool(params.use_debanding)) {
|
||||
// Divide by 255 to align to 8-bit quantization.
|
||||
out_color.rgb += screen_space_dither(gl_FragCoord.xy, 255.0);
|
||||
} else if (bool(params.flags & FLAG_USE_10_BIT_DEBANDING)) {
|
||||
// Divide by 1023 to align to 10-bit quantization.
|
||||
out_color.rgb += screen_space_dither(gl_FragCoord.xy, 1023.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,21 +38,16 @@ void main() {
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
#ifdef SUBPASS
|
||||
layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput input_color;
|
||||
#elif defined(USE_MULTIVIEW)
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
|
||||
#ifdef USE_MULTIVIEW
|
||||
#define SAMPLER_FORMAT sampler2DArray
|
||||
#else
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
#define SAMPLER_FORMAT sampler2D
|
||||
#endif
|
||||
|
||||
layout(set = 0, binding = 0) uniform SAMPLER_FORMAT source_color;
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 2, binding = 0) uniform sampler2DArray source_glow;
|
||||
#else
|
||||
layout(set = 2, binding = 0) uniform sampler2D source_glow;
|
||||
#endif
|
||||
layout(set = 2, binding = 1) uniform sampler2D glow_map;
|
||||
layout(set = 2, binding = 0) uniform SAMPLER_FORMAT source_glow;
|
||||
layout(set = 2, binding = 1) uniform sampler2D glow_map; // TODO needs multiview support
|
||||
|
||||
#ifdef USE_1D_LUT
|
||||
layout(set = 3, binding = 0) uniform sampler2D source_color_correction;
|
||||
@@ -66,8 +61,7 @@ layout(set = 3, binding = 0) uniform sampler3D source_color_correction;
|
||||
#define FLAG_USE_COLOR_CORRECTION (1 << 3)
|
||||
#define FLAG_USE_FXAA (1 << 4)
|
||||
#define FLAG_USE_8_BIT_DEBANDING (1 << 5)
|
||||
#define FLAG_USE_10_BIT_DEBANDING (1 << 6)
|
||||
#define FLAG_CONVERT_TO_SRGB (1 << 7)
|
||||
#define FLAG_CONVERT_TO_SRGB (1 << 6)
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec3 bcs;
|
||||
@@ -93,111 +87,6 @@ params;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
#ifdef USE_GLOW_FILTER_BICUBIC
|
||||
// w0, w1, w2, and w3 are the four cubic B-spline basis functions
|
||||
float w0(float a) {
|
||||
return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f);
|
||||
}
|
||||
|
||||
float w1(float a) {
|
||||
return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
|
||||
}
|
||||
|
||||
float w2(float a) {
|
||||
return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
|
||||
}
|
||||
|
||||
float w3(float a) {
|
||||
return (1.0f / 6.0f) * (a * a * a);
|
||||
}
|
||||
|
||||
// g0 and g1 are the two amplitude functions
|
||||
float g0(float a) {
|
||||
return w0(a) + w1(a);
|
||||
}
|
||||
|
||||
float g1(float a) {
|
||||
return w2(a) + w3(a);
|
||||
}
|
||||
|
||||
// h0 and h1 are the two offset functions
|
||||
float h0(float a) {
|
||||
return -1.0f + w1(a) / (w0(a) + w1(a));
|
||||
}
|
||||
|
||||
float h1(float a) {
|
||||
return 1.0f + w3(a) / (w2(a) + w3(a));
|
||||
}
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec4 texture2D_bicubic(sampler2DArray tex, vec2 uv, int p_lod) {
|
||||
float lod = float(p_lod);
|
||||
vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
|
||||
vec2 pixel_size = vec2(1.0f) / tex_size;
|
||||
|
||||
uv = uv * tex_size + vec2(0.5f);
|
||||
|
||||
vec2 iuv = floor(uv);
|
||||
vec2 fuv = fract(uv);
|
||||
|
||||
float g0x = g0(fuv.x);
|
||||
float g1x = g1(fuv.x);
|
||||
float h0x = h0(fuv.x);
|
||||
float h1x = h1(fuv.x);
|
||||
float h0y = h0(fuv.y);
|
||||
float h1y = h1(fuv.y);
|
||||
|
||||
vec3 p0 = vec3((vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p1 = vec3((vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p2 = vec3((vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p3 = vec3((vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
|
||||
return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
|
||||
(g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
|
||||
}
|
||||
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
|
||||
#else // USE_MULTIVIEW
|
||||
|
||||
vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {
|
||||
float lod = float(p_lod);
|
||||
vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
|
||||
vec2 pixel_size = vec2(1.0f) / tex_size;
|
||||
|
||||
uv = uv * tex_size + vec2(0.5f);
|
||||
|
||||
vec2 iuv = floor(uv);
|
||||
vec2 fuv = fract(uv);
|
||||
|
||||
float g0x = g0(fuv.x);
|
||||
float g1x = g1(fuv.x);
|
||||
float h0x = h0(fuv.x);
|
||||
float h1x = h1(fuv.x);
|
||||
float h0y = h0(fuv.y);
|
||||
float h1y = h1(fuv.y);
|
||||
|
||||
vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
|
||||
vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
|
||||
vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
|
||||
vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
|
||||
|
||||
return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
|
||||
(g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
|
||||
}
|
||||
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
|
||||
#endif // !USE_MULTIVIEW
|
||||
|
||||
#else // USE_GLOW_FILTER_BICUBIC
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, vec3(m_uv, ViewIndex), float(m_lod))
|
||||
#else // USE_MULTIVIEW
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
|
||||
#endif // !USE_MULTIVIEW
|
||||
|
||||
#endif // !USE_GLOW_FILTER_BICUBIC
|
||||
|
||||
// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
|
||||
vec3 tonemap_reinhard(vec3 color, float white) {
|
||||
float white_squared = white * white;
|
||||
@@ -360,11 +249,113 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_GLOW_FILTER_BICUBIC
|
||||
// w0, w1, w2, and w3 are the four cubic B-spline basis functions
|
||||
float w0(float a) {
|
||||
return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f);
|
||||
}
|
||||
|
||||
float w1(float a) {
|
||||
return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
|
||||
}
|
||||
|
||||
float w2(float a) {
|
||||
return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
|
||||
}
|
||||
|
||||
float w3(float a) {
|
||||
return (1.0f / 6.0f) * (a * a * a);
|
||||
}
|
||||
|
||||
// g0 and g1 are the two amplitude functions
|
||||
float g0(float a) {
|
||||
return w0(a) + w1(a);
|
||||
}
|
||||
|
||||
float g1(float a) {
|
||||
return w2(a) + w3(a);
|
||||
}
|
||||
|
||||
// h0 and h1 are the two offset functions
|
||||
float h0(float a) {
|
||||
return -1.0f + w1(a) / (w0(a) + w1(a));
|
||||
}
|
||||
|
||||
float h1(float a) {
|
||||
return 1.0f + w3(a) / (w2(a) + w3(a));
|
||||
}
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 gather_glow(sampler2DArray tex, vec2 uv) { // sample all selected glow levels, view is added to uv later
|
||||
#else
|
||||
vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
|
||||
#endif // defined(USE_MULTIVIEW)
|
||||
vec4 texture2D_bicubic(sampler2DArray tex, vec2 uv, int p_lod) {
|
||||
float lod = float(p_lod);
|
||||
vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
|
||||
vec2 pixel_size = vec2(1.0f) / tex_size;
|
||||
|
||||
uv = uv * tex_size + vec2(0.5f);
|
||||
|
||||
vec2 iuv = floor(uv);
|
||||
vec2 fuv = fract(uv);
|
||||
|
||||
float g0x = g0(fuv.x);
|
||||
float g1x = g1(fuv.x);
|
||||
float h0x = h0(fuv.x);
|
||||
float h1x = h1(fuv.x);
|
||||
float h0y = h0(fuv.y);
|
||||
float h1y = h1(fuv.y);
|
||||
|
||||
vec3 p0 = vec3((vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p1 = vec3((vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p2 = vec3((vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
vec3 p3 = vec3((vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size, ViewIndex);
|
||||
|
||||
return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
|
||||
(g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
|
||||
}
|
||||
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
|
||||
#else // USE_MULTIVIEW
|
||||
|
||||
vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {
|
||||
float lod = float(p_lod);
|
||||
vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
|
||||
vec2 pixel_size = vec2(1.0f) / tex_size;
|
||||
|
||||
uv = uv * tex_size + vec2(0.5f);
|
||||
|
||||
vec2 iuv = floor(uv);
|
||||
vec2 fuv = fract(uv);
|
||||
|
||||
float g0x = g0(fuv.x);
|
||||
float g1x = g1(fuv.x);
|
||||
float h0x = h0(fuv.x);
|
||||
float h1x = h1(fuv.x);
|
||||
float h0y = h0(fuv.y);
|
||||
float h1y = h1(fuv.y);
|
||||
|
||||
vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
|
||||
vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
|
||||
vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
|
||||
vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
|
||||
|
||||
return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
|
||||
(g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
|
||||
}
|
||||
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
|
||||
#endif // !USE_MULTIVIEW
|
||||
|
||||
#else // USE_GLOW_FILTER_BICUBIC
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, vec3(m_uv, ViewIndex), float(m_lod))
|
||||
#else // USE_MULTIVIEW
|
||||
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
|
||||
#endif // !USE_MULTIVIEW
|
||||
|
||||
#endif // !USE_GLOW_FILTER_BICUBIC
|
||||
|
||||
vec3 gather_glow(SAMPLER_FORMAT tex, vec2 uv) { // sample all selected glow levels
|
||||
|
||||
vec3 glow = vec3(0.0f);
|
||||
|
||||
if (params.glow_levels[0] > 0.0001) {
|
||||
@@ -461,8 +452,6 @@ vec3 apply_color_correction(vec3 color) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SUBPASS
|
||||
|
||||
// FXAA 3.11 compact, Ported from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/fxaa.frag
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// MIT License
|
||||
@@ -831,7 +820,6 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif // !SUBPASS
|
||||
|
||||
// From https://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
|
||||
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
|
||||
@@ -850,15 +838,7 @@ vec3 screen_space_dither(vec2 frag_coord, float bit_alignment_diviser) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef SUBPASS
|
||||
// SUBPASS and USE_MULTIVIEW can be combined but in that case we're already reading from the correct layer
|
||||
#ifdef USE_MULTIVIEW
|
||||
// In order to ensure the `SpvCapabilityMultiView` is included in the SPIR-V capabilities, gl_ViewIndex must
|
||||
// be read in the shader. Without this, transpilation to Metal fails to include the multi-view variant.
|
||||
uint vi = ViewIndex;
|
||||
#endif
|
||||
vec4 color = subpassLoad(input_color);
|
||||
#elif defined(USE_MULTIVIEW)
|
||||
vec4 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f);
|
||||
#else
|
||||
vec4 color = textureLod(source_color, uv_interp, 0.0f);
|
||||
@@ -869,17 +849,13 @@ void main() {
|
||||
|
||||
float exposure = params.exposure;
|
||||
|
||||
#ifndef SUBPASS
|
||||
if (bool(params.flags & FLAG_USE_AUTO_EXPOSURE)) {
|
||||
exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_scale);
|
||||
}
|
||||
#endif
|
||||
|
||||
color.rgb *= exposure;
|
||||
|
||||
// Single-pass FXAA and pre-tonemap glow.
|
||||
|
||||
#ifndef SUBPASS
|
||||
if (bool(params.flags & FLAG_USE_FXAA)) {
|
||||
// FXAA must be performed before glow to preserve the "bleed" effect of glow.
|
||||
color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
|
||||
@@ -900,15 +876,13 @@ void main() {
|
||||
color.rgb = apply_glow(color.rgb, glow, params.white);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Tonemap to lower dynamic range.
|
||||
|
||||
color.rgb = apply_tonemapping(color.rgb, params.white);
|
||||
|
||||
// Additional effects.
|
||||
// Post-tonemap glow.
|
||||
|
||||
#ifndef SUBPASS
|
||||
if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_SOFTLIGHT) {
|
||||
// Apply soft light after tonemapping to mitigate the issue of discontinuity
|
||||
// at 1.0 and higher. This makes the issue only appear with HDR output that
|
||||
@@ -921,7 +895,8 @@ void main() {
|
||||
glow = apply_tonemapping(glow, params.white);
|
||||
color.rgb = apply_glow(color.rgb, glow, params.white);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Additional effects.
|
||||
|
||||
if (bool(params.flags & FLAG_USE_BCS)) {
|
||||
// Apply brightness:
|
||||
@@ -964,9 +939,6 @@ void main() {
|
||||
if (bool(params.flags & FLAG_USE_8_BIT_DEBANDING)) {
|
||||
// Divide by 255 to align to 8-bit quantization.
|
||||
color.rgb += screen_space_dither(gl_FragCoord.xy, 255.0);
|
||||
} else if (bool(params.flags & FLAG_USE_10_BIT_DEBANDING)) {
|
||||
// Divide by 1023 to align to 10-bit quantization.
|
||||
color.rgb += screen_space_dither(gl_FragCoord.xy, 1023.0);
|
||||
}
|
||||
|
||||
frag_color = color;
|
||||
|
||||
@@ -0,0 +1,818 @@
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
|
||||
void main() {
|
||||
// old code, ARM driver bug on Mali-GXXx GPUs and Vulkan API 1.3.xxx
|
||||
// https://github.com/godotengine/godot/pull/92817#issuecomment-2168625982
|
||||
//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 vertex_base;
|
||||
if (gl_VertexIndex == 0) {
|
||||
vertex_base = vec2(-1.0, -1.0);
|
||||
} else if (gl_VertexIndex == 1) {
|
||||
vertex_base = vec2(-1.0, 3.0);
|
||||
} else {
|
||||
vertex_base = vec2(3.0, -1.0);
|
||||
}
|
||||
gl_Position = vec4(vertex_base, 0.0, 1.0);
|
||||
uv_interp = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#define SAMPLER_FORMAT sampler2DArray
|
||||
#else
|
||||
#define SAMPLER_FORMAT sampler2D
|
||||
#endif
|
||||
|
||||
#ifdef SUBPASS
|
||||
layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput input_color;
|
||||
#else
|
||||
layout(set = 0, binding = 0) uniform SAMPLER_FORMAT source_color;
|
||||
#endif
|
||||
|
||||
layout(set = 1, binding = 0) uniform SAMPLER_FORMAT source_glow;
|
||||
layout(set = 1, binding = 1) uniform sampler2D glow_map;
|
||||
|
||||
#ifdef USE_1D_LUT
|
||||
layout(set = 2, binding = 0) uniform sampler2D source_color_correction;
|
||||
#else
|
||||
layout(set = 2, binding = 0) uniform sampler3D source_color_correction;
|
||||
#endif
|
||||
|
||||
layout(constant_id = 0) const bool use_bcs = false;
|
||||
layout(constant_id = 1) const bool use_glow = false;
|
||||
layout(constant_id = 2) const bool use_glow_map = false;
|
||||
layout(constant_id = 3) const bool use_color_correction = false;
|
||||
layout(constant_id = 4) const bool use_fxaa = false;
|
||||
layout(constant_id = 5) const bool deband_8_bit = false;
|
||||
layout(constant_id = 6) const bool deband_10_bit = false;
|
||||
layout(constant_id = 7) const bool convert_to_srgb = false;
|
||||
layout(constant_id = 8) const bool tonemapper_linear = false;
|
||||
layout(constant_id = 9) const bool tonemapper_reinhard = false;
|
||||
layout(constant_id = 10) const bool tonemapper_filmic = false;
|
||||
layout(constant_id = 11) const bool tonemapper_aces = false;
|
||||
layout(constant_id = 12) const bool tonemapper_agx = false;
|
||||
layout(constant_id = 13) const bool glow_mode_add = false;
|
||||
layout(constant_id = 14) const bool glow_mode_screen = false;
|
||||
layout(constant_id = 15) const bool glow_mode_softlight = false;
|
||||
layout(constant_id = 16) const bool glow_mode_replace = false;
|
||||
layout(constant_id = 17) const bool glow_mode_mix = false;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec3 bcs;
|
||||
float luminance_multiplier;
|
||||
|
||||
vec2 src_pixel_size;
|
||||
vec2 dest_pixel_size;
|
||||
|
||||
float glow_intensity;
|
||||
float glow_map_strength;
|
||||
float exposure;
|
||||
float white;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
|
||||
vec3 tonemap_reinhard(vec3 color, float white) {
|
||||
float white_squared = white * white;
|
||||
vec3 white_squared_color = white_squared * color;
|
||||
// Equivalent to color * (1 + color / white_squared) / (1 + color)
|
||||
return (white_squared_color + color * color) / (white_squared_color + white_squared);
|
||||
}
|
||||
|
||||
vec3 tonemap_filmic(vec3 color, float white) {
|
||||
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
|
||||
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
|
||||
// has no effect on the curve's general shape or visual properties
|
||||
const float exposure_bias = 2.0f;
|
||||
const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance
|
||||
const float B = 0.30f * exposure_bias;
|
||||
const float C = 0.10f;
|
||||
const float D = 0.20f;
|
||||
const float E = 0.01f;
|
||||
const float F = 0.30f;
|
||||
|
||||
vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
|
||||
float white_tonemapped = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F;
|
||||
|
||||
return color_tonemapped / white_tonemapped;
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
|
||||
// (MIT License).
|
||||
vec3 tonemap_aces(vec3 color, float white) {
|
||||
const float exposure_bias = 1.8f;
|
||||
const float A = 0.0245786f;
|
||||
const float B = 0.000090537f;
|
||||
const float C = 0.983729f;
|
||||
const float D = 0.432951f;
|
||||
const float E = 0.238081f;
|
||||
|
||||
// Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias`
|
||||
const mat3 rgb_to_rrt = mat3(
|
||||
vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias),
|
||||
vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias),
|
||||
vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias));
|
||||
|
||||
const mat3 odt_to_rgb = mat3(
|
||||
vec3(1.60475f, -0.53108f, -0.07367f),
|
||||
vec3(-0.10208f, 1.10813f, -0.00605f),
|
||||
vec3(-0.00327f, -0.07276f, 1.07602f));
|
||||
|
||||
color *= rgb_to_rrt;
|
||||
vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E);
|
||||
color_tonemapped *= odt_to_rgb;
|
||||
|
||||
white *= exposure_bias;
|
||||
float white_tonemapped = (white * (white + A) - B) / (white * (C * white + D) + E);
|
||||
|
||||
return color_tonemapped / white_tonemapped;
|
||||
}
|
||||
|
||||
// Polynomial approximation of EaryChow's AgX sigmoid curve.
|
||||
// x must be within the range [0.0, 1.0]
|
||||
vec3 agx_contrast_approx(vec3 x) {
|
||||
// Generated with Excel trendline
|
||||
// Input data: Generated using python sigmoid with EaryChow's configuration and 57 steps
|
||||
// Additional padding values were added to give correct intersections at 0.0 and 1.0
|
||||
// 6th order, intercept of 0.0 to remove an operation and ensure intersection at 0.0
|
||||
vec3 x2 = x * x;
|
||||
vec3 x4 = x2 * x2;
|
||||
return 0.021 * x + 4.0111 * x2 - 25.682 * x2 * x + 70.359 * x4 - 74.778 * x4 * x + 27.069 * x4 * x2;
|
||||
}
|
||||
|
||||
// This is an approximation and simplification of EaryChow's AgX implementation that is used by Blender.
|
||||
// This code is based off of the script that generates the AgX_Base_sRGB.cube LUT that Blender uses.
|
||||
// Source: https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBasesRGB.py
|
||||
vec3 tonemap_agx(vec3 color) {
|
||||
// Combined linear sRGB to linear Rec 2020 and Blender AgX inset matrices:
|
||||
const mat3 srgb_to_rec2020_agx_inset_matrix = mat3(
|
||||
0.54490813676363087053, 0.14044005884001287035, 0.088827411851915368603,
|
||||
0.37377945959812267119, 0.75410959864013760045, 0.17887712465043811023,
|
||||
0.081384976686407536266, 0.10543358536857773485, 0.73224999956948382528);
|
||||
|
||||
// Combined inverse AgX outset matrix and linear Rec 2020 to linear sRGB matrices.
|
||||
const mat3 agx_outset_rec2020_to_srgb_matrix = mat3(
|
||||
1.9645509602733325934, -0.29932243390911083839, -0.16436833806080403409,
|
||||
-0.85585845117807513559, 1.3264510741502356555, -0.23822464068860595117,
|
||||
-0.10886710826831608324, -0.027084020983874825605, 1.402665347143271889);
|
||||
|
||||
// LOG2_MIN = -10.0
|
||||
// LOG2_MAX = +6.5
|
||||
// MIDDLE_GRAY = 0.18
|
||||
const float min_ev = -12.4739311883324; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY)
|
||||
const float max_ev = 4.02606881166759; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY)
|
||||
|
||||
// Large negative values in one channel and large positive values in other
|
||||
// channels can result in a colour that appears darker and more saturated than
|
||||
// desired after passing it through the inset matrix. For this reason, it is
|
||||
// best to prevent negative input values.
|
||||
// This is done before the Rec. 2020 transform to allow the Rec. 2020
|
||||
// transform to be combined with the AgX inset matrix. This results in a loss
|
||||
// of color information that could be correctly interpreted within the
|
||||
// Rec. 2020 color space as positive RGB values, but it is less common for Godot
|
||||
// to provide this function with negative sRGB values and therefore not worth
|
||||
// the performance cost of an additional matrix multiplication.
|
||||
// A value of 2e-10 intentionally introduces insignificant error to prevent
|
||||
// log2(0.0) after the inset matrix is applied; color will be >= 1e-10 after
|
||||
// the matrix transform.
|
||||
color = max(color, 2e-10);
|
||||
|
||||
// Do AGX in rec2020 to match Blender and then apply inset matrix.
|
||||
color = srgb_to_rec2020_agx_inset_matrix * color;
|
||||
|
||||
// Log2 space encoding.
|
||||
// Must be clamped because agx_contrast_approx may not work
|
||||
// well with values outside of the range [0.0, 1.0]
|
||||
color = clamp(log2(color), min_ev, max_ev);
|
||||
color = (color - min_ev) / (max_ev - min_ev);
|
||||
|
||||
// Apply sigmoid function approximation.
|
||||
color = agx_contrast_approx(color);
|
||||
|
||||
// Convert back to linear before applying outset matrix.
|
||||
color = pow(color, vec3(2.4));
|
||||
|
||||
// Apply outset to make the result more chroma-laden and then go back to linear sRGB.
|
||||
color = agx_outset_rec2020_to_srgb_matrix * color;
|
||||
|
||||
// Blender's lusRGB.compensate_low_side is too complex for this shader, so
|
||||
// simply return the color, even if it has negative components. These negative
|
||||
// components may be useful for subsequent color adjustments.
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 linear_to_srgb(vec3 color) {
|
||||
// Clamping is not strictly necessary for floating point nonlinear sRGB encoding,
|
||||
// but many cases that call this function need the result clamped.
|
||||
color = clamp(color, vec3(0.0), vec3(1.0));
|
||||
const vec3 a = vec3(0.055f);
|
||||
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
|
||||
}
|
||||
|
||||
vec3 srgb_to_linear(vec3 color) {
|
||||
const vec3 a = vec3(0.055f);
|
||||
return mix(pow((color.rgb + a) * (1.0f / (vec3(1.0f) + a)), vec3(2.4f)), color.rgb * (1.0f / 12.92f), lessThan(color.rgb, vec3(0.04045f)));
|
||||
}
|
||||
|
||||
vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR
|
||||
// Ensure color values passed to tonemappers are positive.
|
||||
// They can be negative in the case of negative lights, which leads to undesired behavior.
|
||||
if (tonemapper_linear) {
|
||||
return color;
|
||||
} else if (tonemapper_reinhard) {
|
||||
return tonemap_reinhard(max(vec3(0.0f), color), white);
|
||||
} else if (tonemapper_filmic) {
|
||||
return tonemap_filmic(max(vec3(0.0f), color), white);
|
||||
} else if (tonemapper_aces) {
|
||||
return tonemap_aces(max(vec3(0.0f), color), white);
|
||||
} else { // FLAG_TONEMAPPER_AGX
|
||||
return tonemap_agx(color);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 gather_glow() {
|
||||
vec2 uv = gl_FragCoord.xy * params.dest_pixel_size;
|
||||
return textureLod(source_glow, vec3(uv, ViewIndex), 0.0).rgb;
|
||||
}
|
||||
#else
|
||||
vec3 gather_glow() {
|
||||
vec2 uv = gl_FragCoord.xy * params.dest_pixel_size;
|
||||
return textureLod(source_glow, uv, 0.0).rgb;
|
||||
}
|
||||
#endif // !USE_MULTIVIEW
|
||||
|
||||
// Applies glow using the selected blending mode. Does not handle the mix blend mode.
|
||||
vec3 apply_glow(vec3 color, vec3 glow, float white) {
|
||||
if (glow_mode_add) {
|
||||
return color + glow;
|
||||
} else if (glow_mode_screen) {
|
||||
// Glow cannot be above 1.0 after normalizing and should be non-negative
|
||||
// to produce expected results. It is possible that glow can be negative
|
||||
// if negative lights were used in the scene.
|
||||
// We clamp to white because glow will be normalized to this range.
|
||||
// Note: white cannot be smaller than the maximum output value.
|
||||
glow.rgb = clamp(glow.rgb, 0.0, white);
|
||||
|
||||
// Normalize to white range.
|
||||
//glow.rgb /= white;
|
||||
//color.rgb /= white;
|
||||
//color.rgb = (color.rgb + glow.rgb) - (color.rgb * glow.rgb);
|
||||
// Expand back to original range.
|
||||
//color.rgb *= white;
|
||||
|
||||
// The following is a mathematically simplified version of the above.
|
||||
color.rgb = color.rgb + glow.rgb - (color.rgb * glow.rgb / white);
|
||||
|
||||
return color;
|
||||
} else if (glow_mode_softlight) {
|
||||
// Glow cannot be above 1.0 should be non-negative to produce
|
||||
// expected results. It is possible that glow can be negative
|
||||
// if negative lights were used in the scene.
|
||||
// Note: This approach causes a discontinuity with scene values
|
||||
// at 1.0, but because this glow should have its strongest influence
|
||||
// anchored at 0.25 there is no way around this.
|
||||
glow.rgb = clamp(glow.rgb, 0.0, 1.0);
|
||||
|
||||
color.r = color.r > 1.0 ? color.r : color.r + glow.r * ((color.r <= 0.25f ? ((16.0f * color.r - 12.0f) * color.r + 4.0f) * color.r : sqrt(color.r)) - color.r);
|
||||
color.g = color.g > 1.0 ? color.g : color.g + glow.g * ((color.g <= 0.25f ? ((16.0f * color.g - 12.0f) * color.g + 4.0f) * color.g : sqrt(color.g)) - color.g);
|
||||
color.b = color.b > 1.0 ? color.b : color.b + glow.b * ((color.b <= 0.25f ? ((16.0f * color.b - 12.0f) * color.b + 4.0f) * color.b : sqrt(color.b)) - color.b);
|
||||
|
||||
return color;
|
||||
} else { //replace
|
||||
return glow;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_1D_LUT
|
||||
vec3 apply_color_correction(vec3 color) {
|
||||
color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
|
||||
color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g;
|
||||
color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b;
|
||||
return color;
|
||||
}
|
||||
#else
|
||||
vec3 apply_color_correction(vec3 color) {
|
||||
return textureLod(source_color_correction, color, 0.0).rgb;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SUBPASS
|
||||
|
||||
// FXAA 3.11 compact, Ported from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/fxaa.frag
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Simon Rodriguez
|
||||
//
|
||||
// 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.
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Nvidia Original FXAA 3.11 License
|
||||
//----------------------------------------------------------------------------------
|
||||
// File: es3-kepler\FXAA/FXAA3_11.h
|
||||
// SDK Version: v3.00
|
||||
// Email: gameworks@nvidia.com
|
||||
// Site: http://developer.nvidia.com/
|
||||
//
|
||||
// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//----------------------------------------------------------------------------------
|
||||
//
|
||||
// NVIDIA FXAA 3.11 by TIMOTHY LOTTES
|
||||
//
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
float QUALITY(float q) {
|
||||
return (q < 5 ? 1.0 : (q > 5 ? (q < 10 ? 2.0 : (q < 11 ? 4.0 : 8.0)) : 1.5));
|
||||
}
|
||||
|
||||
float rgb2luma(vec3 rgb) {
|
||||
return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114)));
|
||||
}
|
||||
|
||||
vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
|
||||
const float EDGE_THRESHOLD_MIN = 0.0312;
|
||||
const float EDGE_THRESHOLD_MAX = 0.125;
|
||||
const int ITERATIONS = 12;
|
||||
const float SUBPIXEL_QUALITY = 0.75;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
float lumaUp = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(0, 1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaDown = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(0, -1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaLeft = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(-1, 0)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaRight = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(1, 0)).xyz * exposure * params.luminance_multiplier);
|
||||
|
||||
float lumaCenter = rgb2luma(color);
|
||||
|
||||
float lumaMin = min(lumaCenter, min(min(lumaUp, lumaDown), min(lumaLeft, lumaRight)));
|
||||
float lumaMax = max(lumaCenter, max(max(lumaUp, lumaDown), max(lumaLeft, lumaRight)));
|
||||
|
||||
float lumaRange = lumaMax - lumaMin;
|
||||
|
||||
if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX)) {
|
||||
return color;
|
||||
}
|
||||
|
||||
float lumaDownLeft = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(-1, -1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaUpRight = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(1, 1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaUpLeft = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(-1, 1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaDownRight = rgb2luma(textureLodOffset(source_color, vec3(uv_interp, ViewIndex), 0.0, ivec2(1, -1)).xyz * exposure * params.luminance_multiplier);
|
||||
|
||||
float lumaDownUp = lumaDown + lumaUp;
|
||||
float lumaLeftRight = lumaLeft + lumaRight;
|
||||
|
||||
float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
|
||||
float lumaDownCorners = lumaDownLeft + lumaDownRight;
|
||||
float lumaRightCorners = lumaDownRight + lumaUpRight;
|
||||
float lumaUpCorners = lumaUpRight + lumaUpLeft;
|
||||
|
||||
float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
|
||||
float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
|
||||
|
||||
bool isHorizontal = (edgeHorizontal >= edgeVertical);
|
||||
|
||||
float stepLength = isHorizontal ? params.src_pixel_size.y : params.src_pixel_size.x;
|
||||
|
||||
float luma1 = isHorizontal ? lumaDown : lumaLeft;
|
||||
float luma2 = isHorizontal ? lumaUp : lumaRight;
|
||||
float gradient1 = luma1 - lumaCenter;
|
||||
float gradient2 = luma2 - lumaCenter;
|
||||
|
||||
bool is1Steepest = abs(gradient1) >= abs(gradient2);
|
||||
|
||||
float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
|
||||
|
||||
float lumaLocalAverage = 0.0;
|
||||
if (is1Steepest) {
|
||||
stepLength = -stepLength;
|
||||
lumaLocalAverage = 0.5 * (luma1 + lumaCenter);
|
||||
} else {
|
||||
lumaLocalAverage = 0.5 * (luma2 + lumaCenter);
|
||||
}
|
||||
|
||||
vec2 currentUv = uv_interp;
|
||||
if (isHorizontal) {
|
||||
currentUv.y += stepLength * 0.5;
|
||||
} else {
|
||||
currentUv.x += stepLength * 0.5;
|
||||
}
|
||||
|
||||
vec2 offset = isHorizontal ? vec2(params.src_pixel_size.x, 0.0) : vec2(0.0, params.src_pixel_size.y);
|
||||
vec3 uv1 = vec3(currentUv - offset * QUALITY(0), ViewIndex);
|
||||
vec3 uv2 = vec3(currentUv + offset * QUALITY(0), ViewIndex);
|
||||
|
||||
float lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
lumaEnd1 -= lumaLocalAverage;
|
||||
lumaEnd2 -= lumaLocalAverage;
|
||||
|
||||
bool reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||
bool reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||
bool reachedBoth = reached1 && reached2;
|
||||
|
||||
if (!reached1) {
|
||||
uv1 -= vec3(offset * QUALITY(1), 0.0);
|
||||
}
|
||||
if (!reached2) {
|
||||
uv2 += vec3(offset * QUALITY(1), 0.0);
|
||||
}
|
||||
|
||||
if (!reachedBoth) {
|
||||
for (int i = 2; i < ITERATIONS; i++) {
|
||||
if (!reached1) {
|
||||
lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
lumaEnd1 = lumaEnd1 - lumaLocalAverage;
|
||||
}
|
||||
if (!reached2) {
|
||||
lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
lumaEnd2 = lumaEnd2 - lumaLocalAverage;
|
||||
}
|
||||
reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||
reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||
reachedBoth = reached1 && reached2;
|
||||
if (!reached1) {
|
||||
uv1 -= vec3(offset * QUALITY(i), 0.0);
|
||||
}
|
||||
if (!reached2) {
|
||||
uv2 += vec3(offset * QUALITY(i), 0.0);
|
||||
}
|
||||
if (reachedBoth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float distance1 = isHorizontal ? (uv_interp.x - uv1.x) : (uv_interp.y - uv1.y);
|
||||
float distance2 = isHorizontal ? (uv2.x - uv_interp.x) : (uv2.y - uv_interp.y);
|
||||
|
||||
bool isDirection1 = distance1 < distance2;
|
||||
float distanceFinal = min(distance1, distance2);
|
||||
|
||||
float edgeThickness = (distance1 + distance2);
|
||||
|
||||
bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
|
||||
|
||||
bool correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller;
|
||||
bool correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller;
|
||||
|
||||
bool correctVariation = isDirection1 ? correctVariation1 : correctVariation2;
|
||||
|
||||
float pixelOffset = -distanceFinal / edgeThickness + 0.5;
|
||||
|
||||
float finalOffset = correctVariation ? pixelOffset : 0.0;
|
||||
|
||||
float lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
|
||||
|
||||
float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
|
||||
float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
|
||||
|
||||
float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY;
|
||||
|
||||
finalOffset = max(finalOffset, subPixelOffsetFinal);
|
||||
|
||||
vec3 finalUv = vec3(uv_interp, ViewIndex);
|
||||
if (isHorizontal) {
|
||||
finalUv.y += finalOffset * stepLength;
|
||||
} else {
|
||||
finalUv.x += finalOffset * stepLength;
|
||||
}
|
||||
|
||||
vec3 finalColor = textureLod(source_color, finalUv, 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
return finalColor;
|
||||
|
||||
#else
|
||||
float lumaUp = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(0, 1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaDown = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(0, -1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaLeft = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(-1, 0)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaRight = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(1, 0)).xyz * exposure * params.luminance_multiplier);
|
||||
|
||||
float lumaCenter = rgb2luma(color);
|
||||
|
||||
float lumaMin = min(lumaCenter, min(min(lumaUp, lumaDown), min(lumaLeft, lumaRight)));
|
||||
float lumaMax = max(lumaCenter, max(max(lumaUp, lumaDown), max(lumaLeft, lumaRight)));
|
||||
|
||||
float lumaRange = lumaMax - lumaMin;
|
||||
|
||||
if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX)) {
|
||||
return color;
|
||||
}
|
||||
|
||||
float lumaDownLeft = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(-1, -1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaUpRight = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(1, 1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaUpLeft = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(-1, 1)).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaDownRight = rgb2luma(textureLodOffset(source_color, uv_interp, 0.0, ivec2(1, -1)).xyz * exposure * params.luminance_multiplier);
|
||||
|
||||
float lumaDownUp = lumaDown + lumaUp;
|
||||
float lumaLeftRight = lumaLeft + lumaRight;
|
||||
|
||||
float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
|
||||
float lumaDownCorners = lumaDownLeft + lumaDownRight;
|
||||
float lumaRightCorners = lumaDownRight + lumaUpRight;
|
||||
float lumaUpCorners = lumaUpRight + lumaUpLeft;
|
||||
|
||||
float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
|
||||
float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
|
||||
|
||||
bool isHorizontal = (edgeHorizontal >= edgeVertical);
|
||||
|
||||
float stepLength = isHorizontal ? params.src_pixel_size.y : params.src_pixel_size.x;
|
||||
|
||||
float luma1 = isHorizontal ? lumaDown : lumaLeft;
|
||||
float luma2 = isHorizontal ? lumaUp : lumaRight;
|
||||
float gradient1 = luma1 - lumaCenter;
|
||||
float gradient2 = luma2 - lumaCenter;
|
||||
|
||||
bool is1Steepest = abs(gradient1) >= abs(gradient2);
|
||||
|
||||
float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
|
||||
|
||||
float lumaLocalAverage = 0.0;
|
||||
if (is1Steepest) {
|
||||
stepLength = -stepLength;
|
||||
lumaLocalAverage = 0.5 * (luma1 + lumaCenter);
|
||||
} else {
|
||||
lumaLocalAverage = 0.5 * (luma2 + lumaCenter);
|
||||
}
|
||||
|
||||
vec2 currentUv = uv_interp;
|
||||
if (isHorizontal) {
|
||||
currentUv.y += stepLength * 0.5;
|
||||
} else {
|
||||
currentUv.x += stepLength * 0.5;
|
||||
}
|
||||
|
||||
vec2 offset = isHorizontal ? vec2(params.src_pixel_size.x, 0.0) : vec2(0.0, params.src_pixel_size.y);
|
||||
vec2 uv1 = currentUv - offset * QUALITY(0);
|
||||
vec2 uv2 = currentUv + offset * QUALITY(0);
|
||||
|
||||
float lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
float lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
lumaEnd1 -= lumaLocalAverage;
|
||||
lumaEnd2 -= lumaLocalAverage;
|
||||
|
||||
bool reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||
bool reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||
bool reachedBoth = reached1 && reached2;
|
||||
|
||||
if (!reached1) {
|
||||
uv1 -= offset * QUALITY(1);
|
||||
}
|
||||
if (!reached2) {
|
||||
uv2 += offset * QUALITY(1);
|
||||
}
|
||||
|
||||
if (!reachedBoth) {
|
||||
for (int i = 2; i < ITERATIONS; i++) {
|
||||
if (!reached1) {
|
||||
lumaEnd1 = rgb2luma(textureLod(source_color, uv1, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
lumaEnd1 = lumaEnd1 - lumaLocalAverage;
|
||||
}
|
||||
if (!reached2) {
|
||||
lumaEnd2 = rgb2luma(textureLod(source_color, uv2, 0.0).xyz * exposure * params.luminance_multiplier);
|
||||
lumaEnd2 = lumaEnd2 - lumaLocalAverage;
|
||||
}
|
||||
reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||
reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||
reachedBoth = reached1 && reached2;
|
||||
if (!reached1) {
|
||||
uv1 -= offset * QUALITY(i);
|
||||
}
|
||||
if (!reached2) {
|
||||
uv2 += offset * QUALITY(i);
|
||||
}
|
||||
if (reachedBoth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float distance1 = isHorizontal ? (uv_interp.x - uv1.x) : (uv_interp.y - uv1.y);
|
||||
float distance2 = isHorizontal ? (uv2.x - uv_interp.x) : (uv2.y - uv_interp.y);
|
||||
|
||||
bool isDirection1 = distance1 < distance2;
|
||||
float distanceFinal = min(distance1, distance2);
|
||||
|
||||
float edgeThickness = (distance1 + distance2);
|
||||
|
||||
bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
|
||||
|
||||
bool correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller;
|
||||
bool correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller;
|
||||
|
||||
bool correctVariation = isDirection1 ? correctVariation1 : correctVariation2;
|
||||
|
||||
float pixelOffset = -distanceFinal / edgeThickness + 0.5;
|
||||
|
||||
float finalOffset = correctVariation ? pixelOffset : 0.0;
|
||||
|
||||
float lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
|
||||
|
||||
float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
|
||||
float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
|
||||
|
||||
float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY;
|
||||
|
||||
finalOffset = max(finalOffset, subPixelOffsetFinal);
|
||||
|
||||
vec2 finalUv = uv_interp;
|
||||
if (isHorizontal) {
|
||||
finalUv.y += finalOffset * stepLength;
|
||||
} else {
|
||||
finalUv.x += finalOffset * stepLength;
|
||||
}
|
||||
|
||||
vec3 finalColor = textureLod(source_color, finalUv, 0.0).xyz * exposure * params.luminance_multiplier;
|
||||
return finalColor;
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif // !SUBPASS
|
||||
|
||||
// From https://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
|
||||
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
|
||||
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
|
||||
// This dithering must be applied after encoding changes (linear/nonlinear) have been applied
|
||||
// as the final step before quantization from floating point to integer values.
|
||||
vec3 screen_space_dither(vec2 frag_coord, float bit_alignment_diviser) {
|
||||
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
|
||||
// Removed the time component to avoid passing time into this shader.
|
||||
vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
|
||||
dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0));
|
||||
|
||||
// Subtract 0.5 to avoid slightly brightening the whole viewport.
|
||||
// Use a dither strength of 100% rather than the 37.5% suggested by the original source.
|
||||
return (dither.rgb - 0.5) / bit_alignment_diviser;
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef SUBPASS
|
||||
// SUBPASS and USE_MULTIVIEW can be combined but in that case we're already reading from the correct layer
|
||||
#ifdef USE_MULTIVIEW
|
||||
// In order to ensure the `SpvCapabilityMultiView` is included in the SPIR-V capabilities, gl_ViewIndex must
|
||||
// be read in the shader. Without this, transpilation to Metal fails to include the multi-view variant.
|
||||
uint vi = ViewIndex;
|
||||
#endif
|
||||
vec4 color = subpassLoad(input_color);
|
||||
#elif defined(USE_MULTIVIEW)
|
||||
vec4 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f);
|
||||
#else
|
||||
vec4 color = textureLod(source_color, uv_interp, 0.0f);
|
||||
#endif
|
||||
color.rgb *= params.luminance_multiplier;
|
||||
|
||||
// Exposure
|
||||
|
||||
color.rgb *= params.exposure;
|
||||
|
||||
// Early Tonemap & SRGB Conversion
|
||||
#ifndef SUBPASS
|
||||
if (use_fxaa) {
|
||||
// FXAA must be performed before glow to preserve the "bleed" effect of glow.
|
||||
color.rgb = do_fxaa(color.rgb, params.exposure, uv_interp);
|
||||
}
|
||||
|
||||
if (use_glow && !glow_mode_softlight) {
|
||||
vec3 glow = gather_glow() * params.luminance_multiplier * params.glow_intensity;
|
||||
if (use_glow_map) {
|
||||
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
|
||||
}
|
||||
|
||||
if (glow_mode_mix) {
|
||||
color.rgb = color.rgb * (1.0 - params.glow_intensity) + glow;
|
||||
} else {
|
||||
color.rgb = apply_glow(color.rgb, glow, params.white);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
color.rgb = apply_tonemapping(color.rgb, params.white);
|
||||
|
||||
#ifndef SUBPASS
|
||||
// Glow
|
||||
if (use_glow && glow_mode_softlight) {
|
||||
// Apply soft light after tonemapping to mitigate the issue of discontinuity
|
||||
// at 1.0 and higher. This makes the issue only appear with HDR output that
|
||||
// can exceed a 1.0 output value.
|
||||
vec3 glow = gather_glow() * params.glow_intensity * params.luminance_multiplier;
|
||||
if (use_glow_map) {
|
||||
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
|
||||
}
|
||||
|
||||
glow = apply_tonemapping(glow, params.white);
|
||||
color.rgb = apply_glow(color.rgb, glow, params.white);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Additional effects
|
||||
|
||||
if (use_bcs) {
|
||||
// Apply brightness:
|
||||
// Apply to relative luminance. This ensures that the hue and saturation of
|
||||
// colors is not affected by the adjustment, but requires the multiplication
|
||||
// to be performed on linear-encoded values.
|
||||
color.rgb = color.rgb * params.bcs.x;
|
||||
|
||||
color.rgb = linear_to_srgb(color.rgb);
|
||||
|
||||
// Apply contrast:
|
||||
// By applying contrast to RGB values that are perceptually uniform (nonlinear),
|
||||
// the darkest values are not hard-clipped as badly, which produces a
|
||||
// higher quality contrast adjustment and maintains compatibility with
|
||||
// existing projects.
|
||||
color.rgb = mix(vec3(0.5), color.rgb, params.bcs.y);
|
||||
|
||||
// Apply saturation:
|
||||
// By applying saturation adjustment to nonlinear sRGB-encoded values with
|
||||
// even weights the preceived brightness of blues are affected, but this
|
||||
// maintains compatibility with existing projects.
|
||||
color.rgb = mix(vec3(dot(vec3(1.0), color.rgb) * (1.0 / 3.0)), color.rgb, params.bcs.z);
|
||||
|
||||
if (use_color_correction) {
|
||||
color.rgb = clamp(color.rgb, vec3(0.0), vec3(1.0));
|
||||
color.rgb = apply_color_correction(color.rgb);
|
||||
// When using color correction and convert_to_srgb is false, there
|
||||
// is no need to convert back to linear because the color correction
|
||||
// texture sampling does this for us.
|
||||
} else if (!convert_to_srgb) {
|
||||
color.rgb = srgb_to_linear(color.rgb);
|
||||
}
|
||||
} else if (convert_to_srgb) {
|
||||
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
|
||||
}
|
||||
|
||||
// Debanding should be done at the end of tonemapping, but before writing to the LDR buffer.
|
||||
// Otherwise, we're adding noise to an already-quantized image.
|
||||
if (deband_8_bit) {
|
||||
// Divide by 255 to align to 8-bit quantization.
|
||||
color.rgb += screen_space_dither(gl_FragCoord.xy, 255.0);
|
||||
} else if (deband_10_bit) {
|
||||
// Divide by 1023 to align to 10-bit quantization.
|
||||
color.rgb += screen_space_dither(gl_FragCoord.xy, 1023.0);
|
||||
}
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
@@ -530,11 +530,6 @@ void RenderSceneBuffersRD::allocate_blur_textures() {
|
||||
create_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, get_base_data_format(), usage_bits, RD::TEXTURE_SAMPLES_1, blur_size, view_count, mipmaps_required);
|
||||
create_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, get_base_data_format(), usage_bits, RD::TEXTURE_SAMPLES_1, Size2i(blur_size.x >> 1, blur_size.y >> 1), view_count, mipmaps_required - 1);
|
||||
|
||||
// if !can_be_storage we need a half width version
|
||||
if (!can_be_storage) {
|
||||
create_texture(RB_SCOPE_BUFFERS, RB_TEX_HALF_BLUR, get_base_data_format(), usage_bits, RD::TEXTURE_SAMPLES_1, Size2i(blur_size.x >> 1, blur_size.y), 1, mipmaps_required);
|
||||
}
|
||||
|
||||
// TODO redo this:
|
||||
if (!can_be_storage) {
|
||||
// create 4 weight textures, 2 full size, 2 half size
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
|
||||
#define RB_TEX_BLUR_0 SNAME("blur_0")
|
||||
#define RB_TEX_BLUR_1 SNAME("blur_1")
|
||||
#define RB_TEX_HALF_BLUR SNAME("half_blur") // only for raster!
|
||||
|
||||
#define RB_TEX_BACK_COLOR SNAME("back_color")
|
||||
#define RB_TEX_BACK_DEPTH SNAME("back_depth")
|
||||
|
||||
@@ -507,7 +507,7 @@ RS::EnvironmentGlowBlendMode RendererEnvironmentStorage::environment_get_glow_bl
|
||||
|
||||
float RendererEnvironmentStorage::environment_get_glow_hdr_bleed_threshold(RID p_env) const {
|
||||
Environment *env = environment_owner.get_or_null(p_env);
|
||||
ERR_FAIL_NULL_V(env, 1.0);
|
||||
ERR_FAIL_NULL_V(env, 0.0);
|
||||
return env->glow_hdr_bleed_threshold;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
float glow_bloom = 0.0;
|
||||
float glow_mix = 0.01;
|
||||
RS::EnvironmentGlowBlendMode glow_blend_mode = RS::ENV_GLOW_BLEND_MODE_SCREEN;
|
||||
float glow_hdr_bleed_threshold = 1.0;
|
||||
float glow_hdr_bleed_threshold = 0.0;
|
||||
float glow_hdr_luminance_cap = 12.0;
|
||||
float glow_hdr_bleed_scale = 2.0;
|
||||
float glow_map_strength = 0.0f; // 1.0f in GLES3 ??
|
||||
|
||||
Reference in New Issue
Block a user