XNA - Render target weirdness

Started by
4 comments, last by Bimble Bob 14 years, 2 months ago
OK I've been trying to figure this out and I just can't figure out what is going on. I've created a metaball renderer that uses pixel shaders to achieve the effect. I'm now trying to combine this renderer with standard sprite batch stuff, specifically I want to draw my metaballs OVER some sprites. For some reason whenever I try this I end up with this sort of thing: Obviously the purple colour is no good as I want all of that part to be transparent so I check it out in PIX and found that XNA seems to be clearing to this weird colour whenever I set a new render target. I thought a call to Clear(Color.TransparentBlack) after setting the render target would rectify this but it does not. A bit of background on my metaball rendering stuffs: I have a single screen sized offscreen render target and a screen sized texture. For each metaball I draw to the render target the influence per pixel from that ball and blend the result with the texture. The result being an "influence map" containing the summed influences for each metaball on each pixel. Of course this mean I'm constantly having to change render targets in order to save the contents of the render target to the texture after each pass. Once I have the influence map I do one final pass which will colour the pixels based upon the influence values it samples from the influence texture. Here's the C# code to illustrate what I'm on about:

// Setup some mip mapping states so the drawing doesn't look shit
            graphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Linear;
            graphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Linear;
            graphicsDevice.SamplerStates[0].MipFilter = TextureFilter.Linear;
            graphicsDevice.SamplerStates[0].MipMapLevelOfDetailBias = 0.0f;
            graphicsDevice.SamplerStates[0].MaxMipLevel = 0;            

            for (int i = 0; i < pixels.Length; i++)
            {
                pixels = Color.TransparentBlack.PackedValue;
            }

            graphicsDevice.Textures[0] = null;
            influenceMap.SetData<uint>(pixels);

            //influenceMap = new Texture2D(graphicsDevice, textureWidth, textureHeight);

            //graphicsDevice.Clear(Color.Black);
            // Create an influence map on the GPU
            foreach (Metaball aBall in balls)
            {                
                graphicsDevice.SetRenderTarget(0, offScreen);                
                graphicsDevice.Clear(Color.TransparentBlack);
                graphicsDevice.VertexDeclaration = new VertexDeclaration(graphicsDevice, VertexPositionColor.VertexElements);

                influenceMapEffect.Parameters["influenceMap"].SetValue(influenceMap);
                influenceMapEffect.Parameters["metaball"].SetValue(new Vector3(aBall.X, aBall.Y, aBall.Radius));

                influenceMapEffect.Begin();
                influenceMapEffect.CurrentTechnique.Passes[0].Begin();

                graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, quad, 0, 4, indices, 0, 2);

                influenceMapEffect.CurrentTechnique.Passes[0].End();
                influenceMapEffect.End();

                graphicsDevice.SetRenderTarget(0, null);
                influenceMap = offScreen.GetTexture();
            }
            
            //graphicsDevice.Present();

            graphicsDevice.SetRenderTarget(0, null);

            // Draw the metaballs based upon the influence map
            //graphicsDevice.Clear(Color.TransparentBlack);
            graphicsDevice.VertexDeclaration = new VertexDeclaration(graphicsDevice, VertexPositionColor.VertexElements);

            metaballEffect.Parameters["influenceMap"].SetValue(influenceMap);

            metaballEffect.Begin();
            metaballEffect.CurrentTechnique.Passes[0].Begin();

            graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, quad, 0, 4, indices, 0, 2);

            metaballEffect.CurrentTechnique.Passes[0].End();
            metaballEffect.End();
           

            graphicsDevice.Present();
And the pixel shader for creating the influence map:

void PixelShader(inout float4 color : COLOR0, in float2 screenPos : VPOS)
{        
    float influenceOn = (metaball.z / (float)((screenPos.x - metaball.x) * (screenPos.x - metaball.x) + (screenPos.y - metaball.y) * (screenPos.y - metaball.y)));         
	float4 influenceSum = float4(influenceOn, influenceOn, influenceOn, 1.0f);                    

	influenceSum += tex2D(textureSampler, (screenPos / ViewportSize));	
	//influenceSum.w = metaball.w;  
	
	color = influenceSum;	
}
and the pixel shader for colouring the balls:

void PixelShader(inout float4 color : COLOR0, in float2 screenPos : VPOS)
{
	float4 influenceSum = tex2D(textureSampler, screenPos / ViewportSize); 
	//color = influenceSum;
	float4 ballColour;
	
	ballColour = float4(0.0f, 0.9f, 0.4f, 1.0f);
        
	color = float4(0, 0, 0, 0);     

    if (influenceSum.x >= 0.4f)
    {                    		
		color += ballColour;
		color.z *= ddx(influenceSum.x);
    }
    else if (influenceSum.x >= 0.15f && influenceSum.x < 0.4f)
    {
		color += float4((ballColour.x + 0.03) * influenceSum.x - 0.19, (ballColour.y + 0.1) * influenceSum.x - 0.19, (ballColour.z - 0.25) * influenceSum.x - 0.19, influenceSum.x - 0.19) * 4;
    }
}
It's not a bug... it's a feature!
Advertisement
You probably know way more about the subject than I do, but I'm guessing that the SpriteBatch is setting some renderstates that you don't want to be set. Have you tried comparing your renderstates after you are done drawing your metaballs, and then after you call SpriteBatch.Begin()?
Just by any chance, you are not using the debug runtimes, are you?

If you are, turn them off; since they force a clear every frame alternating between purple and green to catch clearing problems, but sometimes this interferes with very specific effects.

If you aren't, turn them on, they may tell about a problem you didn't know was there.

Cheers
Dark Sylinc
I turned them on and it made my screen start flickering green =/ Not really sure whether that means things are working or not...

I'll try forcing the render states but I thought the ones I did set were the only relevant ones.

EDIT: I guess the green flickering must mean I'm not clearing properly at some point since I can see the sprites in the background so it seems like every other frame isn't working properly. Also worth noting that with the debug things on I can't see the metaballs at all, just the flickering.
It's not a bug... it's a feature!
That Clear you're getting is automatic behavior the frameowork. It's there by default to provide compatibility with the Xbox 360, where render targets only live in special bit of GPU memory and have to be manually copied in if you want them to survive a switch. If you don't want that behavior, you have to create RenderTargets with RenderTargetUsage.PreserveContents.

In your case, what I would do is just render the metaballs first to a render target, then switch to the backbuffer and render the sprites. After rendering all of your sprites, just render the metaball texture on top.
Quote:Original post by MJP
That Clear you're getting is automatic behavior the frameowork. It's there by default to provide compatibility with the Xbox 360, where render targets only live in special bit of GPU memory and have to be manually copied in if you want them to survive a switch. If you don't want that behavior, you have to create RenderTargets with RenderTargetUsage.PreserveContents.

In your case, what I would do is just render the metaballs first to a render target, then switch to the backbuffer and render the sprites. After rendering all of your sprites, just render the metaball texture on top.


Thanks a million, it's working perfectly now!
That's a nice little piece of information to remember for the future.
It's not a bug... it's a feature!

This topic is closed to new replies.

Advertisement