Silverlight 5 BetaでXNAを使った3D描画テスト

 Silverlight 5 Beta (Windows上)でのXNAAPIを使った3D描画ができました。
 不具合のためか頂点周りの問題の回避に苦労しましたが、ポリゴン2枚の初歩的な描画が動くようになりました。現状、シェーダモデル2.0ですが、カスタムシェーダが書けるので、工夫を凝らすことも可能ですね。

数学型について

 XNA用の数学型がまだ含まれていないので、簡素な「Vector3」「Vector4」「Matrix」構造体のコードを追加しています。そのため、少しコードが長くなっています。これは、Silverlight 5のアップデート時に、標準に用意されることでしょう。数学型の代用品+αとして、 http://xelf.codeplex.com/ のコードもお役に立つものと思います。

追記: より標準的であろうものとして → http://code.msdn.microsoft.com/XNA-Math-Helper-DLL-d4d1f7d4

コードについて

 Silverlight 5用のテンプレートプロジェクトに手を加えたコードは次のようになりました。

MainPage.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Silverlight;

namespace XELF.Silverlight5BetaXnaSample {
	public partial class MainPage : UserControl {
		VertexBuffer vertexBuffer;
		VertexShader vertexShader;
		PixelShader pixelShader;
		Matrix worldViewProjection = new Matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
		float aspectRatio;

		public MainPage() {
			InitializeComponent();

			Info.Text = string.Format("RenderMode: {0}\nRenderModeReason: {1}",
				GraphicsDeviceManager.Current.RenderMode.ToString(),
				GraphicsDeviceManager.Current.RenderModeReason.ToString());

			var device = GraphicsDeviceManager.Current.GraphicsDevice;
			var vertices = new Vertex[] {
                    new Vertex(new Vector3(+0.0f, +0.5f, +1.0f), new Vector4(+1.0f, +0.0f, +0.0f, +1.0f)),
                    new Vertex(new Vector3(+0.0f, +1.0f, +0.0f), new Vector4(+0.0f, +1.0f, +0.0f, +1.0f)),
                    new Vertex(new Vector3(-1.0f, -1.0f, +0.0f), new Vector4(+0.0f, +0.0f, +1.0f, +1.0f)),
                    new Vertex(new Vector3(+1.0f, -0.0f, +1.0f), new Vector4(+0.5f, +0.5f, +0.5f, +0.5f)),
            };
			vertexBuffer = new VertexBuffer(device, Vertex.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
			vertexBuffer.SetData(0, vertices, 0, vertices.Length, Vertex.VertexDeclaration.VertexStride);

			var info1 = Application.GetResourceStream(new Uri("main.vs", UriKind.Relative));
			vertexShader = VertexShader.FromStream(device, info1.Stream);
			var info2 = Application.GetResourceStream(new Uri("main.ps", UriKind.Relative));
			pixelShader = PixelShader.FromStream(device, info2.Stream);

			aspectRatio = (float)(Surface.Height / Surface.Width);
		}

		void Surface_Draw(object sender, DrawEventArgs e) {
			var r = e.TotalTime.TotalSeconds * Math.PI * 1;
			worldViewProjection.M11 = (float)Math.Cos(r) * aspectRatio;
			worldViewProjection.M13 = (float)Math.Sin(r) * aspectRatio;
			worldViewProjection.M31 = -(float)Math.Sin(r) * 0.1f;
			worldViewProjection.M33 = (float)Math.Cos(r) * 0.1f;
			worldViewProjection.M34 = 0.5f;

			e.GraphicsDevice.BlendState = BlendState.AlphaBlend;
			e.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
			e.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
			e.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,
				new Microsoft.Xna.Framework.Color(0.0f, 0.0f, 0.5f, 1.0f), 1.0f, 0);
			e.GraphicsDevice.SetVertexShader(vertexShader);
			e.GraphicsDevice.SetPixelShader(pixelShader);
			e.GraphicsDevice.SetVertexBuffer(vertexBuffer);
			e.GraphicsDevice.SetVertexShaderConstantFloat4(0, ref worldViewProjection);
			e.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);

			e.InvalidateSurface();
		}
	}

	struct Vertex {
		public Vector3 Position;
		public float Pad;
		public Vector4 Color;

		public Vertex(Vector3 position, Vector4 color) {
			Position = position;
			Pad = 0;
			Color = color;
		}

		public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
			new VertexElement(4 * 0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
			new VertexElement(4 * 3, VertexElementFormat.Single, VertexElementUsage.TextureCoordinate, 0),
			new VertexElement(4 * 4, VertexElementFormat.Vector4, VertexElementUsage.Color, 0)
		);
	}

	struct Vector3 {
		public float X, Y, Z;
		public Vector3(float x, float y, float z) {
			X = x; Y = y; Z = z;
		}
	}

	struct Vector4 {
		public float X, Y, Z, W;
		public Vector4(float x, float y, float z, float w) {
			X = x; Y = y; Z = z; W = w;
		}
	}

	struct Matrix {
		public float M11, M12, M13, M14;
		public float M21, M22, M23, M24;
		public float M31, M32, M33, M34;
		public float M41, M42, M43, M44;
		public Matrix(
			float m11, float m12, float m13, float m14,
			float m21, float m22, float m23, float m24,
			float m31, float m32, float m33, float m34,
			float m41, float m42, float m43, float m44) {
			M11 = m11; M12 = m12; M13 = m13; M14 = m14;
			M21 = m21; M22 = m22; M23 = m23; M24 = m24;
			M31 = m31; M32 = m32; M33 = m33; M34 = m34;
			M41 = m41; M42 = m42; M43 = m43; M44 = m44;
		}
	}
}

MainPage.xaml

<UserControl x:Class="XELF.Silverlight5BetaXnaSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <StackPanel>
        <DrawingSurface x:Name="Surface" Width="400" Height="300" Draw="Surface_Draw"></DrawingSurface>
        <TextBlock x:Name="Info"></TextBlock>
        <TextBlock x:Name="Info2" FontSize="20">XNA 3D Sample in Silverlight 5 Beta</TextBlock>
        <TextBlock x:Name="Info3">Copyright 2011 XELF</TextBlock>
    </StackPanel>
</UserControl>

vs.hlsl(fxcで「main.vs」へコンパイルし、プロジェクトにリソースとして追加)

// fxc /Tvs_2_0 vs.hlsl /Fo main.vs

float4x4 worldViewProjection : register(c0);

void main(
	float3 inPosition : POSITION,
	float4 inColor : COLOR,
	out float4 outPosition : POSITION,
	out float4 outColor : COLOR
) {
	outPosition = mul(float4(inPosition, 1), worldViewProjection);
	outColor = inColor;
}

ps.hlsl(fxcで「main.ps」へコンパイルし、プロジェクトにリソースとして追加)

// fxc /Tps_2_0 ps.hlsl /Fo main.ps

void main(
	float4 inColor : COLOR,
	out float4 outColor : COLOR
) {
	outColor = inColor;
}

テスト用のhtmlの抜粋 (要点は、GPUアクセラレーションを有効にするタグを追加することです)

<body>
    <form id="form1" runat="server" style="height:100%">
    <div id="silverlightControlHost">
        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
          <param name="enableGPUAcceleration" value="true" />
		  <param name="source" value="ClientBin/XELF.Silverlight5BetaXnaSample.xap"/>
		  <param name="onError" value="onSilverlightError" />
		  <param name="background" value="white" />
		  <param name="minRuntimeVersion" value="5.0.60401.0" />
		  <param name="autoUpgrade" value="true" />
		  <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=5.0.60401.0" style="text-decoration:none">
 			  <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Microsoft Silverlight の取得" style="border-style:none"/>
		  </a>
	    </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
    </form>
</body>