#include <cmath> #include <GL/glut.h> //------------- 各種外部変数 ----------------// struct Point4f { float x,y,z,w; }; Point4f LightPos = {1,3,3, 1.0}; Point4f RoofCoord[3] = { {-1,1,0,1}, {1,1,0,1}, {0,1,1,1}}; Point4f ShadowEnd[3]; double eye[3] = {0,7,8}; //無限遠方計算用 const float InfinitePoint = 100; //-- マウスの状態 --// struct _MOUSE { int Xstart,Ystart; bool flag; double weight; }; _MOUSE MouseStatus={0,0,false,0.5}; //-- 回転関係 --// struct _ObjectRotate { double xAngle,yAngle; }; _ObjectRotate ObjRot={0,0}; //------------ プロトタイプ宣言 ---------------// void Enables(); void display(); void reshape(int w, int h); void myMouseFunc(int button,int state,int x,int y); void myMouseMotion(int x,int y); void DrawRoof(); void DRAW_FLOOR(); void DrawLightPos(); void CalcShadowVolume(const Point4f &LightPos,const Point4f *RoofCoord, Point4f *EndPoint); void DrawScreen(double fovy,double eyeX,double eyeY,double eyeZ,double znear); //------------- OpenGLの初期設定 ------------------// void GLUT_INIT() { glutInitDisplayMode(GLUT_RGBA| GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(640,480); glutCreateWindow("Stencil Shadow"); } void GLUT_CALL_FUNC() { glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(myMouseFunc); glutMotionFunc(myMouseMotion); } void MY_INIT() { glClearColor(1.0, 1.0, 1.0, 1.0); Enables(); //各種有効化 glClearStencil(0); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); CalcShadowVolume(LightPos,RoofCoord,ShadowEnd); } //各種有効化 void Enables() { float light_pos[] ={LightPos.x,LightPos.y,LightPos.z,LightPos.w}; glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_POSITION, light_pos); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); //法線の正規化 } //----------- ここからメイン関数 -------------// int main(int argc, char **argv) { glutInit(&argc,argv); GLUT_INIT(); GLUT_CALL_FUNC(); MY_INIT(); glutMainLoop(); return 0; } //--------- ここから各種コールバック -------------// void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT| GL_STENCIL_BUFFER_BIT); glLoadIdentity(); gluLookAt(eye[0],eye[1],eye[1], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glRotated(ObjRot.xAngle,1,0,0); glRotated(ObjRot.yAngle,0,1,0); //床を描画 static float floorcolor[] = { 0.8, 0.8, 0.8, 1.0 }; glPushMatrix(); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, floorcolor); DRAW_FLOOR(); glPopMatrix(); //通常の描画 static float red[] = { 0.8, 0.2, 0.2, 1.0 }; glPushMatrix(); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, red); glutSolidTeapot(0.7); glPopMatrix(); glDisable(GL_LIGHTING); glDisable(GL_LIGHT0); //ここからステンシルシャドウの処理 //1パス目の描画(ステンシル値だけ書き込む) glEnable(GL_STENCIL_TEST); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);//色情報を書き込まない glDepthMask(GL_FALSE);//デプス値を書き込まない glStencilFunc(GL_ALWAYS, 0, ~0); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP); glEnable(GL_CULL_FACE); glCullFace(GL_BACK);//カリング glBegin(GL_TRIANGLE_FAN); glVertex3f(LightPos.x,LightPos.y,LightPos.z); for(int loop = 0; loop < 3;++loop) { glVertex3f(ShadowEnd[loop].x,ShadowEnd[loop].y,ShadowEnd[loop].z); } glVertex3f(ShadowEnd[0].x,ShadowEnd[0].y,ShadowEnd[0].z); glEnd(); //////////2パス目の描画 glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP); glCullFace(GL_FRONT); //カリング glBegin(GL_TRIANGLE_FAN); glVertex3f(LightPos.x,LightPos.y,LightPos.z); for(int loop = 0; loop < 3;++loop) { glVertex3f(ShadowEnd[loop].x,ShadowEnd[loop].y,ShadowEnd[loop].z); } glVertex3f(ShadowEnd[0].x,ShadowEnd[0].y,ShadowEnd[0].z); glEnd(); //ここからクリップ平面を描画(十分大きく) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);//色書き込みON glDisable(GL_CULL_FACE);//カリングOFF glStencilFunc(GL_NOTEQUAL, 0, ~0); //ステンシル値が0じゃないの部分が影 glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); ////ステンシルバッファを可視化 glPushMatrix(); glRotated(ObjRot.yAngle,0,-1,0); glRotated(ObjRot.xAngle,-1,0,0); glEnable(GL_BLEND); //ブレンド有効化 glColor4f(0,0,0,0.5); DrawScreen(30,eye[0],eye[1],eye[1],1); //スクリーン描画(fovy,znearをわたす) glColor4f(1,1,1,1); glDisable(GL_BLEND); //ブレンド無効化 glDepthMask(GL_TRUE); glPopMatrix(); glDisable(GL_STENCIL_TEST); //屋根の描画 glColor3f(0,1,0); DrawRoof(); //光源部分を描画 glColor3f(1,0,0); DrawLightPos(); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glutSwapBuffers(); } void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); } //マウス関係 void myMouseFunc(int button,int state,int x,int y) { if(button == GLUT_LEFT_BUTTON &&state == GLUT_DOWN){ MouseStatus.Xstart = x; MouseStatus.Ystart = y; MouseStatus.flag = true; } else { MouseStatus.flag =false; } } void myMouseMotion(int x,int y) { int xdis,ydis; if(MouseStatus.flag == false)return; xdis = x - MouseStatus.Xstart; ydis = y - MouseStatus.Ystart; ObjRot.xAngle += (double)ydis * MouseStatus.weight; ObjRot.yAngle += (double)xdis * MouseStatus.weight; MouseStatus.Xstart = x; MouseStatus.Ystart = y; display(); } //------------ ここから各種関数 ---------------------// void DrawLightPos() { glPointSize(3); glBegin(GL_POINTS); glVertex3f(LightPos.x,LightPos.y,LightPos.z); glEnd(); glPointSize(1); } //屋根部分の描画 void DrawRoof() { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(4, GL_FLOAT, 0, &RoofCoord[0]); //1頂点は3つで構成、float型、オフセット0、データ元 glDrawArrays(GL_TRIANGLES,0,3); glDisableClientState(GL_VERTEX_ARRAY); } void DRAW_FLOOR() { glTranslatef(0,-1,0); glRotatef(90,-1,0,0); glNormal3d(0,0,1); //法線にもアフィン変換がかかるので注意 glRectf(-8,8,8,-8); } //------------ シャドウボリュームの末端を計算する ----------------// void CalcShadowVolume(const Point4f &LightPos,const Point4f *RoofCoord, Point4f *EndPoint) { for(int N = 0; N < 3 ;++N) { EndPoint[N].x = LightPos.x + (RoofCoord[N].x - LightPos.x)*InfinitePoint; EndPoint[N].y = LightPos.y + (RoofCoord[N].y - LightPos.y)*InfinitePoint; EndPoint[N].z = LightPos.z + (RoofCoord[N].z - LightPos.z)*InfinitePoint; } } //スクリーンを描画する void DrawScreen(double fovy,double eyeX,double eyeY,double eyeZ,double znear) { static int viewport[4];//ビューポート取得 glGetIntegerv(GL_VIEWPORT, viewport); static double top = znear * tan(0.0174532925*fovy/2.0); static double left = static_cast<double>(viewport[2])/viewport[3] * top; static double theta = atan2(eyeX,eyeZ)/0.0174532925; static double fai = atan2(eyeY,sqrt(eyeX*eyeX+eyeZ*eyeZ))/0.0174532925; static double length = sqrt(eyeX*eyeX+eyeY*eyeY+eyeZ*eyeZ) - 0.001;//原点からの距離 glPushMatrix(); glRotated(theta,0,1,0); glRotated(fai,-1,0,0); glTranslatef(0,0,length-znear); glRectd(-left,top,left,-top); glPopMatrix(); }