Drawing a non rectangular part of Texture2d

There is a little more to it here is a actual working example shader took me a minute to get the bugs out.

game1

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Game1
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Effect effect;
        SpriteFont font;
        string message = "";
        Rectangle boundryRectangle = new Rectangle(120, 120, 200, 250);
        public static Texture2D dotTexture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.GraphicsProfile = GraphicsProfile.HiDef;
            IsMouseVisible = true;
        }
        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 600;
            graphics.ApplyChanges();
            base.Initialize();
        }
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // load up your stuff
            font = Content.Load<SpriteFont>("font");

            // load up your effect;
            effect = Content.Load<Effect>("effect");
            effect.CurrentTechnique = effect.Techniques["ConeTechnique"];

            // make a message
            message =
                " Ok so i had a bit of trouble figuring this out it turns out that it was working after all. " +
                "\n I was wrong when spritebatch passes the pos to the pixel shader. " +
                "\n It passes the untransformed coordinates so it does a bit of magic under the hood. " +
                "\n However its own vertex shader is still lining them up properly even if i pass matrix identity." +
                "\n So there we go if you intend however to pass a projection matrix ill bet youll need to do the transform on the boundry" +
                "\n +\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+"
                ;

            dotTexture = TextureDotCreate(GraphicsDevice);
        }

        public Texture2D TextureDotCreate(GraphicsDevice device)
        {
            Color[] data = new Color[1];
            data[0] = new Color(255, 255, 255, 255);
            Texture2D tex = new Texture2D(device, 1, 1);
            tex.SetData<Color>(data);
            return tex;
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            var mouse = Mouse.GetState();
            playerLookAtTargetPosition = mouse.Position.ToVector2();

            base.Update(gameTime);
        }

        Vector2 playerPosition = new Vector2(100, 100);
        Vector2 playerLookAtTargetPosition = new Vector2(200, 20);
        float rangeInDegrees = 35f;

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Moccasin);

            Matrix m = Matrix.Identity;
            effect.CurrentTechnique = effect.Techniques["ConeTechnique"];
            effect.Parameters["visibleBounds"].SetValue(GetVector4Rectangle(boundryRectangle));
            effect.Parameters["playerPosition"].SetValue(playerPosition);
            effect.Parameters["playerLookAtTarget"].SetValue(playerLookAtTargetPosition);
            effect.Parameters["rangeInDegrees"].SetValue(rangeInDegrees);

            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullNone, effect, m);
            effect.Parameters["TextureA"].SetValue(font.Texture);
            spriteBatch.Draw(dotTexture, boundryRectangle, Color.White);
            spriteBatch.Draw(font.Texture, new Vector2(150, 320), Color.LightGray);
            spriteBatch.DrawString(font, message, new Vector2(100f, 100f), Color.MonoGameOrange);
            spriteBatch.End();

            base.Draw(gameTime);
        }

        public Vector4 GetVector4Rectangle(Rectangle r)
        {
            int h = graphics.GraphicsDevice.Viewport.Height;

            // Gl you gotta flip the y
            //return new Vector4(r.Left, (h - r.Top), r.Right, (h - r.Bottom));

            // Dx dont flip the y.
            return new Vector4(r.Left, r.Top, r.Right, r.Bottom); 
        }
        public Vector4 GetVector4GpuRectangle(Rectangle r, Matrix vp)
        {
            var lt = Vector2.Transform(new Vector2(r.Left, r.Top), vp);
            var rb = Vector2.Transform(new Vector2(r.Right, r.Bottom), vp);
            return new Vector4(lt.X, lt.Y, rb.X, rb.Y);
        }


    }
}

shader

// effect.fx
//

#if OPENGL
#define SV_POSITION POSITION
#define VS_SHADERMODEL vs_3_0
#define PS_SHADERMODEL ps_3_0
#else
#define VS_SHADERMODEL vs_4_0
#define PS_SHADERMODEL ps_4_0
#endif

float4 visibleBounds;
float2 playerPosition;
float2 playerLookAtTarget;
float rangeInDegrees;

Texture2D TextureA;
sampler2D TextureSamplerA = sampler_state
{
    Texture = <TextureA>;
};

// pixel shader
float4 MyPixelShader(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 col = tex2D(TextureSamplerA, texCoord) * color;
    float2 pos = position.xy;
    // i could probably mathematically boolean this down get rid of the ifs in a few ways but im tired.
    float dx = (pos.x - visibleBounds.x) * (visibleBounds.z - pos.x);
    float dy = (pos.y - visibleBounds.y) * (visibleBounds.w - pos.y);
    if (dx < 0.0f) 
        clip(-1);
    if (dy < 0.0f)
        clip(-1);
    return col;
}

// pixel shader
float4 ConePixelShader(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float2 playerToScreenPixelNormal = normalize(position.xy - playerPosition.xy);
    float2 playerLookAtDirection = normalize(playerLookAtTarget.xy - playerPosition.xy);
    float theta = saturate(dot(playerLookAtDirection, playerToScreenPixelNormal));
    float acos = theta * theta;
    float pixelDegreesToLineOfSight = acos * 90.0f;
    
    float4 resultingPixelColor = tex2D(TextureSamplerA, texCoord) * color;

    // clips out anything not within the specified range to the line of site.
    clip(pixelDegreesToLineOfSight - (90 - rangeInDegrees / 2.0));
    return resultingPixelColor;
}

technique BasicColorDrawing
{
    pass P0
    {
        PixelShader = compile PS_SHADERMODEL
        MyPixelShader();
    }
}

technique ConeTechnique
{
    pass P0
    {
        PixelShader = compile PS_SHADERMODEL
        ConePixelShader();
    }
}
2 Likes