Browse Source

filter: Support HDR in CommonFilters (using ACES tonemap)

rdb 5 years ago
parent
commit
cd435ecec8
2 changed files with 88 additions and 2 deletions
  1. 83 1
      direct/src/filter/CommonFilters.py
  2. 5 1
      direct/src/filter/FilterManager.py

+ 83 - 1
direct/src/filter/CommonFilters.py

@@ -27,6 +27,7 @@ from panda3d.core import LVecBase4, LPoint2
 from panda3d.core import Filename
 from panda3d.core import Filename
 from panda3d.core import AuxBitplaneAttrib
 from panda3d.core import AuxBitplaneAttrib
 from panda3d.core import Texture, Shader, ATSNone
 from panda3d.core import Texture, Shader, ATSNone
+from panda3d.core import FrameBufferProperties
 import os
 import os
 
 
 CARTOON_BODY="""
 CARTOON_BODY="""
@@ -177,7 +178,15 @@ class CommonFilters:
                 self.textures[tex].setWrapU(Texture.WMClamp)
                 self.textures[tex].setWrapU(Texture.WMClamp)
                 self.textures[tex].setWrapV(Texture.WMClamp)
                 self.textures[tex].setWrapV(Texture.WMClamp)
 
 
-            self.finalQuad = self.manager.renderSceneInto(textures = self.textures, auxbits=auxbits)
+            fbprops = None
+            clamping = None
+            if "HighDynamicRange" in configuration:
+                fbprops = FrameBufferProperties()
+                fbprops.setFloatColor(True)
+                fbprops.setSrgbColor(False)
+                clamping = False
+
+            self.finalQuad = self.manager.renderSceneInto(textures = self.textures, auxbits=auxbits, fbprops=fbprops, clamping=clamping)
             if (self.finalQuad == None):
             if (self.finalQuad == None):
                 self.cleanup()
                 self.cleanup()
                 return False
                 return False
@@ -255,6 +264,17 @@ class CommonFilters:
             texcoordSets = list(enumerate(texcoordPadding.keys()))
             texcoordSets = list(enumerate(texcoordPadding.keys()))
 
 
             text = "//Cg\n"
             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"
             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"
 
 
@@ -301,6 +321,10 @@ class CommonFilters:
             if ("VolumetricLighting" in configuration):
             if ("VolumetricLighting" in configuration):
                 text += "  uniform float4 k_casterpos,\n"
                 text += "  uniform float4 k_casterpos,\n"
                 text += "  uniform float4 k_vlparams,\n"
                 text += "  uniform float4 k_vlparams,\n"
+
+            if ("ExposureAdjust" in configuration):
+                text += "  uniform float k_exposure,\n"
+
             text += "  out float4 o_color : COLOR)\n"
             text += "  out float4 o_color : COLOR)\n"
             text += "{\n"
             text += "{\n"
             text += "  o_color = tex2D(k_txcolor, %s);\n" % (texcoords["color"])
             text += "  o_color = tex2D(k_txcolor, %s);\n" % (texcoords["color"])
@@ -332,6 +356,14 @@ class CommonFilters:
                 text += "  }\n"
                 text += "  }\n"
                 text += "  o_color += float4(vlcolor * k_vlparams.z, 1);\n"
                 text += "  o_color += float4(vlcolor * k_vlparams.z, 1);\n"
 
 
+            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 ("GammaAdjust" in configuration):
             if ("GammaAdjust" in configuration):
                 gamma = configuration["GammaAdjust"]
                 gamma = configuration["GammaAdjust"]
                 if gamma == 0.5:
                 if gamma == 0.5:
@@ -391,6 +423,11 @@ class CommonFilters:
                 self.ssao[0].setShaderInput("params1", config.numsamples, -float(config.amount) / config.numsamples, config.radius, 0)
                 self.ssao[0].setShaderInput("params1", config.numsamples, -float(config.amount) / config.numsamples, config.radius, 0)
                 self.ssao[0].setShaderInput("params2", config.strength, config.falloff, 0, 0)
                 self.ssao[0].setShaderInput("params2", config.strength, config.falloff, 0, 0)
 
 
+        if (changed == "ExposureAdjust") or fullrebuild:
+            if ("ExposureAdjust" in configuration):
+                stops = configuration["ExposureAdjust"]
+                self.finalQuad.setShaderInput("exposure", 2 ** stops)
+
         self.update()
         self.update()
         return True
         return True
 
 
@@ -578,6 +615,47 @@ class CommonFilters:
             return self.reconfigure(old_enable, "SrgbEncode")
             return self.reconfigure(old_enable, "SrgbEncode")
         return True
         return True
 
 
+    def setHighDynamicRange(self):
+        """ Enables HDR rendering by using a floating-point framebuffer,
+        disabling color clamping on the main scene, and applying a tone map
+        operator (ACES).
+
+        It may also be necessary to use setExposureAdjust to perform exposure
+        compensation on the scene, depending on the lighting intensity.
+
+        .. versionadded:: 1.10.7
+        """
+
+        fullrebuild = (("HighDynamicRange" in self.configuration) is False)
+        self.configuration["HighDynamicRange"] = 1
+        return self.reconfigure(fullrebuild, "HighDynamicRange")
+
+    def delHighDynamicRange(self):
+        if ("HighDynamicRange" in self.configuration):
+            del self.configuration["HighDynamicRange"]
+            return self.reconfigure(True, "HighDynamicRange")
+        return True
+
+    def setExposureAdjust(self, stops):
+        """ Sets a relative exposure adjustment to multiply with the result of
+        rendering the scene, in stops.  A value of 0 means no adjustment, a
+        positive value will result in a brighter image.  Useful in conjunction
+        with HDR, see setHighDynamicRange.
+
+        .. versionadded:: 1.10.7
+        """
+        old_stops = self.configuration.get("ExposureAdjust")
+        if old_stops != stops:
+            self.configuration["ExposureAdjust"] = stops
+            return self.reconfigure(old_stops is None, "ExposureAdjust")
+        return True
+
+    def delExposureAdjust(self):
+        if ("ExposureAdjust" in self.configuration):
+            del self.configuration["ExposureAdjust"]
+            return self.reconfigure(True, "ExposureAdjust")
+        return True
+
     #snake_case alias:
     #snake_case alias:
     del_cartoon_ink = delCartoonInk
     del_cartoon_ink = delCartoonInk
     set_half_pixel_shift = setHalfPixelShift
     set_half_pixel_shift = setHalfPixelShift
@@ -599,3 +677,7 @@ class CommonFilters:
     del_gamma_adjust = delGammaAdjust
     del_gamma_adjust = delGammaAdjust
     set_srgb_encode = setSrgbEncode
     set_srgb_encode = setSrgbEncode
     del_srgb_encode = delSrgbEncode
     del_srgb_encode = delSrgbEncode
+    set_exposure_adjust = setExposureAdjust
+    del_exposure_adjust = delExposureAdjust
+    set_high_dynamic_range = setHighDynamicRange
+    del_high_dynamic_range = delHighDynamicRange

+ 5 - 1
direct/src/filter/FilterManager.py

@@ -22,6 +22,7 @@ from panda3d.core import WindowProperties, FrameBufferProperties
 from panda3d.core import Camera
 from panda3d.core import Camera
 from panda3d.core import OrthographicLens
 from panda3d.core import OrthographicLens
 from panda3d.core import AuxBitplaneAttrib
 from panda3d.core import AuxBitplaneAttrib
+from panda3d.core import LightRampAttrib
 from direct.directnotify.DirectNotifyGlobal import *
 from direct.directnotify.DirectNotifyGlobal import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 
 
@@ -124,7 +125,7 @@ class FilterManager(DirectObject):
 
 
         return winx,winy
         return winx,winy
 
 
-    def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None, fbprops=None):
+    def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None, fbprops=None, clamping=None):
 
 
         """ Causes the scene to be rendered into the supplied textures
         """ Causes the scene to be rendered into the supplied textures
         instead of into the original window.  Puts a fullscreen quad
         instead of into the original window.  Puts a fullscreen quad
@@ -207,6 +208,9 @@ class FilterManager(DirectObject):
         #cs.setShaderAuto()
         #cs.setShaderAuto()
         if (auxbits):
         if (auxbits):
             cs.setAttrib(AuxBitplaneAttrib.make(auxbits))
             cs.setAttrib(AuxBitplaneAttrib.make(auxbits))
+        if clamping is False:
+            # Disables clamping in the shader generator.
+            cs.setAttrib(LightRampAttrib.make_identity())
         self.camera.node().setInitialState(cs.getState())
         self.camera.node().setInitialState(cs.getState())
 
 
         quadcamnode = Camera("filter-quad-cam")
         quadcamnode = Camera("filter-quad-cam")