From 077083938ef57b1c1de529db3c0add8180c7c41f Mon Sep 17 00:00:00 2001 From: Endri Lauson Date: Tue, 7 Sep 2021 16:26:20 -0300 Subject: [PATCH] Add a new high quality tonemapper: ACES Fitted --- doc/classes/Environment.xml | 5 ++- doc/classes/VisualServer.xml | 3 ++ drivers/gles3/rasterizer_scene_gles3.cpp | 2 ++ drivers/gles3/shaders/tonemap.glsl | 45 ++++++++++++++++++++++-- scene/resources/environment.cpp | 3 +- scene/resources/environment.h | 3 +- servers/visual_server.cpp | 1 + servers/visual_server.h | 3 +- 8 files changed, 58 insertions(+), 7 deletions(-) diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index ffe1cb42888..c985660fe41 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -337,7 +337,10 @@ Filmic tonemapper operator. - Academy Color Encoding System tonemapper operator. + Academy Color Encoding System tonemapper operator. Performs an aproximation of the ACES tonemapping curve. + + + High quality Academy Color Encoding System tonemapper operator that matches the industry standard. Performs a more physically accurate curve fit which better simulates how light works in the real world. The color of lights and emissive materials will become lighter as the emissive energy increases, and will eventually become white if the light is bright enough to saturate the camera sensor. Low depth-of-field blur quality (fastest). diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index 1585aa55988..87b1f8fd9f0 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -3798,6 +3798,9 @@ Use the ACES tonemapper. + + Use the ACES Fitted tonemapper. + Lowest quality of screen space ambient occlusion. diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 263f2333577..4a2509c981c 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3863,6 +3863,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p if (env) { state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_FILMIC_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_FILMIC); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_ACES); + state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_FITTED_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_ACES_FITTED); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINHARD_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_REINHARD); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_AUTO_EXPOSURE, env->auto_exposure); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_FILTER_BICUBIC, env->glow_bicubic_upscale); @@ -3964,6 +3965,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_AUTO_EXPOSURE, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_FILMIC_TONEMAPPER, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_TONEMAPPER, false); + state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_FITTED_TONEMAPPER, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINHARD_TONEMAPPER, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_LEVEL1, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_LEVEL2, false); diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index 6804aa613c2..71c5e21a703 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -163,6 +163,37 @@ vec3 tonemap_aces(vec3 color, float white) { return clamp(color_tonemapped / white_tonemapped, vec3(0.0f), vec3(1.0f)); } +// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl +// (MIT License). +vec3 tonemap_aces_fitted(vec3 color, float white) { + const float exposure_bias = 1.8f; + const float A = 0.0245786f; + const float B = 0.000090537f; + const float C = 0.983729f; + const float D = 0.432951f; + const float E = 0.238081f; + + // Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias` + const mat3 rgb_to_rrt = mat3( + vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias), + vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias), + vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias)); + + const mat3 odt_to_rgb = mat3( + vec3(1.60475f, -0.53108f, -0.07367f), + vec3(-0.10208f, 1.10813f, -0.00605f), + vec3(-0.00327f, -0.07276f, 1.07602f)); + + color *= rgb_to_rrt; + vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E); + color_tonemapped *= odt_to_rgb; + + white *= exposure_bias; + float white_tonemapped = (white * (white + A) - B) / (white * (C * white + D) + E); + + return clamp(color_tonemapped / white_tonemapped, vec3(0.0f), vec3(1.0f)); +} + vec3 tonemap_reinhard(vec3 color, float white) { return clamp((white * color + color) / (color * white + white), vec3(0.0f), vec3(1.0f)); } @@ -174,6 +205,12 @@ vec3 linear_to_srgb(vec3 color) { // convert linear rgb to srgb, assumes clamped // inputs are LINEAR, If Linear tonemapping is selected no transform is performed else outputs are clamped [0, 1] color vec3 apply_tonemapping(vec3 color, float white) { + // Ensure color values are positive. + // They can be negative in the case of negative lights, which leads to undesired behavior. +#if defined(USE_REINHARD_TONEMAPPER) || defined(USE_FILMIC_TONEMAPPER) || defined(USE_ACES_TONEMAPPER) || defined(USE_ACES_FITTED_TONEMAPPER) + color = max(vec3(0.0f), color); +#endif + #ifdef USE_REINHARD_TONEMAPPER return tonemap_reinhard(color, white); #endif @@ -186,6 +223,10 @@ vec3 apply_tonemapping(vec3 color, float white) { return tonemap_aces(color, white); #endif +#ifdef USE_ACES_FITTED_TONEMAPPER + return tonemap_aces_fitted(color, white); +#endif + return color; // no other selected -> linear: no color transform applied } @@ -401,9 +442,7 @@ void main() { #endif // Early Tonemap & SRGB Conversion; note that Linear tonemapping does not clamp to [0, 1]; some operations below expect a [0, 1] range and will clamp - // Ensure color values are positive. - // They can be negative in the case of negative lights, which leads to undesired behavior. - color = apply_tonemapping(max(vec3(0.0), color), white); + color = apply_tonemapping(color, white); #ifdef KEEP_3D_LINEAR // leave color as is (-> don't convert to SRGB) diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 8af3df781fb..3d6e806dc49 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -919,7 +919,7 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_grey"), &Environment::get_tonemap_auto_exposure_grey); ADD_GROUP("Tonemap", "tonemap_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES"), "set_tonemapper", "get_tonemapper"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES,ACES Fitted"), "set_tonemapper", "get_tonemapper"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white"); ADD_GROUP("Auto Exposure", "auto_exposure_"); @@ -1138,6 +1138,7 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(TONE_MAPPER_REINHARDT); BIND_ENUM_CONSTANT(TONE_MAPPER_FILMIC); BIND_ENUM_CONSTANT(TONE_MAPPER_ACES); + BIND_ENUM_CONSTANT(TONE_MAPPER_ACES_FITTED); BIND_ENUM_CONSTANT(DOF_BLUR_QUALITY_LOW); BIND_ENUM_CONSTANT(DOF_BLUR_QUALITY_MEDIUM); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index f4fb001da3e..7001ea3fc8d 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -56,7 +56,8 @@ public: TONE_MAPPER_LINEAR, TONE_MAPPER_REINHARDT, TONE_MAPPER_FILMIC, - TONE_MAPPER_ACES + TONE_MAPPER_ACES, + TONE_MAPPER_ACES_FITTED }; enum GlowBlendMode { diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 96cab1d0d40..903063e293d 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2504,6 +2504,7 @@ void VisualServer::_bind_methods() { BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_REINHARD); BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_FILMIC); BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_ACES); + BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_ACES_FITTED); BIND_ENUM_CONSTANT(ENV_SSAO_QUALITY_LOW); BIND_ENUM_CONSTANT(ENV_SSAO_QUALITY_MEDIUM); diff --git a/servers/visual_server.h b/servers/visual_server.h index 28a17caf0f0..306bb7bf5a8 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -778,7 +778,8 @@ public: ENV_TONE_MAPPER_LINEAR, ENV_TONE_MAPPER_REINHARD, ENV_TONE_MAPPER_FILMIC, - ENV_TONE_MAPPER_ACES + ENV_TONE_MAPPER_ACES, + ENV_TONE_MAPPER_ACES_FITTED }; virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_grey) = 0;