Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AGX and AGX Punchy tonemapper options to Environment #5

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/classes/Environment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,12 @@
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_AGX" value="4" enum="ToneMapper">
Use the AgX tonemapper. AgX is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. AgX is less likely to darken parts of the scene compared to [constant TONE_MAPPER_ACES], and can match [constant TONE_MAPPER_FILMIC] more closely with whitepoint values above [code]10.0[/code].
</constant>
<constant name="TONE_MAPPER_AGX_PUNCHY" value="5" enum="ToneMapper">
Use the AgX tonemapper with the "punchy" look. AgX is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. The punchy look increases contrast and saturation, bringing its visuals closer to [constant TONE_MAPPER_ACES].
</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
6 changes: 6 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5231,6 +5231,12 @@
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_AGX" value="4" enum="EnvironmentToneMapper">
Use the AgX tonemapper. AgX is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. AgX is less likely to darken parts of the scene compared to [constant ENV_TONE_MAPPER_ACES], and can match [constant ENV_TONE_MAPPER_FILMIC] more closely with whitepoint values above [code]10.0[/code].
</constant>
<constant name="ENV_TONE_MAPPER_AGX_PUNCHY" value="5" enum="EnvironmentToneMapper">
Use the AgX tonemapper with the "punchy" look. AgX is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. The punchy look increases contrast and saturation, bringing its visuals closer to [constant ENV_TONE_MAPPER_ACES].
</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
91 changes: 84 additions & 7 deletions drivers/gles3/shaders/tonemap_inc.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ vec3 srgb_to_linear(vec3 color) {

#ifdef APPLY_TONEMAPPING

// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float p_white) {
float white_squared = p_white * p_white;
vec3 white_squared_color = white_squared * color;
// Equivalent to color * (1 + color / white_squared) / (1 + color)
return (white_squared_color + color * color) / (white_squared_color + white_squared);
}

vec3 tonemap_filmic(vec3 color, float p_white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
Expand Down Expand Up @@ -76,18 +84,83 @@ vec3 tonemap_aces(vec3 color, float p_white) {
return color_tonemapped / p_white_tonemapped;
}

// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float p_white) {
float white_squared = p_white * p_white;
vec3 white_squared_color = white_squared * color;
// Equivalent to color * (1 + color / white_squared) / (1 + color)
return (white_squared_color + color * color) / (white_squared_color + white_squared);
// Mean error^2: 3.6705141e-06
vec3 agx_default_contrast_approx(vec3 x) {
vec3 x2 = x * x;
vec3 x4 = x2 * x2;

return +15.5 * x4 * x2 - 40.14 * x4 * x + 31.96 * x4 - 6.868 * x2 * x + 0.4298 * x2 + 0.1191 * x - 0.00232;
}

vec3 agx(vec3 val, float white) {
const mat3 agx_mat = transpose(mat3(
0.544813, 0.37379614, 0.08139087,
0.14041554, 0.75414325, 0.10544122,
0.0888119, 0.17888511, 0.73230299));

const float min_ev = -12.47393f;
float max_ev = log2(white);

// Input transform (inset).
val = agx_mat * val;

// Log2 space encoding.
val = clamp(log2(val), min_ev, max_ev);
val = (val - min_ev) / (max_ev - min_ev);

// Apply sigmoid function approximation.
val = agx_default_contrast_approx(val);

return val;
}

vec3 agx_eotf(vec3 val) {
const mat3 agx_mat_inv = transpose(mat3(
1.96489403, -0.85600791, -0.10888612,
-0.29930908, 1.32639189, -0.02708281,
-0.16435644, -0.2382074, 1.40256385));

// Inverse input transform (outset).
val = agx_mat_inv * val;

// sRGB IEC 61966-2-1 2.2 Exponent Reference EOTF Display
// NOTE: We're linearizing the output here. Comment/adjust when
// *not* using a sRGB render target.
val = pow(val, vec3(2.2));

return val;
}

vec3 agx_look_punchy(vec3 val) {
const vec3 lw = vec3(0.2126, 0.7152, 0.0722);
float luma = dot(val, lw);

vec3 offset = vec3(0.0);
vec3 slope = vec3(1.0);
vec3 power = vec3(1.35, 1.35, 1.35);
float sat = 1.4;

// ASC CDL.
val = pow(val * slope + offset, power);
return luma + sat * (val - luma);
}

// Adapted from https://iolite-engine.com/blog_posts/minimal_agx_implementation
vec3 tonemap_agx(vec3 color, float white, bool punchy) {
color = agx(color, white);
if (punchy) {
color = agx_look_punchy(color);
}
color = agx_eotf(color);
return color;
}

#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
#define TONEMAPPER_AGX 4
#define TONEMAPPER_AGX_PUNCHY 5

vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
Expand All @@ -98,8 +171,12 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR
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 if (tonemapper == TONEMAPPER_AGX) {
return tonemap_agx(max(vec3(0.0f), color), p_white, false);
} else { // TONEMAPPER_AGX_PUNCHY
return tonemap_agx(max(vec3(0.0f), color), p_white, true);
}
}

Expand Down
4 changes: 3 additions & 1 deletion scene/resources/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1278,7 +1278,7 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tonemap_white"), &Environment::get_tonemap_white);

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,AgX,AgX Punchy"), "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 +1583,8 @@ 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_AGX);
BIND_ENUM_CONSTANT(TONE_MAPPER_AGX_PUNCHY);

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

enum SDFGIYScale {
Expand Down
91 changes: 84 additions & 7 deletions servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {

#endif // !USE_GLOW_FILTER_BICUBIC

// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float white) {
float white_squared = white * white;
vec3 white_squared_color = white_squared * color;
// Equivalent to color * (1 + color / white_squared) / (1 + color)
return (white_squared_color + color * color) / (white_squared_color + white_squared);
}

vec3 tonemap_filmic(vec3 color, float white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
Expand Down Expand Up @@ -256,12 +264,75 @@ vec3 tonemap_aces(vec3 color, float white) {
return color_tonemapped / white_tonemapped;
}

// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float white) {
float white_squared = white * white;
vec3 white_squared_color = white_squared * color;
// Equivalent to color * (1 + color / white_squared) / (1 + color)
return (white_squared_color + color * color) / (white_squared_color + white_squared);
// Mean error^2: 3.6705141e-06
vec3 agx_default_contrast_approx(vec3 x) {
vec3 x2 = x * x;
vec3 x4 = x2 * x2;

return +15.5 * x4 * x2 - 40.14 * x4 * x + 31.96 * x4 - 6.868 * x2 * x + 0.4298 * x2 + 0.1191 * x - 0.00232;
}

vec3 agx(vec3 val, float white) {
const mat3 agx_mat = transpose(mat3(
0.544813, 0.37379614, 0.08139087,
0.14041554, 0.75414325, 0.10544122,
0.0888119, 0.17888511, 0.73230299));

const float min_ev = -12.47393f;
float max_ev = log2(white);

// Input transform (inset).
val = agx_mat * val;

// Log2 space encoding.
val = clamp(log2(val), min_ev, max_ev);
val = (val - min_ev) / (max_ev - min_ev);

// Apply sigmoid function approximation.
val = agx_default_contrast_approx(val);

return val;
}

vec3 agx_eotf(vec3 val) {
const mat3 agx_mat_inv = transpose(mat3(
1.96489403, -0.85600791, -0.10888612,
-0.29930908, 1.32639189, -0.02708281,
-0.16435644, -0.2382074, 1.40256385));

// Inverse input transform (outset).
val = agx_mat_inv * val;

// sRGB IEC 61966-2-1 2.2 Exponent Reference EOTF Display
// NOTE: We're linearizing the output here. Comment/adjust when
// *not* using a sRGB render target.
val = pow(val, vec3(2.2));

return val;
}

vec3 agx_look_punchy(vec3 val) {
const vec3 lw = vec3(0.2126, 0.7152, 0.0722);
float luma = dot(val, lw);

vec3 offset = vec3(0.0);
vec3 slope = vec3(1.0);
vec3 power = vec3(1.35, 1.35, 1.35);
float sat = 1.4;

// ASC CDL.
val = pow(val * slope + offset, power);
return luma + sat * (val - luma);
}

// Adapted from https://iolite-engine.com/blog_posts/minimal_agx_implementation
vec3 tonemap_agx(vec3 color, float white, bool punchy) {
color = agx(color, white);
if (punchy) {
color = agx_look_punchy(color);
}
color = agx_eotf(color);
return color;
}

vec3 linear_to_srgb(vec3 color) {
Expand All @@ -275,6 +346,8 @@ vec3 linear_to_srgb(vec3 color) {
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
#define TONEMAPPER_AGX 4
#define TONEMAPPER_AGX_PUNCHY 5

vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
Expand All @@ -285,8 +358,12 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR
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 if (params.tonemapper == TONEMAPPER_AGX) {
return tonemap_agx(max(vec3(0.0f), color), white, false);
} else { // TONEMAPPER_AGX_PUNCHY
return tonemap_agx(max(vec3(0.0f), color), white, true);
}
}

Expand Down
2 changes: 2 additions & 0 deletions servers/rendering_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3041,6 +3041,8 @@ void RenderingServer::_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_AGX);
BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_AGX_PUNCHY);

BIND_ENUM_CONSTANT(ENV_SSR_ROUGHNESS_QUALITY_DISABLED);
BIND_ENUM_CONSTANT(ENV_SSR_ROUGHNESS_QUALITY_LOW);
Expand Down
4 changes: 3 additions & 1 deletion servers/rendering_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -1183,7 +1183,9 @@ class RenderingServer : public Object {
ENV_TONE_MAPPER_LINEAR,
ENV_TONE_MAPPER_REINHARD,
ENV_TONE_MAPPER_FILMIC,
ENV_TONE_MAPPER_ACES
ENV_TONE_MAPPER_ACES,
ENV_TONE_MAPPER_AGX,
ENV_TONE_MAPPER_AGX_PUNCHY,
};

virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0;
Expand Down
Loading