SlimDXでテクスチャを表示するサンプル

SlimDXでテクスチャを表示するサンプルを書いてみた。sample.png がアルファ値を持ってる場合はちゃんとアルファブレンドして表示する。

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using SlimDX;
using SlimDX.Direct3D9;
using SlimDX.Windows;

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

        public Vector3 Position;
        public Vector2 TextureCoordinate;
    }

    static class Program {
        // 画面の幅と高さ
        static int _width = 800, _height = 600;
        // Direct3D9 Device
        static Device _device;
        // テクスチャ
        static Texture _texture;
        // テクスチャ描画用の頂点バッファ
        static VertexBuffer _vertices;

        /// <summary>
        /// Direct3D9 Deviceの初期化を行う
        /// </summary>
        /// <param name="form"></param>
        static void InitializeDirect3D9(RenderForm form) {
            var pp = new PresentParameters();
            pp.BackBufferFormat = Format.X8R8G8B8;
            pp.BackBufferCount = 1;
            pp.BackBufferWidth = form.ClientSize.Width;
            pp.BackBufferHeight = form.ClientSize.Height;
            pp.Multisample = MultisampleType.None;
            pp.SwapEffect = SwapEffect.Discard;
            pp.EnableAutoDepthStencil = true;
            pp.AutoDepthStencilFormat = Format.D16;
            pp.PresentationInterval = PresentInterval.Default;
            pp.Windowed = true;
            pp.DeviceWindowHandle = form.Handle;

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

        /// <summary>
        /// テクスチャを読み込んで頂点バッファを作る
        /// </summary>
        static void LoadTexture() {
            // テクスチャを sample.png から読み込む
            ImageInformation info;
            _texture = Texture.FromFile(_device, "sample.png", 0, 0, 1, Usage.None,
                Format.A8R8G8B8, Pool.Managed, Filter.None, Filter.None, 0, out info);

            var description = _texture.GetSurfaceLevel(0).Description;
            // テクスチャの幅と高さ (2のべき乗になるように調整される)
            float textureWidth = description.Width;
            float textureHeight = description.Height;
            // 画像の幅と高さ
            float imageWidth = info.Width;
            float imageHeight = info.Height;

            // 頂点のサイズ (バイト)
            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);

            // テクスチャ座標の指定
            // 0.5ピクセル分だけずらしてやらないと、画像がぼけてしまう
            float x1 = 0.5f / textureWidth, y1 = 0.5f / textureHeight;
            float x2 = (imageWidth + 0.5f) / textureWidth, y2 = (imageHeight + 0.5f) / textureHeight;
            // 頂点バッファにデータを書き込む
            dataStream.WriteRange(new[] {
                new Vertex { Position = new Vector3(0, 0, 0.5f), TextureCoordinate = new Vector2(x1, y1) },
                new Vertex { Position = new Vector3(imageWidth, 0, 0.5f), TextureCoordinate = new Vector2(x2, y1) },
                new Vertex { Position = new Vector3(0, imageHeight, 0.5f), TextureCoordinate = new Vector2(x1, y2) },
                new Vertex { Position = new Vector3(imageWidth, imageHeight, 0.5f), TextureCoordinate = new Vector2(x2, y2) },
            });

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

        /// <summary>
        /// 描画する
        /// </summary>
        static void Render() {
            // 画面をクリアする
            _device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Gray, 1.0f, 0);

            _device.BeginScene();

            // ビュー行列の設定
            // 画面の左上を原点とし、右方向にx軸、下方向にy軸が来るようにしている
            var view = Matrix.Identity;
            view.M22 = -1;
            view.M41 = -_width / 2.0f;
            view.M42 = _height / 2.0f;
            _device.SetTransform(TransformState.View, view);

            // 射影行列の設定
            // 正射影行列を指定して遠近感を消している
            var projection = Matrix.OrthoLH(_width, _height, -0.01f, 1.01f);
            _device.SetTransform(TransformState.Projection, projection);

            // テクスチャを描画
            RenderTexture();

            _device.EndScene();

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

        /// <summary>
        /// テクスチャを描画する
        /// </summary>
        static void RenderTexture() {
            // 左上の座標
            float x = 10, y = 10;
            // (x, y, 0)だけ平行移動
            _device.SetTransform(TransformState.World, Matrix.Translation(x, y, 0));

            // 描画する頂点バッファを指定する
            _device.SetStreamSource(0, _vertices, 0, Marshal.SizeOf(typeof(Vertex)));
            // 頂点バッファのフォーマットを指定する
            _device.VertexFormat = Vertex.Format;

            // テクスチャを指定
            _device.SetTexture(0, _texture);
            // カラー成分の設定
            _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);

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

        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main() {
            var form = new RenderForm("SlimDX Texture Sample");
            form.ClientSize = new Size(_width, _height);
            InitializeDirect3D9(form);
            LoadTexture();
            MessagePump.Run(form, Render);
        }
    }
}