diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index bac34471c199..5580d8575079 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -228,7 +228,7 @@ [b]Note:[/b] Requires [member usage] to be set to [constant USAGE_3D] or [constant USAGE_3D_NO_EFFECTS], since HDR is not supported for 2D. - If [code]true[/code], the result after 3D rendering will not have a linear to sRGB color conversion applied. This is important when the viewport is used as a render target where the result is used as a texture on a 3D object rendered in another viewport. It is also important if the viewport is used to create data that is not color based (noise, heightmaps, pickmaps, etc.). Do not enable this when the viewport is used as a texture on a 2D object or if the viewport is your final output. + If [code]true[/code], the result after 3D rendering will not have a linear to sRGB color conversion applied. This is important when the viewport is used as a render target where the result is used as a texture on a 3D object rendered in another viewport. It is also important if the viewport is used to create data that is not color based (noise, heightmaps, pickmaps, etc.). Do not enable this when the viewport is used as a texture on a 2D object or if the viewport is your final output. For the GLES2 driver this will convert the sRGB output to linear, this should only be used for VR plugins that require input in linear color space! The multisample anti-aliasing mode. A higher number results in smoother edges at the cost of significantly worse performance. A value of 4 is best unless targeting very high-end systems. diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 5275d0e790d6..4ab5b6100287 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -403,6 +403,7 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re canvas->_set_texture_rect_mode(true); canvas->state.canvas_shader.set_custom_shader(0); + canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::LINEAR_TO_SRGB, rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]); canvas->state.canvas_shader.bind(); canvas->canvas_begin(); @@ -421,6 +422,8 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re glBindTexture(GL_TEXTURE_2D, 0); canvas->canvas_end(); + + canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::LINEAR_TO_SRGB, false); } 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) { diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 352da00e70a0..35d87d563424 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -2655,6 +2655,11 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false); storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false); + if (storage->frame.current_rt) { + storage->shaders.copy.set_conditional(CopyShaderGLES2::OUTPUT_LINEAR, storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]); + } else { + storage->shaders.copy.set_conditional(CopyShaderGLES2::OUTPUT_LINEAR, false); + } storage->shaders.copy.bind(); storage->shaders.copy.set_uniform(CopyShaderGLES2::MULTIPLIER, p_energy); @@ -2678,6 +2683,7 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, false); storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false); storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::OUTPUT_LINEAR, false); } void RasterizerSceneGLES2::_post_process(Environment *env, const CameraMatrix &p_cam_projection) { @@ -3312,7 +3318,15 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const } if (!env || env->bg_mode != VS::ENV_BG_KEEP) { - glClearColor(clear_color.r, clear_color.g, clear_color.b, clear_color.a); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]) { + // convert to linear here + Color linear_color = clear_color.to_linear(); + glClearColor(linear_color.r, linear_color.g, linear_color.b, linear_color.a); + + // leave clear_color in sRGB as most of the render pipeline remains in sRGB color space until writing out to frag_color + } else { + glClearColor(clear_color.r, clear_color.g, clear_color.b, clear_color.a); + } glClear(GL_COLOR_BUFFER_BIT); } @@ -3416,6 +3430,13 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const state.default_bg = Color(0, 0, 0, 1); //black as default background for interior } + // make sure we set our output mode correctly + if (storage->frame.current_rt) { + state.scene_shader.set_conditional(SceneShaderGLES2::OUTPUT_LINEAR, storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]); + } else { + state.scene_shader.set_conditional(SceneShaderGLES2::OUTPUT_LINEAR, false); + } + // render opaque things first render_list.sort_by_key(false); _render_render_list(render_list.elements, render_list.element_count, cam_transform, p_cam_projection, p_eye, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, reverse_cull, false, false); @@ -3519,6 +3540,9 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const storage->_copy_screen(); } #endif + + // return to default + state.scene_shader.set_conditional(SceneShaderGLES2::OUTPUT_LINEAR, false); } void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) { @@ -3736,6 +3760,7 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ } state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, true); + state.scene_shader.set_conditional(SceneShaderGLES2::OUTPUT_LINEAR, false); // just in case, should be false already _render_render_list(render_list.elements, render_list.element_count, light_transform, light_projection, 0, RID(), nullptr, 0, bias, normal_bias, flip_facing, false, true); diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 3774db291bfb..f112ee821d97 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -701,5 +701,11 @@ FRAGMENT_SHADER_CODE //use lighting #endif +#ifdef LINEAR_TO_SRGB + // regular Linear -> SRGB conversion + vec3 a = vec3(0.055); + color.rgb = mix((vec3(1.0) + a) * pow(color.rgb, vec3(1.0 / 2.4)) - a, 12.92 * color.rgb, vec3(lessThan(color.rgb, vec3(0.0031308)))); +#endif + gl_FragColor = color; } diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl index e833722ac3d8..8881d667cc76 100644 --- a/drivers/gles2/shaders/copy.glsl +++ b/drivers/gles2/shaders/copy.glsl @@ -187,5 +187,10 @@ void main() { color.rgb *= multiplier; #endif +#ifdef OUTPUT_LINEAR + // sRGB -> linear + color.rgb = mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), vec3(lessThan(color.rgb, vec3(0.04045)))); +#endif + gl_FragColor = color; } diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 9d7a9aa16bb8..624a712c2418 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -2307,6 +2307,11 @@ FRAGMENT_SHADER_CODE #endif //unshaded +#ifdef OUTPUT_LINEAR + // sRGB -> linear + gl_FragColor.rgb = mix(pow((gl_FragColor.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), gl_FragColor.rgb * (1.0 / 12.92), vec3(lessThan(gl_FragColor.rgb, vec3(0.04045)))); +#endif + #else // not RENDER_DEPTH //depth render #ifdef USE_RGBA_SHADOWS diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 29835a70d94c..447c3382a240 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -334,15 +334,49 @@ void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Re RasterizerStorageGLES3::RenderTarget *rt = storage->render_target_owner.getornull(p_render_target); ERR_FAIL_COND(!rt); - Size2 win_size = OS::get_singleton()->get_window_size(); - if (rt->external.fbo != 0) { - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo); + if (rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]) { + // We need to add an sRGB conversion here as we kept our buffer linear (+ a little tone mapping). + + canvas->_set_texture_rect_mode(true); + + canvas->state.canvas_shader.set_custom_shader(0); + canvas->state.canvas_shader.set_conditional(CanvasShaderGLES3::LINEAR_TO_SRGB, true); + canvas->state.canvas_shader.bind(); + + canvas->canvas_begin(); + + glDisable(GL_BLEND); + + // render to our framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + + // output our texture + glActiveTexture(GL_TEXTURE0); + if (rt->external.fbo != 0) { + glBindTexture(GL_TEXTURE_2D, rt->external.color); + } else { + glBindTexture(GL_TEXTURE_2D, rt->color); + } + + canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1)); + + glBindTexture(GL_TEXTURE_2D, 0); + canvas->canvas_end(); + + canvas->state.canvas_shader.set_conditional(CanvasShaderGLES3::LINEAR_TO_SRGB, false); } else { - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + // No conversion needed, take the faster approach + + Size2 win_size = OS::get_singleton()->get_window_size(); + if (rt->external.fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo); + } else { + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + } + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + glBlitFramebuffer(0, 0, rt->width, rt->height, p_screen_rect.position.x, win_size.height - p_screen_rect.position.y - p_screen_rect.size.height, p_screen_rect.position.x + p_screen_rect.size.width, win_size.height - p_screen_rect.position.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); } - glReadBuffer(GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); - glBlitFramebuffer(0, 0, rt->width, rt->height, p_screen_rect.position.x, win_size.height - p_screen_rect.position.y - p_screen_rect.size.height, p_screen_rect.position.x + p_screen_rect.size.width, win_size.height - p_screen_rect.position.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); } 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) { diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 91ef3a715832..24325d2f9faa 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -855,6 +855,13 @@ FRAGMENT_SHADER_CODE //use lighting #endif + +#ifdef LINEAR_TO_SRGB + // regular Linear -> SRGB conversion + vec3 a = vec3(0.055); + color.rgb = mix((vec3(1.0) + a) * pow(color.rgb, vec3(1.0 / 2.4)) - a, 12.92 * color.rgb, lessThan(color.rgb, vec3(0.0031308))); +#endif + //color.rgb *= color.a; frag_color = color; }