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