Sign in to follow this  
Snowprog

PixelShader for blue/greenbox behaviour

Recommended Posts

Snowprog    157
Hi I have two textures. TextureV is something like a videostream. TextureB is a "static" texture wich is loaded from an image-file and used as a background. What I want to do is, to create the effect which is used in greenboxes in television productions. For example I analyse the texture pixel (rgb-values) according to the following algorithm: if ( (r > rLowerlevel) && (r < rUpperlevel) && (g > gLowerlevel) && (g < gUpperlevel) && (b > bLowerlevel) && (b < bUpperlevel)) then { // copy pixel from texteB to textureV } I have a working solution with "standard c++". But I'm thinking to use a pixelshader to have a better performance. I played around with pixelshaders a little bit. What I'm wondering is, if it's possible at all to solve this problem with pixelshaders? As far as I understand by now, there is no way to have decision-branching. So how can I test if the pixel applies to my greenbox keying-value? Can some with experience in pixelshaders please confirm or decline that this is possible? If it is possible, maybe you can point me to the right direction. Thanks in advance

Share this post


Link to post
Share on other sites
Hodgman    51222
Yes, you can use dynamic branching in pixel shaders - it may be slow (or unsupported) on older cards, but all new graphics cards can do it easily.

On older cards you can simulate this kind of blend-on-branch function with clever use of the step and lerp instrisic functions in HLSL:
float isInRange( float low, float high, float input )
{
return step(input,low) * step(high,input);
}

float doesRedPass = isInRange( rLowerlevel, rUpperlevel, r );
float doesGreenPass = isInRange( gLowerlevel, gUpperlevel, g );
float doesBluePass = isInRange( bLowerlevel, bUpperlevel, b );

float blend = doesRedPass * doesGreenPass * doesBluePass;

float3 out = lerp( TextureB, TextureV, blend );
step returns a float that is either 0 or 1. We can treat these floats like booleans. Multiplying two of these floats together is the same as using && on two booleans ;)

So this is equivalent to
bool isInRange( float low, float high, float input )
{
return (input >= low) && (input < high);
}

bool doesRedPass = isInRange( rLowerlevel, rUpperlevel, r );
bool doesGreenPass = isInRange( gLowerlevel, gUpperlevel, g );
bool doesBluePass = isInRange( bLowerlevel, bUpperlevel, b );

bool blend = doesRedPass && doesGreenPass && doesBluePass;

float3 out = blend ? TextureV : TextureB;

Share this post


Link to post
Share on other sites
ET3D    810
You can use your 'if' as is in a pixel shader. Pixel shaders have always had an instruction to assign a value conditionally, and the shader compiler will use that. No need for any form of dynamic branching.

You can also optimise the expression by using vectors and the intrinsics 'step' and 'all'. 'all(step(x,y))' will return 'true' if all components of x are >= the corresponding components of y (i.e., x.r >= y.r && x.g >= r.g && ...).

Share this post


Link to post
Share on other sites
Snowprog    157
Hello

This is my final code and it seems to work. If you find any mistakes or bad style, please let me know. I'm not sure about the first if-statement, because I found some articles which told me NOT to use "if" or similar commands (bad performance?).


//
// Defining some consts
float redUpper: register(c0);
float redLower: register(c1);
float greenUpper: register(c2);
float greenLower: register(c3);
float blueUpper: register(c4);
float blueLower: register(c5);

//
//Defining samplers
sampler2D texSamplerVideo : register(s0); //SetTexture(0,...)
sampler2D texSamplerBackground : register(s1); //SetTexture(1,...)
sampler2D texSamplerForeground : register(s2); //SetTexture(2,...)

//
// Pixelshader Input
struct PS_INPUT
{
float4 Color: COLOR0;
float4 Position: POSITION;
float3 Texture: TEXCOORD0;
};

//
// The MainMethod
float4 KeyingMethod(in PS_INPUT input) : COLOR
{
float4 colorForeground = tex2D(texSamplerForeground, input.Texture);

if (colorForeground.a == 1)
{
return colorForeground;
}
else
{
float4 colorVideo = tex2D(texSamplerVideo, input.Texture);
float4 colorBackground = tex2D(texSamplerBackground, input.Texture);

float rPassed = step(redLower, colorVideo.r) * step(colorVideo.r, redUpper);
float gPassed = step(greenLower, colorVideo.g) * step(colorVideo.g, greenUpper);
float bPassed = step(blueLower, colorVideo.b) * step(colorVideo.b, blueUpper);

float blend = rPassed * gPassed * bPassed;

return lerp( colorVideo, colorBackground, blend );
}
}



To explain what is happening here: I have three Textures: Video, fore- and background. The foreground-texture has to be used as long as the alpha value is 1. The video-texture should only be used, if the videocolor is NOT between the lower and upper levels, otherwise the background-texture has to be used.

Thanks for all your help! You pointed me the right way.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this