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

参考

Game1.cs
/*
 * XnaTexture2D4 正距方位図法
 * 
 * プロジェクトのプロパティ
 * [XNA Game Studio]タブ
 * Use HiDef to access the complete API
 */
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
namespace XnaTexture2D
{
    class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch sprite;
        SpriteFont font;
        VertexBuffer vertexBuffer;
        Effect effect;
        BasicEffect basicEffect;
        Texture2D texture;
        VertexPositionColor[] cross;
 
        EffectParameter fxLat;
        EffectParameter fxLon;
        EffectParameter fxAxis;
        float lat = 35;
        float lon = 135;
 
        // fps
        int fpsSec;
        int fpsDraw = 0;
        int fpsCount = 0;
 
        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>("earthmap1k");
 
            effect = Content.Load<Effect>("Effect1");
            effect.Parameters["EarthMap"].SetValue(texture);
            effect.Parameters["aspect"].SetValue(GraphicsDevice.Viewport.AspectRatio);
            fxLat = effect.Parameters["centerLatRad"];
            fxLon = effect.Parameters["centerLonRad"];
            fxAxis = effect.Parameters["axis"];
            fxLat.SetValue(MathHelper.ToRadians(lat));
            fxLon.SetValue(MathHelper.ToRadians(lon));
            SetAxis();
 
            basicEffect = new BasicEffect(GraphicsDevice);
 
            // 地図
            VertexPositionTexture[] vertices = new VertexPositionTexture[4];
            vertices[0] = new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0));
            vertices[1] = new VertexPositionTexture(new Vector3(1, 1, 0), new Vector2(1, 0));
            vertices[2] = new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0, 1));
            vertices[3] = new VertexPositionTexture(new Vector3(1, -1, 0), new Vector2(1, 1));
 
            vertexBuffer = new VertexBuffer(GraphicsDevice,
                typeof(VertexPositionTexture), 4, BufferUsage.WriteOnly);
            vertexBuffer.SetData(vertices);
 
            // 照準
            cross = new VertexPositionColor[4];
            cross[0] = new VertexPositionColor(new Vector3(-0.05f, 0, 0), Color.White);
            cross[1] = new VertexPositionColor(new Vector3(0.05f, 0, 0), Color.White);
            cross[2] = new VertexPositionColor(new Vector3(0, -0.1f, 0), Color.White);
            cross[3] = new VertexPositionColor(new Vector3(0, 0.1f, 0), Color.White);
 
            base.LoadContent();
        }
 
        protected override void Update(GameTime gameTime)
        {
            float deltaLat = 0;
            float deltaLon = 0;
            bool modified = false;
 
            KeyboardState kState = Keyboard.GetState();
            if (kState.IsKeyDown(Keys.Escape)) Exit();
            if (kState.IsKeyDown(Keys.Up)) deltaLat = 1;
            if (kState.IsKeyDown(Keys.Down)) deltaLat = -1;
            if (kState.IsKeyDown(Keys.Left)) deltaLon = -1;
            if (kState.IsKeyDown(Keys.Right)) deltaLon = 1;
 
            if (deltaLat != 0)
            {
                lat = MathHelper.Clamp(lat + deltaLat, -89.9f, 89.9f);
                fxLat.SetValue(MathHelper.ToRadians(lat)); // -pi/2 - pi/2
                modified = true;
            }
            if (deltaLon != 0)
            {
                lon += deltaLon;
                if (lon <= -180) lon += 360;
                if (180 < lon) lon -= 360;
                fxLon.SetValue(MathHelper.ToRadians(lon)); // -pi - pi
                modified = true;
            }
            if (modified)
            {
                SetAxis();
            }
 
            base.Update(gameTime);
        }
 
        void SetAxis()
        {
            float rad = MathHelper.ToRadians(lat);
            float y = (float)Math.Sin(rad);
            float r = (float)Math.Cos(rad);
            rad = MathHelper.ToRadians(lon);
            float z = (float)Math.Cos(rad) * r;
            float x = (float)Math.Sin(rad) * r;
            Vector3 v = new Vector3(x, y, z);
            v.Normalize();
            fxAxis.SetValue(v);
        }
 
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
 
            GraphicsDevice.SetVertexBuffer(vertexBuffer);
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
            }
 
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, cross, 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:f1} lon={2:f1}", fpsCount, lat, lon);
            sprite.DrawString(font, text, new Vector2(0, 0), Color.Red);
            sprite.End();
 
            base.Draw(gameTime);
        }
    }
}
 

Effect1.fx
// XnaTexture2D4 正距方位図法
texture EarthMap;
float centerLatRad;
float centerLonRad;
float aspect;
float3 axis;
 
sampler TextureSampler = sampler_state
{
	texture = <EarthMap>;
	minfilter = linear;
	magfilter = linear;
};
 
struct VertexShaderInput
{
	float4 Position : POSITION0;
	float2 TexCoord : TEXCOORD0;
};
 
struct VertexShaderOutput
{
	float4 Position : POSITION0;
	float2 TexCoord : TEXCOORD0;
};
 
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
	VertexShaderOutput output;
	output.Position = input.Position;
	output.TexCoord = input.TexCoord;
	return output;
}
 
float4 MulQ(float4 q1, float4 q2)
{
	float4 q;
	q.x = (q1.w * q2.x) + (q1.x * q2.w) + (q1.y * q2.z) - (q1.z * q2.y);
	q.y = (q1.w * q2.y) + (q1.y * q2.w) + (q1.z * q2.x) - (q1.x * q2.z);
	q.z = (q1.w * q2.z) + (q1.z * q2.w) + (q1.x * q2.y) - (q1.y * q2.x);
	q.w = (q1.w * q2.w) - (q1.x * q2.x) - (q1.y * q2.y) - (q1.z * q2.z);
	return q;
}
 
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	float x = input.TexCoord.x * 2.0f - 1.0f; // -1 - 1
	float y = input.TexCoord.y * 2.0f - 1.0f; // -1 - 1
	x *= aspect;
 
	// 距離(=回転軸からの角度)0 - 1
	float dist = sqrt(x * x + y * y);
	if (1.0f < dist)
	{
		discard;
	}
	if (abs(dist - 0.5f) < 0.0015f)
	{
		return float4(1, 1, 0, 1);
	}
	// 方位角(=回転する角度)-pi - pi
	float angleRad = atan2(x, -y);
 
	// 回転する座標
	float4 p;
	float rad = centerLatRad + radians(dist * 180.0f);
	p.y = sin(rad);
	float r = cos(rad);
	p.z = cos(centerLonRad) * r;
	p.x = sin(centerLonRad) * r;
	p.w = 0;
 
	// 回転
	float4 rot;
	angleRad *= 0.5f;
	rot.xyz = axis * sin(angleRad);
	rot.w = cos(angleRad);
	float4 conj;
	conj.xyz = -rot.xyz;
	conj.w = rot.w;
	float4 q = MulQ(MulQ(conj, p), rot);
 
	float lat = 90.0f - degrees(asin(q.y));
	float lon = 180.0f + degrees(atan2(q.x, q.z));
	float eps = 0.2f / (0.2f + dist);
	if (lat % 10.0f < eps || lon % 15.0f < eps)
	{
		return float4(1, 1, 1, 1);
	}
 
	float2 t;
	t.x = lon / 360.0f;
	t.y = lat / 180.0f;
	return tex2D(TextureSampler, t);
}
 
technique Technique1
{
	pass Pass1
	{
		VertexShader = compile vs_3_0 VertexShaderFunction();
		PixelShader = compile ps_3_0 PixelShaderFunction();
	}
}
 
最終更新:2012年12月20日 12:21