Selaa lähdekoodia

filter: Add Khronos PBR Neutral tonemapping operator

Closes #1661
Fixes #1659

Co-authored-by: Raytopia <[email protected]>
rdb 1 vuosi sitten
vanhempi
sitoutus
42e2659636
1 muutettua tiedostoa jossa 50 lisäystä ja 18 poistoa
  1. 50 18
      direct/src/filter/CommonFilters.py

+ 50 - 18
direct/src/filter/CommonFilters.py

@@ -99,9 +99,15 @@ void fshader(out float4 o_color : COLOR,
 """
 """
 
 
 
 
+class ToneMap:
+    ACES = object()
+    PBR_NEUTRAL = object()
+
+
 class FilterConfig:
 class FilterConfig:
     pass
     pass
 
 
+
 class CommonFilters:
 class CommonFilters:
 
 
     """ Class CommonFilters implements certain common image postprocessing
     """ Class CommonFilters implements certain common image postprocessing
@@ -279,16 +285,19 @@ class CommonFilters:
 
 
             text = "//Cg\n"
             text = "//Cg\n"
             if "HighDynamicRange" in configuration:
             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 += "void vshader(float4 vtx_position : POSITION,\n"
             text += "  out float4 l_position : POSITION,\n"
             text += "  out float4 l_position : POSITION,\n"
 
 
@@ -381,10 +390,30 @@ class CommonFilters:
             if ("ExposureAdjust" in configuration):
             if ("ExposureAdjust" in configuration):
                 text += "  o_color.rgb *= k_exposure;\n"
                 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):
             if ("GammaAdjust" in configuration):
                 gamma = configuration["GammaAdjust"]
                 gamma = configuration["GammaAdjust"]
@@ -662,10 +691,10 @@ class CommonFilters:
             return self.reconfigure(old_enable, "SrgbEncode")
             return self.reconfigure(old_enable, "SrgbEncode")
         return True
         return True
 
 
-    def setHighDynamicRange(self):
+    def setHighDynamicRange(self, tonemap=ToneMap.ACES):
         """ Enables HDR rendering by using a floating-point framebuffer,
         """ Enables HDR rendering by using a floating-point framebuffer,
         disabling color clamping on the main scene, and applying a tone map
         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
         It may also be necessary to use setExposureAdjust to perform exposure
         compensation on the scene, depending on the lighting intensity.
         compensation on the scene, depending on the lighting intensity.
@@ -673,8 +702,11 @@ class CommonFilters:
         .. versionadded:: 1.10.7
         .. 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")
         return self.reconfigure(fullrebuild, "HighDynamicRange")
 
 
     def delHighDynamicRange(self):
     def delHighDynamicRange(self):