C#は電気羊の夢を見るか?(引っ越し中)

dtiブログが閉鎖とのことでhttp://tanocs.blog.fc2.com/へ引っ越します。

SlimDXの問題点とSharpDX

ある程度SlimDXでの開発を終え、テストもボチボチ進んだ頃、
「さぁ、仲間内に配布」という所で有る問題が持ち上がってきた。

それは、「必要とするライブラリが多い」という事実。

もともとXNAからSlimDXへ移行したのは
実行するユーザーの負担の少なさを期待してのものだったのだが、
これが外れたのだ。

.Netはそもそも標準インストールではないので、
インストールされていないPCにはインストールが必要となる。
そしてSlimDXはC++ Runtimeが必要なのでこれも必要となる。

結局、これだとユーザーに付随するライブラリを2つ以上要求することになる。
ダメだったときに「.Net入れてね」だけで済まないのだ。

.Netだけであれば「わりと入っているだろう」という楽観の下
.Net 2.0環境で開発を続けていた。
ところが、そうは行かなかった訳だ。


困っていたところに、面白いものを見つけた。
SharpDX
http://code.google.com/p/sharpdx/

これの良いところは
・もちろんC++ Runtimeが不要
・Mono上で動作する(.NET Frameworkも不要)
・SlimDXより高速らしい

悪いところは
・日本語の情報がない


言語の壁があるが、
ある程度対応するAPIが有るであろうことを期待して、
SlimDXから移行をしてみようかと思う。
このエントリーをはてなブックマークに追加
LINEで送る

SlimDXでXAudio2をつかってサイン波を再生する

XAudio2のいろんなサンプルを継ぎ接ぎしてSlimDXに対応させた。


一番困ったのは、転送してるのはByteのくせに、
その中身はShortにしておく必要があるとか、
よく分からない事になってたのに気がつかなくて苦労した。

[2011/11/27追記]
format.BitsPerSample = 16; なんだから当然でした。
24とか32Fによって対応方法を変えてくりゃれ。

32Fはfloatに一旦突っ込んで中身が1.0〜-1.0って表記になる
そいつを、int32に放り込むなら32bitの最大値で掛けて、整数型に直してやればいい。

この方法だと、厳密にはプラス側と、マイナス側で最大1の誤差が出るが、
どう頑張っても聞き分けられないので、無視していいと思う。
[追記終わり]


byte[] byteArray = new byte[waveData.Length * 2]; //バッファ
Buffer.BlockCopy(waveData , 0, byteArray, 0, byteArray.Length);

↑このへん。

それ以外はWaveを扱ったことがある人なら、それほど苦労することはないと思う。
若干クセがあるものの、全体的に簡単な使い心地。
というか、他の音関係のライブラリが難しすぎるんだと思う・・・

そんなわけで、以下サンプル。


XAudio2 device = new XAudio2();
MasteringVoice masteringVoice = new MasteringVoice(device);

WaveFormat format = new WaveFormat();
format.Channels = 1;
format.SamplesPerSecond = 44100;
format.BitsPerSample = 16;
format.BlockAlignment = (short)(format.BitsPerSample / 8 * format.Channels);
format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlignment;
format.FormatTag = WaveFormatTag.Pcm;

// Create the sample data into memory
short[] waveData = new short[format.AverageBytesPerSecond];

//「ラ」を表すsin波(440Hz、1秒)
for (int i = 0; i < waveData.Length; i++)
{
waveData[i] = (short)(32767.0 * Math.Sin(2.0 * Math.PI * 440.0 * i / (double)format.SamplesPerSecond));
}

byte[] byteArray = new byte[waveData.Length * 2]; //バッファ
Buffer.BlockCopy(waveData , 0, byteArray, 0, byteArray.Length);

MemoryStream ms = new MemoryStream(byteArray);
AudioBuffer buffer = new AudioBuffer();
buffer.AudioData = ms;
buffer.AudioBytes = (int)ms.Length;
buffer.LoopCount = XAudio2.LoopInfinite; // 永久にループ
buffer.LoopBegin = 0; // ループ開始位置
buffer.LoopLength = 0; // 再生する長さ。0だと全部。
buffer.Flags = BufferFlags.EndOfStream;

SourceVoice sourceVoice = new SourceVoice(device, format);
sourceVoice.SubmitSourceBuffer(buffer);
sourceVoice.Start(); //再生

MessageBox.Show("終了");

// cleanup the voice
buffer.Dispose();
sourceVoice.Dispose();
ms.Close();
ms.Dispose();

masteringVoice.Dispose();
device.Dispose();
このエントリーをはてなブックマークに追加
LINEで送る

SlimDXでeffectが使える2D描画の方法

偉そうに書いているが、TEXCOORD周りが厳密には間違っている気がする。
見たところの動作は良好。

この実装では必ずClientサイズとTextureサイズが等しいこと。
RenderTargetから値を持ってきた時を想定しているので、この形に。

Effectまわりは自分で追加してくりゃれ。


[StructLayout(LayoutKind.Sequential)]
struct Vertex
{
public const VertexFormat Format = VertexFormat.Position | VertexFormat.Texture1;

public Vector3 Position;
public Vector2 TextureCoordinate;
}

public partial class Form1 : Form
{
// Direct3D9 Device
static Device _device;
// テクスチャ
static Texture _texture;
// テクスチャ描画用の頂点バッファ
static VertexBuffer _vertices;

public Form1()
{
InitializeComponent();

var pp = new PresentParameters();
pp.BackBufferWidth = this.ClientSize.Width;
pp.BackBufferHeight = this.ClientSize.Height;

_device = new Device(new Direct3D(), 0, DeviceType.Hardware, this.Handle, CreateFlags.HardwareVertexProcessing, pp);

// カラー成分の設定
_device.SetTextureStageState(0, TextureStage.ColorOperation, TextureOperation.SelectArg1);
_device.SetTextureStageState(0, TextureStage.ColorArg1, TextureArgument.Texture);
// アルファ成分の設定
_device.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.SelectArg1);
_device.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture);
// ブレンディングモードの設定
_device.SetRenderState(RenderState.AlphaBlendEnable, true);
_device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
_device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);


// テクスチャを sample.png から読み込む
_texture = Texture.FromFile(_device, "test.bmp", 0, 0, 1, Usage.None, Format.A8R8G8B8, Pool.Managed, Filter.None, Filter.None, 0);

var description = _texture.GetSurfaceLevel(0).Description;

// 頂点のサイズ (バイト)
int vertexSize = Marshal.SizeOf(typeof(Vertex));
// 頂点バッファを作成
_vertices = new VertexBuffer(_device, 4 * vertexSize, Usage.WriteOnly, Vertex.Format, Pool.Managed);
// 頂点バッファをロック
var dataStream = _vertices.Lock(0, 4 * vertexSize, LockFlags.None);

// テクスチャ座標の指定
float x1 = 0.5f / description.Width;
float y1 = 0.5f / description.Height;
float x2 = (pp.BackBufferWidth + 0.5f) / description.Width;
float y2 = (pp.BackBufferHeight + 0.5f) / description.Height;

// 頂点バッファにデータを書き込む
dataStream.WriteRange(new[] {
new Vertex { Position = new Vector3(-1.0f, 1.0f, 0.0f), TextureCoordinate = new Vector2(x1, y1) },
new Vertex { Position = new Vector3(1.0f, 1.0f, 0.0f), TextureCoordinate = new Vector2(x2, y1) },
new Vertex { Position = new Vector3(-1.0f, -1.0f, 0.0f), TextureCoordinate = new Vector2(x1, y2) },
new Vertex { Position = new Vector3(1.0f, -1.0f, 0.0f), TextureCoordinate = new Vector2(x2, y2) },
});

// 頂点バッファをアンロック
_vertices.Unlock();
}

public void Run()
{
SlimDX.Windows.MessagePump.Run(this, Draw);
}

void Draw()
{
// 画面をクリアする
_device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);


_device.BeginScene();

// 描画する頂点バッファを指定する
_device.SetStreamSource(0, _vertices, 0, Marshal.SizeOf(typeof(Vertex)));

// 頂点バッファのフォーマットを指定する
_device.VertexFormat = Vertex.Format;

// テクスチャを指定
_device.SetTexture(0, _texture);

// 描画する
_device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);

_device.EndScene();

// 表画面に描画内容を転送
_device.Present();
}

このエントリーをはてなブックマークに追加
LINEで送る

SlimDX.D3D9を使いGPUで描画した結果をCPUで取得する

相変わらず海外を含めてサンプルが見つからない。

SlimDXでGPGPUをDX9のまま実装するため
描画済みのテクスチャを取得する方法を置いとく

描画メソッド内に以下の内容を実行すると、取得ができる。

テスト用のメソッドなので、ビットマップの保存や、
巨大な配列の宣言とかは適宜修正してくりゃれ。


//レンダーターゲットを用意する
renderTexture = new Texture(device, this.ClientSize.Width, this.ClientSize.Height, 1, Usage.RenderTarget, Format.X8R8G8B8, Pool.Default);
renderSurface = renderTexture.GetSurfaceLevel(0);

//レンダーターゲットを保存する
oldRenderSurface = device.GetRenderTarget(0);

//レンダーターゲットを変更する
device.SetRenderTarget(0, renderSurface);

//画面を初期化
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
device.BeginScene();

//データを流しこんで描画
device.SetStreamSource(0, vertices, 0, Marshal.SizeOf(typeof(Vertex)));
device.VertexDeclaration = vertexDecl;
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);

//レンダーターゲットを元に戻す
device.SetRenderTarget(0, oldRenderSurface);
oldRenderSurface.Dispose();

//ストリームを用意
DataStream stream = Texture.ToStream(renderTexture, ImageFileFormat.Bmp); //ファイルストリームと等価

//ビットマップとして流しこむ
Bitmap bmp = (Bitmap)Bitmap.FromStream(stream, false, false);

//内容を保存して確認
bmp.Save("test.bmp", System.Drawing.Imaging.ImageFormat.Bmp);

//直接バイト型として参照も可能
byte[] bs = new byte[stream.Length / sizeof(byte)];
stream.Read(bs, 0, bs.Length);

device.EndScene();
device.Present();



はっきり言って暗中模索のなか、どうにか実行できているレベルなので、
このままでは問題が起こる可能性が多分にある。

メモリリークの可能性や、その他何か気づいた点があったら、
遠慮なくコメントからツッコミをお願いします。


蛇足:
国内のサンプルは何故かテクスチャを
一生懸命BitmapクラスやらImageクラスからメモリ通して作ってるけど、
なんで
Texture tex = Texture.FromFile(device, filePath);
を使わないんじゃろうか・・・

低速であっても、どうせ初期化の一回きりの処理だし、
気にならないと思うんだけど、何か理由があるんだろうか・・・
このエントリーをはてなブックマークに追加
LINEで送る

密かなるSlimDX

最近SlimDXを始めた。

んが、しかし。
XNAとぜんぜん使い勝手が違う。
なんていうか、ラップが薄く、”楽”じゃないなぁという感じ。

しかも!なかなかと変な癖があって
Vector3.Transformの戻り値がVector4だったり、
Colorの持ち方がint型だったり、そこかしこに罠が潜んでいる。

そのあまりの違いに、まともにモデルを表示することもままならず、
途方にくれていた。


その原因はとにかくサンプルがない。
情報の殆どは英語と、なかなか敷居が高い。

たかだかインデックスバッファのサンプルを探しても、
どうやら存在しないのだから泣けてくる。

仕方ないのでDirectXのサンプルやらXNAの記憶とかを便りになんとか、
シンプルなIndexBuffer描画サンプルを作成したので、ここに置いておく。



実行結果


using SlimDX;
using SlimDX.Direct3D9;
using SlimDX.Windows;

using System.Runtime.InteropServices;

namespace IndexbufferTest
{
public partial class Form1 : Form
{
Device device;

public struct TestVertex
{
public Vector3 Position;
public Vector3 Normal;
public int Color;
}

VertexElement[] vertexElementsArray = new VertexElement[]
{
new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
new VertexElement(0, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 0),
new VertexElement(0, 24, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0)
};

Vector3[] vectors = new Vector3[] { new Vector3(-1, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 0, 0),
new Vector3(0, -1, 0)
};

public Form1()
{
InitializeComponent();

var pp = new PresentParameters();
pp.BackBufferWidth = this.ClientSize.Width;
pp.BackBufferHeight = this.ClientSize.Height;
pp.DeviceWindowHandle = this.Handle;

this.device = new Device(new Direct3D(), 0, DeviceType.Hardware, this.Handle, CreateFlags.HardwareVertexProcessing, pp);
}

public void Run()
{
SlimDX.Windows.MessagePump.Run(this, Draw);
}

protected virtual void Draw()
{
int vertexSize = Marshal.SizeOf(typeof(TestVertex));

VertexBuffer vertexBuffer = new VertexBuffer(device, vectors.Length * vertexSize, Usage.None, VertexFormat.None, Pool.Managed);

var vertexData = new TestVertex[vectors.Length];
for (int i = 0; i < vertexData.Length; i++)
{
vertexData[i].Position = vectors[i];
vertexData[i].Normal = new Vector3(0, 0, -1);
vertexData[i].Color = Color.Gold.ToArgb();
}


IndexBuffer indexBuffer = new IndexBuffer(device, vectors.Length * sizeof(uint) * 3 - 2, Usage.None, Pool.Managed, false);
var indices = new uint[]{0, 1, 2,
0, 2, 3};

device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);
device.SetRenderState(RenderState.Lighting, false);
device.BeginScene();

device.VertexDeclaration = new VertexDeclaration(device, vertexElementsArray);

DataStream vertexBufferStream = vertexBuffer.Lock(0, vectors.Length * vertexSize, LockFlags.None);
vertexBufferStream.WriteRange(vertexData);
vertexBuffer.Unlock();
device.SetStreamSource(0, vertexBuffer, 0, vertexSize);

DataStream indexBufferStream = indexBuffer.Lock(0, indices.Length * sizeof(uint), LockFlags.None);
indexBufferStream.WriteRange(indices);
indexBuffer.Unlock();
device.Indices = indexBuffer;

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vectors.Length, 0, indices.Length / 3);

device.EndScene();
device.Present();
}
}
}
このエントリーをはてなブックマークに追加
LINEで送る

 | HOME | 

PAGE TOP ▲

Appendix

■春条

■春条

生息地は愛知
車と甘い物が好きな31歳
特技は無限昼寝

MONOからSharpDXを使う
変態的な手法で、
.NET Frameworkを使わずに
ゲーム作りやってます。

Search

Calender

« | 2017-10 | »
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -

Twitter

Recent Entries

DTIブログポータルへ
このブログを通報
Report Abuse

利用規約