Convert output of GLES2 to linear color space if keep_3d_linear is enabled

This commit is contained in:
Bastiaan Olij 2021-08-17 14:55:37 +10:00
parent 2370fe5c26
commit 73722f3c65
8 changed files with 94 additions and 9 deletions

View file

@ -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.
</member>
<member name="keep_3d_linear" type="bool" setter="set_keep_3d_linear" getter="get_keep_3d_linear" default="false">
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!
</member>
<member name="msaa" type="int" setter="set_msaa" getter="get_msaa" enum="Viewport.MSAA" default="0">
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.

View file

@ -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) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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) {

View file

@ -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;
}