Sign in to follow this  
_Flecko

Rendering to transparent surfaces with shaders

Recommended Posts

I need to render some stuff to transparent surfaces. During my render to surface scene, when I call Device.Clear I use 0x00000000 as my color, and it works. However, the way the pixel shader deals with it differs. What's basically happening is: 1. I render my scene normally to texture 2. I put that texture on a screen-aligned quad and render it to another texture, using a pixel shader with a blur effect If both clears are transparent, then any transparent or semi-transparent pixels don't seem to get processed by the shader - the blur doesn't extend into them. If I make the second clear opaque (completely), then pixels that were semi-transparent on the image get processed, but none of the fully-transparent pixels around it. If both are opaque, then everything works out fine - the blur extends out past the original image, like it's supposed to. My effect looks like this:
void VSColTex(
	in	float4 inPos	: POSITION,
	in	float4 inCoord	: TEXCOORD0,
	in	float4 inDiff	: COLOR0,
	out	float4 outPos	: POSITION,
	out float4 outCoord	: TEXCOORD0,
	out float4 outDiff	: COLOR0
) {
	//Multiply InPos by World, View, Projection
	outPos=mul(inPos,mul(mul(World,View),Proj));
	outCoord=inCoord;
	outDiff=inDiff;
}

//List of adjacent tex coords to use when blurring
const float2 Samples[8] = {
   -1, -1,
    0, -1,
    1, -1,
   -1,  0,
    1,  0,
   -1,  1,
    0,  1,
    1,  1,
};

float4 PSBlur(
	float2 TexCoord	: TEXCOORD0,
	float4 Diffuse	: COLOR0
) : COLOR {
	float4 avg=tex2D(TexSampler,TexCoord);
	//for (int i=0;i<8;i++) avg+=tex2D(TexSampler,TexCoord+float2(SampleDistX*Samples[i].x,SampleDistY*Samples[i].y));
	for (int i=0;i<8;i++) avg+=tex2D(TexSampler,TexCoord+Blurriness/TexSize/3.0*Samples[i]);
	for (int i=0;i<8;i++) avg+=tex2D(TexSampler,TexCoord+Blurriness/TexSize/2.0*Samples[i]);
	for (int i=0;i<8;i++) avg+=tex2D(TexSampler,TexCoord+Blurriness/TexSize*Samples[i]);
	return avg/25*Diffuse;
}

technique Blurry {
	pass P0 {
		//Blending states
		ColorOp[0]=MODULATE;
		ColorArg1[0]=TEXTURE;
		ColorArg2[0]=DIFFUSE;
		
		AlphaOp[0]=MODULATE;
		AlphaArg1[0]=TEXTURE;
		AlphaArg2[0]=DIFFUSE;
		
		SrcBlend=SRCALPHA;
		DestBlend=INVSRCALPHA;
		AlphaBlendEnable=true;
		
		MinFilter[0]=LINEAR;
		MagFilter[0]=LINEAR;
	
		VertexShader=compile vs_1_1 VSColTex();
		PixelShader=compile ps_2_0 PSBlur();
	}
	
}

I've also got pictures, which I hope will make clearer exactly what's happening to the image. The original: Both clears transparent: Only first clear transparent: Both clears opaque: Any guesses what's up? I suspect it's a render state thing, but I'm really no good with those, and trial and error with them didn't get me very far. Anyway, thanks for reading this far and for helping. Edited by Coder: Use source tags [Edited by - Coder on August 17, 2004 4:49:38 AM]

Share this post


Link to post
Share on other sites
It sounds like you have alpha-testing enabled, causing fragments from outside the blur area to be discarded before they ever get to the shader. Disable the alpha test.

Share this post


Link to post
Share on other sites
No luck there. I set the color key to 0 when I create the textures, so it shouldn't interfere anyway. That was a good idea though, I should've thought of that...

Share this post


Link to post
Share on other sites
A8R8G8B8. Maybe I should just post my code :p


blurSize=3.0f; //Temp

//The dimensions the window will be when rendered, including space taken up by blurring
int windowWidth=(int)(width+2*blurSize);
int windowHeight=(int)(height+2*blurSize);

//The size of the texture
int texWidth=textureDimension((int)width); //256
int texHeight=textureDimension((int)height); //512

//Set the device's projection so that it fits the texture size
Matrix projection=device.Transform.Projection; //Store old projection
Matrix windowProjection=SpriteObject.Project(texWidth,texHeight); //Get new projection
if (effect!=null) effect.SetValue("Proj",windowProjection); //Pass projection to effect if necessary
device.Transform.Projection=windowProjection; //Set device's projection for second rts

//Create textures and a matching RTS
Texture sharpTex=new Texture(device,texWidth,texHeight,1,Usage.RenderTarget,Format.A8R8G8B8,Pool.Default);
Texture blurTex=new Texture(device,texWidth,texHeight,1,Usage.RenderTarget,Format.A8R8G8B8,Pool.Default);
RenderToSurface rts=new RenderToSurface(device,texWidth,texHeight,Format.A8R8G8B8,false,DepthFormat.Unknown);

//Create a viewport, also equal in dimensions to the texture size
Viewport view=new Viewport();
view.Width=texWidth;
view.Height=texHeight;
view.MaxZ=1;

//Start the scene, clear the device...
rts.BeginScene(sharpTex.GetSurfaceLevel(0),view);
device.Clear(ClearFlags.Target,System.Drawing.Color.FromArgb(0,0,0,0).ToArgb(),0,0);

//Get the inverse transformation matrix - if drawing with this, transformations will be canceled
Matrix trans=LocalTransform();
trans=Matrix.Invert(trans);

//Shift trans by blur size so that there is space for any blurred pixels the the top and left of the object
trans=Matrix.Multiply(trans,Matrix.Translation(blurSize,blurSize,0));

Draw(trans,Vector2.Empty);

//Finish the scne
rts.EndScene(Filter.None);

//Put the texture in a SpriteObject so it can easily be drawn again
SpriteObject rendered=new SpriteObject(device,new SpriteTexture(device,sharpTex,null,texWidth,texHeight));
rts.BeginScene(blurTex.GetSurfaceLevel(0),view);
device.Clear(ClearFlags.Target,System.Drawing.Color.FromArgb(0,0,0,0).ToArgb(),0,0);

//Prepare the effect
blurEffect.Technique=blurTechnique;
blurEffect.SetValue(worldConstName,Matrix.Identity);
blurEffect.SetValue(blurSizeConstName,blurSize);
blurEffect.SetValue(texSizeConstName,Math.Max(texWidth,texHeight));

//Cycle through effect passes and draw
int passes=blurEffect.Begin(FX.None);
for (int i=0;i<passes;i++) {
blurEffect.BeginPass(i);
rendered.Draw();
blurEffect.EndPass();
}
blurEffect.End();

//Finish the scene up with a linear filter so that decimal blurriness values work
rts.EndScene(Filter.None);

//Get rid of the old blurred sprite, make a new one
Remove(blurred);
blurred=new SpriteObject(device,new SpriteTexture(device,blurTex,null,texWidth,texHeight));
blurred.Alpha=0;

//Shift the blurred sprite up and left, because it was shifted down and right to accomodate blur space when drawing
blurred.X=-blurSize;
blurred.Y=-blurSize;
//foreach (SpriteObject child in children) child.Visible=false;
Add(blurred);

//Restore the old projection
effect.SetValue("Proj",projection);
device.Transform.Projection=projection;

Share this post


Link to post
Share on other sites
Well, I'm stumped. Your blur code is kind of wacky-looking, but I don't see how it would cause this particular issue.

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