開発環境 Microsoft Visual C# 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 Windows Game (4.0)
プロジェクト名 XnaBillboard

参考

snowflake1.png


Game1.cs
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
namespace XnaBillboard
{
    class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch sprite;
        SpriteFont font;
        VertexBuffer vertexBuffer;
        Effect effect;
        EffectParameter fxPosition;
        Texture2D texture;
        List<Vector3> positions;
        Matrix projection;
 
        // fps
        int fpsSec;
        int fpsDraw = 0;
        int fpsCount = 0;
 
        // カメラ
        Vector3 camPos = new Vector3(0, 0, 0);
        int camLat = 30;
        int camLon = 45;
 
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
        }
 
        protected override void LoadContent()
        {
            sprite = new SpriteBatch(GraphicsDevice);
            font = Content.Load<SpriteFont>("SpriteFont1");
            texture = Content.Load<Texture2D>("snowflake1");
 
            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45),
                GraphicsDevice.Viewport.AspectRatio, 1, 200);
 
            effect = Content.Load<Effect>("Billboard");
            effect.Parameters["BillboardWidth"].SetValue(1);
            effect.Parameters["BillboardHeight"].SetValue(1);
            effect.Parameters["Texture"].SetValue(texture);
            effect.Parameters["Projection"].SetValue(projection);
            fxPosition = effect.Parameters["Position"];
 
            // 頂点
            VertexPositionTexture[] vertices = new VertexPositionTexture[4];
            vertices[0].TextureCoordinate = new Vector2(0, 0);
            vertices[1].TextureCoordinate = new Vector2(1, 0);
            vertices[2].TextureCoordinate = new Vector2(0, 1);
            vertices[3].TextureCoordinate = new Vector2(1, 1);
 
            vertexBuffer = new VertexBuffer(GraphicsDevice,
                typeof(VertexPositionTexture), 4, BufferUsage.WriteOnly);
            vertexBuffer.SetData(vertices);
 
            // 雪
            Random rnd = new Random();
            positions = new List<Vector3>();
            for (int n = 0; n < 5000; n++)
            {
                Vector3 pos = new Vector3(rnd.Next(100), rnd.Next(100), rnd.Next(100));
                positions.Add(pos);
            }
 
            base.LoadContent();
        }
 
        protected override void Update(GameTime gameTime)
        {
            KeyboardState kState = Keyboard.GetState();
            if (kState.IsKeyDown(Keys.Escape)) Exit();
            if (kState.IsKeyDown(Keys.W)) Move(0, 0);
            if (kState.IsKeyDown(Keys.S)) Move(180, 0);
            if (kState.IsKeyDown(Keys.A)) Move(0, -90);
            if (kState.IsKeyDown(Keys.D)) Move(0, 90);
            if (kState.IsKeyDown(Keys.Up)) camLat = Math.Min(camLat + 1, 89);
            if (kState.IsKeyDown(Keys.Down)) camLat = Math.Max(camLat - 1, -89);
            if (kState.IsKeyDown(Keys.Left)) camLon = (camLon + 359) % 360;
            if (kState.IsKeyDown(Keys.Right)) camLon = (camLon + 1) % 360;
            if (kState.IsKeyDown(Keys.PageUp)) Move(90, 0);
            if (kState.IsKeyDown(Keys.PageDown)) Move(-90, 0);
 
            base.Update(gameTime);
        }
 
        void Move(int lat, int lon)
        {
            Vector3 move = new Vector3();
            float rad = MathHelper.ToRadians(camLat + lat);
            if (lon == 0)
            {
                move.Y = (float)Math.Sin(rad) * 0.2f;
            }
            float r = (float)Math.Cos(rad) * 0.2f;
            rad = MathHelper.ToRadians(camLon + lon);
            move.X = (float)Math.Cos(rad) * r;
            move.Z = (float)Math.Sin(rad) * r;
            camPos += move;
        }
 
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
 
            Vector3 target;
            float rad = MathHelper.ToRadians(camLat);
            target.Y = (float)Math.Sin(rad);
            float r = (float)Math.Cos(rad);
            rad = MathHelper.ToRadians(camLon);
            target.X = (float)Math.Cos(rad) * r;
            target.Z = (float)Math.Sin(rad) * r;
            Matrix view = Matrix.CreateLookAt(camPos, camPos + target, Vector3.Up);
 
            Vector3 rightVector = Vector3.Cross(target, Vector3.Up);
            rightVector.Normalize();
            Vector3 upVector = Vector3.Cross(rightVector, target);
 
            GraphicsDevice.SetVertexBuffer(vertexBuffer);
            effect.Parameters["View"].SetValue(view);
            effect.Parameters["RightVector"].SetValue(rightVector);
            effect.Parameters["UpVector"].SetValue(upVector);
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                foreach (Vector3 pos in positions)
                {
                    fxPosition.SetValue(pos);
                    pass.Apply();
                    GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
                }
            }
 
            // fps
            fpsDraw++;
            if (gameTime.TotalGameTime.Seconds != fpsSec)
            {
                fpsCount = fpsDraw;
                fpsDraw = 0;
                fpsSec = gameTime.TotalGameTime.Seconds;
            }
 
            sprite.Begin();
            string text = string.Format("fps={0} lat={1} lon={2}", fpsCount, camLat, camLon);
            sprite.DrawString(font, text, new Vector2(0, 0), Color.White);
            text = string.Format("x={0:f1} y={1:f1} z={2:f1}", camPos.X, camPos.Y, camPos.Z);
            sprite.DrawString(font, text, new Vector2(0, 20), Color.White);
            sprite.End();
 
            base.Draw(gameTime);
        }
    }
}
 

Billboard.fx
float4x4 View;
float4x4 Projection;
float3 Position;
float3 RightVector;
float3 UpVector;
float BillboardWidth;
float BillboardHeight;
texture Texture;
 
sampler TextureSampler = sampler_state
{
	Texture = (Texture);
};
 
struct VS_INPUT
{
	float2 TexCoord : TEXCOORD0;
};
 
struct VS_OUTPUT
{
	float4 Position : POSITION0;
	float2 TexCoord : TEXCOORD0;
};
 
VS_OUTPUT VertexShaderFunction(VS_INPUT input)
{
	VS_OUTPUT output;
 
	float3 position = Position;
	position += (input.TexCoord.x - 0.5f) * RightVector * BillboardWidth;
	position += (0.5f - input.TexCoord.y) * UpVector * BillboardHeight;
 
	float4 viewPosition = mul(float4(position, 1), View);
	output.Position = mul(viewPosition, Projection);
	output.TexCoord = input.TexCoord;
	return output;
}
 
float4 PixelShaderFunction(float2 texCoord : TEXCOORD0) : COLOR0
{
	return tex2D(TextureSampler, texCoord);
}
 
technique Billboards
{
	pass
	{
		VertexShader = compile vs_2_0 VertexShaderFunction();
		PixelShader = compile ps_2_0 PixelShaderFunction();
	}
}
 
最終更新:2012年12月23日 20:48
添付ファイル