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 #87260

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions doc/classes/Environment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@
</member>
<member name="tonemap_white" type="float" setter="set_tonemap_white" getter="get_tonemap_white" default="1.0">
The white reference value for tonemapping (also called "whitepoint"). Higher values can make highlights look less blown out, and will also slightly darken the whole scene as a result. Only effective if the [member tonemap_mode] isn't set to [constant TONE_MAPPER_LINEAR]. See also [member tonemap_exposure].
[b]Note:[/b] [member tonemap_white] is ignored when using [constant TONE_MAPPER_AGX] or [constant TONE_MAPPER_AGX_PUNCHY] as the whitepoint is forced to [code]16.016004[/code] with those tonemappers.
</member>
<member name="volumetric_fog_albedo" type="Color" setter="set_volumetric_fog_albedo" getter="get_volumetric_fog_albedo" default="Color(1, 1, 1, 1)">
The [Color] of the volumetric fog when interacting with lights. Mist and fog have an albedo close to [code]Color(1, 1, 1, 1)[/code] while smoke has a darker albedo.
Expand Down Expand Up @@ -423,6 +424,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 the overall scene brightness of [constant TONE_MAPPER_FILMIC] more closely. Since AgX does not provide whitepoint adjustments, the whitepoint is forced to [code]16.016004[/code] when using AgX.
</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]. Since AgX does not provide whitepoint adjustments, the whitepoint is forced to [code]16.016004[/code] when using AgX Punchy.
</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
94 changes: 87 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,86 @@ 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) {
const mat3 agx_mat = transpose(mat3(
0.544813, 0.37379614, 0.08139087,
0.14041554, 0.75414325, 0.10544122,
0.0888119, 0.17888511, 0.73230299));

// AgX does not provide whitepoint adjustments, so hardcode it to a value that matches the Blender appearance closely.
const float white = 16.016004;

const float min_ev = -12.47393;
const 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, bool punchy) {
color = agx(color);
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 +174,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), false);
} else { // TONEMAPPER_AGX_PUNCHY
return tonemap_agx(max(vec3(0.0f), color), true);
}
}

Expand Down
7 changes: 5 additions & 2 deletions scene/resources/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,8 @@ 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_AGX || tone_mapper == TONE_MAPPER_AGX_PUNCHY)) {
// Whitepoint adjustment is not available with AgX or AgX Punchy as it's hardcoded there.
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}

Expand Down Expand Up @@ -1278,7 +1279,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 +1584,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
94 changes: 87 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,78 @@ 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) {
const mat3 agx_mat = transpose(mat3(
0.544813, 0.37379614, 0.08139087,
0.14041554, 0.75414325, 0.10544122,
0.0888119, 0.17888511, 0.73230299));

// AgX does not provide whitepoint adjustments, so hardcode it to a value that matches the Blender appearance closely.
const float white = 16.016004;

const float min_ev = -12.47393;
const 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, bool punchy) {
color = agx(color);
if (punchy) {
color = agx_look_punchy(color);
}
color = agx_eotf(color);
return color;
}

vec3 linear_to_srgb(vec3 color) {
Expand All @@ -275,6 +349,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 +361,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), false);
} else { // TONEMAPPER_AGX_PUNCHY
return tonemap_agx(max(vec3(0.0f), color), 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