locked
PhotoShop Blend Pixel Shader RRS feed

  • Question

  • I have been looking into writing a pixel shader to perform a blend mode similiar to those offered in PhotoShop and have the following XAML...

    <Canvas>
     <Button>
      <Button.Effect>
       <PhotoShopBlendAdd/>
      </Button.Effect>
     </Button>
    </Canvas>

    Inside my "PhotoShopBlendAdd" pixel shader I can get access to the pixels for the button using...

    sampler2D implicitSampler : register(s0);

    ...but how do I get access to the pixels for the Canvas element?

    I know I could create a new RegisterPixelShaderSamplerProperty to set-up and access registry s1 but how is the buffer setup (in XAML/Code) ? Do I need to use a visual brush and if so how is this done?

    What I really need to know is what code do I need and what supporting XAML do I need to have a pixel shader with access to two buffers (ie the foreground pixels for the button and the background pixels for the canvas) which both need to be the same size so the pixel setting will work?

    Anyone any ideas or has anyone done this before?
    Monday, April 13, 2009 12:10 AM

Answers

  • The short answer is that there's no easy way to do this.  The issue is that we do not provide any way to read from the back buffer, which is effectively what you want to do.  You can imagine using a VisualBrush to accomplish this, but it would only work in very specific scenarios.  A VisualBrush's content is an entire visual sub-tree, but it does not contain that sub-tree and everything it is blended into.  Imagine a semi-transparent red rectangle on top of some text - if you put a VisualBrush on just the rectangle and use it as a fill somewhere else, you'll only see the semi-transparent red, not the text that was behind the rectangle in the original location.  In order to get access to everything behind your element, you would effectively need to split your entire visual tree like this:

    Root with 3 children -
    1) all content in your application in front of blend effect
    2) the content to be blended
    3) all content in your application behind blend effect

    Splitting it up this way ensures that the VisualBrush will contain everything that your content would be blended into.  With this set-yp you could put a VisualBrush on 3, limit its viewbox to the area to be blended with 2, and then place your effect on 2.  However, this obviously isn't a very straightforward or re-usable construct.

    I'd say that this is unfortunately impossible to do well in the current framework.  I realize that DirectX makes blending like you want fairly simple, but it just isn't something that WPF exposes at this time.  Adding blend modes as a separate feature is something we've actively considered, but it isn't currently planned for the upcoming release.

    If you want some details on how to write a multi-input effect, Greg Schechter wrote some great samples on his blog:
    http://blogs.msdn.com/greg_schechter/archive/2008/05/09/a-series-on-gpu-based-effects-for-wpf.aspx

    Monday, April 13, 2009 8:26 PM

All replies

  • When you apply a shader to an element it affects that element and all of it's children. 

    Canvas>
     <Canvas.Effect>
       <!-- passes the canvas pixels to shader (s0)-->
     
       <PhotoShopBlendAdd/>
       <Button>
    
    
       </Button>
       </Canvas.Effect>
    </Canvas>
    
    The s0 register represents the incoming bitmap.

    // s0 register contains the incoming bitmap
    sampler2D input : register(s0);
    
    // c0 register contains other input into this shader
    float2 centerPoint: register(c0);
    
    float4 main(float2 uv : TEXCOORD) : COLOR 
    { 
              // uv: cooridinates of a pixel in the input bitmap	
    
    	float4 Color; 
                    
                    // get a color from the input at this x, y location
    	Color= tex2D( input , uv.xy); 
    
                    // return the pixel without changing it
    	return Color; 
    }
    

    Walt | http://wpfwonderland.wordpress.com
    Monday, April 13, 2009 3:29 AM
  • Here's a free utility that lets you play with shaders.  It also generate your C# or VB shader code for you.

    ** Free WPF shader utiliy **
    ** edit HLSL
    ** auto generates your C#/VB shader classes **
    ** auto generates test pages **

    Shazzam - Open source pixel shader utility


    Walt | http://wpfwonderland.wordpress.com
    Monday, April 13, 2009 3:34 AM
  • The short answer is that there's no easy way to do this.  The issue is that we do not provide any way to read from the back buffer, which is effectively what you want to do.  You can imagine using a VisualBrush to accomplish this, but it would only work in very specific scenarios.  A VisualBrush's content is an entire visual sub-tree, but it does not contain that sub-tree and everything it is blended into.  Imagine a semi-transparent red rectangle on top of some text - if you put a VisualBrush on just the rectangle and use it as a fill somewhere else, you'll only see the semi-transparent red, not the text that was behind the rectangle in the original location.  In order to get access to everything behind your element, you would effectively need to split your entire visual tree like this:

    Root with 3 children -
    1) all content in your application in front of blend effect
    2) the content to be blended
    3) all content in your application behind blend effect

    Splitting it up this way ensures that the VisualBrush will contain everything that your content would be blended into.  With this set-yp you could put a VisualBrush on 3, limit its viewbox to the area to be blended with 2, and then place your effect on 2.  However, this obviously isn't a very straightforward or re-usable construct.

    I'd say that this is unfortunately impossible to do well in the current framework.  I realize that DirectX makes blending like you want fairly simple, but it just isn't something that WPF exposes at this time.  Adding blend modes as a separate feature is something we've actively considered, but it isn't currently planned for the upcoming release.

    If you want some details on how to write a multi-input effect, Greg Schechter wrote some great samples on his blog:
    http://blogs.msdn.com/greg_schechter/archive/2008/05/09/a-series-on-gpu-based-effects-for-wpf.aspx

    Monday, April 13, 2009 8:26 PM
  • Thanks for the reply - it was very helpfull

    However, how do I register with Microsoft that I think access to the backbuffer should be made available in the upcoming release. I understand that adding blend modes might be a more substaintial task but adding access to the backbuffer would be trival in comparision and would solve the problem since developers could code their own blend modes if required.

    I've just be playing around with debugging though the .NET source code (shadereffect.cs) and am trying to find out where the backbuffer is located. Perhaps if I could find this then I could attempt to override some class somewhere and expose it somehow for access. Any ideas? I'll much rather find an official way but this might be good enough for a short term solution untill the backbuffer is exposed.

    On a personal level I believe one of the major things that is missing in WPF right now is blend modes so if this post can raise the profile of blend mode then all the better...

    Thanks again for the reply...
    Tuesday, April 14, 2009 12:44 AM

  • This solution doesn't resolve the question which is how can be button pixel shader get access to the canvas sample.

    The button needs both it sample (foreground) and the canvas sample (background) to perform a blend operation.
    Tuesday, April 14, 2009 3:09 PM

  • The pixel shaders used in Shazzam only allow access to the current sample (ie the foreground). You can pass in more foreground samples (using different registers) but the pixel shaders need to have acces to whatever is under the current ui element (ie have access to the backbuffer) so the blending can be done cleanly.
    Tuesday, April 14, 2009 3:12 PM
  • Tuesday, April 14, 2009 5:25 PM
  • Thanks for the reply - it was very helpfull

    However, how do I register with Microsoft that I think access to the backbuffer should be made available in the upcoming release. I understand that adding blend modes might be a more substaintial task but adding access to the backbuffer would be trival in comparision and would solve the problem since developers could code their own blend modes if required.

    I've just be playing around with debugging though the .NET source code (shadereffect.cs) and am trying to find out where the backbuffer is located. Perhaps if I could find this then I could attempt to override some class somewhere and expose it somehow for access. Any ideas? I'll much rather find an official way but this might be good enough for a short term solution untill the backbuffer is exposed.

    On a personal level I believe one of the major things that is missing in WPF right now is blend modes so if this post can raise the profile of blend mode then all the better...

    Thanks again for the reply...

    There isn't any back buffer object in managed code that you'll be able to pass in as an input to the ShaderEffect - if it were that simple we would have exposed it already :)  We're already closing down features for the upcoming release so I can tell you that blend modes will not be coming this time around, but it's certainly something I want to add support for.
    Tuesday, April 14, 2009 7:39 PM
  • I just ran into this post while trying to implement blend mode effects myself. Argh!

    Having blend modes in WPF would be so nice for a designer. This is another voice, saying, 'Let's put them in there!'
    Wednesday, June 10, 2009 9:06 PM
  • I figured out how to use Jeremiah's GlassBehavior (as he suggested above) ... to get blend modes to work in a limited fashion ... and I will be blogging about it in the next couple days I hope. I will post back here when I get the blog post up.

    I already have a few blog posts on blend modes out there already:
    http://www.cplotts.com/2009/06/17/blend-modes-for-silverlight/
    http://www.cplotts.com/2009/06/16/blend-modes-part-i/
    http://www.cplotts.com/2009/06/16/blend-modes-part-ii/

    Again ... I would love to see this implemented in WPF natively. One idea would be to have a blend mode property (that would somehow could be extended to custom blend modes) ... that when set to Normal ... would simply use Opacity and the normal blending mode ... whereas if it was set to a different blend mode ... would ignore Opacity entirely. That way of implementing it seems to make sense to me.

    I can see how it would be difficult to implement in the current framework ... given Brendan's answer above ... but I'm wondering ... somewhere the math is being done for the simple, normal, opacity blend mode ... can't it be extended to support additional blend mode math? I mean, when you set the Opacity on an object ... you don't have to worry at all about where that object sits in the visual tree ... in relation to what is under it and what is over it.

    Ok, I know (given my own experiences as a developer) that sometimes seemingly simple things ... can be extremely hard to implement given a current architecture/design. Not complaining here ... just wishing. :)
    Sunday, June 21, 2009 2:23 PM
  • Here, finally is my blog post about using Jeremiah's GlassBehavior:
    http://www.cplotts.com/2009/06/30/blend-modes-part-iii/

    Unfortunately, using his GlassBehavior doesn't work in all situations ... as Brendan himself implies in the responses above. But, maybe this will help someone.

    Thursday, July 2, 2009 2:11 PM