2
\$\begingroup\$

I want to draw a terrain in a Texture2D at runtime in order to make collisions work with Farseer Physics later on.

What I need is to make some parts of my original texture, which is just a colored rectangle, transparent. Unfortunally this is as straightforward as I thought it would be. Here the code that does not work:

RenderTarget2D target = new RenderTarget2D(GraphicsDevice, 800, 480);
GraphicsDevice.SetRenderTarget(target);
GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin();
spriteBatch.Draw(textureTerrain, new Vector2(0, 200), Color.White);
spriteBatch.Draw(textureCrate, new Vector2(0, 300), Color.Transparent);
spriteBatch.End();

As you may think with this code XNA is drawing an transparent texture on the screen, which is essentially nothing.

\$\endgroup\$
  • \$\begingroup\$ You could draw fullscreen quads at different z levels. draw the transparent one first, close to the camera, and draw the colored one after that, behind the transparent texture. I'm too lazy to supply code right now, but that's how I would do it. This way, you'll use something that's usually deemed a flaw: when a transparent pixel is drawn, it checks what's behind it, and adapts its color to it. If you then draw something behind that, the pixel won't get drawn again, simply because it thinks it's behind something that occludes it. \$\endgroup\$ – Peethor Jul 17 '15 at 10:55
  • \$\begingroup\$ @Peethor Could please provide some samples? I am quite new to game development and I don't really know what you mean by quads and z level in this context. \$\endgroup\$ – user2737037 Jul 17 '15 at 15:29
  • \$\begingroup\$ If you want to create a 'terrain' texture for detecting open spaces by cutting out sections; I've added how to do this with sprites using blendstates in my previous answer (that only dealt with rectangles). \$\endgroup\$ – Felsir Oct 23 '15 at 12:11
2
\$\begingroup\$

You can use custom effect to do it. If you need draw texture without all pixels of some known color then pass this color to your shader and then clip texture pixels that have same color. In other case if parts you want make transparent have different colors you can create texture mask use it in your shader to determine what pixels should be cliped.

An axample (original link): enter image description here

In this case "layer 1" is mask. Parts that are wite determine where base image pixels should be replaced to pixels from texture "layer 0". In your case you can just clip it.

See here how create and use own effects.

And there is shader that can help you (sorry for possible mistakes, writen in notepad). I'm combined both ideas in one shader. Use mask color and/or mask texture as you wish.

texture TextureMap;
texture MaskTextureMap;
float4 MaskColor;

sampler TextureMapSampler = sampler_state // texture 0, same texture that you pass to SpriteBatsh.Draw()
{
    Texture = <TextureMap>;
};
sampler MaskTextureMapSampler = sampler_state // texture 1, you need to set it to shader before draw
{
    Texture = <MaskTextureMap>;
};

float4 PixelShader1(float2 pTexCoord : texcoord0) : color0
{
    float4 color = tex2D(pTexCoord, TextureMapSampler);
    clip(color == MaskColor ? -1 : 1); // discard pixel if it's color is same as mask color

    float4 maskMapColor = tex2D(pTexCoord, MaskTextureMapSampler);
    clip(maskMapColor.a == 1 ? -1 : 1); // discard pixel if mask is not transparent at that texture coords

    return color; // return color from texture map if not cliped; else pixel will be transparent
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShader1();
    }
}
\$\endgroup\$
2
\$\begingroup\$

You could use Texture2D.SetData to modify a section of the texture. Fill the section with array of Color. Using your example code: the array should be the dimension of (crate.width*crate.height) filled with Color.Transparent. The target rectangle is new Rectangle(0,300,crate.width, crate.height).

See: https://msdn.microsoft.com/en-us/library/ff434430.aspx

Another option is to use a custom blendstate, if your "crate" is not rectangular.

        GraphicsDevice.SetRenderTarget(Rendertarget);
        GraphicsDevice.Clear(Color.Transparent);

        BlendState TransparentBlend = new BlendState();

        TransparentBlend.ColorBlendFunction = BlendFunction.Add;
        TransparentBlend.ColorSourceBlend = Blend.Zero;
        TransparentBlend.ColorDestinationBlend = Blend.InverseSourceAlpha;

        TransparentBlend.AlphaBlendFunction = BlendFunction.Add;
        TransparentBlend.AlphaSourceBlend = Blend.Zero;
        TransparentBlend.AlphaDestinationBlend = Blend.InverseSourceAlpha;


        // Draw a Red rectangle to start with.
        spriteBatch.Begin();
        spriteBatch.Draw(Pixel, new Rectangle(0, 0, 200, 200), Color.Red);
        spriteBatch.End();

        spriteBatch.Begin(SpriteSortMode.Immediate, TransparentBlend, null, null, null, null, null);

        // Draw two sprites, slight overlap to demonstrate that combinations work.
        spriteBatch.Draw(Boy, new Vector2(20, 20), Color.White);
        spriteBatch.Draw(Boy, new Vector2(70, 20), Color.White);
        spriteBatch.End();

        GraphicsDevice.SetRenderTarget(null);
        //Cornflower color should show through the gaps.
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        // Show the resulting sprite
        spriteBatch.Draw(Rendertarget, Vector2.Zero, Color.White);
        // Show the Boy sprite as reference.
        spriteBatch.Draw(Boy, new Vector2(220, 20), Color.White);
        spriteBatch.End();

In the example I used Pixel: a 1x1 white texture and Boy is a texture from the http://www.lostgarden.com/2007/05/dancs-miraculously-flexible-game.html tileset.

The above code produces this: The Boy sprite is 'cut' out of the red rectangle

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.