Explorar o código

General development of FilterManager and CommonFiltes

Josh Yelon %!s(int64=18) %!d(string=hai) anos
pai
achega
6ac5500f76

+ 141 - 32
direct/src/filter/CommonFilters.py

@@ -18,30 +18,19 @@ clunky approach.  - Josh
 from FilterManager import FilterManager
 from FilterManager import FilterManager
 from pandac.PandaModules import Point3, Vec3, Vec4
 from pandac.PandaModules import Point3, Vec3, Vec4
 from pandac.PandaModules import NodePath, PandaNode
 from pandac.PandaModules import NodePath, PandaNode
+from pandac.PandaModules import Filename
 from pandac.PandaModules import RenderState, Texture, Shader
 from pandac.PandaModules import RenderState, Texture, Shader
-
-HEADER = """//Cg
-
-void vshader(float4 vtx_position : POSITION,
-             out float4 l_position : POSITION,
-             out float4 l_texcoord : TEXCOORD0,
-             uniform float4 texpad_txcolor,
-             uniform float4x4 mat_modelproj)
-{
-  l_position=mul(mat_modelproj, vtx_position);
-  l_texcoord=(vtx_position.xzxz * texpad_txcolor) + texpad_txcolor;
-}
-"""
+import sys,os
 
 
 CARTOON_BODY="""
 CARTOON_BODY="""
-float4 cartoondelta = k_cartoonseparation * texpix_txcolor.xwyw;
-float4 cartoon_p0 = l_texcoord + cartoondelta.xyzw;
+float4 cartoondelta = k_cartoonseparation * texpix_txnormal.xwyw;
+float4 cartoon_p0 = l_texcoordN + cartoondelta.xyzw;
 float4 cartoon_c0 = tex2D(k_txnormal, cartoon_p0.xy);
 float4 cartoon_c0 = tex2D(k_txnormal, cartoon_p0.xy);
-float4 cartoon_p1 = l_texcoord - cartoondelta.xyzw;
+float4 cartoon_p1 = l_texcoordN - cartoondelta.xyzw;
 float4 cartoon_c1 = tex2D(k_txnormal, cartoon_p1.xy);
 float4 cartoon_c1 = tex2D(k_txnormal, cartoon_p1.xy);
-float4 cartoon_p2 = l_texcoord + cartoondelta.wzyx;
+float4 cartoon_p2 = l_texcoordN + cartoondelta.wzyx;
 float4 cartoon_c2 = tex2D(k_txnormal, cartoon_p2.xy);
 float4 cartoon_c2 = tex2D(k_txnormal, cartoon_p2.xy);
-float4 cartoon_p3 = l_texcoord - cartoondelta.wzyx;
+float4 cartoon_p3 = l_texcoordN - cartoondelta.wzyx;
 float4 cartoon_c3 = tex2D(k_txnormal, cartoon_p3.xy);
 float4 cartoon_c3 = tex2D(k_txnormal, cartoon_p3.xy);
 float4 cartoon_mx = max(cartoon_c0,max(cartoon_c1,max(cartoon_c2,cartoon_c3)));
 float4 cartoon_mx = max(cartoon_c0,max(cartoon_c1,max(cartoon_c2,cartoon_c3)));
 float4 cartoon_mn = min(cartoon_c0,min(cartoon_c1,min(cartoon_c2,cartoon_c3)));
 float4 cartoon_mn = min(cartoon_c0,min(cartoon_c1,min(cartoon_c2,cartoon_c3)));
@@ -50,10 +39,9 @@ float  cartoon_thresh = dot(cartoon_trigger.xyz,float3(1,1,1));
 o_color = lerp(o_color, float4(0,0,0,1), cartoon_thresh);
 o_color = lerp(o_color, float4(0,0,0,1), cartoon_thresh);
 """
 """
 
 
-CARTOON_PARAMS="""
-uniform float4 k_cartoonseparation,
-uniform float4 k_cartooncutoff,
-"""
+class FilterConfig:
+    pass
+
 class CommonFilters:
 class CommonFilters:
 
 
     """ Class CommonFilters implements certain common image postprocessing
     """ Class CommonFilters implements certain common image postprocessing
@@ -64,10 +52,16 @@ class CommonFilters:
         self.configuration = {}
         self.configuration = {}
         self.cleanup()
         self.cleanup()
 
 
+    def loadShader(self, name):
+        fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), name)
+        print "loading shader: ", dir
+        return Shader.load(Filename.fromOsSpecific(fn))
+
     def cleanup(self):
     def cleanup(self):
         self.manager.cleanup()
         self.manager.cleanup()
         self.textures = {}
         self.textures = {}
         self.finalQuad = None
         self.finalQuad = None
+        self.bloom = []
 
 
     def reconfigure(self, fullrebuild, changed):
     def reconfigure(self, fullrebuild, changed):
 
 
@@ -82,31 +76,98 @@ class CommonFilters:
             if (len(configuration) == 0):
             if (len(configuration) == 0):
                 return
                 return
 
 
-            needtexpix = False
             needtex = {}
             needtex = {}
             needtex["color"] = True
             needtex["color"] = True
             if (configuration.has_key("CartoonInk")):
             if (configuration.has_key("CartoonInk")):
                 needtex["normal"] = True
                 needtex["normal"] = True
+            if (configuration.has_key("Bloom")):
+                needtex["bloom0"] = True
+                needtex["bloom1"] = True
+                needtex["bloom2"] = True
+                needtex["bloom3"] = True
             for tex in needtex:
             for tex in needtex:
                 self.textures[tex] = Texture("scene-"+tex)
                 self.textures[tex] = Texture("scene-"+tex)
                 needtexpix = True
                 needtexpix = True
 
 
             self.finalQuad = self.manager.renderSceneInto(textures = self.textures)
             self.finalQuad = self.manager.renderSceneInto(textures = self.textures)
     
     
-            text = HEADER
+            if (configuration.has_key("Bloom")):
+                bloomconf = configuration["Bloom"]
+                bloom0=self.textures["bloom0"]
+                bloom1=self.textures["bloom1"]
+                bloom2=self.textures["bloom2"]
+                bloom3=self.textures["bloom3"]
+                if (bloomconf.size == "large"):
+                    scale=8
+                    downsampler="filter-down4.sha"
+                elif (bloomconf.size == "medium"):
+                    scale=4
+                    downsampler="filter-copy.sha"
+                else:
+                    scale=2
+                    downsampler="filter-copy.sha"
+                self.bloom.append(self.manager.renderQuadInto(colortex=bloom0, div=2,     align=scale))
+                self.bloom.append(self.manager.renderQuadInto(colortex=bloom1, div=scale, align=scale))
+                self.bloom.append(self.manager.renderQuadInto(colortex=bloom2, div=scale, align=scale))
+                self.bloom.append(self.manager.renderQuadInto(colortex=bloom3, div=scale, align=scale))
+                self.bloom[0].setShaderInput("src", self.textures["color"])
+                self.bloom[0].setShader(self.loadShader("filter-bloomi.sha"))
+                self.bloom[1].setShaderInput("src", bloom0)
+                self.bloom[1].setShader(self.loadShader(downsampler))
+                self.bloom[2].setShaderInput("src", bloom1)
+                self.bloom[2].setShader(self.loadShader("filter-bloomx.sha"))
+                self.bloom[3].setShaderInput("src", bloom2)
+                self.bloom[3].setShader(self.loadShader("filter-bloomy.sha"))
+
+            text = "//Cg\n"
+            text += "void vshader(float4 vtx_position : POSITION,\n"
+            text += " out float4 l_position : POSITION,\n"
+            text += " uniform float4 texpad_txcolor,\n"
+            text += " uniform float4 texpix_txcolor,\n"
+            text += " out float4 l_texcoordC : TEXCOORD0,\n"
+            if (configuration.has_key("CartoonInk")):
+                text += " uniform float4 texpad_txnormal,\n"
+                text += " uniform float4 texpix_txnormal,\n"
+                text += " out float4 l_texcoordN : TEXCOORD1,\n"
+            if (configuration.has_key("Bloom")):
+                text += " uniform float4 texpad_txbloom3,\n"
+                text += " out float4 l_texcoordB : TEXCOORD2,\n"
+            text += " uniform float4x4 mat_modelproj)\n"
+            text += "{\n"
+            text += " l_position=mul(mat_modelproj, vtx_position);\n"
+            text += " l_texcoordC=(vtx_position.xzxz * texpad_txcolor) + texpad_txcolor;\n"
+            if (configuration.has_key("CartoonInk")):
+                text += " l_texcoordN=(vtx_position.xzxz * texpad_txnormal) + texpad_txnormal;\n"
+            if (configuration.has_key("Bloom")):
+                text += " l_texcoordB=(vtx_position.xzxz * texpad_txbloom3) + texpad_txbloom3;\n"
+            if (configuration.has_key("HalfPixelShift")):
+                text += " l_texcoordC+=texpix_txcolor*0.5;\n"
+                if (configuration.has_key("CartoonInk")):
+                    text += " l_texcoordN+=texpix_txnormal*0.5;\n"
+            text += "}\n"
+
             text += "void fshader(\n"
             text += "void fshader(\n"
-            text += "float4 l_texcoord : TEXCOORD0,\n"
-            if (needtexpix):
-                text += "uniform float4 texpix_txcolor,\n"
+            text += "float4 l_texcoordC : TEXCOORD0,\n"
+            text += "uniform float4 texpix_txcolor,\n"
+            if (configuration.has_key("CartoonInk")):
+                text += "float4 l_texcoordN : TEXCOORD1,\n"
+                text += "uniform float4 texpix_txnormal,\n"
+            if (configuration.has_key("Bloom")):
+                text += "float4 l_texcoordB : TEXCOORD2,\n"
             for key in self.textures:
             for key in self.textures:
                 text += "uniform sampler2D k_tx" + key + ",\n"
                 text += "uniform sampler2D k_tx" + key + ",\n"
             if (configuration.has_key("CartoonInk")):
             if (configuration.has_key("CartoonInk")):
-                text += CARTOON_PARAMS
+                text += "uniform float4 k_cartoonseparation,\n"
+                text += "uniform float4 k_cartooncutoff,\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, l_texcoord.xy);\n"
+            text += " o_color = tex2D(k_txcolor, l_texcoordC.xy);\n"
             if (configuration.has_key("CartoonInk")):
             if (configuration.has_key("CartoonInk")):
                 text += CARTOON_BODY
                 text += CARTOON_BODY
+            if (configuration.has_key("Bloom")):
+                text += "o_color = saturate(o_color);\n";
+                text += "float4 bloom = 0.5*tex2D(k_txbloom3, l_texcoordB.xy);\n"
+                text += "o_color = 1-((1-bloom)*(1-o_color));\n"
             text += "}\n"
             text += "}\n"
     
     
             print "Using shader: ", text
             print "Using shader: ", text
@@ -120,6 +181,15 @@ class CommonFilters:
                 self.finalQuad.setShaderInput("cartoonseparation", Vec4(separation,0,separation,0))
                 self.finalQuad.setShaderInput("cartoonseparation", Vec4(separation,0,separation,0))
                 self.finalQuad.setShaderInput("cartooncutoff", Vec4(cutoff,cutoff,cutoff,cutoff))
                 self.finalQuad.setShaderInput("cartooncutoff", Vec4(cutoff,cutoff,cutoff,cutoff))
 
 
+        if (changed == "Bloom") or fullrebuild:
+            if (configuration.has_key("Bloom")):
+                bloomconf = configuration["Bloom"]
+                intensity = bloomconf.intensity * 3.0
+                self.bloom[0].setShaderInput("blend", bloomconf.blendx, bloomconf.blendy, bloomconf.blendz, bloomconf.blendw)
+                self.bloom[0].setShaderInput("trigger", bloomconf.mintrigger, 1.0/(bloomconf.maxtrigger-bloomconf.mintrigger), 0.0, 0.0)
+                self.bloom[0].setShaderInput("desat", bloomconf.desat)
+                self.bloom[3].setShaderInput("intensity", intensity, intensity, intensity, intensity)
+
     def setCartoonInk(self, separation=1, cutoff=0.3):
     def setCartoonInk(self, separation=1, cutoff=0.3):
         fullrebuild = (self.configuration.has_key("CartoonInk") == False)
         fullrebuild = (self.configuration.has_key("CartoonInk") == False)
         self.configuration["CartoonInk"] = (separation, cutoff)
         self.configuration["CartoonInk"] = (separation, cutoff)
@@ -128,6 +198,45 @@ class CommonFilters:
     def delCartoonInk(self):
     def delCartoonInk(self):
         if (self.configuration.has_key("CartoonInk")):
         if (self.configuration.has_key("CartoonInk")):
             del self.configuration["CartoonInk"]
             del self.configuration["CartoonInk"]
-            self.reconfigure(True)
-
-
+            self.reconfigure(True, "CartoonInk")
+
+    def setBloom(self, blend=(0.3,0.4,0.3,0.0), mintrigger=0.6, maxtrigger=1.0, desat=0.6, intensity=1.0, size="medium"):
+        if (maxtrigger==None): maxtrigger=mintrigger+0.8
+        oldconfig = self.configuration.get("Bloom", None)
+        fullrebuild = True
+        if (oldconfig) and (oldconfig.size == size):
+            fullrebuild = False
+        newconfig = FilterConfig()
+        (newconfig.blendx, newconfig.blendy, newconfig.blendz, newconfig.blendw) = blend
+        newconfig.maxtrigger = maxtrigger
+        newconfig.mintrigger = mintrigger
+        newconfig.desat = desat
+        newconfig.intensity = intensity
+        newconfig.size = size
+        self.configuration["Bloom"] = newconfig
+        self.reconfigure(fullrebuild, "Bloom")
+
+    def delBloom(self):
+        if (self.configuration.has_key("Bloom")):
+            del self.configuration["Bloom"]
+            self.reconfigure(True, "Bloom")
+
+    def setHalfPixelShift(self):
+        fullrebuild = (self.configuration.has_key("HalfPixelShift") == False)
+        self.configuration["HalfPixelShift"] = 1
+        self.reconfigure(fullrebuild, "HalfPixelShift")
+
+    def delHalfPixelShift(self):
+        if (self.configuration.has_key("HalfPixelShift")):
+            del self.configuration["HalfPixelShift"]
+            self.reconfigure(True, "HalfPixelShift")
+
+    def setFSBloom(self):
+        fullrebuild = (self.configuration.has_key("FSBloom") == False)
+        self.configuration["FSBloom"] = 1
+        self.reconfigure(fullrebuild, "FSBloom")
+
+    def delFSBloom(self):
+        if (self.configuration.has_key("FSBloom")):
+            del self.configuration["FSBloom"]
+            self.reconfigure(True, "FSBloom")

+ 134 - 16
direct/src/filter/FilterManager.py

@@ -61,10 +61,63 @@ class FilterManager:
         self.win = win
         self.win = win
         self.engine = win.getGsg().getEngine()
         self.engine = win.getGsg().getEngine()
         self.region = region
         self.region = region
+        self.wclears = self.getClears(self.win)
+        self.rclears = self.getClears(self.region)
         self.camera = cam
         self.camera = cam
         self.caminit = cam.node().getInitialState()
         self.caminit = cam.node().getInitialState()
-        self.scales = []
         self.buffers = []
         self.buffers = []
+        self.sizes = []
+        self.nextsort = self.win.getSort() - 1000
+        self.basex = 0
+        self.basey = 0
+
+
+    def getClears(self,region):
+        clears = []
+        for i in range(GraphicsOutput.RTPCOUNT):
+            clears.append((region.getClearActive(i), region.getClearValue(i)))
+        return clears
+
+    def setClears(self,region,clears):
+        for i in range(GraphicsOutput.RTPCOUNT):
+            (active, value) = clears[i]
+            region.setClearActive(i, active)
+            region.setClearValue(i, value)
+
+    def setStackedClears(self, region, clears0, clears1):
+        clears = []
+        for i in range(GraphicsOutput.RTPCOUNT):
+            (active, value) = clears0[i]
+            if (active == 0):
+                (active, value) = clears1[i]
+            region.setClearActive(i, active)
+            region.setClearValue(i, value)
+        return clears
+
+    def isFullscreen(self):
+        return ((self.region.getLeft()   == 0.0) and
+                (self.region.getRight()  == 1.0) and
+                (self.region.getBottom() == 0.0) and
+                (self.region.getTop()    == 1.0))
+            
+    def getScaledSize(self, mul, div, align):
+
+        """ Calculate the size of the desired window. Not public. """
+
+        winx = self.win.getXSize()
+        winy = self.win.getYSize()
+
+        if (div != 1):
+            winx = ((winx+align-1) / align) * align
+            winy = ((winy+align-1) / align) * align
+            winx = winx / div
+            winy = winy / div
+
+        if (mul != 1):
+            winx = winx * mul
+            winy = winy * mul
+
+        return winx,winy
 
 
     def renderSceneInto(self, depthtex=False, colortex=False, normaltex=False, textures=None):
     def renderSceneInto(self, depthtex=False, colortex=False, normaltex=False, textures=None):
 
 
@@ -77,9 +130,9 @@ class FilterManager:
         To elaborate on how this all works:
         To elaborate on how this all works:
 
 
         * An offscreen buffer is created.  It is set up to mimic
         * An offscreen buffer is created.  It is set up to mimic
-          the original window - it is the same size, uses the
-          same clear colors, and contains a DisplayRegion that
-          uses the original camera.
+          the original display region - it is the same size,
+          uses the same clear colors, and contains a DisplayRegion
+          that uses the original camera.
 
 
         * A fullscreen quad and an orthographic camera to render
         * A fullscreen quad and an orthographic camera to render
           that quad are both created.  The original camera is
           that quad are both created.  The original camera is
@@ -90,6 +143,18 @@ class FilterManager:
           offscreen buffer.  A shader is applied that tints the
           offscreen buffer.  A shader is applied that tints the
           results pink.
           results pink.
 
 
+        * Automatic shader generation is enabled by default for
+          the main camera.  You can override this by setting
+          shaders on individual nodes, or on the root of your
+          scene graph, but if you do, your own shaders need to
+          generate the outputs that the filter manager is expecting.
+
+        * All clears are disabled on the original display region.
+          If the display region fills the whole window, then clears
+          are disabled on the original window as well.  It is
+          assumed that rendering the full-screen quad eliminates
+          the need to do clears.
+
         Hence, the original window which used to contain the actual
         Hence, the original window which used to contain the actual
         scene, now contains a pink-tinted quad with a texture of the
         scene, now contains a pink-tinted quad with a texture of the
         scene.  It is assumed that the user will replace the shader
         scene.  It is assumed that the user will replace the shader
@@ -104,7 +169,11 @@ class FilterManager:
 
 
         texgroup = (depthtex, colortex, normaltex, None)
         texgroup = (depthtex, colortex, normaltex, None)
 
 
-        buffer = self.createBuffer("filter-base", base.win.getXSize(), base.win.getYSize(), texgroup)
+        # Choose the size of the offscreen buffer.
+
+        winx = self.win.getXSize()
+        winy = self.win.getYSize()
+        buffer = self.createBuffer("filter-base", winx, winy, texgroup)
 
 
         if (buffer == None):
         if (buffer == None):
             return None
             return None
@@ -137,26 +206,68 @@ class FilterManager:
         
         
         self.region.setCamera(quadcam)
         self.region.setCamera(quadcam)
 
 
-        buffer.getDisplayRegion(0).setCamera(self.camera)
-        buffer.getDisplayRegion(0).setActive(1)
+        dr = buffer.getDisplayRegion(0)
+        self.setStackedClears(dr, self.rclears, self.wclears)
+        if (normaltex):
+            dr.setClearActive(GraphicsOutput.RTPAuxRgba0, 1)
+            dr.setClearValue(GraphicsOutput.RTPAuxRgba0, Vec4(0.0,0.0,0.0,0.0))
+        self.region.disableClears()
+        if (self.isFullscreen()):
+            self.win.disableClears()
+        dr.setCamera(self.camera)
+        dr.setActive(1)
 
 
-        self.scales.append(1)
         self.buffers.append(buffer)
         self.buffers.append(buffer)
+        self.sizes.append((1, 1, 1))
 
 
         return quad
         return quad
 
 
-    def renderQuadInto(self, scale=1, depthtex=None, colortex=None, auxtex0=None, auxtex1=None):
+    def renderQuadInto(self, mul=1, div=1, align=1, depthtex=None, colortex=None, auxtex0=None, auxtex1=None):
 
 
         """ Creates an offscreen buffer for an intermediate
         """ Creates an offscreen buffer for an intermediate
         computation. Installs a quad into the buffer.  Returns
         computation. Installs a quad into the buffer.  Returns
-        the fullscreen quad. """
+        the fullscreen quad.  The size of the buffer is initially
+        equal to the size of the main window.  The parameters 'mul',
+        'div', and 'align' can be used to adjust that size. """
+
+        texgroup = (depthtex, colortex, auxtex0, auxtex1)
+
+        winx, winy = self.getScaledSize(mul, div, align)
+        
+        buffer = self.createBuffer("filter-stage", winx, winy, texgroup, depthbits=0)
+
+        if (buffer == None):
+            return None
 
 
-        self.notify.error('renderQuadInto not implemented yet.')
-        return None
+        cm = CardMaker("filter-stage-quad")
+        cm.setFrameFullscreenQuad()
+        quad = NodePath(cm.generate())
+        quad.setDepthTest(0)
+        quad.setDepthWrite(0)
+        quad.setColor(Vec4(1,0.5,0.5,1))
 
 
-    def createBuffer(self, name, xsize, ysize, texgroup):
+        quadcamnode = Camera("filter-quad-cam")
+        lens = OrthographicLens()
+        lens.setFilmSize(2, 2)
+        lens.setFilmOffset(0, 0)
+        lens.setNearFar(-1000, 1000)
+        quadcamnode.setLens(lens)
+        quadcam = quad.attachNewNode(quadcamnode)
+        
+        buffer.getDisplayRegion(0).setCamera(quadcam)
+        buffer.getDisplayRegion(0).setActive(1)
+
+        self.buffers.append(buffer)
+        self.sizes.append((1, 1, 1))
+        
+        return quad
+
+    def createBuffer(self, name, xsize, ysize, texgroup, depthbits=1):
         """ Low-level buffer creation.  Not intended for public use. """
         """ Low-level buffer creation.  Not intended for public use. """
+
+        print "Creating buffer: ",xsize,ysize,texgroup,depthbits
         winprops = WindowProperties()
         winprops = WindowProperties()
+        winprops.setSize(xsize, ysize)
         props = FrameBufferProperties()
         props = FrameBufferProperties()
         props.setRgbColor(1)
         props.setRgbColor(1)
         props.setDepthBits(1)
         props.setDepthBits(1)
@@ -177,6 +288,10 @@ class FilterManager:
             buffer.addRenderTexture(auxtex0, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0)
             buffer.addRenderTexture(auxtex0, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0)
         if (auxtex1):
         if (auxtex1):
             buffer.addRenderTexture(auxtex1, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba1)
             buffer.addRenderTexture(auxtex1, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba1)
+        buffer.setSort(self.nextsort)
+        buffer.disableClears()
+        buffer.getDisplayRegion(0).disableClears()
+        self.nextsort += 1
         return buffer
         return buffer
 
 
 
 
@@ -187,9 +302,12 @@ class FilterManager:
         for buffer in self.buffers:
         for buffer in self.buffers:
             buffer.clearRenderTextures()
             buffer.clearRenderTextures()
             self.engine.removeWindow(buffer)
             self.engine.removeWindow(buffer)
-        self.scales = []
         self.buffers = []
         self.buffers = []
-        self.region.setCamera(self.camera)
+        self.sizes = []
+        self.setClears(self.win, self.wclears)
+        self.setClears(self.region, self.rclears)
         self.camera.node().setInitialState(self.caminit)
         self.camera.node().setInitialState(self.caminit)
-
+        self.nextsort = self.win.getSort() - 1000
+        self.basex = 0
+        self.basey = 0
 
 

+ 85 - 0
direct/src/filter/filter-bloomi.sha

@@ -0,0 +1,85 @@
+//Cg
+//
+// blend.rgb
+//
+//   This shader converts to black-and-white before calculating
+//   scene brightness.  To do this, it uses a weighted average of
+//   R,G,B.  The blend parameter controls the weighting.
+//
+// desat.x
+//
+//   Desaturation level.  If zero, the bloom's color is equal to
+//   the color of the input pixel.  If one, the bloom's color is
+//   white.
+//
+// trigger.x
+//
+//   Must be equal to mintrigger.
+//
+//   mintrigger is the minimum brightness to trigger a bloom,
+//   and maxtrigger is the brightness at which the bloom
+//   reaches maximum intensity.
+//
+// trigger.y
+//
+//   Must be equal to (1.0/(maxtrigger-mintrigger)) where
+//   
+//   mintrigger is the minimum brightness to trigger a bloom,
+//   and maxtrigger is the brightness at which the bloom
+//   reaches maximum intensity.
+//
+
+
+void vshader(float4 vtx_position : POSITION,
+             out float4 l_position : POSITION,
+             out float2 l_texcoordNW : TEXCOORD0,
+             out float2 l_texcoordNE : TEXCOORD1,
+             out float2 l_texcoordSW : TEXCOORD2,
+             out float2 l_texcoordSE : TEXCOORD3,
+             uniform float4 texpad_src,
+             uniform float4 texpix_src,
+             uniform float4x4 mat_modelproj)
+{
+  l_position=mul(mat_modelproj, vtx_position);
+  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  float4 offs = texpix_src * 0.5;
+  l_texcoordNW = c + float2( offs.x, -offs.y);
+  l_texcoordNE = c + float2( offs.x,  offs.y);
+  l_texcoordSW = c + float2(-offs.x, -offs.y);
+  l_texcoordSE = c + float2(-offs.x,  offs.y);
+}
+
+void fshader(float2 l_texcoordNW : TEXCOORD0,
+             float2 l_texcoordNE : TEXCOORD1,
+             float2 l_texcoordSW : TEXCOORD2,
+             float2 l_texcoordSE : TEXCOORD3,
+             uniform sampler2D k_src : TEXUNIT0,
+             out float4 o_color : COLOR,
+             uniform float4 k_blend,
+             uniform float4 k_trigger,
+             uniform float4 k_desat
+             )
+{
+  float4 inputNW = tex2D(k_src, l_texcoordNW);
+  float briteNW = dot(inputNW, k_blend);
+  float scaleNW = saturate((briteNW - k_trigger.x) * k_trigger.y);
+  float4 colorNW = scaleNW * lerp(inputNW, float4(1,1,1,1), k_desat.x);
+
+  float4 inputNE = tex2D(k_src, l_texcoordNE);
+  float briteNE = dot(inputNE, k_blend);
+  float scaleNE = saturate((briteNE - k_trigger.x) * k_trigger.y);
+  float4 colorNE = scaleNE * lerp(inputNE, float4(1,1,1,1), k_desat.x);
+
+  float4 inputSW = tex2D(k_src, l_texcoordSW);
+  float briteSW = dot(inputSW, k_blend);
+  float scaleSW = saturate((briteSW - k_trigger.x) * k_trigger.y);
+  float4 colorSW = scaleSW * lerp(inputSW, float4(1,1,1,1), k_desat.x);
+
+  float4 inputSE = tex2D(k_src, l_texcoordSE);
+  float briteSE = dot(inputSE, k_blend);
+  float scaleSE = saturate((briteSE - k_trigger.x) * k_trigger.y);
+  float4 colorSE = scaleSE * lerp(inputSE, float4(1,1,1,1), k_desat.x);
+
+  o_color = (colorNW + colorNE + colorSW + colorSE) * 0.25;
+}
+

+ 47 - 0
direct/src/filter/filter-bloomx.sha

@@ -0,0 +1,47 @@
+//Cg
+
+void vshader(float4 vtx_position : POSITION,
+             out float4 l_position : POSITION,
+             out float4 l_texcoord0 : TEXCOORD0,
+             out float4 l_texcoord1 : TEXCOORD1,
+             out float4 l_texcoord2 : TEXCOORD2,
+             uniform float4 texpad_src,
+             uniform float4 texpix_src,
+             uniform float4x4 mat_modelproj)
+{
+  l_position=mul(mat_modelproj, vtx_position);
+  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  float offset = texpix_src.x;
+  l_texcoord0 = float4(c.x-offset* -4, c.x-offset* -3, c.x-offset* -2, c.y);
+  l_texcoord1 = float4(c.x-offset* -1, c.x-offset*  0, c.x-offset*  1, c.y);
+  l_texcoord2 = float4(c.x-offset*  2, c.x-offset*  3, c.x-offset*  4, c.y);
+}
+
+void fshader(float4 l_texcoord0 : TEXCOORD0,
+             float4 l_texcoord1 : TEXCOORD1,
+             float4 l_texcoord2 : TEXCOORD2,
+             uniform sampler2D k_src : TEXUNIT0,
+             out float4 o_color : COLOR) {
+  float4 color = float4(0,0,0,0);
+//  color  =  10 * tex2D(k_src, l_texcoord0.xw);
+//  color +=  45 * tex2D(k_src, l_texcoord0.yw);
+//  color += 120 * tex2D(k_src, l_texcoord0.zw);
+//  color += 210 * tex2D(k_src, l_texcoord1.xw);
+//  color += 252 * tex2D(k_src, l_texcoord1.yw);
+//  color += 210 * tex2D(k_src, l_texcoord1.zw);
+//  color += 120 * tex2D(k_src, l_texcoord2.xw);
+//  color +=  45 * tex2D(k_src, l_texcoord2.yw);
+//  color +=  10 * tex2D(k_src, l_texcoord2.zw);
+//  o_color = color / 1022.0;
+
+  color  =  50 * tex2D(k_src, l_texcoord0.xw);
+  color += 100 * tex2D(k_src, l_texcoord0.yw);
+  color += 150 * tex2D(k_src, l_texcoord0.zw);
+  color += 200 * tex2D(k_src, l_texcoord1.xw);
+  color += 200 * tex2D(k_src, l_texcoord1.yw);
+  color += 200 * tex2D(k_src, l_texcoord1.zw);
+  color += 150 * tex2D(k_src, l_texcoord2.xw);
+  color += 100 * tex2D(k_src, l_texcoord2.yw);
+  color +=  50 * tex2D(k_src, l_texcoord2.zw);
+  o_color = color / 1200.0;
+}

+ 38 - 0
direct/src/filter/filter-bloomy.sha

@@ -0,0 +1,38 @@
+//Cg
+
+void vshader(float4 vtx_position : POSITION,
+             out float4 l_position : POSITION,
+             out float4 l_texcoord0 : TEXCOORD0,
+             out float4 l_texcoord1 : TEXCOORD1,
+             out float4 l_texcoord2 : TEXCOORD2,
+             uniform float4 texpad_src,
+             uniform float4 texpix_src,
+             uniform float4x4 mat_modelproj)
+{
+  l_position=mul(mat_modelproj, vtx_position);
+  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  float offset = texpix_src.y;
+  l_texcoord0 = float4(c.y-offset* -4, c.y-offset* -3, c.y-offset* -2, c.x);
+  l_texcoord1 = float4(c.y-offset* -1, c.y-offset*  0, c.y-offset*  1, c.x);
+  l_texcoord2 = float4(c.y-offset*  2, c.y-offset*  3, c.y-offset*  4, c.x);
+}
+
+void fshader(float4 l_texcoord0 : TEXCOORD0,
+             float4 l_texcoord1 : TEXCOORD1,
+             float4 l_texcoord2 : TEXCOORD2,
+             uniform sampler2D k_src : TEXUNIT0,
+             uniform float4 k_intensity,
+             out float4 o_color : COLOR) {
+  float4 color = float4(0,0,0,0);
+  color  =  50 * tex2D(k_src, l_texcoord0.wx);
+  color += 100 * tex2D(k_src, l_texcoord0.wy);
+  color += 150 * tex2D(k_src, l_texcoord0.wz);
+  color += 200 * tex2D(k_src, l_texcoord1.wx);
+  color += 200 * tex2D(k_src, l_texcoord1.wy);
+  color += 200 * tex2D(k_src, l_texcoord1.wz);
+  color += 150 * tex2D(k_src, l_texcoord2.wx);
+  color += 100 * tex2D(k_src, l_texcoord2.wy);
+  color +=  50 * tex2D(k_src, l_texcoord2.wz);
+  o_color = color / 1200.0;
+  o_color = o_color * k_intensity;
+}

+ 20 - 0
direct/src/filter/filter-copy.sha

@@ -0,0 +1,20 @@
+//Cg
+
+
+void vshader(float4 vtx_position : POSITION,
+             out float4 l_position : POSITION,
+             out float2 l_texcoord : TEXCOORD0,
+             uniform float4 texpad_src,
+             uniform float4x4 mat_modelproj)
+{
+  l_position=mul(mat_modelproj, vtx_position);
+  l_texcoord = (vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+}
+
+void fshader(float2 l_texcoord : TEXCOORD0,
+             uniform sampler2D k_src : TEXUNIT0,
+             out float4 o_color : COLOR)
+{
+  o_color = tex2D(k_src, l_texcoord);
+}
+

+ 33 - 0
direct/src/filter/filter-down4.sha

@@ -0,0 +1,33 @@
+//Cg
+
+void vshader(float4 vtx_position : POSITION,
+             out float4 l_position : POSITION,
+             out float2 l_texcoordNW : TEXCOORD0,
+             out float2 l_texcoordNE : TEXCOORD1,
+             out float2 l_texcoordSW : TEXCOORD2,
+             out float2 l_texcoordSE : TEXCOORD3,
+             uniform float4 texpad_src,
+             uniform float4 texpix_src,
+             uniform float4x4 mat_modelproj)
+{
+  l_position=mul(mat_modelproj, vtx_position);
+  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_texcoordNW = c + float2( texpix_src.x, -texpix_src.y);
+  l_texcoordNE = c + float2( texpix_src.x,  texpix_src.y);
+  l_texcoordSW = c + float2(-texpix_src.x, -texpix_src.y);
+  l_texcoordSE = c + float2(-texpix_src.x,  texpix_src.y);
+}
+
+void fshader(float2 l_texcoordNW : TEXCOORD0,
+             float2 l_texcoordNE : TEXCOORD1,
+             float2 l_texcoordSW : TEXCOORD2,
+             float2 l_texcoordSE : TEXCOORD3,
+             uniform sampler2D k_src : TEXUNIT0,
+             out float4 o_color : COLOR) {
+  float4 colorNW = tex2D(k_src, l_texcoordNW);
+  float4 colorNE = tex2D(k_src, l_texcoordNE);
+  float4 colorSW = tex2D(k_src, l_texcoordSW);
+  float4 colorSE = tex2D(k_src, l_texcoordSE);
+  o_color = (colorNW + colorNE + colorSW + colorSE);
+}
+