1
\$\begingroup\$

I am learning shaders, and I figured I would try to use them to create sort of a predator effect. I got to the point where I rendered my level to a rendertarget and passed that to the shader file, and attempted to get the color from it. This, by my understanding, should render the player completely invisible from the code I have. I have included relevant portions of my code.

Shader Code:

float4x4 World;
float4x4 View;
float4x4 Projection;

sampler s0;
texture land;
sampler land_sample = sampler_state{ Texture = land; };

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    float4 color = tex2D(s0, coords);
    float4 land_color = tex2D(land_sample, coords);

    if (color.a)
        return land_color;

    return color;
}

Main Draw Method:

protected override void Draw(GameTime gameTime)
{
    //First draw the level to the land rendertarget
    GraphicsDevice.SetRenderTarget(land);
    GraphicsDevice.Clear(Color.Black);

    spriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, null, null, null, camera.Transform);
    levelOne.Draw(spriteBatch, gameTime);
    spriteBatch.End();

    GraphicsDevice.SetRenderTarget(null);

    spriteBatch.Begin();

    //Draw land
    spriteBatch.Draw(land, new Vector2(0, 0), Color.White);

    spriteBatch.End();

    //Draw the player and npcs normally
    spriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, null, null, null, camera.Transform);
    npcs.ForEach(n => n.Draw(spriteBatch, gameTime));

    effect.Parameters["land"].SetValue(land);
    effect.CurrentTechnique.Passes[0].Apply();
    player.Draw(spriteBatch, gameTime);

    spriteBatch.End();

    base.Draw(gameTime);
}

Level Draw Code:

public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
{
    //Will cull tiles from level based on camera position later
    for (int i = 0; i < tiles.Length; i++)
    {
        for (int j = 0; j < tiles[i].Length; j++)
        {
            tiles[i][j].Draw(spriteBatch, gameTime);
        }
    }
}

Based on the tutorial I was following, this should replace the color in the player's sprite with that of the render target, but it does not. Furthermore, the 'level' drawn in the player's sprite is scaled down a bit.

enter image description here

My question is why this isn't working? Is there something I'm missing in the shader code? Also, as a beginner shader learner please try to keep your responses rather simple, or point me to a reference. Thanks! :)

\$\endgroup\$
  • \$\begingroup\$ I believe it has something to do with the fact that the sprite animates, and uses SpriteEffects in the Draw arguments to flip if necessary. \$\endgroup\$ – TheUnrealMegashark Apr 10 '15 at 23:08
  • \$\begingroup\$ A quad, mirrored, is the same quad. To "flip" it, SpriteBatch just inverts the UV X's and/or Y's. This samples the input texture, mirrored and/or upside-down. \$\endgroup\$ – Jon Apr 18 '15 at 8:17
  • \$\begingroup\$ What exactly is a "predator effect"? \$\endgroup\$ – Philipp Apr 18 '15 at 9:42
  • \$\begingroup\$ I'm thinking he means the wiggly shimmer that highlights Predator's movement parallax while he's stealthed. \$\endgroup\$ – Jon Apr 18 '15 at 10:44
1
\$\begingroup\$
float4 color = tex2D(s0, coords);
float4 land_color = tex2D(land_sample, coords);

You are sampling the "world" using the SpriteBatch quad's UV's. The blue UV's are used to sample the color-texture; the yellows are used to sample the world-texture. A Draw() parameter and/or the camera matrix is inverting the Y-axis, so it is using the bottom corner as (0,0) instead of the top, with Y increasing as you go "up".

UV.X = (pixelPosition.X / (TextureWidth - 1));
UV.Y = (pixelPosition.Y / (TextureHeight - 1));
UV.Y = 1.0f - UV.y; //invert Y

//I'm starting to forget XNA... (I think SpriteBatch does this for you)
//If it looks very close, but not quite...
//UV.X = ((pixelPosition.X + 0.5f) / (TextureWidth - 1)); //May also need to offset all
//UV.Y = ((pixelPosition.Y + 0.5f) / (TextureHeight - 1));//samples by one half-texel

UV diagram

Texel-alignment:
In case you need this, an example 2x2 texture. It also doesn't show the inverted Y, but the idea is the same; I'm willing to generate the inverted diagrams if you need additional assistance.

//screen-coordinates are -1:1 range
pixelPosition += 1; //move to 0:2 range
pixelPosition /= 2; //scale to 0:1 range
pixelPosition.Y = 1.0f - pixelPosition.Y; //invert Y
//for screenPosition (-1,1), pixelPosition calculates to (0,0)
UV.X = ((pixelPosition.X + 0.5f) / (TextureWidth - 1));
//UV.X = ((0 + 0.5f) / (2 - 1));
//UV.X = (0.5f / 1);
UV.Y = ((pixelPosition.Y + 0.5f) / (TextureHeight - 1));
//UV.Y = ((0 + 0.5f) / (2 - 1));
//UV.Y = (0.5f / 1);

enter image description here

\$\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.