【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; };
ね、簡単でしょ!