summaryrefslogtreecommitdiff
path: root/servers/rendering/renderer_rd/shaders/effects
diff options
context:
space:
mode:
Diffstat (limited to 'servers/rendering/renderer_rd/shaders/effects')
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl148
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl26
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl215
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl37
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl253
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/copy.glsl284
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl167
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/tonemap.glsl72
8 files changed, 1188 insertions, 14 deletions
diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl
new file mode 100644
index 0000000000..96f5c3e9f2
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl
@@ -0,0 +1,148 @@
+/* clang-format off */
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "blur_raster_inc.glsl"
+
+layout(location = 0) out vec2 uv_interp;
+/* clang-format on */
+
+void main() {
+ vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+ uv_interp = base_arr[gl_VertexIndex];
+
+ gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "blur_raster_inc.glsl"
+
+layout(location = 0) in vec2 uv_interp;
+/* clang-format on */
+
+layout(set = 0, binding = 0) uniform sampler2D source_color;
+
+#ifdef GLOW_USE_AUTO_EXPOSURE
+layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
+#endif
+
+layout(location = 0) out vec4 frag_color;
+
+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;
+ 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);
+ color += texture(source_color, uv_interp + vec2(-0.5, 0.5) * pix_size);
+ frag_color = color / 4.0;
+
+#endif
+
+#ifdef MODE_GAUSSIAN_BLUR
+
+ // Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect
+
+ // note, for blur blur.luminance_multiplier is irrelavant, we would be multiplying and then dividing by this amount.
+
+ 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.214607;
+ color += texture(source_color, uv_interp + vec2(1.0, 0.0) * pix_size) * 0.189879;
+ color += texture(source_color, uv_interp + vec2(2.0, 0.0) * pix_size) * 0.131514;
+ color += texture(source_color, uv_interp + vec2(3.0, 0.0) * pix_size) * 0.071303;
+ color += texture(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size) * 0.189879;
+ color += texture(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size) * 0.131514;
+ color += texture(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size) * 0.071303;
+ 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.38774;
+ color += texture(source_color, uv_interp + vec2(0.0, 1.0) * pix_size) * 0.24477;
+ color += texture(source_color, uv_interp + vec2(0.0, 2.0) * pix_size) * 0.06136;
+ color += texture(source_color, uv_interp + vec2(0.0, -1.0) * pix_size) * 0.24477;
+ color += texture(source_color, uv_interp + vec2(0.0, -2.0) * pix_size) * 0.06136;
+ frag_color = color;
+ }
+#endif
+
+#ifdef MODE_GAUSSIAN_GLOW
+
+ //Glow uses larger sigma 1 for a more rounded blur effect
+
+#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; \
+ }
+
+ 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;
+ }
+
+#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_grey;
+#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
+
+#ifdef MODE_COPY
+ vec4 color = textureLod(source_color, uv_interp, 0.0);
+ frag_color = color;
+#endif
+}
diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl
new file mode 100644
index 0000000000..730504571a
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl
@@ -0,0 +1,26 @@
+#define FLAG_HORIZONTAL (1 << 0)
+#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 1)
+#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
+
+ // 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_exposure; // 04 - 36
+ float glow_white; // 04 - 40
+ float glow_luminance_cap; // 04 - 44
+ float glow_auto_exposure_grey; // 04 - 48
+
+ float luminance_multiplier; // 04 - 52
+ float res1; // 04 - 56
+ float res2; // 04 - 60
+ float res3; // 04 - 64
+}
+blur;
diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl
new file mode 100644
index 0000000000..0438671dd2
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl
@@ -0,0 +1,215 @@
+#[compute]
+
+#version 450
+
+#VERSION_DEFINES
+
+#define BLOCK_SIZE 8
+
+layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
+
+#ifdef MODE_GEN_BLUR_SIZE
+layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
+layout(set = 1, binding = 0) uniform sampler2D source_depth;
+#endif
+
+#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL) || defined(MODE_BOKEH_CIRCULAR)
+layout(set = 1, binding = 0) uniform sampler2D color_texture;
+layout(rgba16f, set = 0, binding = 0) uniform restrict writeonly image2D bokeh_image;
+#endif
+
+#ifdef MODE_COMPOSITE_BOKEH
+layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
+layout(set = 1, binding = 0) uniform sampler2D source_bokeh;
+#endif
+
+// based on https://www.shadertoy.com/view/Xd3GDl
+
+#include "bokeh_dof_inc.glsl"
+
+#ifdef MODE_GEN_BLUR_SIZE
+
+float get_depth_at_pos(vec2 uv) {
+ float depth = textureLod(source_depth, uv, 0.0).x;
+ if (params.orthogonal) {
+ depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+ } else {
+ depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+ }
+ return depth;
+}
+
+float get_blur_size(float depth) {
+ if (params.blur_near_active && depth < params.blur_near_begin) {
+ return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative
+ }
+
+ if (params.blur_far_active && depth > params.blur_far_begin) {
+ return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
+ }
+
+ return 0.0;
+}
+
+#endif
+
+#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
+
+vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
+ dir *= pixel_size;
+ vec4 color = texture(color_texture, uv);
+
+ vec4 accum = color;
+ float total = 1.0;
+
+ float blur_scale = params.blur_size / float(params.blur_steps);
+
+ if (params.use_jitter) {
+ uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
+ }
+
+ for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
+ if (i == 0) {
+ continue;
+ }
+ float radius = float(i) * blur_scale;
+ vec2 suv = uv + dir * radius;
+ radius = abs(radius);
+
+ vec4 sample_color = texture(color_texture, suv);
+ float limit;
+
+ if (sample_color.a < color.a) {
+ limit = abs(sample_color.a);
+ } else {
+ limit = abs(color.a);
+ }
+
+ limit -= DEPTH_GAP;
+
+ float m = smoothstep(radius - 0.5, radius + 0.5, limit);
+
+ accum += mix(color, sample_color, m);
+
+ total += 1.0;
+ }
+
+ return accum / total;
+}
+
+#endif
+
+void main() {
+ ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+
+ if (any(greaterThan(pos, params.size))) { //too large, do nothing
+ return;
+ }
+
+ vec2 pixel_size = 1.0 / vec2(params.size);
+ vec2 uv = vec2(pos) / vec2(params.size);
+
+#ifdef MODE_GEN_BLUR_SIZE
+ uv += pixel_size * 0.5;
+ //precompute size in alpha channel
+ float depth = get_depth_at_pos(uv);
+ float size = get_blur_size(depth);
+
+ vec4 color = imageLoad(color_image, pos);
+ color.a = size;
+ imageStore(color_image, pos, color);
+#endif
+
+#ifdef MODE_BOKEH_BOX
+
+ //pixel_size*=0.5; //resolution is doubled
+ if (params.second_pass || !params.half_size) {
+ uv += pixel_size * 0.5; //half pixel to read centers
+ } else {
+ uv += pixel_size * 0.25; //half pixel to read centers from full res
+ }
+
+ vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
+
+ vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+ imageStore(bokeh_image, pos, color);
+
+#endif
+
+#ifdef MODE_BOKEH_HEXAGONAL
+
+ //pixel_size*=0.5; //resolution is doubled
+ if (params.second_pass || !params.half_size) {
+ uv += pixel_size * 0.5; //half pixel to read centers
+ } else {
+ uv += pixel_size * 0.25; //half pixel to read centers from full res
+ }
+
+ vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
+
+ vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+ if (params.second_pass) {
+ dir = normalize(vec2(-1.0, 0.577350269189626));
+
+ vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
+
+ color.rgb = min(color.rgb, color2.rgb);
+ color.a = (color.a + color2.a) * 0.5;
+ }
+
+ imageStore(bokeh_image, pos, color);
+
+#endif
+
+#ifdef MODE_BOKEH_CIRCULAR
+
+ if (params.half_size) {
+ pixel_size *= 0.5; //resolution is doubled
+ }
+
+ uv += pixel_size * 0.5; //half pixel to read centers
+
+ vec4 color = texture(color_texture, uv);
+ float accum = 1.0;
+ float radius = params.blur_scale;
+
+ for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
+ vec2 suv = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
+ vec4 sample_color = texture(color_texture, suv);
+ float sample_size = abs(sample_color.a);
+ if (sample_color.a > color.a) {
+ sample_size = clamp(sample_size, 0.0, abs(color.a) * 2.0);
+ }
+
+ float m = smoothstep(radius - 0.5, radius + 0.5, sample_size);
+ color += mix(color / accum, sample_color, m);
+ accum += 1.0;
+ radius += params.blur_scale / radius;
+ }
+
+ color /= accum;
+
+ imageStore(bokeh_image, pos, color);
+#endif
+
+#ifdef MODE_COMPOSITE_BOKEH
+
+ uv += pixel_size * 0.5;
+ vec4 color = imageLoad(color_image, pos);
+ vec4 bokeh = texture(source_bokeh, uv);
+
+ float mix_amount;
+ if (bokeh.a < color.a) {
+ mix_amount = min(1.0, max(0.0, max(abs(color.a), abs(bokeh.a)) - DEPTH_GAP));
+ } else {
+ mix_amount = min(1.0, max(0.0, abs(color.a) - DEPTH_GAP));
+ }
+
+ color.rgb = mix(color.rgb, bokeh.rgb, mix_amount); //blend between hires and lowres
+
+ color.a = 0; //reset alpha
+ imageStore(color_image, pos, color);
+#endif
+}
diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl
new file mode 100644
index 0000000000..b90a527554
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl
@@ -0,0 +1,37 @@
+layout(push_constant, std430) uniform Params {
+ ivec2 size;
+ float z_far;
+ float z_near;
+
+ bool orthogonal;
+ float blur_size;
+ float blur_scale;
+ int blur_steps;
+
+ bool blur_near_active;
+ float blur_near_begin;
+ float blur_near_end;
+ bool blur_far_active;
+
+ float blur_far_begin;
+ float blur_far_end;
+ bool second_pass;
+ bool half_size;
+
+ bool use_jitter;
+ float jitter_seed;
+ uint pad[2];
+}
+params;
+
+//used to work around downsampling filter
+#define DEPTH_GAP 0.0
+
+const float GOLDEN_ANGLE = 2.39996323;
+
+//note: uniform pdf rand [0;1[
+float hash12n(vec2 p) {
+ p = fract(p * vec2(5.3987, 5.4421));
+ p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
+ return fract(p.x * p.y * 95.4307);
+}
diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
new file mode 100644
index 0000000000..a3b3938ee9
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
@@ -0,0 +1,253 @@
+/* clang-format off */
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "bokeh_dof_inc.glsl"
+
+layout(location = 0) out vec2 uv_interp;
+/* clang-format on */
+
+void main() {
+ vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+ uv_interp = base_arr[gl_VertexIndex];
+
+ gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+#include "bokeh_dof_inc.glsl"
+
+layout(location = 0) in vec2 uv_interp;
+/* clang-format on */
+
+#ifdef MODE_GEN_BLUR_SIZE
+layout(location = 0) out float weight;
+
+layout(set = 0, binding = 0) uniform sampler2D source_depth;
+#else
+layout(location = 0) out vec4 frag_color;
+#ifdef OUTPUT_WEIGHT
+layout(location = 1) out float weight;
+#endif
+
+layout(set = 0, binding = 0) uniform sampler2D source_color;
+layout(set = 1, binding = 0) uniform sampler2D source_weight;
+#ifdef MODE_COMPOSITE_BOKEH
+layout(set = 2, binding = 0) uniform sampler2D original_weight;
+#endif
+#endif
+
+//DOF
+// Bokeh single pass implementation based on https://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html
+
+#ifdef MODE_GEN_BLUR_SIZE
+
+float get_depth_at_pos(vec2 uv) {
+ float depth = textureLod(source_depth, uv, 0.0).x;
+ if (params.orthogonal) {
+ depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+ } else {
+ depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+ }
+ return depth;
+}
+
+float get_blur_size(float depth) {
+ if (params.blur_near_active && depth < params.blur_near_begin) {
+ return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative
+ }
+
+ if (params.blur_far_active && depth > params.blur_far_begin) {
+ return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
+ }
+
+ return 0.0;
+}
+
+#endif
+
+#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
+
+vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
+ dir *= pixel_size;
+ vec4 color = texture(source_color, uv);
+ color.a = texture(source_weight, uv).r;
+
+ vec4 accum = color;
+ float total = 1.0;
+
+ float blur_scale = params.blur_size / float(params.blur_steps);
+
+ if (params.use_jitter) {
+ uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
+ }
+
+ for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
+ if (i == 0) {
+ continue;
+ }
+ float radius = float(i) * blur_scale;
+ vec2 suv = uv + dir * radius;
+ radius = abs(radius);
+
+ vec4 sample_color = texture(source_color, suv);
+ sample_color.a = texture(source_weight, suv).r;
+ float limit;
+
+ if (sample_color.a < color.a) {
+ limit = abs(sample_color.a);
+ } else {
+ limit = abs(color.a);
+ }
+
+ limit -= DEPTH_GAP;
+
+ float m = smoothstep(radius - 0.5, radius + 0.5, limit);
+
+ accum += mix(color, sample_color, m);
+
+ total += 1.0;
+ }
+
+ return accum / total;
+}
+
+#endif
+
+void main() {
+ vec2 pixel_size = 1.0 / vec2(params.size);
+ vec2 uv = uv_interp;
+
+#ifdef MODE_GEN_BLUR_SIZE
+ uv += pixel_size * 0.5;
+ float center_depth = get_depth_at_pos(uv);
+ weight = get_blur_size(center_depth);
+#endif
+
+#ifdef MODE_BOKEH_BOX
+ //pixel_size*=0.5; //resolution is doubled
+ if (params.second_pass || !params.half_size) {
+ uv += pixel_size * 0.5; //half pixel to read centers
+ } else {
+ uv += pixel_size * 0.25; //half pixel to read centers from full res
+ }
+
+ float alpha = texture(source_color, uv).a; // retain this
+ vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
+
+ vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+ frag_color = color;
+ frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+ weight = color.a;
+#endif
+
+#endif
+
+#ifdef MODE_BOKEH_HEXAGONAL
+
+ //pixel_size*=0.5; //resolution is doubled
+ if (params.second_pass || !params.half_size) {
+ uv += pixel_size * 0.5; //half pixel to read centers
+ } else {
+ uv += pixel_size * 0.25; //half pixel to read centers from full res
+ }
+
+ float alpha = texture(source_color, uv).a; // retain this
+
+ vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
+
+ vec4 color = weighted_filter_dir(dir, uv, pixel_size);
+
+ if (params.second_pass) {
+ dir = normalize(vec2(-1.0, 0.577350269189626));
+
+ vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
+
+ color.rgb = min(color.rgb, color2.rgb);
+ color.a = (color.a + color2.a) * 0.5;
+ }
+
+ frag_color = color;
+ frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+ weight = color.a;
+#endif
+
+#endif
+
+#ifdef MODE_BOKEH_CIRCULAR
+ if (params.half_size) {
+ pixel_size *= 0.5; //resolution is doubled
+ }
+
+ uv += pixel_size * 0.5; //half pixel to read centers
+
+ vec4 color = texture(source_color, uv);
+ float alpha = color.a; // retain this
+ color.a = texture(source_weight, uv).r;
+
+ vec4 color_accum = color;
+ float accum = 1.0;
+
+ float radius = params.blur_scale;
+ for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
+ vec2 uv_adj = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
+
+ vec4 sample_color = texture(source_color, uv_adj);
+ sample_color.a = texture(source_weight, uv_adj).r;
+
+ float limit;
+
+ if (sample_color.a < color.a) {
+ limit = abs(sample_color.a);
+ } else {
+ limit = abs(color.a);
+ }
+
+ limit -= DEPTH_GAP;
+
+ float m = smoothstep(radius - 0.5, radius + 0.5, limit);
+ color_accum += mix(color_accum / accum, sample_color, m);
+ accum += 1.0;
+
+ radius += params.blur_scale / radius;
+ }
+
+ color_accum = color_accum / accum;
+
+ frag_color.rgb = color_accum.rgb;
+ frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
+#ifdef OUTPUT_WEIGHT
+ weight = color_accum.a;
+#endif
+
+#endif
+
+#ifdef MODE_COMPOSITE_BOKEH
+ frag_color.rgb = texture(source_color, uv).rgb;
+
+ float center_weigth = texture(source_weight, uv).r;
+ float sample_weight = texture(original_weight, uv).r;
+
+ float mix_amount;
+ if (sample_weight < center_weigth) {
+ mix_amount = min(1.0, max(0.0, max(abs(center_weigth), abs(sample_weight)) - DEPTH_GAP));
+ } else {
+ mix_amount = min(1.0, max(0.0, abs(center_weigth) - DEPTH_GAP));
+ }
+
+ // let alpha blending take care of mixing
+ frag_color.a = mix_amount;
+#endif
+}
diff --git a/servers/rendering/renderer_rd/shaders/effects/copy.glsl b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
new file mode 100644
index 0000000000..3a4ef86ef0
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
@@ -0,0 +1,284 @@
+#[compute]
+
+#version 450
+
+#VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#define FLAG_HORIZONTAL (1 << 0)
+#define FLAG_USE_BLUR_SECTION (1 << 1)
+#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 2)
+#define FLAG_DOF_NEAR_FIRST_TAP (1 << 3)
+#define FLAG_GLOW_FIRST_PASS (1 << 4)
+#define FLAG_FLIP_Y (1 << 5)
+#define FLAG_FORCE_LUMINANCE (1 << 6)
+#define FLAG_COPY_ALL_SOURCE (1 << 7)
+#define FLAG_HIGH_QUALITY_GLOW (1 << 8)
+#define FLAG_ALPHA_TO_ONE (1 << 9)
+
+layout(push_constant, std430) uniform Params {
+ ivec4 section;
+ ivec2 target;
+ uint flags;
+ uint pad;
+ // Glow.
+ float glow_strength;
+ float glow_bloom;
+ float glow_hdr_threshold;
+ float glow_hdr_scale;
+
+ float glow_exposure;
+ float glow_white;
+ float glow_luminance_cap;
+ float glow_auto_exposure_grey;
+ // DOF.
+ float camera_z_far;
+ float camera_z_near;
+ uint pad2[2];
+
+ vec4 set_color;
+}
+params;
+
+#ifdef MODE_CUBEMAP_ARRAY_TO_PANORAMA
+layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
+#elif defined(MODE_CUBEMAP_TO_PANORAMA)
+layout(set = 0, binding = 0) uniform samplerCube source_color;
+#elif !defined(MODE_SET_COLOR)
+layout(set = 0, binding = 0) uniform sampler2D source_color;
+#endif
+
+#ifdef GLOW_USE_AUTO_EXPOSURE
+layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
+#endif
+
+#if defined(MODE_LINEARIZE_DEPTH_COPY) || defined(MODE_SIMPLE_COPY_DEPTH)
+layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
+#elif defined(DST_IMAGE_8BIT)
+layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
+#else
+layout(rgba32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
+#endif
+
+#ifdef MODE_GAUSSIAN_BLUR
+shared vec4 local_cache[256];
+shared vec4 temp_cache[128];
+#endif
+
+void main() {
+ // Pixel being shaded
+ ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+
+#ifndef MODE_GAUSSIAN_BLUR // Gaussian blur needs the extra threads
+ if (any(greaterThanEqual(pos, params.section.zw))) { //too large, do nothing
+ return;
+ }
+#endif
+
+#ifdef MODE_MIPMAP
+
+ ivec2 base_pos = (pos + params.section.xy) << 1;
+ vec4 color = texelFetch(source_color, base_pos, 0);
+ color += texelFetch(source_color, base_pos + ivec2(0, 1), 0);
+ color += texelFetch(source_color, base_pos + ivec2(1, 0), 0);
+ color += texelFetch(source_color, base_pos + ivec2(1, 1), 0);
+ color /= 4.0;
+ color = mix(color, vec4(100.0, 100.0, 100.0, 1.0), isinf(color));
+ color = mix(color, vec4(100.0, 100.0, 100.0, 1.0), isnan(color));
+
+ imageStore(dest_buffer, pos + params.target, color);
+#endif
+
+#ifdef MODE_GAUSSIAN_BLUR
+
+ // First pass copy texture into 16x16 local memory for every 8x8 thread block
+ vec2 quad_center_uv = clamp(vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.5) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
+ uint dest_index = gl_LocalInvocationID.x * 2 + gl_LocalInvocationID.y * 2 * 16;
+
+#ifdef MODE_GLOW
+ if (bool(params.flags & FLAG_HIGH_QUALITY_GLOW)) {
+ vec2 quad_offset_uv = clamp((vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.0)) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
+
+ local_cache[dest_index] = (textureLod(source_color, quad_center_uv, 0) + textureLod(source_color, quad_offset_uv, 0)) * 0.5;
+ local_cache[dest_index + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.z, 0.0), 0)) * 0.5;
+ local_cache[dest_index + 16] = (textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0) + textureLod(source_color, quad_offset_uv + vec2(0.0, 1.0 / params.section.w), 0)) * 0.5;
+ local_cache[dest_index + 16 + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.zw), 0)) * 0.5;
+ } else
+#endif
+ {
+ local_cache[dest_index] = textureLod(source_color, quad_center_uv, 0);
+ local_cache[dest_index + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0);
+ local_cache[dest_index + 16] = textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0);
+ local_cache[dest_index + 16 + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0);
+ }
+#ifdef MODE_GLOW
+ if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
+ // Tonemap initial samples to reduce weight of fireflies: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
+ local_cache[dest_index] /= 1.0 + dot(local_cache[dest_index].rgb, vec3(0.299, 0.587, 0.114));
+ local_cache[dest_index + 1] /= 1.0 + dot(local_cache[dest_index + 1].rgb, vec3(0.299, 0.587, 0.114));
+ local_cache[dest_index + 16] /= 1.0 + dot(local_cache[dest_index + 16].rgb, vec3(0.299, 0.587, 0.114));
+ local_cache[dest_index + 16 + 1] /= 1.0 + dot(local_cache[dest_index + 16 + 1].rgb, vec3(0.299, 0.587, 0.114));
+ }
+ const float kernel[4] = { 0.174938, 0.165569, 0.140367, 0.106595 };
+#else
+ // Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect.
+ const float kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 };
+#endif
+ memoryBarrierShared();
+ barrier();
+
+ // Horizontal pass. Needs to copy into 8x16 chunk of local memory so vertical pass has full resolution
+ uint read_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * 32 + 4;
+ vec4 color_top = vec4(0.0);
+ color_top += local_cache[read_index] * kernel[0];
+ color_top += local_cache[read_index + 1] * kernel[1];
+ color_top += local_cache[read_index + 2] * kernel[2];
+ color_top += local_cache[read_index + 3] * kernel[3];
+ color_top += local_cache[read_index - 1] * kernel[1];
+ color_top += local_cache[read_index - 2] * kernel[2];
+ color_top += local_cache[read_index - 3] * kernel[3];
+
+ vec4 color_bottom = vec4(0.0);
+ color_bottom += local_cache[read_index + 16] * kernel[0];
+ color_bottom += local_cache[read_index + 1 + 16] * kernel[1];
+ color_bottom += local_cache[read_index + 2 + 16] * kernel[2];
+ color_bottom += local_cache[read_index + 3 + 16] * kernel[3];
+ color_bottom += local_cache[read_index - 1 + 16] * kernel[1];
+ color_bottom += local_cache[read_index - 2 + 16] * kernel[2];
+ color_bottom += local_cache[read_index - 3 + 16] * kernel[3];
+
+ // rotate samples to take advantage of cache coherency
+ uint write_index = gl_LocalInvocationID.y * 2 + gl_LocalInvocationID.x * 16;
+
+ temp_cache[write_index] = color_top;
+ temp_cache[write_index + 1] = color_bottom;
+
+ memoryBarrierShared();
+ barrier();
+
+ // If destination outside of texture, can stop doing work now
+ if (any(greaterThanEqual(pos, params.section.zw))) {
+ return;
+ }
+
+ // Vertical pass
+ uint index = gl_LocalInvocationID.y + gl_LocalInvocationID.x * 16 + 4;
+ vec4 color = vec4(0.0);
+
+ color += temp_cache[index] * kernel[0];
+ color += temp_cache[index + 1] * kernel[1];
+ color += temp_cache[index + 2] * kernel[2];
+ color += temp_cache[index + 3] * kernel[3];
+ color += temp_cache[index - 1] * kernel[1];
+ color += temp_cache[index - 2] * kernel[2];
+ color += temp_cache[index - 3] * kernel[3];
+
+#ifdef MODE_GLOW
+ if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
+ // Undo tonemap to restore range: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
+ color /= 1.0 - dot(color.rgb, vec3(0.299, 0.587, 0.114));
+ }
+
+ color *= params.glow_strength;
+
+ if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
+#ifdef GLOW_USE_AUTO_EXPOSURE
+
+ color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.glow_auto_exposure_grey;
+#endif
+ color *= params.glow_exposure;
+
+ float luminance = max(color.r, max(color.g, color.b));
+ float feedback = max(smoothstep(params.glow_hdr_threshold, params.glow_hdr_threshold + params.glow_hdr_scale, luminance), params.glow_bloom);
+
+ color = min(color * feedback, vec4(params.glow_luminance_cap));
+ }
+#endif
+ imageStore(dest_buffer, pos + params.target, color);
+
+#endif
+
+#ifdef MODE_SIMPLE_COPY
+
+ vec4 color;
+ if (bool(params.flags & FLAG_COPY_ALL_SOURCE)) {
+ vec2 uv = vec2(pos) / vec2(params.section.zw);
+ if (bool(params.flags & FLAG_FLIP_Y)) {
+ uv.y = 1.0 - uv.y;
+ }
+ color = textureLod(source_color, uv, 0.0);
+
+ } else {
+ color = texelFetch(source_color, pos + params.section.xy, 0);
+
+ if (bool(params.flags & FLAG_FLIP_Y)) {
+ pos.y = params.section.w - pos.y - 1;
+ }
+ }
+
+ if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {
+ color.rgb = vec3(max(max(color.r, color.g), color.b));
+ }
+
+ if (bool(params.flags & FLAG_ALPHA_TO_ONE)) {
+ color.a = 1.0;
+ }
+
+ imageStore(dest_buffer, pos + params.target, color);
+
+#endif
+
+#ifdef MODE_SIMPLE_COPY_DEPTH
+
+ vec4 color = texelFetch(source_color, pos + params.section.xy, 0);
+
+ if (bool(params.flags & FLAG_FLIP_Y)) {
+ pos.y = params.section.w - pos.y - 1;
+ }
+
+ imageStore(dest_buffer, pos + params.target, vec4(color.r));
+
+#endif
+
+#ifdef MODE_LINEARIZE_DEPTH_COPY
+
+ float depth = texelFetch(source_color, pos + params.section.xy, 0).r;
+ depth = depth * 2.0 - 1.0;
+ depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
+ vec4 color = vec4(depth / params.camera_z_far);
+
+ if (bool(params.flags & FLAG_FLIP_Y)) {
+ pos.y = params.section.w - pos.y - 1;
+ }
+
+ imageStore(dest_buffer, pos + params.target, color);
+#endif
+
+#if defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
+
+ const float PI = 3.14159265359;
+ vec2 uv = vec2(pos) / vec2(params.section.zw);
+ if (bool(params.flags & FLAG_FLIP_Y)) {
+ uv.y = 1.0 - uv.y;
+ }
+ float phi = uv.x * 2.0 * PI;
+ float theta = uv.y * PI;
+
+ vec3 normal;
+ normal.x = sin(phi) * sin(theta) * -1.0;
+ normal.y = cos(theta);
+ normal.z = cos(phi) * sin(theta) * -1.0;
+
+#ifdef MODE_CUBEMAP_TO_PANORAMA
+ vec4 color = textureLod(source_color, normal, params.camera_z_far); //the biggest the lod the least the acne
+#else
+ vec4 color = textureLod(source_color, vec4(normal, params.camera_z_far), 0.0); //the biggest the lod the least the acne
+#endif
+ imageStore(dest_buffer, pos + params.target, color);
+#endif
+
+#ifdef MODE_SET_COLOR
+ imageStore(dest_buffer, pos + params.target, params.set_color);
+#endif
+}
diff --git a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl
new file mode 100644
index 0000000000..9787c9879d
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl
@@ -0,0 +1,167 @@
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+#ifdef MULTIVIEW
+#ifdef has_VK_KHR_multiview
+#extension GL_EXT_multiview : enable
+#define ViewIndex gl_ViewIndex
+#else // has_VK_KHR_multiview
+#define ViewIndex 0
+#endif // has_VK_KHR_multiview
+#endif //MULTIVIEW
+
+#ifdef MULTIVIEW
+layout(location = 0) out vec3 uv_interp;
+#else
+layout(location = 0) out vec2 uv_interp;
+#endif
+
+layout(push_constant, std430) uniform Params {
+ vec4 section;
+ vec2 pixel_size;
+ bool flip_y;
+ bool use_section;
+
+ bool force_luminance;
+ uint pad[3];
+}
+params;
+
+void main() {
+ vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+ uv_interp.xy = base_arr[gl_VertexIndex];
+#ifdef MULTIVIEW
+ uv_interp.z = ViewIndex;
+#endif
+ vec2 vpos = uv_interp.xy;
+ if (params.use_section) {
+ vpos = params.section.xy + vpos * params.section.zw;
+ }
+
+ gl_Position = vec4(vpos * 2.0 - 1.0, 0.0, 1.0);
+
+ if (params.flip_y) {
+ uv_interp.y = 1.0 - uv_interp.y;
+ }
+}
+
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+#ifdef MULTIVIEW
+#ifdef has_VK_KHR_multiview
+#extension GL_EXT_multiview : enable
+#define ViewIndex gl_ViewIndex
+#else // has_VK_KHR_multiview
+#define ViewIndex 0
+#endif // has_VK_KHR_multiview
+#endif //MULTIVIEW
+
+layout(push_constant, std430) uniform Params {
+ vec4 section;
+ vec2 pixel_size;
+ bool flip_y;
+ bool use_section;
+
+ bool force_luminance;
+ bool alpha_to_zero;
+ bool srgb;
+ uint pad;
+}
+params;
+
+#ifdef MULTIVIEW
+layout(location = 0) in vec3 uv_interp;
+#else
+layout(location = 0) in vec2 uv_interp;
+#endif
+
+#ifdef MULTIVIEW
+layout(set = 0, binding = 0) uniform sampler2DArray source_color;
+#ifdef MODE_TWO_SOURCES
+layout(set = 1, binding = 0) uniform sampler2DArray source_depth;
+layout(location = 1) out float depth;
+#endif /* MODE_TWO_SOURCES */
+#else
+layout(set = 0, binding = 0) uniform sampler2D source_color;
+#ifdef MODE_TWO_SOURCES
+layout(set = 1, binding = 0) uniform sampler2D source_color2;
+#endif /* MODE_TWO_SOURCES */
+#endif /* MULTIVIEW */
+
+layout(location = 0) out vec4 frag_color;
+
+vec3 linear_to_srgb(vec3 color) {
+ //if going to srgb, clamp from 0 to 1.
+ 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)));
+}
+
+void main() {
+#ifdef MULTIVIEW
+ vec3 uv = uv_interp;
+#else
+ vec2 uv = uv_interp;
+#endif
+
+#ifdef MODE_PANORAMA_TO_DP
+ // Note, multiview and panorama should not be mixed at this time
+
+ //obtain normal from dual paraboloid uv
+#define M_PI 3.14159265359
+
+ float side;
+ uv.y = modf(uv.y * 2.0, side);
+ side = side * 2.0 - 1.0;
+ vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
+ normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
+ normal *= -side;
+ normal = normalize(normal);
+
+ //now convert normal to panorama uv
+
+ vec2 st = vec2(atan(normal.x, normal.z), acos(normal.y));
+
+ if (st.x < 0.0) {
+ st.x += M_PI * 2.0;
+ }
+
+ uv = st / vec2(M_PI * 2.0, M_PI);
+
+ if (side < 0.0) {
+ //uv.y = 1.0 - uv.y;
+ uv = 1.0 - uv;
+ }
+#endif
+
+#ifdef MULTIVIEW
+ vec4 color = textureLod(source_color, uv, 0.0);
+#ifdef MODE_TWO_SOURCES
+ // In multiview our 2nd input will be our depth map
+ depth = textureLod(source_depth, uv, 0.0).r;
+#endif /* MODE_TWO_SOURCES */
+
+#else
+ vec4 color = textureLod(source_color, uv, 0.0);
+#ifdef MODE_TWO_SOURCES
+ color += textureLod(source_color2, uv, 0.0);
+#endif /* MODE_TWO_SOURCES */
+#endif /* MULTIVIEW */
+ if (params.force_luminance) {
+ color.rgb = vec3(max(max(color.r, color.g), color.b));
+ }
+ if (params.alpha_to_zero) {
+ color.rgb *= color.a;
+ }
+ if (params.srgb) {
+ color.rgb = linear_to_srgb(color.rgb);
+ }
+ frag_color = color;
+}
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index 19a9350137..a8ccdea60b 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -44,7 +44,11 @@ layout(set = 0, binding = 0) uniform sampler2D source_color;
#endif
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
+#ifdef 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;
#ifdef USE_1D_LUT
@@ -118,6 +122,36 @@ float h1(float a) {
return 1.0f + w3(a) / (w2(a) + w3(a));
}
+#ifdef 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 // 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);
@@ -145,12 +179,17 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {
}
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
+#endif // !MULTIVIEW
-#else
+#else // USE_GLOW_FILTER_BICUBIC
+#ifdef MULTIVIEW
+#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, vec3(m_uv, ViewIndex), float(m_lod))
+#else // MULTIVIEW
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
+#endif // !MULTIVIEW
-#endif
+#endif // !USE_GLOW_FILTER_BICUBIC
vec3 tonemap_filmic(vec3 color, float white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
@@ -231,7 +270,11 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always o
}
}
+#ifdef 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(MULTIVIEW)
vec3 glow = vec3(0.0f);
if (params.glow_levels[0] > 0.0001) {
@@ -383,12 +426,13 @@ vec3 screen_space_dither(vec2 frag_coord) {
void main() {
#ifdef SUBPASS
// SUBPASS and MULTIVIEW can be combined but in that case we're already reading from the correct layer
- vec3 color = subpassLoad(input_color).rgb * params.luminance_multiplier;
+ vec4 color = subpassLoad(input_color);
#elif defined(MULTIVIEW)
- vec3 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f).rgb * params.luminance_multiplier;
+ vec4 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f);
#else
- vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb * params.luminance_multiplier;
+ vec4 color = textureLod(source_color, uv_interp, 0.0f);
#endif
+ color.rgb *= params.luminance_multiplier;
// Exposure
@@ -400,7 +444,7 @@ void main() {
}
#endif
- color *= exposure;
+ color.rgb *= exposure;
// Early Tonemap & SRGB Conversion
#ifndef SUBPASS
@@ -413,19 +457,19 @@ void main() {
}
if (params.use_fxaa) {
- color = do_fxaa(color, exposure, uv_interp);
+ color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
}
#endif
if (params.use_debanding) {
// For best results, debanding should be done before tonemapping.
// Otherwise, we're adding noise to an already-quantized image.
- color += screen_space_dither(gl_FragCoord.xy);
+ color.rgb += screen_space_dither(gl_FragCoord.xy);
}
- color = apply_tonemapping(color, params.white);
+ color.rgb = apply_tonemapping(color.rgb, params.white);
- color = linear_to_srgb(color); // regular linear -> SRGB conversion
+ color.rgb = linear_to_srgb(color.rgb); // regular linear -> SRGB conversion
#ifndef SUBPASS
// Glow
@@ -439,19 +483,19 @@ void main() {
glow = apply_tonemapping(glow, params.white);
glow = linear_to_srgb(glow);
- color = apply_glow(color, glow);
+ color.rgb = apply_glow(color.rgb, glow);
}
#endif
// Additional effects
if (params.use_bcs) {
- color = apply_bcs(color, params.bcs);
+ color.rgb = apply_bcs(color.rgb, params.bcs);
}
if (params.use_color_correction) {
- color = apply_color_correction(color);
+ color.rgb = apply_color_correction(color.rgb);
}
- frag_color = vec4(color, 1.0f);
+ frag_color = color;
}