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

ColorMap.pngは下記サイトからダウンロードする。

参考

Program_3.cs
// #3
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
class Game1 : Game
{
	GraphicsDeviceManager graphics;
	VertexBuffer vb;
	Effect effect;
	RenderTarget2D renderTarget;
	bool updateCachedTexture = true;
	Vector2 offset = new Vector2(-0.25f, 0.0f);
	float zoom = 3.0f;
 
	// 頂点構造体
	struct MyVertex : IVertexType
	{
		Vector2 Position;
		Vector2 UV;
		public MyVertex(Vector2 position, Vector2 uv)
		{
			Position = position;
			UV = uv;
		}
		public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration(
			new VertexElement[] {
				new VertexElement(0, VertexElementFormat.Vector2,
					VertexElementUsage.Position, 0),
				new VertexElement(8, VertexElementFormat.Vector2,
					VertexElementUsage.TextureCoordinate, 0),
			});
		VertexDeclaration IVertexType.VertexDeclaration { get { return VertexDeclaration;} }
	}
 
	public Game1()
	{
		graphics = new GraphicsDeviceManager(this);
		Content.RootDirectory = "Content";
 
		graphics.PreferredBackBufferWidth = 800;
		graphics.PreferredBackBufferHeight = 600;
		Window.AllowUserResizing = true;
	}
 
	protected override void LoadContent()
	{
		const int numVertices = 4;
		vb = new VertexBuffer(GraphicsDevice, typeof(MyVertex), numVertices,
			BufferUsage.None);
		MyVertex[] vertices = new MyVertex[numVertices] {
			new MyVertex(new Vector2(-1.0f,  1.0f), new Vector2(0.0f, 1.0f)), // LT
			new MyVertex(new Vector2( 1.0f,  1.0f), new Vector2(1.0f, 1.0f)), // RT
			new MyVertex(new Vector2(-1.0f, -1.0f), new Vector2(0.0f, 0.0f)), // LB
			new MyVertex(new Vector2( 1.0f, -1.0f), new Vector2(1.0f, 0.0f)), // RB
		};
		vb.SetData(vertices);
 
		// ContentにMandelbrot.fxを追加しておく
		effect = Content.Load<Effect>("Mandelbrot");
		// ContentにColorMap.pngを追加しておく
		effect.Parameters["colorMapTexture"].SetValue(Content.Load<Texture2D>("ColorMap"));
 
		if (renderTarget == null) {
			renderTarget = new RenderTarget2D(
				GraphicsDevice,
				GraphicsDevice.PresentationParameters.BackBufferWidth,
				GraphicsDevice.PresentationParameters.BackBufferHeight);
			updateCachedTexture = true;
		}
 
		base.LoadContent();
	}
 
	protected override void UnloadContent()
	{
		if (renderTarget != null) {
			renderTarget.Dispose();
			renderTarget = null;
		}
 
		base.UnloadContent();
	}
 
	protected override void Update(GameTime gameTime)
	{
		float oldZoom = zoom;
		Vector2 oldOffset = offset;
 
		// 移動量
		float offsetMove = 0.01f * (float)Math.Log(zoom + 1.0f);
 
		KeyboardState key = Keyboard.GetState();
		if (key.IsKeyDown(Keys.Left)) {
			offset.X -= offsetMove;
		}
		if (key.IsKeyDown(Keys.Right)) {
			offset.X += offsetMove;
		}
		if (key.IsKeyDown(Keys.Down)) {
			offset.Y -= offsetMove;
		}
		if (key.IsKeyDown(Keys.Up)) {
			offset.Y += offsetMove;
		}
		if (key.IsKeyDown(Keys.PageDown)) {
			zoom *= 1.05f;
		}
		if (key.IsKeyDown(Keys.PageUp))
		{
			zoom /= 1.05f;
		}
		if (key.IsKeyDown(Keys.Escape)) {
			Exit();
		}
 
		updateCachedTexture |= (oldZoom != zoom || oldOffset != offset);
 
		base.Update(gameTime);
	}
 
	protected override void Draw(GameTime gameTime)
	{
		GraphicsDevice.Clear(Color.CornflowerBlue);
 
		// 再描画が必要なときだけテクスチャ・レンダリングを行う
		if (updateCachedTexture) {
			//エフェクトのパラメータ更新
			effect.Parameters["Zoom"].SetValue(zoom);
			effect.Parameters["Aspect"].SetValue(GraphicsDevice.Viewport.AspectRatio);
			effect.Parameters["Offset"].SetValue(offset);
 
			RenderTargetBinding[] rtb = GraphicsDevice.GetRenderTargets();
			GraphicsDevice.SetRenderTarget(renderTarget);
 
			// エフェクト描画
			effect.CurrentTechnique = effect.Techniques["Mandelbrot"];
			GraphicsDevice.SetVertexBuffer(vb);
			foreach (EffectPass pass in effect.CurrentTechnique.Passes)
			{
				pass.Apply();
				GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
			}
 
			GraphicsDevice.SetRenderTargets(rtb);
			updateCachedTexture = false;
		}
 
		// テクスチャにレンダリングされたマンデルブロ集合のイメージをそのまま表示する
		effect.Parameters["cachedTexture"].SetValue((Texture2D)renderTarget);
		effect.CurrentTechnique = effect.Techniques["Cached"];
		GraphicsDevice.SetVertexBuffer(vb);
		foreach(EffectPass pass in effect.CurrentTechnique.Passes) {
			pass.Apply();
			GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
		}
		effect.Parameters["cachedTexture"].SetValue((Texture2D)null);
 
		base.Draw(gameTime);
	}
}
 
class Program
{
	static void Main()
	{
		using (Game1 game = new Game1()) {
			game.IsMouseVisible = true;
			game.Run();
		}
	}
}
 

Mandelbrot_3.fx
float Aspect = 1.0f;
float Zoom = 1.0f;
float2 Offset = float2(0.0f, 0.0f);
texture colorMapTexture;
texture cachedTexture;
 
// サンプラー
sampler ColorMap = sampler_state
{
	Texture = <colorMapTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Wrap;
};
 
sampler CachedMap = sampler_state
{
	Texture = <cachedTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Wrap;
};
 
// 構造体
struct VertexIn
{
	float2 Pos : POSITION;
	float2 UV : TEXCOORD0;
};
 
struct VertexOut
{
	float4 ScreenPos : POSITION;
	float2 UV : TEXCOORD0;
};
 
struct PixelIn
{
	float2 UV : TEXCOORD0;
};
 
struct PixelOut
{
	float4 Color : COLOR0;
};
 
// Mandelbrot
VertexOut MandelbrotVS(VertexIn input)
{
	VertexOut output;
	output.ScreenPos = float4(input.Pos, 0.0f, 1.0f);
	output.UV = (input.UV - 0.5f) * float2(1.0f, -Aspect) * Zoom + Offset;
	return output;
}
 
PixelOut GetMandelbrotColor(int n, uniform int MaxIterate)
{
	PixelOut output;
	if (n < MaxIterate) {
		float u = (float)n / (float)MaxIterate;
		output.Color.rgb = tex1D(ColorMap, 2.0f * u);
	} else {
		output.Color.rgb = 0.0f;
	}
	output.Color.a = 1.0f;
	return output;
}
 
PixelOut MandelbrotPSImpl(PixelIn input, uniform int MaxIterate)
{
	float2 p = 0;
	int n;
	for (n = 0; n < MaxIterate && dot(p, p) < 4.0f; n++) {
		p = float2(p.x * p.x - p.y * p.y, 2.0f * p.x * p.y) + input.UV;
	}
	return GetMandelbrotColor(n, MaxIterate);
}
 
PixelOut MandelbrotPSImpl(PixelIn input, uniform int Loop1, uniform int Loop2)
{
	const int MaxIterate = Loop1 * 128 + Loop2;
 
	float2 p = 0;
	bool finite = true;
	int n = 0;
	for (int j = 0; j < Loop1 && finite; j++) {
		for (int i = 0; i < 128 && finite; i++) {
			p = float2(p.x * p.x - p.y * p.y, 2.0f * p.x * p.y) + input.UV;
			finite = dot(p, p) < 4.0f;
			n++;
		}
	}
	if (Loop2 > 0) {
		for (int j = 0; j < Loop2 && dot(p, p); j++) {
			p = float2(p.x * p.x - p.y * p.y, 2.0f * p.x * p.y) + input.UV;
			n++;
		}
	}
	return GetMandelbrotColor(n, MaxIterate);
}
 
PixelOut MandelbrotPS(PixelIn input, uniform int MaxIterate)
{
	if (MaxIterate > 254) {
		return MandelbrotPSImpl(input, MaxIterate / 128, MaxIterate % 128);
	} else {
		return MandelbrotPSImpl(input, MaxIterate);
	}
}
 
// CachedImage
VertexOut CachedImageVS(VertexIn input)
{
	VertexOut output;
	output.ScreenPos = float4(input.Pos, 0.0f, 1.0f);
	output.UV = input.UV;
	return output;
}
 
PixelOut CachedImageShader(PixelIn input)
{
	PixelOut output;
	output.Color = tex2D(CachedMap, input.UV);
	return output;
}
 
// レンダー
technique Mandelbrot
{
	pass Pass0
	{
		VertexShader = compile vs_3_0 MandelbrotVS();
		PixelShader = compile ps_3_0 MandelbrotPS(128);
	}
}
 
technique Cached
{
	pass Pass0
	{
		VertexShader = compile vs_3_0 CachedImageVS();
		PixelShader = compile ps_3_0 CachedImageShader();
	}
}
 
最終更新:2012年11月07日 21:47