テクスチャーの貼り付け

Page creation date :

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

今回はポリゴンに画像を貼って見ましょう(正確には描きこむの方が正しい)。この画像のことをテクスチャーと呼びます。テクスチャーは基本的に画像ファイルから読み込んで作成します。

ゲームなどではこのテクスチャーは必ずといっていいほど使用されますし、よりリアルなモデルとして見せるために有効に活用されます。

テクスチャーの貼り付け

今回のメインコードファイルを載せます。

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 partial class MainSample : IDisposable
    {
        /// <summary>
        /// 頂点バッファ
        /// </summary>
        private VertexBuffer _vertexBuffer = null;

        /// <summary>
        /// テクスチャー
        /// </summary>
        private Texture _texture = null;


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

            try
            {
                // Direct3D デバイス作成
                this.CreateDevice(topLevelForm);

                // フォントの作成
                this.CreateFont();
            }
            catch (DirectXException ex)
            {
                // 例外発生
                MessageBox.Show(ex.ToString(), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            // カメラの設定
            this.SettingCamera();

            // 四角形ポリゴンを表示するための頂点バッファを作成
            this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionTextured),
                4, this._device, Usage.None, CustomVertex.PositionTextured.Format, Pool.Managed);

            // 4点の情報を格納するためのメモリを確保
            CustomVertex.PositionTextured[] vertices = new CustomVertex.PositionTextured[4];

            // 各頂点を設定
            vertices[0] = new CustomVertex.PositionTextured(-4.0f, 4.0f, 0.0f, 0.0f, 0.0f);
            vertices[1] = new CustomVertex.PositionTextured(4.0f, 4.0f, 0.0f, 1.0f, 0.0f);
            vertices[2] = new CustomVertex.PositionTextured(-4.0f, -4.0f, 0.0f, 0.0f, 1.0f);
            vertices[3] = new CustomVertex.PositionTextured(4.0f, -4.0f, 0.0f, 1.0f, 1.0f);

            // 頂点バッファをロックする
            using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
            {
                // 頂点データを頂点バッファにコピーします
                data.Write(vertices);

                // 頂点バッファをロック解除します
                this._vertexBuffer.Unlock();
            }

            // テクスチャー作成
            this._texture = TextureLoader.FromFile(this._device, "Texture.jpg");

            // ライトを無効
            this._device.RenderState.Lighting = false;

            return true;
        }

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

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


            // テクスチャーをセット
            this._device.SetTexture(0, this._texture);

            // 頂点バッファをデバイスのデータストリームにバインド
            this._device.SetStreamSource(0, this._vertexBuffer, 0);

            // 描画する頂点のフォーマットをセット
            this._device.VertexFormat = CustomVertex.PositionTextured.Format;

            // レンダリング(描画)
            this._device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);


            // 文字列の描画
            this._font.DrawText(null, "テクスチャーをポリゴンに貼り付ける", 0, 0, Color.White);

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

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

        /// <summary>
        /// リソースの破棄をするために呼ばれる
        /// </summary>
        public void Dispose()
        {
            // テクスチャーを解放
            if (this._texture != null)
            {
                this._texture.Dispose();
            }

            // 頂点バッファを解放
            if (this._vertexBuffer != null)
            {
                this._vertexBuffer.Dispose();
            }

            // フォントのリソースを解放
            if (this._font != null)
            {
                this._font.Dispose();
            }

            // Direct3D デバイスのリソース解放
            if (this._device != null)
            {
                this._device.Dispose();
            }
        }
    }
}
MainSamplePartial.cs ファイルのコードはこちらです。

MainSamplePartial.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
{
    public partial class MainSample
    {
        /// <summary>
        /// メインフォーム
        /// </summary>
        private MainForm _form = null;

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

        /// <summary>
        /// Direct3D 用フォント
        /// </summary>
        private Microsoft.DirectX.Direct3D.Font _font = null;


        /// <summary>
        /// Direct3D デバイスの作成
        /// </summary>
        /// <param name="topLevelForm">トップレベルウインドウ</param>
        private void CreateDevice(MainForm topLevelForm)
        {
            // PresentParameters。デバイスを作成する際に必須
            // どのような環境でデバイスを使用するかを設定する
            PresentParameters pp = new PresentParameters();

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

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

            // 深度ステンシルバッファ。3Dでは前後関係があるので通常 true
            pp.EnableAutoDepthStencil = true;

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

            try
            {
                // デバイスの作成
                this.CreateDevice(topLevelForm, pp);
            }
            catch (DirectXException ex)
            {
                // 例外発生
                throw ex;
            }
        }
        /// <summary>
        /// Direct3D デバイスの作成
        /// </summary>
        /// <param name="topLevelForm">トップレベルウインドウ</param>
        /// <param name="presentationParameters">PresentParameters 構造体</param>
        private void CreateDevice(MainForm topLevelForm, PresentParameters presentationParameters)
        {
            // 実際にデバイスを作成します。
            // 常に最高のパフォーマンスで作成を試み、
            // 失敗したら下位パフォーマンスで作成するようにしている。
            try
            {
                // ハードウェアによる頂点処理、ラスタライズを行う
                // 最高のパフォーマンスで処理を行えます。
                // ビデオカードによっては実装できない処理が存在します。
                this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
                    CreateFlags.HardwareVertexProcessing, presentationParameters);
            }
            catch (DirectXException ex1)
            {
                // 作成に失敗
                Debug.WriteLine(ex1.ToString());
                try
                {
                    // ソフトウェアによる頂点処理、ハードウェアによるラスタライズを行う
                    this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
                        CreateFlags.SoftwareVertexProcessing, presentationParameters);
                }
                catch (DirectXException ex2)
                {
                    // 作成に失敗
                    Debug.WriteLine(ex2.ToString());
                    try
                    {
                        // ソフトウェアによる頂点処理、ラスタライズを行う
                        // パフォーマンスはとても低いです。
                        // その代わり、ほとんどの処理を制限なく行えます。
                        this._device = new Device(0, DeviceType.Reference, topLevelForm.Handle,
                            CreateFlags.SoftwareVertexProcessing, presentationParameters);
                    }
                    catch (DirectXException ex3)
                    {
                        // 作成に失敗
                        // 事実上デバイスは作成できません。
                        throw ex3;
                    }
                }
            }
        }

        /// <summary>
        /// フォントの作成
        /// </summary>
        private void CreateFont()
        {
            try
            {
                // フォントデータの構造体を作成
                FontDescription fd = new FontDescription();

                // 構造体に必要なデータをセット
                fd.Height = 12;
                fd.FaceName = "MS ゴシック";

                // フォントを作成
                this._font = new Microsoft.DirectX.Direct3D.Font(this._device, fd);
            }
            catch (DirectXException ex)
            {
                // 例外発生
                throw ex;
            }
        }

        /// <summary>
        /// カメラの設定
        /// </summary>
        private void SettingCamera()
        {
            // ビュー変換行列を設定
            this._device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 0.0f, -10.0f),
                new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));

            // 射影変換を設定
            this._device.Transform.Projection = Matrix.PerspectiveFovLH(
                Geometry.DegreeToRadian(60.0f),
                (float)this._device.Viewport.Width / (float)this._device.Viewport.Height,
                1.0f, 100.0f);
        }
    }
}

先にテクスチャーについて簡単に説明します。

まず、テクスチャーをポリゴンに貼り付けるには、頂点データに「UV座標(テクスチャー座標)」という情報を持たせなければいけません。UV座標とは「頂点にテクスチャーのどの位置を合わせるか」を表したものです。

まず、作成されたテクスチャーにはUV座標が下のように定義されています。これはどの場合でも同じです。

テクスチャーのUV座標

今回のポリゴンは四角形なので、テクスチャーのUV座標と同じように頂点にUV座標を設定することにより、ぴったりテクスチャーが貼り付けられるわけです。


/// <summary>
/// テクスチャー
/// </summary>
private Texture _texture = null;

フィールドに「Texture」を宣言します。これが画像を読み込んだときに色のデータを記憶させるための変数です。この「Texture」を使用して画像データをポリゴンに貼り付けます


// 四角形ポリゴンを表示するための頂点バッファを作成
this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionTextured),
    4, this._device, Usage.None, CustomVertex.PositionTextured.Format, Pool.Managed);

今回から頂点データに使用する構造体が変わります。「CustomVertex.PositionTextured」です。これは頂点データに「位置」と「UV座標」を持たせることが出来ます。前回まで使用していた「色」も含めた構造体も使用できますが、テクスチャー自体にも色があるので今回は使用しません。

CustomVertex.PositionTextured 構造体

X 位置の x 成分
Y 位置の y 成分
Z 位置の z 成分
Tu テクスチャ座標の u 成分
Tv テクスチャ座標の v 成分

// 4点の情報を格納するためのメモリを確保
CustomVertex.PositionTextured[] vertices = new CustomVertex.PositionTextured[4];

// 各頂点を設定
vertices[0] = new CustomVertex.PositionTextured(-4.0f, 4.0f, 0.0f, 0.0f, 0.0f);
vertices[1] = new CustomVertex.PositionTextured(4.0f, 4.0f, 0.0f, 1.0f, 0.0f);
vertices[2] = new CustomVertex.PositionTextured(-4.0f, -4.0f, 0.0f, 0.0f, 1.0f);
vertices[3] = new CustomVertex.PositionTextured(4.0f, -4.0f, 0.0f, 1.0f, 1.0f);

構造体が変わったので、設定するパラメータも変化します。UV座標をテクスチャーがそのまま貼りつくように設定してください。

後は今までどおりデータを書き込みます。


// テクスチャー作成
this._texture = TextureLoader.FromFile(this._device, "Texture.jpg");

テクスチャーの作成コードになりますが、たった1行で済みます。

TextureLoader.FromFile」メソッドで Direct3D デバイスと画像ファイル名を指定するだけです。ファイル名は「絶対パス」か「カレントディレクトリからの相対パス」を指定します。カレントディレクトリは基本的に変更しなければ実行ファイルのあるディレクトリになります。今回の画像も実行ファイルと同じ場所においてあります。

ちなみに、読み込める画像形式は「.bmp」「.dds」「.dib」「.hdr」「.jpg」「.pfm」「.png」「.ppm」「.tga」です。


// テクスチャーをセット
this._device.SetTexture(0, this._texture);

描画メソッドのコードです。「Device.SetTexture」メソッドに作成したテクスチャーを指定すると、UV座標が設定されているポリゴンにテクスチャーが適用されるようになります。

このテクスチャーは再度別なテクスチャーを設定したり、解除するまではそのまま使われることになります。

Device.SetTexture メソッド

テクスチャをデバイスにセット
stage デバイスステージ。テクスチャーを同時に複数使用しないのであれば基本的に 0 でいい。
texture 使用するテクスチャー

// 頂点バッファをデバイスのデータストリームにバインド
this._device.SetStreamSource(0, this._vertexBuffer, 0);
// 描画する頂点のフォーマットをセット
this._device.VertexFormat = CustomVertex.PositionTextured.Format;
// レンダリング(描画)
this._device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);

指定するフォーマットの構造体が違う点を除けば他は同じです。


// テクスチャーを解放
if (this._texture != null)
{
    this._texture.Dispose();
}

最後にテクスチャーを明示的に破棄しています。