diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index 9315026623..d109ef7b91 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -788,6 +788,7 @@ public: void restore_render_target() {} void clear_render_target(const Color &p_color) {} void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0) {} + void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) {} void end_frame(bool p_swap_buffers) {} void finalize() {} diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 263f210fa2..30776091a4 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -1049,6 +1049,43 @@ void RasterizerCanvasGLES2::draw_generic_textured_rect(const Rect2 &p_rect, cons glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +void RasterizerCanvasGLES2::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + Vector2 half_size; + if (storage->frame.current_rt) { + half_size = Vector2(storage->frame.current_rt->width, storage->frame.current_rt->height); + } else { + half_size = OS::get_singleton()->get_window_size(); + } + half_size *= 0.5; + Vector2 offset((p_rect.position.x - half_size.x) / half_size.x, (p_rect.position.y - half_size.y) / half_size.y); + Vector2 scale(p_rect.size.x / half_size.x, p_rect.size.y / half_size.y); + + float aspect_ratio = p_rect.size.x / p_rect.size.y; + + // setup our lens shader + state.lens_shader.bind(); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::OFFSET, offset); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::SCALE, scale); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::K1, p_k1); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::K2, p_k2); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::EYE_CENTER, p_eye_center); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::UPSCALE, p_oversample); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::ASPECT_RATIO, aspect_ratio); + + // bind our quad buffer + _bind_quad_buffer(); + + // and draw + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + // and cleanup + glBindBuffer(GL_ARRAY_BUFFER, 0); + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + glDisableVertexAttribArray(i); + } +} + void RasterizerCanvasGLES2::draw_window_margins(int *black_margin, RID *black_image) { } @@ -1161,6 +1198,8 @@ void RasterizerCanvasGLES2::initialize() { state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); state.canvas_shader.bind(); + + state.lens_shader.init(); } void RasterizerCanvasGLES2::finalize() { diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index cda3ec79e7..cf1c239b6e 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/drivers/gles2/rasterizer_canvas_gles2.h @@ -34,6 +34,7 @@ #include "servers/visual/rasterizer.h" #include "shaders/canvas.glsl.gen.h" +#include "shaders/lens_distorted.glsl.gen.h" // #include "shaders/canvas_shadow.glsl.gen.h" @@ -70,6 +71,7 @@ public: bool canvas_texscreen_used; CanvasShaderGLES2 canvas_shader; // CanvasShadowShaderGLES3 canvas_shadow_shader; + LensDistortedShaderGLES2 lens_shader; bool using_texture_rect; bool using_ninepatch; @@ -117,6 +119,7 @@ public: void _bind_quad_buffer(); void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src); + void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); void initialize(); void finalize(); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 1dd594cc20..b17f02f589 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -380,6 +380,26 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re canvas->canvas_end(); } +void RasterizerGLES2::output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + ERR_FAIL_COND(storage->frame.current_rt); + + RasterizerStorageGLES2::RenderTarget *rt = storage->render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + glDisable(GL_BLEND); + + // render to our framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); + + // output our texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt->color); + + canvas->draw_lens_distortion_rect(p_screen_rect, p_k1, p_k2, p_eye_center, p_oversample); + + glBindTexture(GL_TEXTURE_2D, 0); +} + void RasterizerGLES2::end_frame(bool p_swap_buffers) { if (OS::get_singleton()->is_layered_allowed()) { diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h index 45a9db73f2..97f8ee7c1c 100644 --- a/drivers/gles2/rasterizer_gles2.h +++ b/drivers/gles2/rasterizer_gles2.h @@ -59,6 +59,7 @@ public: virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0); + virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); virtual void end_frame(bool p_swap_buffers); virtual void finalize(); diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub index acb93fff8f..d959d3f740 100644 --- a/drivers/gles2/shaders/SCsub +++ b/drivers/gles2/shaders/SCsub @@ -20,3 +20,4 @@ if 'GLES2_GLSL' in env['BUILDERS']: # env.GLES2_GLSL('exposure.glsl'); # env.GLES2_GLSL('tonemap.glsl'); # env.GLES2_GLSL('particles.glsl'); + env.GLES2_GLSL('lens_distorted.glsl'); diff --git a/drivers/gles2/shaders/lens_distorted.glsl b/drivers/gles2/shaders/lens_distorted.glsl new file mode 100644 index 0000000000..d541db9bf9 --- /dev/null +++ b/drivers/gles2/shaders/lens_distorted.glsl @@ -0,0 +1,62 @@ +/* clang-format off */ +[vertex] + +attribute highp vec2 vertex; // attrib:0 +/* clang-format on */ + +uniform vec2 offset; +uniform vec2 scale; + +varying vec2 uv_interp; + +void main() { + + uv_interp = vertex.xy * 2.0 - 1.0; + + vec2 v = vertex.xy * scale + offset; + gl_Position = vec4(v, 0.0, 1.0); +} + +/* clang-format off */ +[fragment] + +uniform sampler2D source; //texunit:0 +/* clang-format on */ + +uniform vec2 eye_center; +uniform float k1; +uniform float k2; +uniform float upscale; +uniform float aspect_ratio; + +varying vec2 uv_interp; + +void main() { + vec2 coords = uv_interp; + vec2 offset = coords - 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 + coords = offset + eye_center; + coords /= upscale; + + // and check our color + if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + } else { + coords = (coords + vec2(1.0)) / vec2(2.0); + gl_FragColor = texture2D(source, coords); + } +} diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 856c83e297..a9b46baf53 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1869,6 +1869,39 @@ void RasterizerCanvasGLES3::draw_generic_textured_rect(const Rect2 &p_rect, cons glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + Vector2 half_size; + if (storage->frame.current_rt) { + half_size = Vector2(storage->frame.current_rt->width, storage->frame.current_rt->height); + } else { + half_size = OS::get_singleton()->get_window_size(); + } + half_size *= 0.5; + Vector2 offset((p_rect.position.x - half_size.x) / half_size.x, (p_rect.position.y - half_size.y) / half_size.y); + Vector2 scale(p_rect.size.x / half_size.x, p_rect.size.y / half_size.y); + + float aspect_ratio = p_rect.size.x / p_rect.size.y; + + // setup our lens shader + state.lens_shader.bind(); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::OFFSET, offset); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::SCALE, scale); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::K1, p_k1); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::K2, p_k2); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::EYE_CENTER, p_eye_center); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::UPSCALE, p_oversample); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio); + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_item_ubo); + glBindVertexArray(data.canvas_quad_array); + + // and draw + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindVertexArray(0); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0); +} + void RasterizerCanvasGLES3::draw_window_margins(int *black_margin, RID *black_image) { Vector2 window_size = OS::get_singleton()->get_window_size(); @@ -2058,6 +2091,7 @@ void RasterizerCanvasGLES3::initialize() { state.canvas_shader.init(); state.canvas_shader.set_base_material_tex_index(2); state.canvas_shadow_shader.init(); + state.lens_shader.init(); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); state.canvas_shadow_shader.set_conditional(CanvasShadowShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index bc4ea80328..3f306003b4 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -35,6 +35,7 @@ #include "servers/visual/rasterizer.h" #include "shaders/canvas_shadow.glsl.gen.h" +#include "shaders/lens_distorted.glsl.gen.h" class RasterizerSceneGLES3; @@ -72,6 +73,7 @@ public: bool canvas_texscreen_used; CanvasShaderGLES3 canvas_shader; CanvasShadowShaderGLES3 canvas_shadow_shader; + LensDistortedShaderGLES3 lens_shader; bool using_texture_rect; bool using_ninepatch; @@ -141,6 +143,7 @@ public: virtual void reset_canvas(); void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src); + void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); void initialize(); void finalize(); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 6f13df621f..a97cd32faa 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -359,6 +359,26 @@ void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Re #endif } +void RasterizerGLES3::output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + ERR_FAIL_COND(storage->frame.current_rt); + + RasterizerStorageGLES3::RenderTarget *rt = storage->render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + glDisable(GL_BLEND); + + // render to our framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + + // output our texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt->color); + + canvas->draw_lens_distortion_rect(p_screen_rect, p_k1, p_k2, p_eye_center, p_oversample); + + glBindTexture(GL_TEXTURE_2D, 0); +} + void RasterizerGLES3::end_frame(bool p_swap_buffers) { if (OS::get_singleton()->is_layered_allowed()) { diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 543011aff3..477e0dfd48 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -59,6 +59,7 @@ public: virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0); + virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); virtual void end_frame(bool p_swap_buffers); virtual void finalize(); diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index f1811fa7b5..27fd1514e7 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -20,3 +20,4 @@ if 'GLES3_GLSL' in env['BUILDERS']: env.GLES3_GLSL('exposure.glsl'); env.GLES3_GLSL('tonemap.glsl'); env.GLES3_GLSL('particles.glsl'); + env.GLES3_GLSL('lens_distorted.glsl'); diff --git a/modules/mobile_vr/shaders/lens_distorted.glsl b/drivers/gles3/shaders/lens_distorted.glsl similarity index 87% rename from modules/mobile_vr/shaders/lens_distorted.glsl rename to drivers/gles3/shaders/lens_distorted.glsl index 92604c891c..7b9d0b347f 100644 --- a/modules/mobile_vr/shaders/lens_distorted.glsl +++ b/drivers/gles3/shaders/lens_distorted.glsl @@ -3,16 +3,18 @@ layout(location = 0) in highp vec4 vertex_attrib; /* clang-format on */ -layout(location = 4) in vec2 uv_in; -uniform float offset_x; +uniform vec2 offset; +uniform vec2 scale; out vec2 uv_interp; void main() { - uv_interp = uv_in; - gl_Position = vec4(vertex_attrib.x + offset_x, vertex_attrib.y, 0.0, 1.0); + uv_interp = vertex_attrib.xy * 2.0 - 1.0; + + vec2 v = vertex_attrib.xy * scale + offset; + gl_Position = vec4(v, 0.0, 1.0); } /* clang-format off */ diff --git a/modules/mobile_vr/SCsub b/modules/mobile_vr/SCsub index e5725ceb6f..4bd184f025 100644 --- a/modules/mobile_vr/SCsub +++ b/modules/mobile_vr/SCsub @@ -6,5 +6,3 @@ Import('env_modules') env_mobile_vr = env_modules.Clone() env_mobile_vr.add_source_files(env.modules_sources, '*.cpp') - -SConscript("shaders/SCsub") diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 2cabc7bd59..b9a404709e 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -297,49 +297,6 @@ bool MobileVRInterface::initialize() { mag_current_min = Vector3(0, 0, 0); mag_current_max = Vector3(0, 0, 0); -#if !defined(SERVER_ENABLED) - // build our shader - if (lens_shader == NULL) { - ///@TODO need to switch between GLES2 and GLES3 version, Reduz suggested moving this into our drivers and making this a core shader - // create a shader - lens_shader = new LensDistortedShaderGLES3(); - - // create our shader stuff - lens_shader->init(); - - glGenBuffers(1, &half_screen_quad); - glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad); - { - /* clang-format off */ - const float qv[16] = { - 0, -1, - -1, -1, - 0, 1, - -1, 1, - 1, 1, - 1, 1, - 1, -1, - 1, -1, - }; - /* clang-format on */ - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - - glGenVertexArrays(1, &half_screen_array); - glBindVertexArray(half_screen_array); - glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad); - glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); - glEnableVertexAttribArray(0); - glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8); - glEnableVertexAttribArray(4); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - } -#endif - // reset our orientation orientation = Basis(); @@ -362,17 +319,6 @@ void MobileVRInterface::uninitialize() { arvr_server->clear_primary_interface_if(this); } -#if !defined(SERVER_ENABLED) - // cleanup our shader and buffers - if (lens_shader != NULL) { - glDeleteVertexArrays(1, &half_screen_array); - glDeleteBuffers(1, &half_screen_quad); - - delete lens_shader; - lens_shader = NULL; - } -#endif - initialized = false; }; }; @@ -448,48 +394,30 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t // We must have a valid render target ERR_FAIL_COND(!p_render_target.is_valid()); - // We must have an initialised shader - ERR_FAIL_COND(lens_shader != NULL); - // Because we are rendering to our device we must use our main viewport! ERR_FAIL_COND(p_screen_rect == Rect2()); - float offset_x = 0.0; + Rect2 dest = p_screen_rect; float aspect_ratio = 0.5 * p_screen_rect.size.x / p_screen_rect.size.y; Vector2 eye_center; + // we output half a screen + dest.size.x *= 0.5; + if (p_eye == ARVRInterface::EYE_LEFT) { - offset_x = -1.0; eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); } else if (p_eye == ARVRInterface::EYE_RIGHT) { + dest.position.x = dest.size.x; eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); } + // we don't offset the eye center vertically (yet) + eye_center.y = 0.0; // unset our render target so we are outputting to our main screen by making RasterizerStorageGLES3::system_fbo our current FBO VSG::rasterizer->set_current_render_target(RID()); - // now output to screen - // VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0); - - // get our render target - RID eye_texture = VSG::storage->render_target_get_texture(p_render_target); - uint32_t texid = VS::get_singleton()->texture_get_texid(eye_texture); -#if !defined(SERVER_ENABLED) - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texid); - - lens_shader->bind(); - lens_shader->set_uniform(LensDistortedShaderGLES3::OFFSET_X, offset_x); - lens_shader->set_uniform(LensDistortedShaderGLES3::K1, k1); - lens_shader->set_uniform(LensDistortedShaderGLES3::K2, k2); - lens_shader->set_uniform(LensDistortedShaderGLES3::EYE_CENTER, eye_center); - lens_shader->set_uniform(LensDistortedShaderGLES3::UPSCALE, oversample); - lens_shader->set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio); - - glBindVertexArray(half_screen_array); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindVertexArray(0); -#endif + // and output + VSG::rasterizer->output_lens_distorted_to_screen(p_render_target, dest, k1, k2, eye_center, oversample); }; void MobileVRInterface::process() { @@ -512,8 +440,6 @@ MobileVRInterface::MobileVRInterface() { k1 = 0.215; k2 = 0.215; last_ticks = 0; - - lens_shader = NULL; }; MobileVRInterface::~MobileVRInterface() { diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index 63cad4c738..05b6331f94 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -34,10 +34,6 @@ #include "servers/arvr/arvr_interface.h" #include "servers/arvr/arvr_positional_tracker.h" -#if !defined(SERVER_ENABLED) -#include "shaders/lens_distorted.glsl.gen.h" -#endif - /** @author Bastiaan Olij @@ -60,14 +56,6 @@ private: float eye_height; uint64_t last_ticks; -#if !defined(SERVER_ENABLED) - LensDistortedShaderGLES3 *lens_shader; - GLuint half_screen_quad; - GLuint half_screen_array; -#else - void *lens_shader; -#endif - real_t intraocular_dist; real_t display_width; real_t display_to_lens; diff --git a/modules/mobile_vr/shaders/SCsub b/modules/mobile_vr/shaders/SCsub deleted file mode 100644 index 97a3598598..0000000000 --- a/modules/mobile_vr/shaders/SCsub +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -if 'GLES3_GLSL' in env['BUILDERS']: - env.GLES3_GLSL('lens_distorted.glsl'); diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 207f13ac1a..8c3736a1bf 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -1104,6 +1104,7 @@ public: virtual void restore_render_target() = 0; virtual void clear_render_target(const Color &p_color) = 0; virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0) = 0; + virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) = 0; virtual void end_frame(bool p_swap_buffers) = 0; virtual void finalize() = 0;