filter: Add Khronos PBR Neutral tonemapping operator

Closes #1661
Fixes #1659

Co-authored-by: Raytopia <54505044+raytopianprojects@users.noreply.github.com>
This commit is contained in:
rdb 2024-10-24 15:39:43 +02:00
parent 822d35d15b
commit 42e2659636
1 changed files with 50 additions and 18 deletions

View File

@ -99,9 +99,15 @@ void fshader(out float4 o_color : COLOR,
"""
class ToneMap:
ACES = object()
PBR_NEUTRAL = object()
class FilterConfig:
pass
class CommonFilters:
""" Class CommonFilters implements certain common image postprocessing
@ -279,16 +285,19 @@ class CommonFilters:
text = "//Cg\n"
if "HighDynamicRange" in configuration:
text += "static const float3x3 aces_input_mat = {\n"
text += " {0.59719, 0.35458, 0.04823},\n"
text += " {0.07600, 0.90834, 0.01566},\n"
text += " {0.02840, 0.13383, 0.83777},\n"
text += "};\n"
text += "static const float3x3 aces_output_mat = {\n"
text += " { 1.60475, -0.53108, -0.07367},\n"
text += " {-0.10208, 1.10813, -0.00605},\n"
text += " {-0.00327, -0.07276, 1.07602},\n"
text += "};\n"
tonemap = configuration["HighDynamicRange"]
if tonemap is ToneMap.ACES:
text += "static const float3x3 aces_input_mat = {\n"
text += " {0.59719, 0.35458, 0.04823},\n"
text += " {0.07600, 0.90834, 0.01566},\n"
text += " {0.02840, 0.13383, 0.83777},\n"
text += "};\n"
text += "static const float3x3 aces_output_mat = {\n"
text += " { 1.60475, -0.53108, -0.07367},\n"
text += " {-0.10208, 1.10813, -0.00605},\n"
text += " {-0.00327, -0.07276, 1.07602},\n"
text += "};\n"
text += "void vshader(float4 vtx_position : POSITION,\n"
text += " out float4 l_position : POSITION,\n"
@ -381,10 +390,30 @@ class CommonFilters:
if ("ExposureAdjust" in configuration):
text += " o_color.rgb *= k_exposure;\n"
# With thanks to Stephen Hill!
if ("HighDynamicRange" in configuration):
text += " float3 aces_color = mul(aces_input_mat, o_color.rgb);\n"
text += " o_color.rgb = saturate(mul(aces_output_mat, (aces_color * (aces_color + 0.0245786f) - 0.000090537f) / (aces_color * (0.983729f * aces_color + 0.4329510f) + 0.238081f)));\n"
if "HighDynamicRange" in configuration:
tonemap = configuration["HighDynamicRange"]
if tonemap is ToneMap.ACES:
# With thanks to Stephen Hill!
text += " float3 aces_color = mul(aces_input_mat, o_color.rgb);\n"
text += " o_color.rgb = saturate(mul(aces_output_mat, (aces_color * (aces_color + 0.0245786f) - 0.000090537f) / (aces_color * (0.983729f * aces_color + 0.4329510f) + 0.238081f)));\n"
elif tonemap is ToneMap.PBR_NEUTRAL:
text += " const float start_compression = 0.8 - 0.04;\n"
text += " const float desaturation = 0.15;\n"
text += " float x = min(o_color.r, min(o_color.g, o_color.b));\n"
text += " float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;\n"
text += " o_color.rgb -= offset;\n"
text += " float peak = max(o_color.r, max(o_color.g, o_color.b));\n"
text += " if (peak >= start_compression) {\n"
text += " const float d = 1.0 - start_compression;\n"
text += " float new_peak = 1.0 - d * d / (peak + d - start_compression);\n"
text += " o_color.rgb *= new_peak / peak;\n"
text += " float g = 1.0 - 1.0 / (desaturation * (peak - new_peak) + 1.0);\n"
text += " o_color.rgb = lerp(o_color.rgb, new_peak * float3(1, 1, 1), g);\n"
text += "}\n"
if ("GammaAdjust" in configuration):
gamma = configuration["GammaAdjust"]
@ -662,10 +691,10 @@ class CommonFilters:
return self.reconfigure(old_enable, "SrgbEncode")
return True
def setHighDynamicRange(self):
def setHighDynamicRange(self, tonemap=ToneMap.ACES):
""" Enables HDR rendering by using a floating-point framebuffer,
disabling color clamping on the main scene, and applying a tone map
operator (ACES).
operator (ACES or Khronos PBR Neutral).
It may also be necessary to use setExposureAdjust to perform exposure
compensation on the scene, depending on the lighting intensity.
@ -673,8 +702,11 @@ class CommonFilters:
.. versionadded:: 1.10.7
"""
fullrebuild = (("HighDynamicRange" in self.configuration) is False)
self.configuration["HighDynamicRange"] = 1
fullrebuild = "HighDynamicRange" not in self.configuration or \
self.configuration["HighDynamicRange"] is not tonemap
if tonemap is not ToneMap.ACES and tonemap is not ToneMap.PBR_NEUTRAL:
raise ValueError("Invalid value for tonemap")
self.configuration["HighDynamicRange"] = tonemap
return self.reconfigure(fullrebuild, "HighDynamicRange")
def delHighDynamicRange(self):