2007年11月16日

Ogre Wiki - Basic Tutorial 3

Basic Tutorial 3 : Terrain, Sky, Fog, and the Root object

Terrainってなんでしょうね。地形{ちけい}、地勢{ちせい}、地域{ちいき}、領域{りょういき}、範囲{はんい}、分野 by alc.

準備:C++の知識とこれまでのチュートリアルやってりゃOK.

導入:このチュートリアルは、地形、空、霧をあなたのOgreアプリケーションでどのように操作するか説明します。このチュートリアルの後で、Skyboxes、Skyplanes、そしてSkydomesの違いとその使い方を理解するべきです。霧の種類の違いと使い方についても知るべきです。
チュートリアルを通じて、ゆっくりコードをあなたのプロジェクトに追加して、結果を良く見てください。最終的な状態のソースコードはここで見ることができます。何か問題が発生したら、このソースコードと良く見比べてみてください。

実行結果:ん〜霧は暗いからSkyDomeと地形で。やっぱり影が欲しいねぇ。今回はいまいち訳がよくわからなかったので適当です。間違ってそうなところもあるのでてきとーにお願いします。

screenshot_5.png

おまけ

Ogre 3DのDemo_Water.exeではちゅねミクを泳がせてみました。
半端じゃない速度で雨の降る水面をぶっちぎります。

screenshot_6.png

スタート:
チュートリアルの前に、こちらで準備したコードを示します。プロジェクトを作成して下記コードを追加してください。

#include "ExampleApplication.h"

class TutorialApplication : public ExampleApplication
{
protected:
public:
    TutorialApplication()
    {
    }

    ~TutorialApplication() 
    {
    }
protected:
    void chooseSceneManager(void)
    {
    }

    void createScene(void)
    {
    }
};

#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
    // Create application object
    TutorialApplication app;

    try {
        app.go();
    } catch(Exception& e) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        fprintf(stderr, "An exception has occurred: %s\n",
                e.getFullDescription().c_str());
#endif
    }

    return 0;
}

今回はchooseSceneManagerというメンバ関数が新しいですね。
えー。コンパイルはいいけど実行はしないでください。エラー終了します。って書いてましたね。実行したら落ちました。

Root ObjectとSceneManagerの作成
Root
このデモでは、Ogreに地形を表示します。ExampleApplicationにデフォルトで設定しているものの代わりに、SceneManagerをTerrainSceneManagerに設定する必要があります。chooseSceneManager関数を探して、次のコードを追加してください。

mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);

Rootオブジェクト(mRootはRootのインスタンス)は、Ogreオブジェクトの"core"です。Ogreのオブジェクト間の関係のUMLダイアグラムはここでみることができます。RenderSystemを除くほとんどすべてのオブジェクトはこの練習で見ることができます。このコードの中で、私たちはRootノードにST_EXTERIOR_CLOSEタイプのSceneManagerを要求しています。Rootオブジェクトは、SceneManagerEnumeratorに問い合わせて、あなたが要求したSceneManagerのタイプを探して、それを返します。
あなたのアプリケーションに設定したあとは、(OgreのRootオブジェクトを操作することができるようになって)、SceneManagerEnumeratorを直接触ることはできません。

SceneManagerの作成
将来あなたが混乱しないように、SceneManagerの作成と格納について話しておきたいと思います。SceneManagerはSingleton(別物、一つずつ起こるもの)ではありません。SceneNodes/Lights/などと違って、"new SceneManager()"宣言(RootのcreateScenemanagerメソッドを使う必要はありません)でいくつでもSceneManagerを作ることができます。あなたは複数のSceneManagerを持つことができ、ジオメトリやEntityを同時に複数に分けて入れることができます。複数のViewportを使って同時に複数のSceneManagerを表示することもできますし、Viewportを再作成することによっていつでもこれらのSceneManagerを交換することができます(Intermediate Tutorial 4で説明します)。

なぜSceneManagerオブジェクトを手動で作るのではなくcreateSceneManager関数を使うのでしょうか?その理由は、Ogreが私たちに提供するプラグイン・システムは、一つのSceneManagerにおいて素晴らしい自由度を扱うことができるからです。SceneTypeでは少しの種類のシーンしか定義されていません。そのため、ExampleApplicationは私たちのSceneManagerとしてST_GENERICを選んでいました。これはあなたがplugins.cfgファイルをいじくりまわさない限りSceneManagerクラスの基本的なものだと思ってもらっていいです。OctreeSceneManagerはST_GENERICの登録とOctreeSceneManagerプラグインを使うのであればSceneManagerクラスの基本をオーバーライドします。OctreeSceneManagerは見えないものを表示しないことで一般的なSceneManagerより速いです。もしあなたがOctreeSceneManagerプラグインをplugins.cfgから取り除くのであれば、ST_GENERICを要求するときは基本的なSceneManagerをおそらく使用するとよいでしょう、もしくはあなたのプラグインに依存したいい何かを使えるかもしれません。これはこのシステムの美しいところです。

このように、SceneManagerを作るときのRootオブジェクトの基本的な使い方について説明しました。実際、私たちはSceneType列挙の使用する代わりに文字列からSceneManagerに要求することができます。Ogreは、あなたがSceneManagerの種類を数を定義することができて、必要に応じて作ったり削除したりすることができる自由なSceneManager工場を使います。例えば、"FooSceneManager"と呼ばれるSceneManagerを作ることを許可するプラグインをインストールします。私たちは次を呼ぶことによって作ります:

// これはプロジェクトに追加しないでください。
mSceneMgr = mRoot->createSceneManager("FooSceneManager");

これは、デフォルト名のFooSceneManagerを作ります。SceneManagerにはちゃんとcreateSceneManagerに2つ目の引数である名前をいつも指定するべきだす。例えば、もし2つのSceneManagerを作りたい場合は、次のように名前を付けることができます。

// これはプロジェクトに追加しないでください。
mSceneMgr1 = mRoot->createSceneManager("FooSceneManager", "foo");
mSceneMgr2 = mRoot->createSceneManager("FooSceneManager", "bar");

SceneManagerに名前を付けることで、私たちはこれ以上それらへのポインタを保持する必要がなくなります。Rootオブジェクトがそれをしてくれます。もし"foo"と"bar"のSceneManagerが使いたくなったら、次のようにすることができます。

// これはプロジェクトに追加しないでください。
SceneManager *foo = mRoot->getSceneManager("foo");
SceneManager *bar = mRoot->getSceneManager("bar");

SceneManagerを完全に使い終わったら、RootのdestroySceneManager関数を使用して破棄します。
このチュートリアルではカバーしないことですが、SceneManagerFactoryクラスのサブクラス化によって、あなた自身のSceneManager工場を定義することもできます。これは、あなたがあなた自身のSceneManagerを(サブクラス化することによって)作成するときや、標準的なSceneManagerが欲しい時や、カメラや光源、ジオメトリをロードするときなど、あなたのアプリケーションを少し変更するときにとても便利です。

以上、SceneManagerの説明部分の適当な訳ですが、よくわかりませんね。専門家の人和訳してください。

地形
Sceneに地形を追加
それでは実際に地形を作りましょう。基本となるSceneManagerはシーンを作る目的に使用するサブクラスのsetWorldGeometry関数を定義します。TerrainSceneManagerクラスとともに、地形の設定を読み込むためのファイル名を期待します。createScene関数に以下のコードを追加してください。

mSceneMgr->setWorldGeometry("terrain.cfg");

コンパイルして実行してください。簡単です。もし困ったことにカメラの初期位置が地形の下だったなら、カメラを地形の上に設定したいかもしれません。

terrain.cfgファイル
terrain.cfgファイルにはたくさんのオプションがあり、ここでは画像の変更と地形の生成方法のための基本的なことについてしか説明しません。設定ファイルの詳細についてはここで確認してください。一つの大きな注意点は、TerrainSceneManagerはpaging functionality(ページ機能?)を設計しているが、まだ実装していないことです。ページ地形は、地形の塊が壊れたときにそれを見ることができるシステムです?これは巨大な世界でもフレームレートを落とさないことが可能です。OgreプラグインのPaging Scene Managerを参照してください。

TerrainSceneManagerはハイトマップを使って地形を生成します。"Heightmap.image"プロパティを設定することによってハイトマップを指定することができます。WorldTextureプロパティを設定することで使用するテクスチャを指定することができます。地形のシーン管理は"DetailTexture"プロパティも設定することができます。これはより現実感のある地形をWorldTextureに作成するためのものです。これらの画像はterrain.cfgに指定されており、Media/materials/texturesフォルダで見ることができます。
ハイトマップの作成方法の詳細についてはフォーラムで議論されています。ハイトマップで検索して探してみてください。

Lighting Terrain
前のチュートリアルで光源と影を設定しました。しかし、悪い話ですが、TerrainSceneManagerで動作させるのは簡単ではありません。現在のところ、一般的な光源を取得するより詳細なテクスチャを使って光源の効果を追加する方が簡単であることだけ知っておいてください?私たちは、霧のセクションで"Fake Darkness"の実行の仕方を見直します。もし地形の光源の使い方を知りたいのならば、Paging_Scene_Managerの使い方を見るべきです。それはこれらの機能を良くサポートしています。


OgreはSkyBoxes、SkyDomes、そしてSkyPlanesといった異なった3つの種類の空を提供します。それぞれの詳細を見ていきましょう。chooseSceneManager関数にExample/*テクスチャを使用するためのコードを追加しましょう。

ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

SkyBoxes
SkyBoxは、シーンのすべてのオブジェクトを覆うような基本的な巨大な立方体です。これを描画するための最も良い方法は、あなたにだけそれを見せることです。createSceneに次の行を追加してください。

mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");

コンパイルして実行してみてください。Neat huh?(SkyBoxは実際には滑らかでなく解像度が低いことに注意してください。高い解像度のSkyBoxはもっとよく見えます。)SkyBoxにはいくつかの便利なパラメータがあります。最初のオプションはSkyBoxを有効にするかどうかです。SkyBoxを無効にしたい場合は、mSceneMgr->setSkyBox(false,"");を呼ぶだけでできます。2つ目のパラメータはSkyBoxのための材質スクリプトです。
3つ目と4つ目のパラメータは理解すべき重要なものです。3つめはSkyBoxのカメラからの距離を設定します。4つ目はSkyBoxがシーンの前に描画されるかどうかの設定です。SkyBoxのパラメータを変更した場合にどうなるか見てみましょう。デフォルトで5000単位を10単位というとても近くに設定してみます。

何も変わりません!その理由は4つ目のパラメータをコントロールしていないからです。SkyBoxが最初に描かれれば、地形は後で描画されるので、SkyBoxはいつも背後に現れます。(近クリップ距離より近くに設定するべきではありません)見える物体のみ描画して、最後にSkyBoxを表示することで実行速度の改善ができるため、SkyBoxは最初に描画することは実際には望まれません。なのでSkyBoxは最後に描画しましょう。

mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 5000, false);

再び同じ状態だったでしょう。しかし、今回はSkyBoxの見えない部分はレンダリングされていないのです。これはあなたが注意しなければならないテクニックの一つです。もしSkyBoxを近くに設定しすぎると、シーンのジオメトリが切れてしまいます。試してみましょう。

mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 100, false);

あなたが今見たように、地形がSkyBoxを"pokes through"したでしょう(突き抜ける?)。これは望んでいることではありません。もしSkyBoxを使用するのであれば、あなたがどのようにそれを使用したいか決める必要があります。地形の後でSkyBoxを高速にレンダリングしたいのであれば、控え目に、そしてジオメトリが目立たなくならないように注意する必要があります。一般的には、2番目のパラメータはデフォルトが一番安全な選択です。

SkyDomes
SkyDomesは、SkyBoxに似ていて、setSkyDome関数で使用できます。こちらはカメラと表示するものを囲むような巨大な立方体を作成します。SkyBoxとの大きな違いは、球状にテクスチャが投影されることです。立方体が見えますが、テクスチャは球の表面にかぶさったように見えます。不利な点は、立方体の底にはテクスチャが張られないことで、それを隠すために何らかの地形が必要であることです。
このOgreが提供するサンプルテクスチャは、SkyDomeがはっきり見えます。createSceneのsetSkyBoxを次のコードを書き換えてください。

mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

これを実行してカメラを動かすと、いい感じに移動できると思います。これを見たあと、Rボタンでメッシュ表示に切り替えてください。そうすると、立方体がみえますが、雲は上の方に球状に張られていることがわかると思います。
最初の2つのパラメータは、setSkyBoxと同じです。mScenemgr->setSkyDome(false,"");で無効にできるのも同じです。3つ目のパラメータはSkyDomeの曲率です。APIリファレンスのsuggestsでは2から65の間の値を使用しています。小さいと距離のエフェクトが良いが、大きい値はゆがみが少なくスムーズなエフェクトができます。2から65の間で3つ目のパラメータを変更してっ見て、違いを見てください。距離のエフェクトはAPIリファレンスを参照してください。curvatureを2にしたものと64にしたものの画像が張られています(省略)。

4つ目のパラメータはテクスチャを並べる倍率です。テクスチャのサイズに依存します。このパラメータはint型ではなくfloat型
(Real型)であることに注意が必要です。1.234倍に並べることができ、あなたのアプリケーションでよく見えるでしょう。5つ目と6つ目のパラメータはSkyBoxセクションで説明した距離と最初に描画するかどうかの設定です。

SkyPlanes
SlyPlaneはSkyBoxやSkyDomeと大きく違います。立方体の代わりに一つの平面を使います。(SkyPlaneの設定では地形の中心方向と近くの何かが必要です)SkyDomeのコードを削除して、まずは平面を作成し、面を下に向けます。setSkyPlane関数では、距離パラメータはないです。そのかわり、平面の変数dに使われます。

Plane plane;
plane.d = 1000;
plane.normal = Vector3::NEGATIVE_UNIT_Y;

これで平面を定義できましたので、SkyPlaneを作ることができます。4つ目のパラメータはSkyPlaneのサイズ(このケースでは1500x1500単位)で、5番目がテクスチャを何倍して?何回?並べるかを指定することに注意が必要です。

mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 1500, 75);

コンパイルして実行すると、2つの問題があることが分かります。1つ目は、テクスチャの解像度が低くて、ちゃんと並べられていないことです。これは良くて解像度の高いテクスチャを作成すればすぐに直せます。しかしながら、主要な問題は、縦の遠くを見ると、SkyPlaneが終わっていることです。たとえよいテクスチャがあったとしてっも、途中で切れていたら良くありません。SkyPlaneを使うときは、高い壁に囲まれていて、空しか見えない時にしか使えません。SkyBoxやSkyDomeを作らなくて良い状況の時だけSkyPlaneを使いましょう。

SkyPlaneでできることはこれがすべてではありません。6番目のパラメータが設定できます。これは"renderFirst"に似ているパラメータです。7番目のパラメータは、SkyPlaneの曲率を設定することができます。つまり、平面だけでなく、曲面も使用できます。SkyPlaneを使用するときは、xとy方向の値も設定する必要があります。8番目と9番目の関数はxとyの値です。

mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 1500, 50, true, 1.5f, 150, 150);

コンパイルして実行すれば、SkyPlaneが前よりはよく見えるでしょう。宇宙の代わりに雲の材質を使用してみましょう。

mSceneMgr->setSkyPlane(true, plane, "Examples/CloudySky", 1500, 40, true, 1.5f, 150, 150);

コンパイルして実行してみてください。雲の動きはSkyDomeの時より少し悪いように見えるでしょう。特に地形の水平線あたりで。
他の注意点としては、mSceneMgr->setSkyPlane(false,Plane(),"");でSkyPlaneを消すことができます。

どれをつかえばいいの?
あなたが作るアプリケーション次第です。すべてを見回す必要があり、-y方向も見るのであれば、SkyBoxを使うしかないでしょう。もし地形や床があるのであれば、SkyDomeを使えば現実感の高い結果が得られるでしょう。水平線が見えないような状況では、SkyPlaneで良い結果が得られ、GPUのコストも少ないでしょう。SkyPlaneを使用する主要な理由は、次のセクションで見ますが、霧の効果が素晴らしいからです。
これらは提案です。あなたのアプリケーションでいろいろ実験して一番よく見えるものを選んでください。


霧の概要
Ogreでは、霧を使うのはとても簡単です。あなたが霧を使う前に知っておくべき警告が一つだけあります。TerrainSceneManagerを使用するとき、setWorldGeometry関数の前のsetFog関数を呼び出すときに注意する必要があります。(他のSceneManagerの場合は問題ありません)最初にこれを呼ぶことで、異なる頂点プログラムが霧と地形の生成に選ばれます。(Ogre 1.0.0ではバグがあり、このせいで霧が表示されませんでした。Ogre1.0.1では修正されました)
始める前に、createScene関数の中をsetWorldGeometryを除いて消してください。
最も重要なことは、霧のentityを実際に作るわけではないということです。霧はオブジェクトにかけるフィルタです。つまり、オブジェクトが何もない場合は、霧も見ることができないということです。実際、ビューポートで設定した背景色しか見えないでしょう。よって、霧を正確にみるためには、霧の色を背景に設定する必要があります。
霧には2つの基本的な種類があります。線形と指数です。線形的な霧は、線形に霧がかかっていって、指数関数的な霧は指数関数的に霧がかかっていきます。違いは例を見ればすぐわかります。

霧の種類
まずは線形的な霧を見てみましょう。最も理解しやすい霧です。setWorldGeometryを呼んだ後に、ビューポートの背景色を設定します。私たちはこれをcreateViewport関数をオーバーライドすることによってできます。(最後のチュートリアルで行います)しかし、時々私たちはビューポートを毎回再構築しなくても設定するひうようがあります。このようにします。

ColourValue fadeColour(0.9, 0.9, 0.9);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);

getNumViewportsメンバ関数を使用して、直接ビューポートを取得することができます。しかし、これは特殊なケースです。(ビューポートが一つしかないときにしかできません。)背景色を設定したら、霧を作成することができます。setWorldGeometry関数を呼ぶ前に実行しなければならないことを忘れないでください!

mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 50, 500);

setFog関数の最初のパラメータは、霧の種類です。(今回は線形)2番目のパラメータは霧の色です。(今回はとても明るい灰色またはC#でいうところのWhiteSmokeです)3つ目のパラメータは線形の霧では使用しません。4番目と5番目のパラメータは霧の範囲を設定します。このケースでは50から始まって500で止めます。これは、0〜50の間は霧がかからず、50〜500の間にカメラからの距離にしたがって霧がかかるということを意味します。500から外は霧を見ることはできません。コンパイルして実行してみてください。

もう一つの指数関数的な霧は、開始と終了の幅を設定する代わりに、霧の密度を設定します。setFog関数を次のように置き換えてください。

mSceneMgr->setFog(FOG_EXP, fadeColour, 0.005);

コンパイルして実行してみてください。もしあなたがDirectXを使ってレンダリングしているのであれば、全く霧がかかっていないでしょう。setFogをsetWorldGeometryの後に呼んでください。OpenGLレンダラは説明した通りの振舞いだと思います。これは異なった見え方がする霧を生成します。指数関数的な霧はもう一つあり、濃度の変化量が異なります。霧の種類にFOG_EXP2を指定してみてください。

mSceneMgr->setFog(FOG_EXP2, fadeColour, 0.003);

コンパイルして実行してみてください。霧はOgreが供給する3つの関数間で内部的に変更することが殆どです。実験的に3つの関数を試して、一番よく見えるものを選択してください。

霧と空
SkyBoxやSkyDomeと一緒に霧を使おうとした時に興味深い問題が発生します。SkyDomeとSkyBoxは立方体なので、霧をそれらと使用すると霧が球状のルールを持っているため問題が発生します。createScene関数の中身を削除して、下記のプログラムに書き換えてください。

ColourValue fadeColour(0.9, 0.9, 0.9);
mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 50, 515);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);
mSceneMgr->setWorldGeometry("terrain.cfg");
mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8, 500);

コンパイルして実行してみてください。カメラを動かすと、SkyDomeが見えてしまうことがある?
これは私たちが望んだことではありません。代わりにSkyPlaneを使用してみましょう。createSceneのコードを置き換えてください。

ColourValue fadeColour(0.9, 0.9, 0.9);
mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 0, 130);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);

Plane plane;
plane.d = 100;
plane.normal = Vector3::NEGATIVE_UNIT_Y;

mSceneMgr->setWorldGeometry("terrain.cfg");

mSceneMgr->setSkyPlane(true, plane, "Examples/CloudySky", 500, 20, true, 0.5, 150, 150);

このように見えるのが正しいのです。もしあなたが上方に空を見ることができるのであれば、しかしそれは面白い方法です?曲率を使用しているかどうかは問題ではなく、この問題は水平線をSkyPlaneで見ることができるかどうかという問題で解決できます。
これは、空の実態に影響しない霧を作成する方法の一つですが、空のテクスチャのための材質テクスチャを修正する必要があります。上に書いた霧のトリックは、いくつかのケースで便利な粋なグラフィックを実行するために使えます。霧に暗い色を設定する代わりに、とても暗い色を設定してどうなるか見てみましょう。(SkyPlaneをたったのカメラから10単位の位置に設定して、霧をその前に設定しています)

ColourValue fadeColour(0.1, 0.1, 0.1);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);
mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 10, 150);

mSceneMgr->setWorldGeometry("terrain.cfg");

Plane plane;
plane.d = 10;
plane.normal = Vector3::NEGATIVE_UNIT_Y;

mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 100, 45, true, 0.5, 150, 150);

コンパイルして実行すると、次のような結果が得られるでしょう。
そんなに恐れないでください。もちろん、このようなhackをする代わりに光源を使用するべきでしょう。しかし、これは霧の自由度を示していて、このエンジンで可能な面白いものの一つです。黒い霧も面白い効果が得られます。FPSゲームの"blindness"や"darkness"というようなスペル効果です。

Basic Tutorial 4ではFrame ListnersとUnbuffered Inputについて紹介します。
posted by シンドラー at 20:08 | Comment(0) | TrackBack(1) | Ogre Wiki - Basic Tutorials | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック

IBM ホームページ・ビルダー11 学割版 (新
Excerpt: IBM ホームページ・ビルダー11 学割版 (新価格版)・超ド級グラフィックボード・「普及版ハイエンドGPU」GeForce8800GT搭載パソコン・Firefoxにインストールしている機能拡張一覧・..
Weblog: グラフィックのレス
Tracked: 2008-01-22 13:38