Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion drivers/gles3/effects/post_effects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ void PostEffects::_draw_screen_triangle() {
glBindVertexArray(0);
}

void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, float p_srgb_white, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) {
void PostEffects::post_copy(
GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color,
GLuint p_source_depth, bool p_ssao_enabled, int p_ssao_quality_level, float p_ssao_strength, float p_ssao_radius,
Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity,
float p_srgb_white, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) {
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
Expand All @@ -103,6 +107,19 @@ void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuin
if (p_glow_buffers != nullptr) {
flags |= PostShaderGLES3::USE_GLOW;
}
if (p_ssao_enabled) {
if (p_ssao_quality_level == RS::ENV_SSAO_QUALITY_VERY_LOW) {
flags |= PostShaderGLES3::USE_SSAO_ABYSS;
} else if (p_ssao_quality_level == RS::ENV_SSAO_QUALITY_LOW) {
flags |= PostShaderGLES3::USE_SSAO_LOW;
} else if (p_ssao_quality_level == RS::ENV_SSAO_QUALITY_HIGH) {
flags |= PostShaderGLES3::USE_SSAO_HIGH;
} else if (p_ssao_quality_level == RS::ENV_SSAO_QUALITY_ULTRA) {
flags |= PostShaderGLES3::USE_SSAO_MEGA;
} else {
flags |= PostShaderGLES3::USE_SSAO_MED;
}
}
if (p_luminance_multiplier != 1.0) {
flags |= PostShaderGLES3::USE_LUMINANCE_MULTIPLIER;
}
Expand All @@ -118,6 +135,20 @@ void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuin
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

if (p_ssao_enabled) {
glActiveTexture(GL_TEXTURE3);
glBindTexture(texture_target, p_source_depth);
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Thanks to mrjustaguy!
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

post.shader.version_set_uniform(PostShaderGLES3::SSAO_INTENSITY, p_ssao_strength, post.shader_version, mode, flags);
post.shader.version_set_uniform(PostShaderGLES3::SSAO_RADIUS_FRAC, p_ssao_radius, post.shader_version, mode, flags);
post.shader.version_set_uniform(PostShaderGLES3::SSAO_PRN_UV, // This converts the UV coordinate into a pseudo-random number.
p_source_size.x * 1.087f * ((1.0f + sqrt(5.0f)) / 2.0f),
p_source_size.y * 1.087f * ((9.0f + sqrt(221.0f)) / 10.0f),
post.shader_version, mode, flags);
}

if (p_glow_buffers != nullptr) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, p_glow_buffers[0].color);
Expand All @@ -137,6 +168,10 @@ void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuin
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
}
if (p_ssao_enabled) {
glActiveTexture(GL_TEXTURE3);
glBindTexture(texture_target, 0);
}

// Return back to nearest
glActiveTexture(GL_TEXTURE0);
Expand Down
5 changes: 4 additions & 1 deletion drivers/gles3/effects/post_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ class PostEffects {
PostEffects();
~PostEffects();

void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, float p_srgb_white, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color,
GLuint p_source_depth, bool p_ssao_enabled, int p_ssao_quality_level, float p_ssao_strength, float p_ssao_radius,
Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity,
float p_srgb_white, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
};

} //namespace GLES3
Expand Down
45 changes: 42 additions & 3 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,7 @@ void RasterizerSceneGLES3::environment_set_ssr_roughness_quality(RS::Environment
}

void RasterizerSceneGLES3::environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) {
ssao_quality = p_quality;
}

void RasterizerSceneGLES3::environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) {
Expand Down Expand Up @@ -2275,6 +2276,15 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
}
}

bool ssao_enabled = false;
if (p_environment.is_valid()) {
ssao_enabled = environment_get_ssao_enabled(p_environment);
if (ssao_enabled) {
// If SSAO is enabled, we apply tonemapping etc. in post, so disable it during rendering
apply_color_adjustments_in_post = true;
}
}

// Assign render data
// Use the format from rendererRD
RenderDataGLES3 render_data;
Expand Down Expand Up @@ -2552,6 +2562,11 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, rb->internal_size.x, rb->internal_size.y);

// If SSAO is enabled, we definitely need the depth buffer.
if (ssao_enabled) {
scene_state.used_depth_texture = true;
}

// Do depth prepass if it's explicitly enabled
bool use_depth_prepass = config->use_depth_prepass;

Expand Down Expand Up @@ -2835,6 +2850,18 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
rb->check_glow_buffers();
}

// Check if we want and can have SSAO.
bool ssao_enabled = false;
float ssao_strength = 4.0;
float ssao_radius = 0.5;
if (p_render_data->environment.is_valid()) {
ssao_enabled = environment_get_ssao_enabled(p_render_data->environment);
// This SSAO is not implemented the same way, but uses the intensity and radius
// in a similar way. The parameters are scaled so the SSAO defaults look ok.
ssao_strength = environment_get_ssao_intensity(p_render_data->environment) * 2.0;
ssao_radius = environment_get_ssao_radius(p_render_data->environment) * 0.5;
}

uint64_t bcs_spec_constants = 0;
if (p_render_data->environment.is_valid()) {
bool use_bcs = environment_get_adjustments_enabled(p_render_data->environment);
Expand Down Expand Up @@ -2882,6 +2909,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
if (fbo_int != 0) {
// Apply glow/bloom if requested? then populate our glow buffers
GLuint color = fbo_int != 0 ? rb->get_internal_color() : texture_storage->render_target_get_color(render_target);

// We need to pass this in for SSAO.
GLuint depth_buffer = fbo_int != 0 ? rb->get_internal_depth() : texture_storage->render_target_get_depth(render_target);

const GLES3::Glow::GLOWLEVEL *glow_buffers = nullptr;
if (glow_enabled) {
glow_buffers = rb->get_glow_buffers();
Expand All @@ -2898,7 +2929,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
}

// Copy color buffer
post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, srgb_white, 0, false, bcs_spec_constants);
post_effects->post_copy(fbo_rt, target_size, color,
depth_buffer, ssao_enabled, ssao_quality, ssao_strength, ssao_radius,
internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity,
srgb_white, 0, false, bcs_spec_constants);

// Copy depth buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int);
Expand Down Expand Up @@ -2945,6 +2979,9 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
const GLES3::Glow::GLOWLEVEL *glow_buffers = nullptr;
GLuint source_color = fbo_int != 0 ? rb->get_internal_color() : texture_storage->render_target_get_color(render_target);

// Moved this up so SSAO could use it too.
GLuint read_depth = rb->get_internal_depth();

if (glow_enabled) {
glow_buffers = rb->get_glow_buffers();

Expand All @@ -2966,11 +3003,13 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend

glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v);
post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, srgb_white, v, true, bcs_spec_constants);
post_effects->post_copy(fbos[2], target_size, source_color,
read_depth, ssao_enabled, ssao_quality, ssao_strength, ssao_radius,
internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity,
srgb_white, v, true, bcs_spec_constants);
}

// Copy depth
GLuint read_depth = rb->get_internal_depth();
GLuint write_depth = texture_storage->render_target_get_depth(render_target);

glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]);
Expand Down
33 changes: 33 additions & 0 deletions drivers/gles3/shaders/effects/post.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ USE_LUMINANCE_MULTIPLIER = false
USE_BCS = false
USE_COLOR_CORRECTION = false
USE_1D_LUT = false
USE_SSAO_ABYSS = false
USE_SSAO_LOW = false
USE_SSAO_MED = false
USE_SSAO_HIGH = false
USE_SSAO_MEGA = false

#[vertex]
layout(location = 0) in vec2 vertex_attrib;
Expand Down Expand Up @@ -86,6 +91,29 @@ vec3 apply_color_correction(vec3 color) {
#endif // USE_1D_LUT
#endif // USE_COLOR_CORRECTION

#if defined(USE_SSAO_ABYSS) || defined(USE_SSAO_LOW) || defined(USE_SSAO_MED) || defined(USE_SSAO_HIGH) || defined(USE_SSAO_MEGA)
#define USE_SOME_SSAO
uniform float ssao_intensity;
uniform float ssao_radius_frac;
uniform vec2 ssao_prn_UV;
#ifdef USE_MULTIVIEW
// VR will have 2 depth buffers.
uniform sampler2DArray depth_buffer_array; // texunit:3
#else
uniform sampler2D depth_buffer; // texunit:3
Comment thread
clayjohn marked this conversation as resolved.
#endif
#if defined(USE_SSAO_ABYSS)
// Use the tiny 2-sample version.
#include "../s4ao_micro_inc.glsl"
#elif defined(USE_SSAO_HIGH) || defined(USE_SSAO_MEGA)
// Use the rings version for the higher qualities.
#include "../s4ao_mega_inc.glsl"
#else
// Use the more generic NxN grid version.
#include "../s4ao_inc.glsl"
#endif
#endif

in vec2 uv_interp;

layout(location = 0) out vec4 frag_color;
Expand Down Expand Up @@ -131,6 +159,11 @@ void main() {

color.rgb = srgb_to_linear(color.rgb);

#if defined(USE_SOME_SSAO)
// Putting SSAO after the conversion to linear color, though it might be better before the glow.
color.rgb *= s4ao(uv_interp); // The USE_SSAO_X controls the number of samples.
#endif

color.rgb = apply_tonemapping(color.rgb, white);

#ifdef USE_BCS
Expand Down
63 changes: 63 additions & 0 deletions drivers/gles3/shaders/s4ao_inc.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// S4AO (Stupid Simple Screen Space Ambient Occlusion) - Jonathan Dummer (O1S)

// The sample_width should be even, else the midpoint is at UV.
// Takes sample_width^2 samples in a grid, with the corners notched.
#if defined(USE_SSAO_LOW)
const int sample_width = 2;
#elif defined(USE_SSAO_HIGH)
const int sample_width = 6;
#else
const int sample_width = 4;
#endif
const int notch_01 = int(sample_width > 3); // Set to 1 to skip the corner samples, 0 to include them.
const float sample_mid = (float(sample_width) - 1.0) * 0.50001; // Can't be exactly 0.5 in case sample_width is odd.
#if defined(USE_SSAO_LOW)
const float inv_half_width = 1.0 / sample_mid; // The 2x2 sampling looks wider as all samples are at radius.
#else
const float inv_half_width = 1.7 / sample_mid; // Bake in the 1.7 scale for the random rotation.
#endif
const float average_samples = 1.0 / float(sample_width * sample_width - 4 * notch_01); // 1 / number_of_samples
const float ssao_falloff_frac = 0.25;
// Perform the SSAO.
float s4ao(vec2 UV) {
#ifdef USE_MULTIVIEW
float depth = texture(depth_buffer_array, vec3(UV, view)).r;
#else
float depth = texture(depth_buffer, UV).r;
Comment thread
clayjohn marked this conversation as resolved.
#endif
float radius = max(1e-4f, depth * ssao_radius_frac);
float inv_falloff = 1.0f / max(1e-4f, depth * ssao_falloff_frac);
// Random 2D rotation per pixel (+/-45 deg, with 0 having a lower probability).
// The random cosine vector is vec2( 0.5, -0.5 to +0.5 ) and *1.7 makes the average length ~ 1.
vec2 rcos = (inv_half_width * radius) * vec2(0.5f, fract(dot(UV, ssao_prn_UV)) - 0.5f);
vec2 rsin = rcos.yx * vec2(-1, 1); // Perpendicular to the random cosine vector.
// Grab the samples and determine the occlusion.
float occlusion = 0.0f;
vec2 base_duv = -sample_mid * rsin;
for (int j = sample_width; --j >= 0;) {
#if defined(USE_SSAO_LOW)
// Low quality uses 2x2 samples, no notching.
vec2 duv = -sample_mid * rcos + base_duv;
for (int i = sample_width; --i >= 0;) {
#else
// Will uses 4x4 or 6x6 samples, with the corners notched out.
int o = /*notch_01 &*/ int((j <= 0) || (j >= (sample_width - 1))); // Notch corners of the grid.
vec2 duv = (float(o) - sample_mid) * rcos + base_duv;
for (int i = sample_width - o - o; --i >= 0;) {
#endif
#ifdef USE_MULTIVIEW
float dz = texture(depth_buffer_array, vec3(UV + duv, view)).r - depth;
#else
float dz = texture(depth_buffer, UV + duv).r - depth;
#endif
float validity = smoothstep(1.0f, 0.0f, dz * inv_falloff);
occlusion += normalize(vec3(duv, dz)).z * validity; // How 'directly overhead' is it?
duv += rcos; // March along the rcos direction with i.
}
base_duv += rsin; // March along the rsin direction with j.
}
// Adjust the occlusion for intensity, and # samples.
occlusion *= ssao_intensity * average_samples;
occlusion = clamp(1.0f - occlusion, 0.0f, 1.0f);
return occlusion * occlusion;
}
50 changes: 50 additions & 0 deletions drivers/gles3/shaders/s4ao_mega_inc.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// S4AO (Stupid Simple Screen Space Ambient Occlusion) - Jonathan Dummer (O1S)
// The mega version uses N concentric rings of samples.

#if defined(USE_SSAO_MEGA)
const int rings = 4; // Start with the outer ring.
const int samps[] = int[](24, 18, 12, 6); // ( 9, 6, 3 ) is a minimum, but I want better.
#else
const int rings = 3; // Start with the outer ring.
const int samps[] = int[](15, 10, 5, 1); // ( 9, 6, 3 ) is a minimum, but I want better.
#endif
const float average_samples = 1.0 / float(samps[0] + samps[1] * int(rings > 1) + samps[2] * int(rings > 2) + samps[3] * int(rings > 3));
const float ssao_falloff_frac = 0.25;
// Perform the SSAO.
float s4ao(vec2 UV) {
#ifdef USE_MULTIVIEW
float depth = texture(depth_buffer_array, vec3(UV, view)).r;
#else
float depth = texture(depth_buffer, UV).r;
#endif
float inv_falloff = 1.0f / max(1e-4f, depth * ssao_falloff_frac);
// Random 2D rotation per pixel (0..1 -> parabola approximating a 180 deg arc)
float r01 = fract(dot(UV, ssao_prn_UV));
vec2 rcos = vec2(r01 - 0.5f, 2.0f * (r01 - r01 * r01)) * (2.0f * depth * ssao_radius_frac); // 180 degrees.
vec2 rsin = rcos.yx * vec2(-1, 1); // Perpendicular to the random cosine vector.
// Grab the samples and determine the occlusion.
float occlusion = 0.0f;
float ring_shrink = 0.75f; // Shrink every ring.
for (int r = 0; r < rings; ++r) {
float dt = (6.283185307f) / float(samps[r]);
float t = float(r & 1) * 0.5f * dt;
for (int s = 0; s < samps[r]; ++s) {
vec2 duv = cos(t) * rcos + sin(t) * rsin;
#ifdef USE_MULTIVIEW
float dz = texture(depth_buffer_array, vec3(UV + duv, view)).r - depth;
#else
float dz = texture(depth_buffer, UV + duv).r - depth;
#endif
// How 'directly overhead' is it? Factor in the falloff depth.
occlusion += normalize(vec3(duv, dz)).z * smoothstep(1.0f, 0.0f, dz * inv_falloff);
t += dt;
}
// The next ring will be smaller.
rcos *= ring_shrink;
rsin *= ring_shrink;
}
// Adjust the occlusion for intensity, and # samples.
occlusion *= ssao_intensity * average_samples;
occlusion = 1.0f - clamp(occlusion, 0.0f, 1.0f);
return occlusion * occlusion;
}
32 changes: 32 additions & 0 deletions drivers/gles3/shaders/s4ao_micro_inc.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// S4AO (Stupid Simple Screen Space Ambient Occlusion) - Jonathan Dummer (O1S)
// This micro version uses only 3 depth samples, the midpoint and a randomly-rotated, balanced pair.

const mediump float ssao_falloff_frac = 0.25;
// Perform the SSAO.
float s4ao(vec2 UV) {
#ifdef USE_MULTIVIEW
mediump float depth = texture(depth_buffer_array, vec3(UV, view)).r;
#else
mediump float depth = texture(depth_buffer, UV).r;
#endif
mediump float inv_falloff = 1.0f / max(1e-4f, depth * ssao_falloff_frac);
// Random 2D rotation per pixel (0..1 -> parabola approximating a 180 deg arc)
mediump float r01 = fract(dot(UV, ssao_prn_UV));
mediump vec2 duv = vec2(r01 - 0.5f, 2.0f * (r01 - r01 * r01)) * (2.0f * depth * ssao_radius_frac); // 180 degrees.
// Grab the samples and determine the occlusion.
mediump float occlusion = 0.0f;
for (int s = 0; s < 2; ++s) {
#ifdef USE_MULTIVIEW
mediump float dz = texture(depth_buffer_array, vec3(UV + duv, view)).r - depth;
#else
mediump float dz = texture(depth_buffer, UV + duv).r - depth;
#endif
// How 'directly overhead' is it? Factor in the falloff depth.
occlusion += normalize(vec3(duv, dz)).z * mix(1.0f, 0.0f, dz * inv_falloff);
// Mirror the next sample.
duv = -duv;
}
// Adjust the occlusion for intensity, and # samples.
occlusion = 1.0f - clamp(occlusion * 0.5f * ssao_intensity, 0.0f, 1.0f);
return occlusion * occlusion;
}
9 changes: 9 additions & 0 deletions scene/resources/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,15 @@ void Environment::_validate_property(PropertyInfo &p_property) const {
}
}

if (OS::get_singleton()->get_current_rendering_method() != "forward_plus") {
// Hide SSAO properties that only work in Forward+.
if (p_property.name.begins_with("ssao_")) {
if ((p_property.name != "ssao_enabled") && (p_property.name != "ssao_radius") && (p_property.name != "ssao_intensity")) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
}

if (p_property.name == "background_color") {
if (bg_mode != BG_COLOR && ambient_source != AMBIENT_SOURCE_COLOR) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
Expand Down
Loading