Simplestar Game

How do I create a 3D game ?
http://simplestar.syuriken.jp/
since 17/01/2009
  • BACK
  • HOME
  • NEXT
  • |

:::::::::::033話 エフェクトファイル入門:::::::::::

こんにちは、星姫です。
DirectX 10 のサンプルを触るといきなりエフェクトファイル(.fx)を使用します。
(何の説明もなしとは言いませんが…理解に苦しみますよね。)
そこで、今回はエフェクトファイルの一部分である頂点シェーダをちょこっと触って
理解の突破口を開こうと思います。

前回作った SimpleDX10 ライブラリを使って、エフェクトファイルの作業のみに焦点を当てます。

今回の結果動画

頂点シェーダをちょこっと触るのが今回のレッスンです。

ここから本題に入ります。

外部ライブラリを使用する Windows アプリケーション

まずは、SimpleDX10 を利用する環境を整えないといけませんね。
アプリケーションの種類を 016話 で紹介したやり方で Windows アプリケーションにして、空のプロジェクトを作成してください。
@ヘッダーファイルに次の StdAfx.h ファイルを追加します。

StdAfx.h


Aソースファイルに次の StdAfx.cpp, EntryPoint.cpp を追加します。

StdAfx.cpp


EntryPoint.cpp


これだけではコンパイルは通りませんので、次の作業をします。
B SimpleDX10.zip をダウンロードし
プロジェクトがあるフォルダの下に展開します。

Cプロジェクトのプロパティで「C++」→「追加のインクルードディレクトリ」に次の要領で
展開したフォルダの include フォルダへのパスを設定します。
$(ProjectDir)\ExtLibs\SimpleDX10\include、StdAfx.h があるフォルダへのパスの設定も忘れずに!

D同じくプロジェクトのプロパティで「リンカ」→「追加のライブラリディレクトリ」に次の要領で
展開したフォルダの lib フォルダへのパスを設定します。
$(ProjectDir)\ExtLibs\SimpleDX10\lib

E「C++」→「コード生成」にて設定している形式(ディフォルトでは「マルチスレッド デバッグ DLL (/MDd)」)
に合うライブラリを「リンカ」→「入力」→「追加の依存ファイル」の項目に指定してください。
ディフォルトなら「SimpleDX10_md_debug.lib」ですね。

F「C++」→「プリコンパイル済みヘッダー」→「プリコンパイル済みヘッダーの作成/使用」の項目を
「プリコンパイル済みヘッダー ファイルを使用する (/Yu)」にします。
さらに、StdAfx.cpp ファイルのプロパティで「プリコンパイル済みヘッダーの作成/使用」の項目を
「プリコンパイル済みヘッダーを作成する (/Yc)」にします。

@〜Fまで設定を完了させて、初めてコンパイルが通ります。
(注意!DirectX SDK のサンプルを動かせる環境でないと、これまたコンパイルできません。詳細は 017話 でやりました。)

さて、実行してみると次のように、指定したウィンドウサイズ、指定したウィンドウタイトルでウィンドウが表示されると思います。

上記のプロジェクトを実行したときの様子。

ここまで用意できた後の話をします。
ちなみに、ここまでの作業は「外部ライブラリを使うときに必ず行う作業」です。
忘れたなら、ここにきて思い出していただけたら幸いです。

モデルデータの用意

さて、何かを描画するにしても、モデルデータが無くてはテストのしようがありません。
SimpleDX10 で使えるモデルデータというものを用意してみましょう。

まずは Blender で適当にモデルを作成します。
今回は「Grid」を作成し、「Vキー」で使えるようになる、「頂点ペイント」機能を用いて Simple な文字を書いてみました。
(シーズン的にクリスマス色を使用)

Blender で作成したモデル(Blender の 3D ビューでの表示)

Grid とは、このように平面をグリッド状にしたモデルのことです。
文字の部分を気持ち程度盛り上げてみました(笑)

モデルができたら、028話 で作成しました SimplestarExporter.zip を使って中間モデルデータを出力します。

SimplestarExporter の UI 画面の様子。
「Export All」を押しましたが、選択されていれば「Export Sel」でも同じことです。
ボタンは図に示したように、ディフォルトの状態のままでOKです。

こちらが、出力された中間モデルデータファイル(.xml)です。
XML Notepad 2007 でファイルの内容を表示しています。

030話 で作成しました、中間モデルデータファイル(.xml)から描画に必要な情報のみを取り出してバイナリデータを出力するツール
SimpleConverter.zip を使ってバイナリデータファイル(.smpl)を出力します。
図に示すように、頂点座標と頂点カラーにチェックを入れて出力します。

こちら、.smpl バイナリファイルの内容を Stirling(スターリング) を使って、表示した様子です。
このファイルを読み込み、解析して、頂点バッファとインデックスバッファを作成するコードを 031話 で紹介しました。
同じ機能を備えた SMPL クラスを作成し、頂点バッファとインデックスバッファを作成できるようにしておきましょう。
ここで、エフェクトを使う場合における問題点に気付きました。

現在のモデルデータの問題点

現在 SimpleConverter はオブジェクト一つにつき、一つの.smplファイルを出力します。
SimpleDX10 で .smplファイルを使おうとすると、次の問題が発生することが予想できます。

・マテリアルごとに頂点レイアウトが異なるはずが、.smplファイルでは、ファイルに対しフォーマットが一つ設定される。
(これでは、オブジェクトにマテリアルを一つしか設定できないのと同じ)

理由は簡単で、そもそも現在のモデルデータにマテリアル情報を載せていないからです。
(作られるのは、頂点バッファとインデックスバッファのみ)
つまり、SimpleDX10 で使いやすい新しいデータフォーマットを作成する必要がある、ということ。
今回は、エフェクトファイルの入門講座なので、今回用にコードを用意しますが、今回以降で使いまわすことは無いものとします。
(フォーマット変更に関係する話、Blender 2.55 のエクスポーターをいじり、マルチテクスチャデータが出力可能なことを確認。
こっちのエクスポーターが完成したら、これまで作ってきたものはプロトタイプとして、実際に活用されることなくお蔵入りするかも…)

で、Blender 2.55 用のエクスポーターが完成しました。(2010/12/05) これをダウンロードに置いておきます。

ということで、SimpleStarExporter(Blender 2.49b用)とSimpleConverter(SimpleStarExporterのxmlファイル変換器)はお蔵入り決定!
今回の使用が最後ですね、さようなら(笑)

SMPL クラスの実装

.smplファイルパスをコンストラクタで渡すと、頂点バッファとインデックスバッファの情報を持ったデータクラスになる。
という、イメージで作成します。

こんな感じ↓

/**********************************************************************//**
      @class            SMPL
      @brief            .smplファイルより作られるデータクラス
      @author            Simplestar
      @par    [説明]     
                外部はメモリの管理を意識することはありません。
    */
/***********************************************************************/
    class SMPL
    {
        //------------------------------------------------------------------------
        // Private Variables
        //------------------------------------------------------------------------
    private:
        const char* m_pData;    //!< ファイルデータポインタ
       
        //------------------------------------------------------------------------
        // Public Functions
        //------------------------------------------------------------------------
    public:

        explicit SMPL(const char* pFilePath);        //!< コンストラクタ
        virtual ~SMPL();                            //!< デストラクタ

        // データフォーマットの取得
        unsigned int    GetFormat(void) const;
        // 頂点数の取得
        unsigned int    GetVertexNumber(void) const;
        // インデックス数の取得
        unsigned int    GetIndexNumber(void) const;
        // 頂点ストライドの取得
        unsigned int    GetStride(void) const;
        // 頂点バッファの取得
        const void*        GetVertexBuffer(void) const;
        // 頂点バッファサイズの取得
        unsigned int    GetVertexBufferSize(void) const;
        // インデックスバッファの取得
        const void*        GetIndexBuffer(void) const;
        // インデックスバッファのサイズの取得
        unsigned int    GetIndexBufferSize(void) const;
    };

あとはメインのファイルをこう↓書けば、完成です。

このプロジェクトを実行すると、次の結果が得られます。

(モデルが回転しているだけの映像)

/**************************************************************************//**
    @file        EntryPoint.cpp
    @brief        アプリケーションのエントリポイント
    @author        Simplestar
    @date        2010-11-21
    @par   
        [説明]
            アプリケーションのエントリポイントが記述されています。
    @note
        Copyright &copy; 2010  SimpleStar Game
        http://simplestar.syuriken.jp/
*/
/***************************************************************************/

//------------------------------------------------------------------------
// File Dependencies
//------------------------------------------------------------------------

#include    "StdAfx.h"
#include    "DebugPrint.h"
#include    "SMPL.h"
#include    <SimpleDX10.h>

//------------------------------------------------------------------------
// namespace Dependencies
//------------------------------------------------------------------------

using namespace Simplestar;
using namespace SimpleSample033;

//------------------------------------------------------------------------
// Type Definitions
//------------------------------------------------------------------------

#define    SMDX10    Simplestar::SimpleDX10::Instance()        // SimpleDX10インスタンスの別名(シングルトン)
#define        FPS        60

//------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------

namespace
{
    static const char WINDOW_TITLE[]            = "Window - Title";            // ウィンドウのタイトル
    static const RESOLUTION_TYPE WINDOW_SIZE    = {800, 600};                // ウィンドウの初期サイズ
   
    //! アプリケーションが返すエラーコード
    typedef enum ErrorCode_App
    {
        ERROR_NONE = 0,                //!< 正常終了
        ERROR_INIT_SMDX10,            //!< SimpleDX10 の初期化処理に失敗
    };
}

//------------------------------------------------------------------------
// Implements
//------------------------------------------------------------------------

/**********************************************************************//**
    @brief        アプリケーションのエントリポイント
    @param[in]    hInstance    :    プログラムを識別するためのハンドル
    @param[in]    hPrevInstance    :    Win16 の名残(常にNULL)
    @param[in]    lpCmdLine    :    コマンドライン引数
    @param[in]    nCmdShow    :    アプリケーションの初期表示方法
    @par    [説明]
                アプリケーションのエントリポイントです。
    @return        #ErrorCode_App
*/
/***********************************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    ZeroMemory(&msg, sizeof(MSG));

    // ウィンドウの初期化
    {
        if(SimpleDX10::ERROR_SETRNDTGT_NONE != SMDX10.SetRenderTarget(WINDOW_SIZE, false, hInstance, nCmdShow))
        {
            return ERROR_INIT_SMDX10;
        }
        SMDX10.SetWindowTitle(WINDOW_TITLE);
    }

    // モデルデータのロード
    SMPL smpl("D:\\SVN_work\\Samples\\SimpleSample033\\Data\\smpl\\SimplePlate.smpl");

    // データの準備
    unsigned int effectIndex = 0;

    // 頂点バッファの作成
    SMDX10.CreateVertexBuffer(smpl.GetVertexBufferSize(), smpl.GetVertexBuffer());
    // インデックスバッファの作成
    SMDX10.CreateIndexBuffer(smpl.GetIndexBufferSize(), smpl.GetIndexBuffer());
    // エフェクトの作成
    if (!SMDX10.AppendEffect("D:\\SVN_work\\Samples\\SimpleSample033\\Data\\fx\\Tutorial05.fx", effectIndex))
    {
        DEBUG_PRINT("エフェクトの作成に失敗");
    }
    // 頂点レイアウトの作成
    SMDX10.CreateVertexLayout(SMPL_FORMAT_POS3_COL4, effectIndex, 0);

    // メッシュ情報の作成
    MESH_INFO_TYPE meshInfo;
    {
        meshInfo.count = smpl.GetIndexNumber();
        meshInfo.offset = 0;
        meshInfo.stride = smpl.GetStride();
    }

    // エフェクト変数の設定
    ID3D10Effect* pEffect = SMDX10.GetEffect(effectIndex, pEffect);

    // エフェクト変数へのアクセス子を取得
    ID3D10EffectMatrixVariable* pWorldVariable = pEffect->GetVariableByName( "World" )->AsMatrix();
    ID3D10EffectMatrixVariable* pViewVariable = pEffect->GetVariableByName( "View" )->AsMatrix();
    ID3D10EffectMatrixVariable* pProjectionVariable = pEffect->GetVariableByName( "Projection" )->AsMatrix();

    // モデル位置の指定
    D3DXMATRIX matWorld;
    D3DXMatrixIdentity(&matWorld);

    // カメラ位置の指定
    D3DXMATRIX matCamera;
    D3DXMatrixTranslation(&matCamera, 0.0f, 1.0f, 2.0f);

    // ビュー行列の取得
    D3DXMATRIX matView = SMDX10.GetViewMatrix(matCamera, D3DXVECTOR3(0.0f, 0.0f, 0.0f));

    // プロジェクション行列の取得
    D3DXMATRIX matProjection = SMDX10.GetProjectionMatrix(45.0f);

    // エフェクト変数の設定
    pWorldVariable->SetMatrix( ( float* )&matWorld );
    pViewVariable->SetMatrix( ( float* )&matView );
    pProjectionVariable->SetMatrix( ( float* )&matProjection );

    // 開始時刻の記録
    DWORD sysTimeStart = timeGetTime();
    DWORD sysTimeLast = sysTimeStart;
    DWORD sysTimeCur = sysTimeStart;

    // 最小タイマ分解能を、1ミリ秒単位で指定
    timeBeginPeriod( 1 );

    // メッセージ処理ループ
    while(TRUE)
    {
        Sleep(1);
        sysTimeCur = timeGetTime();
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT)
            {
                break;
            }
            else
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        else if (sysTimeCur - sysTimeLast >= 1000/FPS )
        {
            sysTimeLast = sysTimeCur;
           
            // 経過時間の更新(秒)
            float t = ( sysTimeCur - sysTimeStart ) / 1000.0f;

            // モデルのY軸回転
            D3DXMatrixRotationY( &matWorld, t );

            // エフェクト変数の設定
            pWorldVariable->SetMatrix( ( float* )&matWorld );

            // 画面のクリア
            SMDX10.ClearTargetView();
            // バックバッファへ描画
            SMDX10.DrawMesh(meshInfo, effectIndex, 0);
            // フロントバッファへ表示
            SMDX10.Present();           
        }
    }

    // 最小タイマ分解能、1ミリ秒単位での指定を解除
    timeEndPeriod( 1 );

    return ERROR_NONE;
}

//****************************************************************************
// End Of File.
//

エフェクトファイルとは?

この段階になって、初めてエフェクトファイルについて勉強できるようになります。
やっと、です。やっぱり初心者にはハードル高いですよね…

とりあえず、今回使用したエフェクトファイルの内容を確認してみましょう。

Tutorial05.fx ファイル内の記述

//--------------------------------------------------------------------------------------
// File: Tutorial05.fx
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
matrix World;
matrix View;
matrix Projection;

//--------------------------------------------------------------------------------------
struct VS_INPUT
{
    float4 Pos : POSITION;
    float4 Color : COLOR;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR;
};


//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Color = input.Color;
   
    return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_Target
{
    return input.Color;
}


//--------------------------------------------------------------------------------------
technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}


今回はグリッドに波打ってもらいましょう。
ということで、Vertex Shader と書かれている所に注目!

ワールド変換行列とビュー変換行列で頂点位置を移動させている所があると思いますが、ここに次のコードを挿入します。

output.Pos.y = output.Pos.y + 0.06f * sin(World._11 * 3.14f + output.Pos.x * 8.0f) + 0.06f * sin(World._11 * 3.14f + output.Pos.z * 8.0f);

すると、冒頭で示した動画の結果が得られます。

ここで、参考にしたページがこちら。

今回のように、Posの中に.yプロパティがあるかどうかを確かめるために参照したページがこちら↓
成分ごとの演算 (DirectX HLSL)

正弦波とか、算術関数はここに一覧があります。
組み込み関数 (DirectX HLSL)

つまり、HLSLで困ったらここ↓に来れば何かしら情報が得られるはず…次回はもう少し凝ったシェーダプログラミングを勉強してみましょう。
HLSL リファレンス

2010/11/21 初記。
2010/11/23 追加。
2010/12/05 追加。

:::::::::::033話 エフェクトファイル入門:::::::::::

  • BACK
  • HOME
  • NEXT
ホームページ制作 フリー素材 無料WEB素材
Copyright (C) Simplestar All Rights Reserved.