diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index 4d74a4996b9..e7b5a1721e7 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -115,31 +115,56 @@ CopyEffects::~CopyEffects() { copy.shader.version_free(copy.shader_version); } -void CopyEffects::copy_to_rect(const Rect2 &p_rect) { - bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); +void CopyEffects::copy_to_rect(const Rect2 &p_rect, bool p_linear_to_srgb) { + uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0; + + bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION, specializations); if (!success) { return; } - copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION, specializations); draw_screen_quad(); } -void CopyEffects::copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod) { +void CopyEffects::copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod, bool p_linear_to_srgb) { ERR_FAIL_COND(p_type != Texture::TYPE_LAYERED && p_type != Texture::TYPE_3D); CopyShaderGLES3::ShaderVariant variant = p_type == Texture::TYPE_LAYERED ? CopyShaderGLES3::MODE_COPY_SECTION_2D_ARRAY : CopyShaderGLES3::MODE_COPY_SECTION_3D; + uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0; - bool success = copy.shader.version_bind_shader(copy.shader_version, variant); + bool success = copy.shader.version_bind_shader(copy.shader_version, variant, specializations); if (!success) { return; } - copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant); - copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant); - copy.shader.version_set_uniform(CopyShaderGLES3::LOD, p_lod, copy.shader_version, variant); + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::LOD, p_lod, copy.shader_version, variant, specializations); + draw_screen_quad(); +} + +void CopyEffects::copy_with_lens_distortion(const Rect2 &p_rect, float p_layer, const Vector2 &p_eye_center, float p_k1, float p_k2, float p_upscale, float p_aspect_ration, bool p_linear_to_srgb) { + CopyShaderGLES3::ShaderVariant variant = CopyShaderGLES3::MODE_LENS_DISTORTION; + + uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0; + + bool success = copy.shader.version_bind_shader(copy.shader_version, variant, specializations); + if (!success) { + return; + } + + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::LOD, 0.0, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::EYE_CENTER, p_eye_center.x, p_eye_center.y, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::K1, p_k1, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::K2, p_k1, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::UPSCALE, p_upscale, copy.shader_version, variant, specializations); + copy.shader.version_set_uniform(CopyShaderGLES3::ASPECT_RATIO, p_aspect_ration, copy.shader_version, variant, specializations); + draw_screen_quad(); } diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index e78d887da11..b50ad9a3e19 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -60,8 +60,9 @@ public: ~CopyEffects(); // These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array. - void copy_to_rect(const Rect2 &p_rect); - void copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod = 0.0f); + void copy_to_rect(const Rect2 &p_rect, bool p_linear_to_srgb = false); + void copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod = 0.0f, bool p_linear_to_srgb = false); + void copy_with_lens_distortion(const Rect2 &p_rect, float p_layer, const Vector2 &p_eye_center, float p_k1, float p_k2, float p_upscale, float p_aspect_ration, bool p_linear_to_srgb = false); void copy_to_and_from_rect(const Rect2 &p_rect); void copy_screen(float p_multiply = 1.0); void copy_cube_to_rect(const Rect2 &p_rect); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index fd32961b9da..95ddf175291 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -387,18 +387,26 @@ RasterizerGLES3::RasterizerGLES3() { RasterizerGLES3::~RasterizerGLES3() { } -void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer, bool p_first) { - GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_render_target); +void RasterizerGLES3::_blit_render_target_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen &p_blit, bool p_first) { + GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_blit.render_target); ERR_FAIL_NULL(rt); // We normally render to the render target upside down, so flip Y when blitting to the screen. bool flip_y = true; + bool linear_to_srgb = false; if (rt->overridden.color.is_valid()) { // If we've overridden the render target's color texture, that means we // didn't render upside down, so we don't need to flip it. // We're probably rendering directly to an XR device. flip_y = false; + + // It is 99% likely our texture uses the GL_SRGB8_ALPHA8 texture format in + // which case we have a GPU sRGB to Linear conversion on texture read. + // We need to counter this. + // Unfortunately we do not have an API to check this as Godot does not + // track this. + linear_to_srgb = true; } #ifdef WINDOWS_ENABLED @@ -410,7 +418,7 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); if (p_first) { - if (p_screen_rect.position != Vector2() || p_screen_rect.size != rt->size) { + if (p_blit.dst_rect.position != Vector2() || p_blit.dst_rect.size != rt->size) { // Viewport doesn't cover entire window so clear window to black before blitting. // Querying the actual window size from the DisplayServer would deadlock in separate render thread mode, // so let's set the biggest viewport the implementation supports, to be sure the window is fully covered. @@ -421,7 +429,7 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display } } - Vector2 screen_rect_end = p_screen_rect.get_end(); + Vector2 screen_rect_end = p_blit.dst_rect.get_end(); // Adreno (TM) 3xx devices have a bug that create wrong Landscape rotation of 180 degree // Reversing both the X and Y axis is equivalent to rotating 180 degrees @@ -431,8 +439,8 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display flip_x = !flip_x; } - Vector2 p1 = Vector2(flip_x ? screen_rect_end.x : p_screen_rect.position.x, flip_y ? screen_rect_end.y : p_screen_rect.position.y); - Vector2 p2 = Vector2(flip_x ? p_screen_rect.position.x : screen_rect_end.x, flip_y ? p_screen_rect.position.y : screen_rect_end.y); + Vector2 p1 = Vector2(flip_x ? screen_rect_end.x : p_blit.dst_rect.position.x, flip_y ? screen_rect_end.y : p_blit.dst_rect.position.y); + Vector2 p2 = Vector2(flip_x ? p_blit.dst_rect.position.x : screen_rect_end.x, flip_y ? p_blit.dst_rect.position.y : screen_rect_end.y); Vector2 size = p2 - p1; Rect2 screenrect = Rect2(Vector2(flip_x ? 1.0 : 0.0, flip_y ? 1.0 : 0.0), Vector2(flip_x ? -1.0 : 1.0, flip_y ? -1.0 : 1.0)); @@ -450,10 +458,12 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ZERO); - if (rt->view_count > 1) { - copy_effects->copy_to_rect_3d(screenrect, p_layer, GLES3::Texture::TYPE_LAYERED); + if (p_blit.lens_distortion.apply && (p_blit.lens_distortion.k1 != 0.0 || p_blit.lens_distortion.k2)) { + copy_effects->copy_with_lens_distortion(screenrect, p_blit.multi_view.use_layer ? p_blit.multi_view.layer : 0, p_blit.lens_distortion.eye_center, p_blit.lens_distortion.k1, p_blit.lens_distortion.k2, p_blit.lens_distortion.upscale, p_blit.lens_distortion.aspect_ratio, linear_to_srgb); + } else if (rt->view_count > 1) { + copy_effects->copy_to_rect_3d(screenrect, p_blit.multi_view.use_layer ? p_blit.multi_view.layer : 0, GLES3::Texture::TYPE_LAYERED, 0.0, linear_to_srgb); } else { - copy_effects->copy_to_rect(screenrect); + copy_effects->copy_to_rect(screenrect, linear_to_srgb); } glBindTexture(GL_TEXTURE_2D, 0); @@ -462,12 +472,7 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display // is this p_screen useless in a multi window environment? void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) { for (int i = 0; i < p_amount; i++) { - const BlitToScreen &blit = p_render_targets[i]; - - RID rid_rt = blit.render_target; - - Rect2 dst_rect = blit.dst_rect; - _blit_render_target_to_screen(rid_rt, p_screen, dst_rect, blit.multi_view.use_layer ? blit.multi_view.layer : 0, i == 0); + _blit_render_target_to_screen(p_screen, p_render_targets[i], i == 0); } } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 1d2f29d0d50..36d7716ff01 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -83,7 +83,7 @@ protected: RasterizerSceneGLES3 *scene = nullptr; static RasterizerGLES3 *singleton; - void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer, bool p_first = true); + void _blit_render_target_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen &p_blit, bool p_first = true); public: RendererUtilities *get_utilities() { return utilities; } diff --git a/drivers/gles3/shaders/effects/copy.glsl b/drivers/gles3/shaders/effects/copy.glsl index 06f63ba6298..d6fe865edb1 100644 --- a/drivers/gles3/shaders/effects/copy.glsl +++ b/drivers/gles3/shaders/effects/copy.glsl @@ -6,6 +6,7 @@ mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY mode_copy_section_source = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define MODE_COPY_FROM mode_copy_section_3d = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_3D mode_copy_section_2d_array = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_2D_ARRAY +mode_lens_distortion = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_2D_ARRAY \n#define APPLY_LENS_DISTORTION mode_screen = #define MODE_SIMPLE_COPY \n#define MODE_MULTIPLY mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP @@ -15,6 +16,8 @@ mode_cube_to_panorama = #define CUBE_TO_PANORAMA #[specializations] +CONVERT_LINEAR_TO_SRGB = false + #[vertex] layout(location = 0) in vec2 vertex_attrib; @@ -93,6 +96,14 @@ uniform sampler2D source; // texunit:0 #endif // !(defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA)) +#ifdef APPLY_LENS_DISTORTION +uniform vec2 eye_center; +uniform float k1; +uniform float k2; +uniform float upscale; +uniform float aspect_ratio; +#endif // APPLY_LENS_DISTORTION + layout(location = 0) out vec4 frag_color; // This expects 0-1 range input, outside that range it behaves poorly. @@ -101,22 +112,69 @@ vec3 srgb_to_linear(vec3 color) { return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); } +// This expects 0-1 range input. +vec3 linear_to_srgb(vec3 color) { + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); +} + void main() { #ifdef MODE_SIMPLE_COPY + vec2 uv = uv_interp; + +#ifdef APPLY_LENS_DISTORTION + uv = uv * 2.0 - 1.0; + vec2 offset = uv - eye_center; + + // take aspect ratio into account + offset.y /= aspect_ratio; + + // distort + vec2 offset_sq = offset * offset; + float radius_sq = offset_sq.x + offset_sq.y; + float radius_s4 = radius_sq * radius_sq; + float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4); + offset *= distortion_scale; + + // reapply aspect ratio + offset.y *= aspect_ratio; + + // add our eye center back in + uv = offset + eye_center; + uv /= upscale; + + // and check our color + if (uv.x < -1.0 || uv.y < -1.0 || uv.x > 1.0 || uv.y > 1.0) { + frag_color = vec4(0.0, 0.0, 0.0, 1.0); + } else { + uv = uv * 0.5 + 0.5; +#endif // APPLY_LENS_DISTORTION + #ifdef USE_TEXTURE_3D - vec4 color = textureLod(source_3d, vec3(uv_interp, layer), lod); + vec4 color = textureLod(source_3d, vec3(uv, layer), lod); #elif defined(USE_TEXTURE_2D_ARRAY) - vec4 color = textureLod(source_2d_array, vec3(uv_interp, layer), lod); + vec4 color = textureLod(source_2d_array, vec3(uv, layer), lod); #else - vec4 color = texture(source, uv_interp); + vec4 color = texture(source, uv); #endif // USE_TEXTURE_3D +#ifdef CONVERT_LINEAR_TO_SRGB + // Reading from a *_SRGB texture source will have converted data to linear, + // but we should output in sRGB! + color.rgb = linear_to_srgb(color.rgb); +#endif + #ifdef MODE_MULTIPLY - color *= multiply; + color *= multiply; #endif // MODE_MULTIPLY - frag_color = color; + frag_color = color; + +#ifdef APPLY_LENS_DISTORTION + } +#endif // APPLY_LENS_DISTORTION + #endif // MODE_SIMPLE_COPY #ifdef MODE_SIMPLE_COLOR