2014年5月31日土曜日

OgreのBasic Tutorial 3をやってみた

今回のチュートリアルでは、地形(Terrain)、空(Sky)、そして霧(Fog)をやるらしい。

地形

地形に関連したコンポーネントとして、Terrain、Paging、Propertyがあるらしい。Pagingは後のチュートリアルで扱い今回は扱わないとのこと。
指示に従ってDemoAppクラスに、ヘッダファイルをインクルードし、Private変数とメソッドを追加。
#include <Terrain/OgreTerrain.h>
#include <Terrain/OgreTerrainGroup.h>


 :
private:
    Ogre::TerrainGlobalOptions* mTerrainGlobals;
    Ogre::TerrainGroup* mTerrainGroup;
    bool mTerrainsImported;
 
    void defineTerrain(long x, long y);
    void initBlendMaps(Ogre::Terrain* terrain);
    void configureTerrainDefaults(Ogre::Light* light);
 :


今回のチュートリアルでは2つの大きな構成要素として、TerrainGroupとTerrainを使う。もう1つ補助的な要素として、TerrainGlobalOptionsも使う。
TerrainGroupは、複数のTerrainを集約したもの。この分割によってLOD(level of detail)による描画、つまり各地形へのカメラからの距離に応じて詳細度を変えて描画ができる。
地形はマテリアルを貼られたタイルから構成される。今回はPagingも使わず、1つのTerrainGroupのみを使うとのこと。

カメラの設定

まずカメラの設定。OgreFramework::initOgre(...)のカメラの設定を次のように変更
    m_pCamera->setPosition(Ogre::Vector3(1683, 50, 2116));
    m_pCamera->lookAt(Ogre::Vector3(1963, 50, 1660));
    m_pCamera->setNearClipDistance(0.1);
    m_pCamera->setFarClipDistance(50000);
    
    if (m_pRoot->getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_INFINITE_FAR_PLANE))
    {
        m_pCamera->setFarClipDistance(0);   // enable infinite far clip distance if we can
    }


カメラ位置の微妙に細かい数値の根拠は分からない。無限ファークリップがサポートされていれば、ファークリップに0を設定して無限ファークリップを使う。iPhoneで実行すると、このif文の中が実行されるのでサボートされているようである。

光源の設定

次に光源の設定。地形はライトマップの計算に平行光源を使うそうなので、DemoApp::setupDemoScene()で平行光源を配置。ついでに環境光も設定。
    Ogre::Vector3 lightdir(0.55, -0.3, 0.75);
    lightdir.normalise();
    
    Ogre::SceneManager *sceneMgr = OgreFramework::getSingletonPtr()->m_pSceneMgr;
    Ogre::Light* light = sceneMgr->createLight("tstLight");
    light->setType(Ogre::Light::LT_DIRECTIONAL);
    light->setDirection(lightdir);
    light->setDiffuseColour(Ogre::ColourValue::White);
    light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));
    
    sceneMgr->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2));


オプションの設定

次は地形オプションの生成。オプションにはグローバルオプションとローカルオプションがあるらしいが、最初はグローバルオプションから。グローバルオプションは全ての地形に共通的な設定。
    mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();


地形グループ

そして、地形グループを作成する。
    mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(sceneMgr, Ogre::Terrain::ALIGN_X_Z, 513, 12000.0f);
    mTerrainGroup->setFilenameConvention(Ogre::String("BasicTutorial3Terrain"), Ogre::String("dat"));
    mTerrainGroup->setOrigin(Ogre::Vector3::ZERO);


TerrainGroupクラスのコンストラクタには、シーンマネージャと、整列オプション、地形のサイズ、ワールドサイズを渡す。
そして、保存(?)する時に使用したい名前を設定し、原点位置を指定する。
次は平行光源を引数に、初期設定。
    configureTerrainDefaults(light);


全ての地形を設定し読み込む。
    for (long x = 0; x <= 0; ++x)
        for (long y = 0; y <= 0; ++y)
            defineTerrain(x, y);
    
    mTerrainGroup->loadAllTerrains(true);


ここでは、地形を1つしか使わないので、ループも一回しか通らない。

ブレンドマップ

次は、ブレンドマップの計算
    if (mTerrainsImported)
    {
        Ogre::TerrainGroup::TerrainIterator ti = mTerrainGroup->getTerrainIterator();
        while(ti.hasMoreElements())
        {
            Ogre::Terrain* t = ti.getNext()->instance;
            initBlendMaps(t);
        }
    }


そもそもブレンドマップとはなんぞや? なんだけど意味はよく分からない。その辺の説明は無いので知識有りが前提なんだろうか。とりあえずここでやってることは、全ての地形をループし、後で出てくるinitBlendMaps(...)を実行している。

後始末

最後にTerrain生成処理の後始末。
    mTerrainGroup->freeTemporaryResources();


チュートリアルに提示されているソースでは、以下のコードがカメラの設定の後にあったので、DemoApp::initialiseRTShaderSystem(...)の中に同様に記述。
    Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(Ogre::TFO_ANISOTROPIC);
    Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(7);


地形の初期値の設定

Privateメソッド DemoApp::configureTerrainDefaults(...)を作成して、以下のコードを書く。
void DemoApp::configureTerrainDefaults(Ogre::Light* light)
{
    Ogre::SceneManager *sceneMgr = OgreFramework::getSingletonPtr()->m_pSceneMgr;
    // Configure global
    mTerrainGlobals->setMaxPixelError(8);
    // testing composite map
    mTerrainGlobals->setCompositeMapDistance(3000);
}


MaxPixelErrorは、Terrainの緻密さを決めるもので、値が小さいほど頂点が増えて緻密になるが、その分性能が落ちる。
CompositeMapDistanceは、Terrainがライトマップを描く時の距離の指定。この辺はよく分からない。
次は、引数で渡された平行光源を使ったライトマップの設定。
    mTerrainGlobals->setLightMapDirection(light->getDerivedDirection());
    mTerrainGlobals->setCompositeMapAmbient(sceneMgr->getAmbientLight());
    mTerrainGlobals->setCompositeMapDiffuse(light->getDiffuseColour());


色や光源の設定を平行光源と環境光から取得して設定している。
次はインポートデータの設定
    Ogre::Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
    defaultimp.terrainSize = 513;
    defaultimp.worldSize = 12000.0f;
    defaultimp.inputScale = 600;
    defaultimp.minBatchSize = 33;
    defaultimp.maxBatchSize = 65;


terrainSizeとworldSizeは、mTerrainGroupに設定した値と合わせてある。inputScaleはハイトマップ画像の拡縮の指定。この辺の意味もよく分からない。
最後にテクスチャの設定
    defaultimp.layerList.resize(3);
    defaultimp.layerList[0].worldSize = 100;
    defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_diffusespecular.dds");
    defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_normalheight.dds");
    defaultimp.layerList[1].worldSize = 30;
    defaultimp.layerList[1].textureNames.push_back("grass_green-01_diffusespecular.dds");
    defaultimp.layerList[1].textureNames.push_back("grass_green-01_normalheight.dds");
    defaultimp.layerList[2].worldSize = 200;
    defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_diffusespecular.dds");
    defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_normalheight.dds");


defaultimp.layerList.resize(3)で、Terrainのテクスチャレイヤの数を指定し、それぞれのワールドサイズ、テクスチャ名を渡している。
ワールドサイズは、各テクスチャの大きさを指定するもので、値が小さいほどテクスチャレイヤの解像度が上がる。
規定のマテリアルジェネレータはレイヤ毎に次の2つのテクスチャをとる。1つは鏡面反射色をアルファチャネルに可能した拡散色テクスチャで、もう1つは、高さをアルファチャネルに格納したノーマルマップ
この辺もなんだかちんぷんかんぷんだけど、詳しくは、Ogre Terrain Texturesを読めとのこと。

地形の定義

次は、DemoApp::defineTerrain(...)メソッドの実装


void DemoApp::defineTerrain(long x, long y)
{
    Ogre::String filename = mTerrainGroup->generateFilename(x, y);
    if (Ogre::ResourceGroupManager::getSingleton().resourceExists(mTerrainGroup->getResourceGroup(), filename))
    {
        mTerrainGroup->defineTerrain(x, y);
    }
    else
    {
        Ogre::Image img;
        getTerrainImage(x % 2 != 0, y % 2 != 0, img);
        mTerrainGroup->defineTerrain(x, y, &img);
        mTerrainsImported = true;
    }
}


最初に、Terrainの生成に使用するファイル名をTerrainGroupから取得している。
次に、リソースグループ中にそのファイル名があるかチェックする。もしあれば既にバイナリの地形ファイルを生成済みということで読み込む必要はない。もしなければ、地形を生成しなければならないので、イメージを読み込んで設定する。ここで使われているgetTerrainImage()は次のとおり。
この関数は一度しか使わないユーティリティ関数なのでクラスメンバでなくローカルな関数になっている。
void getTerrainImage(bool flipX, bool flipY, Ogre::Image& img)
{
    img.load("terrain.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
    if (flipX)
        img.flipAroundY();
    if (flipY)
        img.flipAroundX();
 
}


リソースの規定の場所から、terrain.pngを読み、必要に応じて反転させている。反転を使うことで1つの513x513サイズのハイトマップだけでシームレスな無限の地形を模倣することができるらしい。

地形のブレンド

次は、DemoApp::initBlendMaps(...)の実装。configureTerrainDefaults(...)の中で定義した3つのテクスチャレイヤーをタイルの高さに応じてブレンドする。
void DemoApp::initBlendMaps(Ogre::Terrain* terrain)
{
    Ogre::TerrainLayerBlendMap* blendMap0 = terrain->getLayerBlendMap(1);
    Ogre::TerrainLayerBlendMap* blendMap1 = terrain->getLayerBlendMap(2);
    Ogre::Real minHeight0 = 70;
    Ogre::Real fadeDist0 = 40;
    Ogre::Real minHeight1 = 70;
    Ogre::Real fadeDist1 = 15;
    float* pBlend0 = blendMap0->getBlendPointer();
    float* pBlend1 = blendMap1->getBlendPointer();
    for (Ogre::uint16 y = 0; y < terrain->getLayerBlendMapSize(); ++y)
    {
        for (Ogre::uint16 x = 0; x < terrain->getLayerBlendMapSize(); ++x)
        {
            Ogre::Real tx, ty;
            
            blendMap0->convertImageToTerrainSpace(x, y, &tx, &ty);
            Ogre::Real height = terrain->getHeightAtTerrainPosition(tx, ty);
            Ogre::Real val = (height - minHeight0) / fadeDist0;
            val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
            *pBlend0++ = val;
            
            val = (height - minHeight1) / fadeDist1;
            val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
            *pBlend1++ = val;
        }
    }
    blendMap0->dirty();
    blendMap1->dirty();
    blendMap0->update();
    blendMap1->update();
}


チュートリアルでもこのコードの詳しい説明はない。なんにしろハイトマップに格納された値を使って地形の3つのレイヤをブレンドしているらしい。

実行

ということでいよいよ実行...
上で使われているテクスチャは例によってSDKからコピーしておく
File copy
しかし、ビルドするとリンクでエラーになった。
Link error
全てTerrain関係のエラー。Terrainのライブラリが不足している様子。SDKのlib/releaseに、libOgreTerrainStatic.aというそれっぽいライブラリがあったので、Targetの"Build Settings"で、Other Linker Flagsに"-lOgreTerrainStatic"を追加したらビルドできた。...正しいのか?
Addlib
で、今度こそ実行
Terrain1
ん〜、ダメ。なんとなく地形っぽい影は見えるんだけど、背景のページュ色に黒い模様らしきものが出ているだけ...フレームレートも30FPSしか出ていない。
チュートリアルでもこの時点で表示されるべき結果のスクリーンショットが無いので、何が正しいのか分からない。
ためしに、シミュレータで動かしてみる。
Terrain2
お! なんか出た。しかし、フレームレートが極端に低い1FPS前後しか出ていない。遅すぎ...
う〜ん、困った。そもそもポリゴン数が42000というのはiPhoneでは多すぎな気がする。まぁこの辺はパラメータで調整できるんだろうけど。しかし、iPhoneの環境では地形の利用は諦めた方がいいのかもしれない。どっちみち実機では表示できないし。
疲れたので、続きはまた今度。
しかし、チュートリアルの3回目にしてこの内容(しかもベーシックチュートリアル)って、ハードル高すぎないか?

2014年5月28日水曜日

OgreのBasic Tutorial 2をやってみた

今回はチュートリアルの2回目(http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Basic+Tutorial+2&structure=Tutorials)をやってみた。

カメラ、光源、影に関する話。カメラは先走って前のチュートリアルでもちょっと触った。

カメラ

カメラは3次元空間のシーンの中で実際に画面に描画される範囲を指定するもので、iPhone用のテンプレートでは、OgreFramework::initOgre(...)の中で次のように生成されている。


m_pCamera = m_pSceneMgr->createCamera("Camera"); m_pCamera->setPosition(Vector3(0, 60, 160)); m_pCamera->lookAt(Vector3(0, 0, 0)); m_pCamera->setNearClipDistance(1); : m_pCamera->setAspectRatio(Real(m_pViewport->getActualWidth()) / Real(m_pViewport->getActualHeight()));

シーンマネージャのcreateCamera(...)でカメラのインスタンスを生成し、生成したカメラの位置をsetPosition(...)で指定し、カメラが見ている場所をlookAt(...)で指定している。

setNearClipDistance(...)は、カメラから見えている範囲で、実際に描画する一番手前の位置を指定している。一番奥の位置を指定するための、setFarClipDistance(...)というメソッドもある。

Camera

ちなみに、setNearClipDistance(...)の引数を160にしてみると、こうなった。

Ogre cut

ニアクリップがOgreの顔の辺りになったので一部が表示されなくなった。

setAspectRatio(...)は、画角(縦と横の比率)を指定するもので、描画先となるビューポートの比率と合わせておく必要がある。上の指定だとちゃんと

Ogre port

のように表示されるが、これを


m_pCamera->setAspectRatio(Real(m_pViewport->getActualWidth()) * 2 / Real(m_pViewport->getActualHeight()));

とすると、

Ogre thin

のようになってしまう。

チュートリアルの1をやった時にiPhoneを横向きにしたら、Ogreの顔がでろ〜んとなったのも、iPhoneの描画先のサイズとカメラの画角が合わなくなったからだと思う。iPhoneの回転のイベントで画角(若しくはビューポートの方?)を合わせてやればよさそう。

光源と影

次は、3Dシーンにリアリティと臨場感を与えてくれる光源と影の話

Ogreでは、次の3種類の影が標準で使える。

  1. Modulative Texture Shadows(乗算式テクスチャシャドー?)
  2. Modulative Stencil Shadows(乗算式ステンシルシャドー?)
  3. Additive Stencil Shadows(加算式ステンシルシャドー?)

仕組みについてはよく分からないので割愛。最も計算コストが高いのが、1. 次が3.で、2.が一番安上がりだけど精度も落ちるとのこと。

とりあえず、チュートリアルのcreateScene(...)の内容をほぼそのまま(シーンマネージャの参照先だけiPhone用のテンプレートに合わせて変更)使ってみる。


DemoApp::setupDemoScene() { Ogre::SceneManager *sceneMgr = OgreFramework::getSingletonPtr()->m_pSceneMgr; sceneMgr->setAmbientLight(Ogre::ColourValue(0, 0, 0)); sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE); Ogre::Entity* entNinja = sceneMgr->createEntity("Ninja", "ninja.mesh"); entNinja->setCastShadows(true); sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(entNinja); Ogre::Plane plane(Ogre::Vector3::UNIT_Y, 0); Ogre::MeshManager::getSingleton().createPlane("ground", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane, 1500, 1500, 20, 20, true, 1, 5, 5, Ogre::Vector3::UNIT_Z); Ogre::Entity* entGround = sceneMgr->createEntity("GroundEntity", "ground"); sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(entGround); entGround->setMaterialName("Examples/Rockwall"); entGround->setCastShadows(false); }

今回はモデルとしてOgreの代わりに忍者を使っている。これはテンプレートから作成したプロジェクトには含まれていないけど、SDKの中にそれっぽいのがあったのでプロジェクトにコピーしてみた。

Ninja copy

また、Examples.materialの中の忍者の設定をみると、テクスチャとしてnskingr.jpgが指定されているので、これもSDKからプロジェクトへコピー。

Ninja texture

忍者を立たせる平面を、Ogre::MeshManager::getSingleton().createPlane(...)で作っているが、これのエンティティに設定しているマテリアル、"Examples/Rockwall"も、テクスチャとして"rockwall.tga"を使っているので、同じくこれもSDKからプロジェクトへコピー

Rockwall copy

Ogre::EntityのsetCastShadows(...)で影を他の物体へ投影するかどうかを指定する。忍者は影を投影し、平面は投影しない設定にしている。影の生成は負荷のかかる処理なので不要なら無効にした方がいいらしい。

ひとまず実行してみる。

Ninja silhouette

暗っ!

環境光を真っ暗(Ogre::ColourValue(0, 0, 0))にしたので当然といえば当然。しかし、忍者らしき怪しい黒い人影は表示されている。

ということで次は光源を配置する。

Ogreでは、次の3種類の光源が標準で使える。

  1. Point(点光源)…光源の周りを均等に照らす。リアル世界でいうと暗闇をぽつんと照らす豆電球をイメージすると分かりやすい。
  2. Spotlight(スポットライト)…懐中電灯の様に特定の方向に向かって照らす光源。位置の他、方向や照らす範囲(角度)等を指定する。
  3. Directional(指向性光源)…太陽や月の光のように一定方向から均等に照らす。

チュートリアルの内容どおり、光源を配置する処理をDemoApp::setupDemoScene()に追加する。


: Ogre::Light* pointLight = sceneMgr->createLight("pointLight"); pointLight->setType(Ogre::Light::LT_POINT); pointLight->setPosition(Ogre::Vector3(0, 150, 250)); pointLight->setDiffuseColour(1.0, 0.0, 0.0); pointLight->setSpecularColour(1.0, 0.0, 0.0); Ogre::Light* directionalLight = sceneMgr->createLight("directionalLight"); directionalLight->setType(Ogre::Light::LT_DIRECTIONAL); directionalLight->setDiffuseColour(Ogre::ColourValue(.25, .25, 0)); directionalLight->setSpecularColour(Ogre::ColourValue(.25, .25, 0)); directionalLight->setDirection(Ogre::Vector3( 0, -1, 1 )); Ogre::Light* spotLight = sceneMgr->createLight("spotLight"); spotLight->setType(Ogre::Light::LT_SPOTLIGHT); spotLight->setDiffuseColour(0, 0, 1.0); spotLight->setSpecularColour(0, 0, 1.0); spotLight->setDirection(-1, -1, 0); spotLight->setPosition(Ogre::Vector3(300, 300, 0)); spotLight->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(50));

ポイントライト、点光源、平行光源を1つづつ配置して、光の色の設定などしている。"diffuse colour"(colourはcolorのイギリス式綴り)は、拡散光のことで物体を一様に照らす光の色の成分。"specular colour"は、反射光のことで、光と視線の入射角によって変わる色の成分。反射光を使うことで、金属のようなつるつるした質感を表現できる。

とりあえず、実行してみる。

Ninja

平面は照らされたけど、忍者はまだ少し暗い。それにどうも影が投影されていない。

カメラの位置を変えてみる。


m_pCamera->setPosition(Vector3(100, 200, -200));

Ninja2

やはり、影が投影されていない。チュートリアルによるとこの時点で影が投影されるはずなんだけど...

コンソールをみると「このデバイスはハードウェアステンシルに対応していない」といった内容のログが表示されていた。

Warning

ググってみたら、Ogreのフォーラムで質問がされていた。どうも、iPhoneでは、ステンシルシャドーが使えないみたい。ハード的には対応しているけどソースの修正が必要らしい...

面倒くさそうだったので、とりあえず、テクスチャシャドーの方を使ってみる。


// sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE); sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_TEXTURE_MODULATIVE);

結果は、

Ninja3

お!なんか微妙に表示された。でもなんかちょっと汚い感じの影で、頭の部分の影は消えている。

カメラをもう少し引いてみる。


m_pCamera->setPosition(Vector3(100, 300, -300));

Ninja4

あぁ、影が消えた...

試しに、平行光源とスポットライトをコメントアウトして点光源だけにしてみる。


Ogre::Light* pointLight = sceneMgr->createLight("pointLight"); pointLight->setType(Ogre::Light::LT_POINT); pointLight->setPosition(Ogre::Vector3(0, 150, 250)); pointLight->setDiffuseColour(1.0, 0.0, 0.0); pointLight->setSpecularColour(1.0, 0.0, 0.0); /* Ogre::Light* directionalLight = sceneMgr->createLight("directionalLight"); directionalLight->setType(Ogre::Light::LT_DIRECTIONAL); directionalLight->setDiffuseColour(Ogre::ColourValue(.25, .25, 0)); directionalLight->setSpecularColour(Ogre::ColourValue(.25, .25, 0)); directionalLight->setDirection(Ogre::Vector3( 0, -1, 1 )); Ogre::Light* spotLight = sceneMgr->createLight("spotLight"); spotLight->setType(Ogre::Light::LT_SPOTLIGHT); spotLight->setDiffuseColour(0, 0, 1.0); spotLight->setSpecularColour(0, 0, 1.0); spotLight->setDirection(-1, -1, 0); spotLight->setPosition(Ogre::Vector3(300, 300, 0)); spotLight->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(50)); */

Ninja5

前方に影ができた。

同じように今度は、平行光源だけにしてみると、

Ninja6

影はできない。

でも近づいてみるとできてる。

Ninja7

次に、スポットライトだけにしてみる。

Ninja8

これも影はできない。

近づいてみると、

Ninja9

できてる。

ん〜...原因はわからないけど、iPhoneで影を出したい場合は点光源だけにした方がよさげ。ステンシルバッファが使えれば他の光源を使ってカメラを引いた場合でも影ができるのだろうか?

とりあえず、今日はここまで。

最後に、照明を明るい環境光だけにして忍者の全身撮影。カシャッ

Ninja10

お疲れさまでした。

2014年5月26日月曜日

Ogre for iPhoneのアンインストール

今日はOgre for iPhoneのアンインストール方法を書く。

別に、筆者がOgreに飽きてこのブログをこれでやめるというわけではなく、中にはそういう人もいるかもしれないので一応ここで紹介しておくことにする。

まず、SDKだけど、これは単純にフォルダごと削除すればオッケー

Del sdk

残るはXcodeに入れたテンプレートの削除だけど、これは

/Users/[USER NAME]/Library/Developer/Xcode/Templates

の中にあるOgreというフォルダをまるごと削除すればオッケー

この場所は通常はFinderで見られないので、Finderの「移動」メニューをクリックし、optionキーを押す。

Show library

すると、メニューの中に「ライブラリ」という項目が表示されるので、これをクリックして、後は表示されたディレクトリを「Developer」=>「Xcode」=>「Templates」とみていけば、Ogreというディレクトリが見つかるので、これを削除する。

Ogre template

すると、Xcodeのプロジェクト作成の中からOgreのテンプレートが消えているはず。

2014年5月23日金曜日

OgreのBasic Tutorial 1その2

昨日の続き

拡大・縮小と回転

2匹目の鬼をシーンに追加してみる。
これは簡単。setupDemoScene()の中で名前を変えて同じメッシュのエンティティを生成し、新たに生成したノードに割り当てる。
単純に追加すると1匹目の鬼と位置が被ってしまうので、2匹目は位置を変える。これは、createChildSceneNode(...)の第2引数で指定できる。
Ogreは、OpenGLとDirectXのどちらもサポートするけど、座標系はOpenGLと同じ右手座標系(Z座標は手前が正方向)らしい。
// 1匹目の鬼
Ogre::Entity* ogreHead = sceneMgr->createEntity("Head", "ogrehead.mesh");
// Create a SceneNode and attach the Entity to it
Ogre::SceneNode* headNode = sceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
headNode->attachObject(ogreHead);
 
// 2匹目を追加
Ogre::Entity* ogreHead2 = sceneMgr->createEntity("Head2", "ogrehead.mesh");
// Create a SceneNode and attach the Entity to it
Ogre::SceneNode* headNode2 = sceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode2", Ogre::Vector3( 50, 0, -100 ));
headNode2->attachObject(ogreHead2);
Twoogres
2匹目のEntityとNodeをそれぞれ1匹目とは別に新たに生成しているけど、1匹目のEntityを2匹目にも使い回せないのか試してみた。
// 2匹目を追加
// Ogre::Entity* ogreHead2 = sceneMgr->createEntity("Head2", "ogrehead.mesh");
// Create a SceneNode and attach the Entity to it
Ogre::SceneNode* headNode2 = sceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode2", Ogre::Vector3( 50, 0, -100 ));
headNode2->attachObject(ogreHead);  // <== 1匹目の鬼のエンティティを使ってみる
結果はダメ。こうなった。
Badogre
1匹目の鬼の顔がどよ〜んと暗くなり、2匹目の姿は影も形もない。位置はNodeで指定しているのでEntityは使い回せるかと思ったけどダメみたい。この辺の仕組みはまだよく理解できていない。
2匹目を追加できたはいいけど、iPhoneの画面では大きすぎるので、鬼を小さくしてみる。(カメラをズームアウトしてもいいんだけど)
これは、ノードのScale(...)で指定できる。
// 1匹目の鬼
Ogre::Entity* ogreHead = sceneMgr->createEntity("Head", "ogrehead.mesh");
// Create a SceneNode and attach the Entity to it
Ogre::SceneNode* headNode = sceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
headNode->attachObject(ogreHead);
headNode->scale(0.5, 0.5, 0.5);
 
// 2匹目を追加
Ogre::Entity* ogreHead2 = sceneMgr->createEntity("Head2", "ogrehead.mesh");
// Create a SceneNode and attach the Entity to it
Ogre::SceneNode* headNode2 = sceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode2", Ogre::Vector3( 50, 0, -100 ));
headNode2->attachObject(ogreHead2);
headNode2->scale(0.5, 0.5, 0.5);
Scale
各軸とも同じ値を指定したけど、当然それぞれの方向に違う値も指定できる。
// 1匹目の鬼
 :
headNode->scale(0.2, 0.5, 0.5);
 
// 2匹目の鬼
 :
headNode2->scale(0.5, 0.3, 0.5);
Scale2
次は回転させてみる。
// 1匹目の鬼
 :
headNode->scale(0.5, 0.5, 0.5);
headNode->rotate(Ogre::Vector3( 0, 0, 1 ), Ogre::Degree(45));
 
// 2匹目の鬼
 :
headNode2->scale(0.5, 0.5, 0.5);
headNode2->rotate(Ogre::Vector3( 0, 0, 1 ), Ogre::Degree(-90));
Rotate
上の書き方では、z軸を回転軸として1匹目の鬼を45°、2匹目の鬼を-90°回転させている。他にもpitch(...),yaw(...),roll(...)という回転用のメソッドがあって、それぞれ、x軸, y軸, z軸を回転軸として回転させる。
上の例の場合では、headNode->roll(Ogre::Degree(45)); と書いても同じ。

カメラの移動

今度は、鬼ではなくてカメラの方を動かしてみる。
その前に鬼のスケールと回転はコメントアウトしておく。
// 1匹目の鬼
 :
// headNode->scale(0.5, 0.5, 0.5);
// headNode->rotate(Ogre::Vector3( 0, 0, 1 ), Ogre::Degree(45));
// 2匹目の鬼
 :
// headNode2->scale(0.5, 0.5, 0.5);
// headNode2->rotate(Ogre::Vector3( 0, 0, 1 ), Ogre::Degree(-90));
カメラは、iPhone用のテンプレートを使った場合、OgreFramework.cppのinitOgre(...)の中で生成している。この辺はチュートリアルで使われている内容と違うので紛らわしい。
とりあえず、カメラの位置をもっと手前の方に動かしてみる。
// m_pCamera->setPosition(Vector3(0, 60, 60));
m_pCamera->setPosition(Vector3(0, 60, 150));
Cameraposition
カメラの回転もノードと同様のメソッドでできる。但し、lookAt(...)の後でやらないと反映されなかった。
m_pCamera->setPosition(Vector3(0, 60, 150));
m_pCamera->lookAt(Vector3(0, 0, 0));
m_pCamera->roll(Ogre::Degree(30));
Cameraroll
で、この辺のソースを見てたら、背景色を指定しているコードを発見。ピンクに変えてみる。
m_pCamera->lookAt(Vector3(0, 0, 0));
m_pCamera->roll(Ogre::Degree(30));
m_pCamera->setNearClipDistance(1);
    
m_pViewport = m_pRenderWnd->addViewport(m_pCamera);
// m_pViewport->setBackgroundColour(ColourValue(0.8f, 0.7f, 0.6f, 1.0f));
m_pViewport->setBackgroundColour(ColourValue(1.0f, 0.4f, 0.7f, 1.0f));  // ピンクに変更
   
m_pCamera->setAspectRatio(Real(m_pViewport->getActualWidth()) / Real(m_pViewport->getActualHeight()));
 
m_pViewport->setCamera(m_pCamera);
Pinkcolor
今日はここまで。
WebGL関連の記事を書くつもりで始めたブログだけど、いきなりOgreにハマってしまった。WebGLもやりたいけど...

追記...

Ogre用とWebGL用とブログを分けて、WebGL関連の記事は、http://glabc.blogspot.jp/ に書くことにしました。

2014年5月22日木曜日

OgreのBasic Tutorial 1をやってみた

せっかくOgre(読み方はオーガで鬼という意味。個人的にはオグレって読んでるけど...)をインストールしたので、ちょっとここのチュートリアルをやってみた。 その前に、全体像をここで勉強。

オブジェクト指向を名前に冠してるだけあって、シングルトンパターンやファクトリメソッドパターン等が目に付く。

生成メソッドが、create〜()だったり、newを使ったり、そして破棄メソッドが、remove〜()だったり、destroy〜()だったり、deleteを使ったりってのが若干気にかかる。

あと、ところどころで登場する、“aaa:Bbb:0”という形式の謎の文字列

まぁ、そのうち分かるかな...とりあえず下の図を意識しとくといいかも。

で、

チュートリアル

Basic Tutorial 1 は、昨日のデモとたいして変わらない。

TutorialApplication::createScene()で、表示する内容を構築している。


void createScene(void)
{
// Set the scene's ambient light
mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));

// Create an Entity
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

// Create a SceneNode and attach the Entity to it
Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
headNode->attachObject(ogreHead);

// Create a Light and set its position
Ogre::Light* light = mSceneMgr->createLight("MainLight");
light->setPosition(20.0f, 80.0f, 50.0f);
}

シーンに環境光(AmbientLight)を配置し、鬼の頭を追加(鬼の頭のメッシュをもったエンティティをノードに割り当て、そのノードをシーンに追加?)し、光源(環境光とは別の指向性をもった光)を配置している。 昨日のテンプレートから作成したプロジェクトでは、setupDemoScene()の中でシーンを構築してるので、既存のコードをコメントアウトして、上のコードを入れてみた。


void DemoApp::setupDemoScene()
{
/*
OgreFramework::getSingletonPtr()->m_pSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");

OgreFramework::getSingletonPtr()->m_pSceneMgr->createLight("Light")->setPosition(75,75,75);

m_pOgreHeadEntity = OgreFramework::getSingletonPtr()->m_pSceneMgr->createEntity("OgreHeadEntity", "ogrehead.mesh");
m_pOgreHeadNode = OgreFramework::getSingletonPtr()->m_pSceneMgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode");
m_pOgreHeadNode->attachObject(m_pOgreHeadEntity);
*/
Ogre::SceneManager *sceneMgr = OgreFramework::getSingletonPtr()->m_pSceneMgr;
// Set the scene's ambient light
sceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));
// Create an Entity
Ogre::Entity* ogreHead = sceneMgr->createEntity("Head", "ogrehead.mesh");
// Create a SceneNode and attach the Entity to it
Ogre::SceneNode* headNode = sceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
headNode->attachObject(ogreHead);
// Create a Light and set its position
Ogre::Light* light = sceneMgr->createLight("MainLight");
light->setPosition(20.0f, 80.0f, 50.0f);
}

createEntity()に渡されている、"ogrehead.mesh"は、ここにある。

Ogremesh

中身はバイナリ。多分モデリングデータ。

で、実行すると...

Ogre port

背景が昨日と違ってベージュ色になってるのは、


OgreFramework::getSingletonPtr()->m_pSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");

の部分をコメントアウトしたから。

SpaceSkyBoxって名前から察するに、宇宙空間のキューブマップの指定っぽい。

この"Examples/SpaceSkyBox"っていうのは、media/materials/scripts/Examples.mateial の中に記述があって、そこに書かれてるstevecube.jpgという画像ファイルは、media/packs/skybox.zipに固められている。

Scripts

Skybox

描画方法をスクリプトファイルに外出しで定義しておいて、プログラム側から呼び出して利用する感じ?

ちなみに、iPhoneを横向きにするとこうなる。

Ogre land

でろ〜ん

まぁ愛嬌。これは昨日のデモでも同じ。

シーンの作成部分に限って言えば結構素直というか面白いかも、オグレ

2014年5月21日水曜日

Ogre iPhoneのインストール

Ogre3DiPhone port版があったのでここを参考に入れてみた。
OGRE (Object-Oriented Graphics Rendering Engine) は、クロスプラットフォームな3D描画エンジン。ゲームの開発等に使われている。

まずは、ダウンロード


ここから、「OGRE 1.9 SDK for iOS」を選んでダウンロード
Download
195Mもある。結構デカい。
ダウンロードしたパッケージの中のSDKを適当なフォルダへドラッグ
Sdk
次に、ここからプロジェクトのテンプレートと、iOS7用依存ファイルをダウンロード
Template
テンプレートはダウンロードしたパッケージを開いて指示に従ってインストール
Template install
iOS7依存ファイルの方は、先ほどインストールしたSDKのルートフォルダ直下にコピー
Depend

これで、SDKのインスートールが終わったので、

実際にプロジェクトを作ってみる。


プロジェクトの新規作成でOgreのテンプレートを選ぶ。
Project1
オプションで、「Use OpenGL ES 2.0」と「Use libc++」にチェックをつける。(つけないとコンパイルでエラーになった)
Project2
ブロジェクトが作成されたら、いざビルド!といきたいところだけど、そのままだとエラーになるので、いくつか設定が必要
まず、Valid Architecturesにarmv7sが含まれていると、リンクで失敗するので、削除する。
Armv7s
それから、プロジェクトのLibrary Search Pathsと、ターゲットのHeader Search Pathsがそれぞれ次のように設定されていることを確認する。
Library Path
Header Path
上の設定で使用されているOGRE_SDK_ROOTをユーザ定義設定に追加する。値は、OgreSDKフォルダの場所を設定する。
User setting
Sdk root
これで晴れてビルドできるようになったので、実際に実機で動かしてみる。

実行...ドンっ!

Ogre

デカっ!

コンソールにログが出力されており、よく見るとエラーらしきものも混じっている。とりあえず、動いているのでよしとする。
Ogreはもともとデスクトップ用に開発されており、言語はC++で開発されている。Windows,Mac,Linuxで動作させられる。ソースをチラと見てみたがなかなか難解。クラス数も膨大でこれを覚えるのは結構しんどいと思う。しかも、Ogreはその名の通り、グラフィック専用のライブラリであり、音やネットワーク等の機能は含まれない。これらが必要なら別途それ用のライブラリを使う必要がある。ただし割とドキュメント類は揃っているようす。英語だけど。
Objective-CはC++もサポートしてるので、ソースの拡張子cppをmmにして、Objective-C++にすれば、Cocoaのクラスも使える。(はず...試してない)
ひとまず、今日はここまで。

※ Ogreをアンインストールする場合は、こちら