wacchoz’s note

プログラミングとか数学について

【PTAM(3/3)】 PTAMでの影

前回のプログラムでは影が描画されないのがイマイチと思った人もいるかもしれない。
ステンシルバッファを使った平面投影シャドウを使ってみる。

ARDriver.cc の MakeFrameBuffer() を以下に差し替えて、ステンシルバッファを使えるようにする。

void ARDriver::MakeFrameBuffer()
{
  cout << "  ARDriver: Creating FBO... ";
 
  glGenTextures(1, &mnFrameBufferTex);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB,mnFrameBufferTex);
  glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 
       GL_RGBA, mirFBSize.x, mirFBSize.y, 0, 
       GL_RGBA, GL_UNSIGNED_BYTE, NULL); 
  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

  GLuint DepthBuffer;
  glGenRenderbuffersEXT(1, &DepthBuffer);
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, DepthBuffer);
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, mirFBSize.x, mirFBSize.y);

  glGenFramebuffersEXT(1, &mnFrameBuffer);
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mnFrameBuffer); 
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 
    GL_TEXTURE_RECTANGLE_ARB, mnFrameBufferTex, 0);
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, 
  	       GL_RENDERBUFFER_EXT, DepthBuffer);
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, 
GL_RENDERBUFFER_EXT, DepthBuffer);

  CheckFramebufferStatus();
  cout << " .. created FBO." << endl;
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

そしてEyeGame.ccを以下に差し替え。

#include "EyeGame.h"
#include "OpenGL.h"
#include <cvd/convolution.h>
#include <mmsystem.h>

#include    "MMD/PMDModel.h"
#include    "MMD/VMDMotion.h"

using namespace CVD;

cPMDModel    g_clPMDModel;
cVMDMotion    g_clVMDMotion;

float        g_fPrevFrame = 0.0f;
DWORD        g_dwStartTime = 0;

MMD_Matrix        g_matPlanarProjection;

EyeGame::EyeGame()
{
    mbInitialised = false;

    SetCurrentDirectory( "C:\\PTAM\\Model" );
    g_clPMDModel.load("C:\\PTAM\\Model\\初音ミク.pmd");
    g_clVMDMotion.load("C:\\PTAM\\motion\\適当なモーションファイル.vmd");

    g_clPMDModel.setMotion( &g_clVMDMotion, true );

    g_dwStartTime = timeGetTime();
    g_fPrevFrame = 0.0f;
}


void EyeGame::DrawStuff(Vector<3> v3CameraPos)
{
    if(!mbInitialised)
        Init();

    mnFrameCounter ++;

    DWORD time;
    float fDiffFrame;

    time = timeGetTime() - g_dwStartTime;
    fDiffFrame= (float)time / 30.0f - g_fPrevFrame;
    g_clPMDModel.updateMotion(fDiffFrame);
    g_clPMDModel.updateSkinning();
    g_fPrevFrame += fDiffFrame;

    glClearStencil(0); 
    glClear(GL_STENCIL_BUFFER_BIT);

    glEnable( GL_CULL_FACE );
    glEnable( GL_ALPHA_TEST );
    glEnable( GL_BLEND );
    glFrontFace(GL_CW);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_NORMALIZE);

    GLfloat af[4]; 
    af[0]=0.7; af[1]=0.7; af[2]=0.7; af[3]=1.0;
    glLightfv(GL_LIGHT0, GL_AMBIENT, af);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, af);
    af[0]=1.0; af[1]=1.0; af[2]=1.0; af[3]=1.0;
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, af);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0);

    glMatrixMode(GL_MODELVIEW);

    af[0]=150.0; af[1]=-300.0; af[2]=200.0; af[3]=0.0;
    glLightfv(GL_LIGHT0, GL_POSITION, af);

    // 平面投影行列の作成
    Vector4        vec4Plane = { 0.0f, 1.0f, 0.0f, 0.0f };        
// { a, b, c, d } → ax + by + cz + d = 0 (投影したい平面の方程式)
    Vector3        vec4LightPos = { af[0], af[2], af[1] };    // ライトの位置
    MatrixPlanarProjection( g_matPlanarProjection, &vec4Plane, &vec4LightPos );

    glPushMatrix();
        glScalef( 0.05f, 0.05f, -0.05f);
        glRotatef(-90.0, 1.0, 0.0, 0.0);

        g_clPMDModel.render();

        // ステンシルバッファに影の形を描画
        glDisable( GL_CULL_FACE );
        glDisable( GL_TEXTURE_2D );
        glDisable( GL_LIGHTING );

        glEnable( GL_STENCIL_TEST );
        glStencilFunc( GL_ALWAYS, 1, ~0 );
        glStencilOp( GL_REPLACE, GL_KEEP, GL_REPLACE );

        glColorMask( 0, 0, 0, 0 );
        glDepthMask( 0 );

        glMultMatrixf( (const float *)g_matPlanarProjection );
        g_clPMDModel.renderForShadow();        // 影用の描画

        glColorMask( 1, 1, 1, 1 );

        // ステンシルバッファの影の形を塗りつぶす
        float    fWndW = 640.0f,
                fWndH = 480.0f;

        glStencilFunc( GL_EQUAL, 1, ~0);
        glStencilOp( GL_KEEP, GL_KEEP ,GL_KEEP );

        glDisable( GL_DEPTH_TEST );

        glMatrixMode( GL_PROJECTION );
        glPushMatrix();
            glLoadIdentity();
            gluOrtho2D( 0.0f, fWndW, 0.0f, fWndH );

            glMatrixMode( GL_MODELVIEW );
            glPushMatrix();
                glLoadIdentity();

                glColor4f( 0.2f, 0.2f, 0.2f, 0.6f );

                glBegin( GL_TRIANGLE_FAN );
                    glVertex2f( 0.0f, fWndH );
                    glVertex2f( fWndW, fWndH );
                    glVertex2f( fWndW, 0.0f );
                    glVertex2f( 0.0f, 0.0f );
                glEnd();

                glMatrixMode( GL_PROJECTION );
            glPopMatrix();
            glMatrixMode( GL_MODELVIEW );
        glPopMatrix();

        glDepthMask( 1 );
        glEnable( GL_LIGHTING );
        glEnable( GL_DEPTH_TEST );
        glDisable( GL_STENCIL_TEST );
    glPopMatrix();

    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST); 
    glDisable(GL_CULL_FACE);
};

void EyeGame::Reset()
{
    mnFrameCounter = 0;
    g_dwStartTime = timeGetTime();
    g_fPrevFrame = 0.0f;
    g_clPMDModel.setMotion( &g_clVMDMotion, true );
};

void EyeGame::Init()
{
    if(mbInitialised) return;
    mbInitialised = true;
};

ね、簡単でしょ!
f:id:wacchoz:20181106230141j:plain

【PTAM(2/3)】 PTAMにモデル描画

PTAMに目玉ではなく自分でモデルを描画してみよう。

変更するファイルはEyeGame.ccだ。
void EyeGame::DrawStuff(Vector<3> v3CameraPos) の中に描画するコードを書けばいい。

とりあえず
for(int i=0; i<4; i++)
から
glDisable(GL_TEXTURE_2D);
コメントアウトしておこう。

glMatrixMode(GL_MODELVIEW);
の下に以下を追加。

    glLineWidth(10.0f);
    glColor3f(1.0f,  0.0f,  0.0f);
    glBegin(  GL_LINES  );
        glVertex3f(0.0f,  0.0f,  0.0f);
        glVertex3f(100.0f,  0.0f,  0.0f);
    glEnd();

    glColor3f(0.0f,  1.0f,  0.0f);
    glBegin(  GL_LINES  );
        glVertex3f(0.0f,  0.0f,  0.0f);
        glVertex3f(0.0f,  100.0f,  0.0f);
    glEnd();

    glColor3f(0.0f,  0.0f,  1.0f);
    glBegin(  GL_LINES  );
        glVertex3f(0.0f,  0.0f,  0.0f);
        glVertex3f(0.0f,  0.0f,  100.0f);
    glEnd();

f:id:wacchoz:20181106225351j:plain

ここでの座標系は、右方向にX軸、奥行き方向にY軸、上方向にZ軸だとわかる。
右手系のようだ。



これだけでは面白くないので、MMDMikuMikuDance)モデルを描画してみよう。

ARTK_MMD (http://ppyy.if.land.to/)

  • artk_mmd_src_no_bullet.zipをダウンロード。
  • \ARTK_MMD_src\Src\MMD を丸ごとC:\PTAM\includeへ。
  • PTAMプロジェクトに、C:\PTAM\include\MMD 内の全ファイルを追加
  • このライブラリ内のMatrixクラスの名前が他のライブラリとぶつかってしまうので、MMD_Matrixとでも全置換しておこう。
  • C:\PTAM\model に適当なMMDモデルを入れておく
  • C:\PTAM\motion に適当なモーションファイルを入れておく

EyeGame.ccを以下に差し替え。

#include  "EyeGame.h"
#include  "OpenGL.h"
#include  <cvd/convolution.h>
#include  <mmsystem.h>

#include    "MMD/PMDModel.h"
#include    "MMD/VMDMotion.h"

using  namespace  CVD;

cPMDModel    g_clPMDModel;
cVMDMotion    g_clVMDMotion;

float        g_fPrevFrame  =  0.0f;
DWORD        g_dwStartTime  =  0;

EyeGame::EyeGame()
{
    mbInitialised  =  false;

    SetCurrentDirectory(  "C:\\PTAM\\Model"  );
    g_clPMDModel.load("C:\\PTAM\\Model\\初音ミク.pmd");
    g_clVMDMotion.load("C:\\PTAM\\motion\\適当なモーションファイル.vmd");

    g_clPMDModel.setMotion(  &g_clVMDMotion,  true  );

    g_dwStartTime  =  timeGetTime();
    g_fPrevFrame  =  0.0f;
}


void  EyeGame::DrawStuff(Vector<3>  v3CameraPos)
{
    if(!mbInitialised)
        Init();

    mnFrameCounter  ++;

    DWORD  time;
    float  fDiffFrame;

    time  =  timeGetTime()  -  g_dwStartTime;
    fDiffFrame=  (float)time  /  30.0f  -  g_fPrevFrame;
    g_clPMDModel.updateMotion(fDiffFrame);
    g_clPMDModel.updateSkinning();
    g_fPrevFrame  +=  fDiffFrame;

    glEnable(  GL_CULL_FACE  );
    glEnable(  GL_ALPHA_TEST  );
    glEnable(  GL_BLEND  );
    glFrontFace(GL_CW);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_NORMALIZE);

    GLfloat  af[4];  
    af[0]=0.7;  af[1]=0.7;  af[2]=0.7;  af[3]=1.0;
    glLightfv(GL_LIGHT0,  GL_AMBIENT,  af);
    glLightfv(GL_LIGHT0,  GL_DIFFUSE,  af);
    af[0]=1.0;  af[1]=-2.0;  af[2]=1.0;  af[3]=0.0;
    glLightfv(GL_LIGHT0,  GL_POSITION,  af);
    af[0]=1.0;  af[1]=1.0;  af[2]=1.0;  af[3]=1.0;
    glMaterialfv(GL_FRONT_AND_BACK,  GL_SPECULAR,  af);
    glMaterialf(GL_FRONT_AND_BACK,  GL_SHININESS,  50.0);

    glMatrixMode(GL_MODELVIEW);

    glPushMatrix();
        glScalef(  0.05f,  0.05f,  -0.05f);
        glRotatef(-90.0,  1.0,  0.0,  0.0);
        g_clPMDModel.render();
    glPopMatrix();

    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);  
    glDisable(GL_CULL_FACE);    
};

void  EyeGame::Reset()
{
    mnFrameCounter  =  0;
    g_dwStartTime  =  timeGetTime();
    g_fPrevFrame  =  0.0f;
    g_clPMDModel.setMotion(  &g_clVMDMotion,  true  );
};

void  EyeGame::Init()
{
    if(mbInitialised)  return;
    mbInitialised  =  true;
};

ね、簡単でしょ!
f:id:wacchoz:20181106225711j:plain

【PTAM(1/3)】 PTAMコンパイル

PTAMを入れたパソコンがぶっ壊れてからだいぶ長い時間がたつが、超久々に再度コンパイルしてみた。
前回やったときは資料も少なくて苦労して丸1日かかりました。

今からやる人も少ないだろうが、備忘録として残しておく。

今回はこのページを参考に。

マーカレスAR(PTAM)のソースコードを動かしてみた(WindowsXP VisualC++) | happymeme

コンパイル環境:
   Windows 7 (64bit), Visual Studio 2005


①PTAM(http://www.robots.ox.ac.uk/~gk/PTAM/)
-PTAM.zipをダウンロード。
-C:\PTAMに解凍。
-README.txtを熟読。
-C:\PTAM\includeとC:\PTAM\libフォルダを作っておく。

Lapack and BLAS(http://www.fi.muni.cz/~xsvobod2/misc/lapack/)
-lapack-MT-release.zipとheaders.tar.gzの2ファイルをダウンロード。
-headersの中のblaslapackは丸ごと、C:PTAM\includeへ
-lapack-MT-releaseを解凍してできた2つのlibファイルを、C:\PTAM\libへ。
-2つのdllファイルをC:\PTAM\Releaseへ。

③pthreads(http://sourceware.org/pthreads-win32/)
-pthreads-w32-2-9-1-release.zipをダウンロード。
-\Pre-built.2\include内の3つのヘッダファイルをC:\PTAM\inclueへ。
-\Pre-build.2\lib内の3つのlibファイルを、C:\PTAM\libへ。
-\Pre-build.2\dll内のdllファイルを、C:\PTAM\Releaseへ。

④GLEW(http://glew.sourceforge.net/)
-glew-1.9.0-win32.zipをダウンロード。
-include内のGLフォルダを丸ごとC:\PTAM\includeへ。
-lib内の4つのlibファイルをC:\PTAM\libへ。
-bin内のglew32.dllをC:\PTAM\Releaseへ。

⑤CMU1394 camera driver(http://www.cs.cmu.edu/~iwan/1394/)
-1394camera646_src.zipをダウンロード。
-ソリューションを開き、1394cameraをReleaseビルド。
-生成された1394camera.dllをC:\PTAM\Releaseへ。
-生成された1394camera.libをC:\PTAM\libへ。
-1394camera内のすべてのヘッダファイルをC:\PTAM\includeへ。

⑥libjpeg for win32(http://gnuwin32.sourceforge.net/packages/jpeg.htm)
-jpeg-6b-4.exeをダウンロード。
-普通にインストール。インストール場所はとりあえずデフォルトのまま。

CVSでLibCVD、TooN、GVars3をダウンロード。
(記憶ではTooNがかなりバージョンにシビアだったかと。必ずこの方法をとること。)
-CygwinなどCVSを使える環境をそろえる。
-以下のコマンドでファイルをダウンロード。
# export CVS_RSH=ssh
# cvs -z3 -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/toon co -D "Mon May 11 16:29:26 BST 2009" TooN
# cvs -z3 -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/libcvd co -D "Mon May 11 16:29:26 BST 2009" libcvd
# cvs -z3 -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/libcvd co -D "Mon May 11 16:29:26 BST 2009" gvars3

⑧LibCVD
-build内のソリューションを開きReleaseビルド。
 この際にC:\PTAM\includeとC:\Program Files (x86)\GnuWin32\includeにインクルードパスを通しておく。
-libcvd.libをC:\PTAM\libへ。
-cvdフォルダを丸ごとC:\PTAM\includeへ。

⑨Toon
-フォルダごと丸ごとC:\PTAM\includeへ。

fltk(http://www.fltk.org/)
README.txtには触れられていないのだが、fltkが必要。
-fltk-2.0.x-alpha-r9296.tar.gzをダウンロード(必ず2.0系をダウンロードのこと)
-fltkフォルダを丸ごとC:\PTAM\includeへ。

⑩gvars3
-build内のソリューションを開きReleaseビルド。
 (インクルードパスにC:\PTAM\includeを通しておく。)
-config.hが存在しないエラーが出るため、config.h.inをconfig.hにリネーム。
-gvars3.hの261行目でエラーが出るので、

http://written.4403.biz/archives/2009/05/vs2008-artoolkit-ptam.html

の通り、以下のように変更。

template<class T> static T& get(const std::string& name, const T& default_val=T(), int flags=0);

-生成したgvars3.libをC:\PTAM\libへ。
-gvars3フォルダは丸ごとC:\PTAM\includeへ。


⑪PTAM
-\Build\Win32内の4つのファイルをC:\PTAMへ。
-ソリューションを開き。C:\PTAM\includeにパスを通しビルド。
-blas_win32.lib、lapack_win32.libが無いとエラーが出るので、リンクするファイルをblas_win32_MT.lib、lapack_win32_MT.libに変更。

これでビルドできます。

⑫実行
-C:\PTAM\Releaseにdllがあることを確認した上でCameraCalibrator.exeを実行。
 ここでdllが見つからないというエラーが出た場合は、該当ファイルを確認して下さい。

Webカメラの使用
-ここからPTAM_webcam.zipをダウンロード

http://kougaku-navi.net/backyard/index.html
-readmeの通りにやりましょう。
-ewclibはver1.2を使用。
-ビルドするとdshow.hが無いとエラーが出たので、とりあえず手持ちのDirectX9(Summer 2003)のCD-ROMのインストールをしようと思ったが、Windows7にはインストールさせてくれないようなので、手動コピーしてパスを通す。
-error C2146: 構文エラー : ';' が、識別子 'PVOID64' の前に必要です。
 のエラーが出たら、PlatformSDKのインクルード優先度を上に持っていく。

⑭実行
-*.cfgファイルを全てC:\PTAM\Releaseへ。
-CameraCalibratorを実行しcalib_pattern.pdfを印刷したものをカメラに写し、GrabFrameを押します。角度を変えて何度か繰り返します。
-Optimizeを押す
-Save
-終了

-PTAMを実行
-Spacebarを押して、カメラを「平行」に移動し、再びSpacebarを押す
 (手を中心に回転するのではなく平行移動です)
-グリッドが表示されれば成功。Draw AR Onを押せば目玉が表示されます。


ね、簡単でしょ!

f:id:wacchoz:20181106224325j:plain