Skip to content

Commit

Permalink
Add Tony McMapface as a tonemapping mode
Browse files Browse the repository at this point in the history
  • Loading branch information
jirisvd committed Sep 24, 2024
1 parent 6681f25 commit d63c94e
Show file tree
Hide file tree
Showing 23 changed files with 230 additions and 5 deletions.
5 changes: 5 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,11 @@ Copyright: 2014-2021, Syoyo Fujita
2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC
License: BSD-3-clause

Files: ./thirdparty/tony-mc-mapface/
Comment: Tony McMapface
Copyright: 2023, Tomasz Stachowiak
License: Expat

Files: ./thirdparty/ufbx/
Comment: ufbx
Copyright: 2020, Samuli Raivio
Expand Down
3 changes: 3 additions & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ opts.Add("vsproj_name", "Name of the Visual Studio solution", "godot")
opts.Add("import_env_vars", "A comma-separated list of environment variables to copy from the outer environment.", "")
opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False))
opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
opts.Add(BoolVariable("disable_tony_mc_mapface", "Disable Tony McMapface tonemapping mode (decreases binary size by ~300 KB)", False))
opts.Add("build_profile", "Path to a file containing a feature build profile", "")
opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
Expand Down Expand Up @@ -1007,6 +1008,8 @@ if env["disable_advanced_gui"]:
Exit(255)
else:
env.Append(CPPDEFINES=["ADVANCED_GUI_DISABLED"])
if env["disable_tony_mc_mapface"]:
env.Append(CPPDEFINES=["TONY_MC_MAPFACE_DISABLED"])
if env["minizip"]:
env.Append(CPPDEFINES=["MINIZIP_ENABLED"])
if env["brotli"]:
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/Environment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@
Use the Academy Color Encoding System tonemapper. ACES is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. ACES typically has a more contrasted output compared to [constant TONE_MAPPER_REINHARDT] and [constant TONE_MAPPER_FILMIC].
[b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x.
</constant>
<constant name="TONE_MAPPER_TONY_MC_MAPFACE" value="4" enum="ToneMapper">
Tony McMapface is an artist-friendly tonemapper designed to stay close to the input color in order to achieve a neutral look that does not increase contrast or saturation. It can prevent hue shift issues common with other tonemappers. It samples its own custom lookup table (LUT) to determine the output color.
</constant>
<constant name="GLOW_BLEND_MODE_ADDITIVE" value="0" enum="GlowBlendMode">
Additive glow blending mode. Mostly used for particles, glows (bloom), lens flare, bright sources.
</constant>
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5204,6 +5204,9 @@
Use the Academy Color Encoding System tonemapper. ACES is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. ACES typically has a more contrasted output compared to [constant ENV_TONE_MAPPER_REINHARD] and [constant ENV_TONE_MAPPER_FILMIC].
[b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x.
</constant>
<constant name="ENV_TONE_MAPPER_TONY_MC_MAPFACE" value="4" enum="EnvironmentToneMapper">
Tony McMapface is an artist-friendly tonemapper designed to stay close to the input color in order to achieve a neutral look that does not increase contrast or saturation. It can prevent hue shift issues common with other tonemappers. It samples its own custom lookup table (LUT) to determine the output color.
</constant>
<constant name="ENV_SSR_ROUGHNESS_QUALITY_DISABLED" value="0" enum="EnvironmentSSRRoughnessQuality">
Lowest quality of roughness filter for screen-space reflections. Rough materials will not have blurrier screen-space reflections compared to smooth (non-rough) materials. This is the fastest option.
</constant>
Expand Down
13 changes: 13 additions & 0 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2752,6 +2752,19 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
glTexParameteri(texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
}

if (environment_get_tone_mapper(p_render_data->environment) == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE) {
RID tony_mc_mapface_lut = environment_get_tony_mc_mapface_lut(p_render_data->environment);
if (tony_mc_mapface_lut.is_valid()) {
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_3D, texture_storage->texture_get_texid(tony_mc_mapface_lut));
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
}
}

if (view_count == 1) {
Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/shaders/sky.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void main() {

#define M_PI 3.14159265359

#define SKY_SHADER
#include "tonemap_inc.glsl"

in vec2 uv_interp;
Expand Down
23 changes: 22 additions & 1 deletion drivers/gles3/shaders/tonemap_inc.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ layout(std140) uniform TonemapData { //ubo:0
float saturation;
};

uniform sampler3D tony_mc_mapface_lut; //texunit:3

// This expects 0-1 range input.
vec3 linear_to_srgb(vec3 color) {
//color = clamp(color, vec3(0.0), vec3(1.0));
Expand Down Expand Up @@ -80,10 +82,21 @@ vec3 tonemap_reinhard(vec3 color, float p_white) {
return (p_white * color + color) / (color * p_white + p_white);
}

// https://github.com/h3r2tic/tony-mc-mapface/blob/main/shader/tony_mc_mapface.hlsl
vec3 tonemap_tony_mc_mapface(vec3 stimulus) {
vec3 encoded = stimulus / (stimulus + 1.0f);

const float LUT_DIMS = 48.0f;
vec3 uv = encoded * ((LUT_DIMS - 1.0f) / LUT_DIMS) + 0.5f / LUT_DIMS;

return texture(tony_mc_mapface_lut, uv).rgb;
}

#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
#define TONEMAPPER_TONY_MC_MAPFACE 4

vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color
// Ensure color values passed to tonemappers are positive.
Expand All @@ -94,8 +107,16 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always
return tonemap_reinhard(max(vec3(0.0f), color), p_white);
} else if (tonemapper == TONEMAPPER_FILMIC) {
return tonemap_filmic(max(vec3(0.0f), color), p_white);
} else { // TONEMAPPER_ACES
} else if (tonemapper == TONEMAPPER_ACES) {
return tonemap_aces(max(vec3(0.0f), color), p_white);
} else { // TONEMAPPER_TONY_MC_MAPFACE
#ifdef SKY_SHADER
// Sampling the Tony McMapface LUT in the sky shader leads to pitch black shadows if the "Sky" background
// mode is enabled for the environment. Avoid this by returning the color as is.
return color;
#else
return tonemap_tony_mc_mapface(max(vec3(0.0f), color));
#endif
}
}

Expand Down
11 changes: 9 additions & 2 deletions scene/resources/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ void Environment::_validate_property(PropertyInfo &p_property) const {
}
}

if (p_property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) {
if (p_property.name == "tonemap_white" && (tone_mapper == TONE_MAPPER_LINEAR || tone_mapper == TONE_MAPPER_TONY_MC_MAPFACE)) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}

Expand Down Expand Up @@ -1277,8 +1277,14 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tonemap_white", "white"), &Environment::set_tonemap_white);
ClassDB::bind_method(D_METHOD("get_tonemap_white"), &Environment::get_tonemap_white);

#ifdef TONY_MC_MAPFACE_DISABLED
const char *tonemap_modes = "Linear,Reinhard,Filmic,ACES";
#else
const char *tonemap_modes = "Linear,Reinhard,Filmic,ACES,Tony McMapface";
#endif

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, tonemap_modes), "set_tonemapper", "get_tonemapper");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white");

Expand Down Expand Up @@ -1583,6 +1589,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_TONY_MC_MAPFACE);

BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_ADDITIVE);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_SCREEN);
Expand Down
1 change: 1 addition & 0 deletions scene/resources/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Environment : public Resource {
TONE_MAPPER_REINHARDT,
TONE_MAPPER_FILMIC,
TONE_MAPPER_ACES,
TONE_MAPPER_TONY_MC_MAPFACE,
};

enum SDFGIYScale {
Expand Down
14 changes: 14 additions & 0 deletions servers/rendering/renderer_rd/effects/tone_mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
u_color_correction_texture.append_id(default_sampler);
u_color_correction_texture.append_id(p_settings.color_correction_texture);

RD::Uniform u_tony_mc_mapface_lut;
u_tony_mc_mapface_lut.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_tony_mc_mapface_lut.binding = 0;
u_tony_mc_mapface_lut.append_id(default_sampler);
u_tony_mc_mapface_lut.append_id(p_settings.tony_mc_mapface_lut);

RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode);
ERR_FAIL_COND(shader.is_null());

Expand All @@ -172,6 +178,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 4, u_tony_mc_mapface_lut), 4);

RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
Expand Down Expand Up @@ -245,6 +252,12 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
u_color_correction_texture.append_id(default_sampler);
u_color_correction_texture.append_id(p_settings.color_correction_texture);

RD::Uniform u_tony_mc_mapface_lut;
u_tony_mc_mapface_lut.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_tony_mc_mapface_lut.binding = 0;
u_tony_mc_mapface_lut.append_id(default_sampler);
u_tony_mc_mapface_lut.append_id(p_settings.tony_mc_mapface_lut);

RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode);
ERR_FAIL_COND(shader.is_null());

Expand All @@ -253,6 +266,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 4, u_tony_mc_mapface_lut), 4);

RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 3u);
Expand Down
1 change: 1 addition & 0 deletions servers/rendering/renderer_rd/effects/tone_mapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class ToneMapper {
RS::EnvironmentToneMapper tonemap_mode = RS::ENV_TONE_MAPPER_LINEAR;
float exposure = 1.0;
float white = 1.0;
RID tony_mc_mapface_lut;

bool use_auto_exposure = false;
float auto_exposure_scale = 0.5;
Expand Down
17 changes: 17 additions & 0 deletions servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,11 +612,19 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende

tonemap.use_debanding = rb->get_use_debanding();
tonemap.texture_size = Vector2i(color_size.x, color_size.y);
tonemap.tony_mc_mapface_lut = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_BLACK);

if (p_render_data->environment.is_valid()) {
tonemap.tonemap_mode = environment_get_tone_mapper(p_render_data->environment);
tonemap.white = environment_get_white(p_render_data->environment);
tonemap.exposure = environment_get_exposure(p_render_data->environment);

if (tonemap.tonemap_mode == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE) {
RID tony_mc_mapface_lut = environment_get_tony_mc_mapface_lut(p_render_data->environment);
if (tony_mc_mapface_lut.is_valid()) {
tonemap.tony_mc_mapface_lut = texture_storage->texture_get_rd_texture(tony_mc_mapface_lut);
}
}
}

tonemap.use_color_correction = false;
Expand Down Expand Up @@ -706,10 +714,19 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr

RendererRD::ToneMapper::TonemapSettings tonemap;

tonemap.tony_mc_mapface_lut = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_BLACK);

if (p_render_data->environment.is_valid()) {
tonemap.tonemap_mode = environment_get_tone_mapper(p_render_data->environment);
tonemap.exposure = environment_get_exposure(p_render_data->environment);
tonemap.white = environment_get_white(p_render_data->environment);

if (tonemap.tonemap_mode == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE) {
RID tony_mc_mapface_lut = environment_get_tony_mc_mapface_lut(p_render_data->environment);
if (tony_mc_mapface_lut.is_valid()) {
tonemap.tony_mc_mapface_lut = texture_storage->texture_get_rd_texture(tony_mc_mapface_lut);
}
}
}

// We don't support glow or auto exposure here, if they are needed, don't use subpasses!
Expand Down
17 changes: 16 additions & 1 deletion servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ layout(set = 3, binding = 0) uniform sampler2D source_color_correction;
layout(set = 3, binding = 0) uniform sampler3D source_color_correction;
#endif

layout(set = 4, binding = 0) uniform sampler3D tony_mc_mapface_lut;

#define FLAG_USE_BCS (1 << 0)
#define FLAG_USE_GLOW (1 << 1)
#define FLAG_USE_AUTO_EXPOSURE (1 << 2)
Expand Down Expand Up @@ -260,6 +262,16 @@ vec3 tonemap_reinhard(vec3 color, float white) {
return (white * color + color) / (color * white + white);
}

// https://github.com/h3r2tic/tony-mc-mapface/blob/main/shader/tony_mc_mapface.hlsl
vec3 tonemap_tony_mc_mapface(vec3 stimulus) {
const vec3 encoded = stimulus / (stimulus + 1.0f);

const float LUT_DIMS = 48.0f;
const vec3 uv = encoded * ((LUT_DIMS - 1.0f) / LUT_DIMS) + 0.5f / LUT_DIMS;

return texture(tony_mc_mapface_lut, uv).rgb;
}

vec3 linear_to_srgb(vec3 color) {
//if going to srgb, clamp from 0 to 1.
color = clamp(color, vec3(0.0), vec3(1.0));
Expand All @@ -271,6 +283,7 @@ vec3 linear_to_srgb(vec3 color) {
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
#define TONEMAPPER_TONY_MC_MAPFACE 4

vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color
// Ensure color values passed to tonemappers are positive.
Expand All @@ -281,8 +294,10 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always o
return tonemap_reinhard(max(vec3(0.0f), color), white);
} else if (params.tonemapper == TONEMAPPER_FILMIC) {
return tonemap_filmic(max(vec3(0.0f), color), white);
} else { // TONEMAPPER_ACES
} else if (params.tonemapper == TONEMAPPER_ACES) {
return tonemap_aces(max(vec3(0.0f), color), white);
} else { // TONEMAPPER_TONY_MC_MAPFACE
return tonemap_tony_mc_mapface(max(vec3(0.0f), color));
}
}

Expand Down
4 changes: 4 additions & 0 deletions servers/rendering/renderer_scene_render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ float RendererSceneRender::environment_get_white(RID p_env) const {
return environment_storage.environment_get_white(p_env);
}

RID RendererSceneRender::environment_get_tony_mc_mapface_lut(RID p_env) const {
return environment_storage.environment_get_tony_mc_mapface_lut(p_env);
}

// Fog

void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode) {
Expand Down
1 change: 1 addition & 0 deletions servers/rendering/renderer_scene_render.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class RendererSceneRender {
RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const;
float environment_get_exposure(RID p_env) const;
float environment_get_white(RID p_env) const;
RID environment_get_tony_mc_mapface_lut(RID p_env) const;

// Fog
void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode);
Expand Down
8 changes: 8 additions & 0 deletions servers/rendering/storage/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@

Import("env")

import make_tony_mc_mapface_lut

env.add_source_files(env.servers_sources, "*.cpp")

env.CommandNoCache(
"tony_mc_mapface_lut.gen.h",
["make_tony_mc_mapface_lut.py", "#thirdparty/tony-mc-mapface/tony_mc_mapface.dds"],
env.Run(make_tony_mc_mapface_lut.run)
)
Loading

0 comments on commit d63c94e

Please sign in to comment.