Direct3D の初期化と基本処理

Page updated :

The page you are currently viewing does not support the selected display language.

コードを下のように書き換え、実行すると下のように表示されます。

Direct3D の初期化と基本処理

今回のメインコードファイルを載せます。部分的な説明に関してはコードの下の方で説明しています。

MainSample.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MDXSample
{
    /// <summary>
    /// メインサンプルクラス
    /// </summary>
    public class MainSample : IDisposable
    {
        /// <summary>
        /// メインフォーム
        /// </summary>
        private MainForm _form = null;

        /// <summary>
        /// Direct3D デバイス
        /// </summary>
        private Device _device = null;


        /// <summary>
        /// アプリケーションの初期化
        /// </summary>
        /// <param name="topLevelForm">トップレベルウインドウ</param>
        /// <returns>全ての初期化がOKなら true, ひとつでも失敗したら false を返すようにする</returns>
        /// <remarks>
        /// false を返した場合は、自動的にアプリケーションが終了するようになっている
        /// </remarks>
        public bool InitializeApplication(MainForm topLevelForm)
        {
            // フォームの参照を保持
            this._form = topLevelForm;

            // PresentParameters。デバイスを作成する際に必須
            // どのような環境でデバイスを使用するかを設定する
            PresentParameters pp = new PresentParameters();

            // ウインドウモードなら true、フルスクリーンモードなら false を指定
            pp.Windowed = true;

            // スワップ効果。とりあえず「Discard」を指定。
            pp.SwapEffect = SwapEffect.Discard;


            // 実際にデバイスを作成します。
            // 常に最高のパフォーマンスで作成を試み、
            // 失敗したら下位パフォーマンスで作成するようにしている。
            try
            {
                // ハードウェアによる頂点処理、ラスタライズを行う
                // 最高のパフォーマンスで処理を行えます。
                // ビデオカードによっては実装できない処理が存在します。
                this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
                    CreateFlags.HardwareVertexProcessing, pp);
            }
            catch (DirectXException ex1)
            {
                // 作成に失敗
                Debug.WriteLine(ex1.ToString());
                try
                {
                    // ソフトウェアによる頂点処理、ハードウェアによるラスタライズを行う
                    this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
                        CreateFlags.SoftwareVertexProcessing, pp);
                }
                catch (DirectXException ex2)
                {
                    // 作成に失敗
                    Debug.WriteLine(ex2.ToString());
                    try
                    {
                        // ソフトウェアによる頂点処理、ラスタライズを行う
                        // パフォーマンスはとても低いです。
                        // その代わり、ほとんどの処理を制限なく行えます。
                        this._device = new Device(0, DeviceType.Reference, topLevelForm.Handle,
                            CreateFlags.SoftwareVertexProcessing, pp);
                    }
                    catch (DirectXException ex3)
                    {
                        // 作成に失敗
                        // 事実上デバイスは作成できません。
                        MessageBox.Show(ex3.ToString(), "エラー",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// メインループ処理
        /// </summary>
        public void MainLoop()
        {
            // 描画内容を単色でクリア
            this._device.Clear(ClearFlags.Target, Color.DarkBlue, 1.0f, 0);

            // 「BeginScene」と「EndScene」の間に描画内容を記述する
            this._device.BeginScene();

            // 描画はここまで
            this._device.EndScene();

            // 実際のディスプレイに描画
            this._device.Present();
        }

        /// <summary>
        /// リソースの破棄をするために呼ばれる
        /// </summary>
        public void Dispose()
        {
            // Direct3D デバイスのリソース解放
            if (this._device != null)
            {
                this._device.Dispose();
            }
        }
    }
}

using System.Diagnostics;

DirectX とは関係ないですが、後で「Debug」クラスを使用するので、ネームスペースを記述しておきます。


/// <summary>
/// Direct3D デバイス
/// </summary>
private Device _device = null;

Direct3D デバイスです。Direct3D 関連のプログラムを行う場合はほとんどといっていいほどこれを使用します。とりあえず null で初期化します。


// PresentParameters。デバイスを作成する際に必須
// どのような環境でデバイスを使用するかを設定する
PresentParameters pp = new PresentParameters();

// ウインドウモードなら true、フルスクリーンモードなら false を指定
pp.Windowed = true;

// スワップ効果。とりあえず「Discard」を指定。
pp.SwapEffect = SwapEffect.Discard;

Direct3D デバイスを作成するためには「PresentParameters」構造体を作成する必要があります。

PresentParameters」構造体にはかなりたくさんの項目がありますが、本来はすべて設定する必要はありません。必要な項目だけ設定すればいいでしょう。上の項目は最低限必要なので設定しています。

各パラメータに関しての説明はコメントの通りです。また DirectX のヘルプや、DirectX 関連書籍にも詳しく書いてあるのでそちらを参考にしてください。

ちなみに下は使われることがあるものを、ひと通り設定してみたコードです。

// PresentParameters。デバイスを作成する際に必須
// どのような環境でデバイスを使用するかを設定する
PresentParameters pp = new PresentParameters();

// ウインドウモードなら true、フルスクリーンモードなら false を指定
pp.Windowed = true;

// スワップ効果。とりあえず「Discard」を指定。
pp.SwapEffect = SwapEffect.Discard;

// 深度ステンシルバッファ。3Dでは普通使用するが、今回はまだ使わないので false
pp.EnableAutoDepthStencil = false;

// 自動深度ステンシル サーフェイスのフォーマット。
// 「D16」に対応しているビデオカードは多いが、前後関係の精度があまりよくない。
// できれば「D24S8」を指定したいところ。
// ただし、上記の EnableAutoDepthStencil を false にしているので今回は Unknown
pp.AutoDepthStencilFormat = DepthFormat.Unknown;

// Direct3D デバイスが使用するバックバッファのサイズ。解像度とも言い換えられる
// 0 を指定した場合は、自動的に 下記の DeviceWindow のクライアントサイズと同じになる
// フルスクリーンモードの場合はサイズに制限があるので注意
pp.BackBufferWidth = 0;
pp.BackBufferHeight = 0;

// 描画対象のウインドウまたはコントロール。
// 通常はトップレベルウインドウを渡すが、絶対ではない
pp.DeviceWindow = form;
pp.DeviceWindowHandle = form.Handle;

// バックバッファのフォーマット。「R5G6B5」は対応しているビデオカードが多い。
// ウインドウモードなら「Unknown」を指定してもよい。
pp.BackBufferFormat = Format.Unknown;

// バックバッファの数。「0」で問題ない。(「0」は「1」と同じらしい )
pp.BackBufferCount = 0;

// マルチサンプリングのレベル。使わないなら「None」を指定。
pp.MultiSample = MultiSampleType.None;

// マルチサンプルの品質レベル。上で「MultiSampleType.None」を指定しているので「0」
pp.MultiSampleQuality = 0;

// ウインドウモードでは「0」でなければならない。
// フルスクリーンモードでは「60」や「75」が一般的
pp.FullScreenRefreshRateInHz = 0;

// ウインドウモードの場合は「Default」か「Immediate」を指定。
// Default は 垂直帰線間隔を待機します(一定の間隔で画面更新)
// Immediate は 待機せず直ちに更新します
pp.PresentationInterval = PresentInterval.Default;

// アプリケーションでマルチスレッドが使えるかどうか
pp.ForceNoMultiThreadedFlag = false;

// 提示フラグ。詳しくはヘルプの PresentFlag を参照してください。
pp.PresentFlag = PresentFlag.None;

// 実際にデバイスを作成します。
// 常に最高のパフォーマンスで作成を試み、
// 失敗したら下位パフォーマンスで作成するようにしている。
try
{
    // ハードウェアによる頂点処理、ラスタライズを行う
    // 最高のパフォーマンスで処理を行えます。
    // ビデオカードによっては実装できない処理が存在します。
    this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
        CreateFlags.HardwareVertexProcessing, pp);
}
catch (DirectXException ex1)
{
    // 作成に失敗
    Debug.WriteLine(ex1.ToString());
    try
    {
        // ソフトウェアによる頂点処理、ハードウェアによるラスタライズを行う
        this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
            CreateFlags.SoftwareVertexProcessing, pp);
    }
    catch (DirectXException ex2)
    {
        // 作成に失敗
        Debug.WriteLine(ex2.ToString());
        try
        {
            // ソフトウェアによる頂点処理、ラスタライズを行う
            // パフォーマンスはとても低いです。
            // その代わり、ほとんどの処理を制限なく行えます。
            this._device = new Device(0, DeviceType.Reference, topLevelForm.Handle,
                CreateFlags.SoftwareVertexProcessing, pp);
        }
        catch (DirectXException ex3)
        {
            // 作成に失敗
            // 事実上デバイスは作成できません。
            MessageBox.Show(ex3.ToString(), "エラー",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
            return false;
        }
    }
}

「PresentParameters」構造体を作成したら、実際に「Direct3D デバイス」を作成します。

Device クラスを作成しますが、いくつかのパターンで作成できるようにしています。

Device コンストラクタ
Direct3D デバイスを作成します。
adapter 物理デバイスのインデックスです。通常「0」を指定します。これは複数の物理デバイスが存在する場合に「1」や「2」を設定して、使用する物理デバイスを切り替えることができます。
deviceType ラスタライズをどのように行うかを設定しています。ハードウェアでデバイスの作成を試み、失敗したらリファレンスラスタライズを指定するようにしています
renderWindowHandle 描画対象のウインドウ(フォーム)またはコントロールを渡します。null を指定した場合は「PresentParameter」構造体の「DeviceWindow」を指定するか、後ほど出てくる「Device.Present」メソッドでコントロールを指定します。
behaviorFlags デバイスの作成フラグです。通常「HardwareVertexProcessing」を指定し、失敗したら「SoftwareVertexProcessing」で作成を試みます。他にも組み合わせがいくつかあるので調べてみてください。
presentationParameters 作成した「PresentParameter」構造体を渡します。

今回のコードは基本的にすべてハードウェアで動作するように作成していますが、作成に失敗した場合、ソフトウェアで動作するように再設定しています。もし、すべてのパターンが失敗したら、初期化失敗として false を返しています。

ただし、今回の Device 作成方法はよく使われる形ではありますが、アプリケーションによって形は異なります。そのソフトにあわせた作成方法を記述してください。

ちなみに、デバイスの作成で例外が発生したときに「Debug.WriteLine」を使用していますが、これはデバッグ用メッセージとして文字列を出力するためのメソッドです。「DirectXException.ToString」メソッドでエラー内容を文字列として変換して出力しています。実はこの処理はまったく必要なく、なくてもぜんぜん問題ありません。なぜわざわざ使っているのかというと、例外変数の「ex1」と「ex2」を一回も使用しないと、ビルド時に警告として残ってしまうため、それを消すためにわざと余分な処理を書いているのです。


// 描画内容を単色でクリア
this._device.Clear(ClearFlags.Target, Color.DarkBlue, 1.0f, 0);

// 「BeginScene」と「EndScene」の間に描画内容を記述する
this._device.BeginScene();

// 描画はここまで
this._device.EndScene();

// 実際のディスプレイに描画
this._device.Present();

実際のメインループの処理です。

まず最初に前回まで画面に描画された内容をクリアします。クリアしないと前の描画が内容が残ったままになってしまうので必ずクリアします。

Device.Clear メソッド
ビューポート内のバッファをクリアします。
flags クリアする内容を指定します。ClearFlags 列挙型から任意の組み合わせで指定します。
color flags で ClearFlags.Target を指定している場合、画面を塗りつぶす色を指定します。
zdepth flags で ClearFlags.Stencil を指定している場合、Zバッファ の初期化値を指定します。通常 1.0
stencil flags で ClearFlags.ZBuffer を指定している場合、ステンシルバッファ の初期化値を指定します。通常 0

今回クリアする内容は「」のみです。「ClearFlags.Target」を指定しているので、第2引数で指定した色で塗りつぶしています。

今回「DarkBlue」で塗りつぶしていますが、なぜ黒で塗りつぶさないのかというと、後の Tips でポリゴンなどを描画したときに設定をミスしてしまうとポリゴンが真っ黒く塗りつぶされてしまうことがあります。この場合、背景が真っ黒だと、ポリゴンが表示されていないのか、黒く塗りつぶされているのかわからないためです。

第3引数はZバッファのクリアする値です。ただし、今回はZバッファを指定していないのでこの値は何の影響も与えません。通常は「1.0」を指定しますが、多分これを変えることはほとんどないはずです。Zバッファについては後の Tips で説明します。

第4引数はステンシルバッファのクリアする値です。ステンシルバッファについても使用する機会があればそのときに説明します。とりあえず「0」をセットしています。

さて、今回は何も描画していませんが、もし、ポリゴンなどを描画する場合はすべて「Device.BeginScene」と「Device.EndScene」の間に記述することになっています。これはそういう仕様なのでそうしてください。

最後に描画した内容を実際のスクリーンに送るのに「Device.Present」メソッドを使用して送ります。


// Direct3D デバイスのリソース解放
if (this._device != null)
{
    this._device.Dispose();
}

フォームが閉じたときに Direct3D デバイスを解放するようにしています。