Export (0) Print
Expand All

How to: Draw Point Sprites

This article demonstrates how to create and draw point sprites.

The code in this article presumes that the application has a camera installed with a view and projection matrix.

To create a point sprite shader

  1. First, a shader for point sprites must have a pixel shader that accepts a texture coordinate. On Xbox 360, these are specified by the special SPRITETEXCOORD semantic; on PC, any texture coordinate such as TEXCOORD0 will suffice. The pixel shader returns the color of the texel at that coordinate in the texture being used to draw the sprite.

    uniform extern texture SpriteTexture;
    struct PS_INPUT
    {
        #ifdef XBOX
            float2 TexCoord : SPRITETEXCOORD;
        #else
            float2 TexCoord : TEXCOORD0;
        #endif
        
    };
    sampler Sampler = sampler_state
    {
        Texture = <SpriteTexture>;
    };                        
    float4 PixelShader(PS_INPUT input) : COLOR0
    {
        float2 texCoord;
    
        texCoord = input.TexCoord.xy;
    
        return tex2D(Sampler, texCoord);
    }
  2. A vertex shader for point sprites only needs to return a POSITION0 for the vertex, transformed by the world-view-projection matrix.

    uniform extern float4x4 WVPMatrix;
    float4 VertexShader(float4 pos : POSITION0) : POSITION0
    {
        return mul(pos, WVPMatrix);
    }

To draw a group of point sprites

  1. In LoadGraphicsContent, load an Effect object for your point sprites, and set the texture the point sprites will draw. This example also creates a random set of point sprites to draw, using an array of VertexPositionColor elements.

    Effect PointSpritesEffect;
    VertexPositionColor[] SpriteArray;
    
    protected override void LoadGraphicsContent( bool loadAllContent )
    {
        if (loadAllContent)
        {
            PointSpritesEffect = content.Load<Effect>( "pointsprites" );
            PointSpritesEffect.Parameters["SpriteTexture"].SetValue( content.Load<Texture2D>( "torch" ) );
            SpriteArray = new VertexPositionColor[200];
            Random rand = new Random();
            for (int i = 0; i < SpriteArray.Length; i++)
            {
                SpriteArray[i].Position = new Vector3( rand.Next( 100 ) / 10f,
                   rand.Next( 100 ) / 10f, rand.Next( 100 ) / 10f );
                SpriteArray[i].Color = Color.WhiteSmoke;
            }
        }
        ...
    }
  2. In Draw, set the proper render states on the GraphicsDevice. In this case, you want to set PointSpriteEnable to true, and DepthBufferWriteEnable to false. If your shader does not set the PSIZE semantic itself, you will want to set a PointSize here. The PointSize is the width of the point sprite on screen, in pixels. Setting the PSIZE semantic in the shader allows the shader to vary the size of each point. Using the PointSize render state instead will cause each point to be rendered as the same size.

    In this example, we also setup additive blending by setting AlphaBlendEnable to true, and the SourceBlend and DestinationBlend to Blend.One.

    graphics.GraphicsDevice.RenderState.PointSpriteEnable = true;
    graphics.GraphicsDevice.RenderState.PointSize = 32.0f;
    graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;
    graphics.GraphicsDevice.RenderState.SourceBlend = Blend.One;
    graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.One;
    graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = false;
  3. Set the VertexDeclaration on the GraphicsDevice to the type of vertex you will be drawing (in this case, VertexPositionColor).

    graphics.GraphicsDevice.VertexDeclaration
        = new VertexDeclaration( graphics.GraphicsDevice, VertexPositionColor.VertexElements );
  4. Call SetValue for any draw-time parameters to your Effect, such as the world-view-projection matrix.

    Matrix WVPMatrix = Matrix.Identity * myCamera.ViewMatrix * projectionMatrix;
    PointSpritesEffect.Parameters["WVPMatrix"].SetValue( WVPMatrix );
  5. Call Begin on your Effect, and for each pass in the effect, call DrawUserPrimitives for the point sprites.

    PointSpritesEffect.Begin();
    foreach (EffectPass pass in PointSpritesEffect.CurrentTechnique.Passes)
    {
        pass.Begin();
        graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>( PrimitiveType.PointList,
            SpriteArray, 0, SpriteArray.Length );
        pass.End();
    }
    PointSpritesEffect.End();
  6. Reset your render states for drawing any subsequent objects.

    graphics.GraphicsDevice.RenderState.PointSpriteEnable = false;
    graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
    graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
    graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

The Complete Example

uniform extern float4x4 WVPMatrix;

uniform extern texture SpriteTexture;
struct PS_INPUT
{
    #ifdef XBOX
        float2 TexCoord : SPRITETEXCOORD;
    #else
        float2 TexCoord : TEXCOORD0;
    #endif
    
};
sampler Sampler = sampler_state
{
    Texture = <SpriteTexture>;
};                        
float4 PixelShader(PS_INPUT input) : COLOR0
{
    float2 texCoord;

    texCoord = input.TexCoord.xy;

    return tex2D(Sampler, texCoord);
}

float4 VertexShader(float4 pos : POSITION0) : POSITION0
{
    return mul(pos, WVPMatrix);
}


technique PointSpriteTechnique
{
    pass P0
    {
        vertexShader = compile vs_2_0 VertexShader();
        pixelShader = compile ps_2_0 PixelShader();
    }
}
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    ContentManager content;


    SampleArcBallCamera myCamera;

    public Game1()
    {
        graphics = new GraphicsDeviceManager( this );
        content = new ContentManager( Services );

        myCamera = new SampleArcBallCamera( SampleArcBallCameraMode.Free );
    }

    protected override void Initialize()
    {
        base.Initialize();
    }

    Effect PointSpritesEffect;
    VertexPositionColor[] SpriteArray;
    Matrix projectionMatrix;

    protected override void LoadGraphicsContent( bool loadAllContent )
    {
        if (loadAllContent)
        {
            PointSpritesEffect = content.Load<Effect>( "pointsprites" );
            PointSpritesEffect.Parameters["SpriteTexture"].SetValue( content.Load<Texture2D>( "torch" ) );
            SpriteArray = new VertexPositionColor[200];
            Random rand = new Random();
            for (int i = 0; i < SpriteArray.Length; i++)
            {
                SpriteArray[i].Position = new Vector3( rand.Next( 100 ) / 10f,
                   rand.Next( 100 ) / 10f, rand.Next( 100 ) / 10f );
                SpriteArray[i].Color = Color.WhiteSmoke;
            }
        }
        myCamera.Target = new Vector3( 0, 0, 0 );
        myCamera.Distance = -50f;
        projectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, 4.0f / 3.0f, 1.0f, 10000f );
    }

    protected override void UnloadGraphicsContent( bool unloadAllContent )
    {
        if (unloadAllContent == true)
        {
            content.Unload();
        }
    }

    protected override void Update( GameTime gameTime )
    {
        // Allows the default game to exit on Xbox 360 and Windows.
        GamePadState PlayerOne = GamePad.GetState( PlayerIndex.One );
        if (PlayerOne.Buttons.Back == ButtonState.Pressed)
            this.Exit();

        // TODO: Add your update logic here.
        if (PlayerOne.Buttons.A == ButtonState.Pressed)
        {
            myCamera.Target = new Vector3( 0, 0, 0 );
            myCamera.Distance = -50f;
        }
        myCamera.OrbitUp( PlayerOne.ThumbSticks.Right.Y / 4 );
        myCamera.OrbitRight( PlayerOne.ThumbSticks.Right.X / 4 );

        base.Update( gameTime );
    }

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

        // TODO: Add your drawing code here.
        graphics.GraphicsDevice.RenderState.PointSpriteEnable = true;
        graphics.GraphicsDevice.RenderState.PointSize = 32.0f;
        graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;
        graphics.GraphicsDevice.RenderState.SourceBlend = Blend.One;
        graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.One;
        graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = false;

        graphics.GraphicsDevice.VertexDeclaration
            = new VertexDeclaration( graphics.GraphicsDevice, VertexPositionColor.VertexElements );

        Matrix WVPMatrix = Matrix.Identity * myCamera.ViewMatrix * projectionMatrix;
        PointSpritesEffect.Parameters["WVPMatrix"].SetValue( WVPMatrix );

        PointSpritesEffect.Begin();
        foreach (EffectPass pass in PointSpritesEffect.CurrentTechnique.Passes)
        {
            pass.Begin();
            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>( PrimitiveType.PointList,
                SpriteArray, 0, SpriteArray.Length );
            pass.End();
        }
        PointSpritesEffect.End();

        graphics.GraphicsDevice.RenderState.PointSpriteEnable = false;
        graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
        graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
        graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

        base.Draw( gameTime );
    }
}
Show:
© 2014 Microsoft