看了簡單shadow volume 主要參考:http://www.zwqxin.com/archives/opengl/shadow-volume-1.html 和http://bbs.pfan.cn/post-252901.html
實現:一個方形 的陰影體產生
效果:,藍色爲產生陰影體的正方形, 黃色代表點光源(其實沒開光源),紅色爲陰影。
主要運用:簡單的z_pass。 沒有進行邊緣檢測等,直接用正方形四個點產生陰影體。
疑問和缺點:沒有用vector<> 直接用的數組,但是用的很不好,感覺很麻煩。
剛用vbo,不熟悉,在繪製黑色屏幕時, 是不是每一次都要指定相應的vertex buffer ??但是前面指定過了,難道沒用?
兩個缺點:第一個視點在陰影體內的話可以用z_fail解決,
從圖中看出,其實球體的底部也被陰影繪製了,這個怎麼解決,恐怕通過volume沒法解決????還是進行麻煩的檢測是不是第一個接收陰影照射的面???
zwq:這是因爲我們生成陰影錐體的時候用的是模型的本地座標來進行計算;但是實際上你一般會經過座標系變換,把物體“移動”到一個位置,譬如右移3單位,那麼你的陰影錐計算就會出錯。所以一般兩個選擇:把物體的所有頂點x座標都+3,再計算陰影錐;把光源位置x座標-3(轉換到模型座標系),計算陰影錐。哪一種計算量少點很明顯。
附完整代碼:
#include "GLee.h"
#include <gl/glut.h>
#include <stdlib.h>
#include <gl/GL.h>
static GLfloat LightPos[] = { 0.0f, 3.0f, 0.0f, 1.0f}; // Light Position
static GLfloat LightAmb[] = {0.7f, 0.7f, 0.7f, 1.0f}; // 環境光
static GLfloat LightDif[] = {1.0f, 1.0f, 1.0f, 1.0f}; // 漫射光
//float LightSpc[] = {1.0f, 1.0f, 1.0f, .0f}; // Specular Light Values
const float light_r= 0.3;//假設正方形爲光源
void init(void )
{
glClearColor(0.0,0.0,0.0,0.0);
//glLightfv(GL_LIGHT0, GL_POSITION, LightPos); // Set Light1 Position
//glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmb); // Set Light1 Ambience
//glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDif); // Set Light1 Diffuse
//glLightfv(GL_LIGHT1, GL_SPECULAR, LightSpc); // Set Light1 Specular
//glEnable(GL_LIGHT0); // Enable Light1
//glEnable(GL_LIGHTING);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
}
void reshap(int width,int height )
{
glViewport(0,0,(GLsizei)width,(GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45,(GLfloat)width/(GLfloat)height,0.1,200);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0,1.0,15.0,0.0,0.0,-10.0,0.0,1.0,0.0);
//glLoadIdentity();
}
void shadow_volume(GLfloat vetex_dest[4][3],GLfloat light_position[3])
{
GLfloat volume_vertex[8][3]=
{
vetex_dest[0][0],vetex_dest[0][1],vetex_dest[0][2], //0
vetex_dest[1][0],vetex_dest[1][1],vetex_dest[1][2], //1
vetex_dest[2][0],vetex_dest[2][1],vetex_dest[2][2], //2
vetex_dest[3][0],vetex_dest[3][1],vetex_dest[3][2], //3
(vetex_dest[0][0]-light_position[0])*100+vetex_dest[0][0], //4
(vetex_dest[0][1]-light_position[1])*100+vetex_dest[0][1],
(vetex_dest[0][2]-light_position[2])*100+vetex_dest[0][2],
(vetex_dest[1][0]-light_position[0])*100+vetex_dest[1][0],//5
(vetex_dest[1][1]-light_position[1])*100+vetex_dest[1][1],
(vetex_dest[1][2]-light_position[2])*100+vetex_dest[1][2],
(vetex_dest[2][0]-light_position[0])*100+vetex_dest[2][0],//6
(vetex_dest[2][1]-light_position[1])*100+vetex_dest[2][1],
(vetex_dest[2][2]-light_position[2])*100+vetex_dest[2][2],
(vetex_dest[3][0]-light_position[0])*100+vetex_dest[3][0],//7
(vetex_dest[3][1]-light_position[1])*100+vetex_dest[3][1],
(vetex_dest[3][2]-light_position[2])*100+vetex_dest[3][2],
};
GLint volume_index[4][4]= //must保證逆時針順序
{
0,3,7,4, //front
0,4,5,1, //right
1,5,6,2,
2,6,7,3,
};
GLuint vol_v_buffer=0,
vol_i_buffer=0;
glGenBuffers(1,&vol_v_buffer);
glGenBuffers(1,&vol_i_buffer);
glBindBuffer(GL_ARRAY_BUFFER,vol_v_buffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(volume_vertex),volume_vertex,GL_STATIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,vol_i_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(volume_index),volume_index,GL_STATIC_DRAW);
glInterleavedArrays(GL_V3F,0,0);
glDrawElements(GL_QUADS,24,GL_UNSIGNED_INT,NULL);
}
void keyboard(unsigned char key,int x,int y);
void display(void )
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
//open light
GLfloat vertex_list[][6]=
{
{ 0.8, 0.8, 0.8, 4.0, -4.0, -4.0}, //四邊形裏端
{0.8, 0.8, 0.8, 4.0, 4.0, -4.0},
{0.8, 0.8, 0.8, -4.0, 4.0, -4.0},
{0.8, 0.8, 0.8, -4.0, -4.0, -4.0},
{0.8, 0.8, 0.8, 4.0, -4.0, 4.0}, //底端
{0.8, 0.8, 0.8, 4.0, -4.0, -4.0},
{0.8, 0.8, 0.8, -4.0, -4.0, -4.0},
{0.8, 0.8, 0.8, -4.0, -4.0, 4.0},
{0.8, 0.8, 0.8, -4.0, -4.0, 4.0}, //左側
{0.8, 0.8, 0.8, -4.0, -4.0, -4.0},
{0.8, 0.8, 0.8, -4.0, 4.0, -4.0},
{0.8, 0.8, 0.8, -4.0, 4.0, 4.0},
0.0 , 0.0, 0.8, 2.0, -2.0, 2.0, //陰影產生者 12 13 14 15
0.0 , 0.0, 0.8, 2.0, -2.0, -2.0,
0.0 , 0.0, 0.8, -2.0, -2.0, -2.0,
0.0 , 0.0, 0.8, -2.0, -2.0, 2.0,
0.8, 0.8, 0.0, LightPos[0]+light_r, LightPos[1]-light_r, LightPos[2]-light_r, //假定爲光源
0.8, 0.8, 0.0, LightPos[0]+light_r, LightPos[1]+light_r, LightPos[2]-light_r,
0.8, 0.8, 0.0, LightPos[0]-light_r, LightPos[1]+light_r, LightPos[2]-light_r,
0.8, 0.8, 0.0, LightPos[0]-light_r, LightPos[1]-light_r, LightPos[2]-light_r,
0.8,0.0,0.0, 20,-20,10, //直接在投影矩陣裏繪製大型quad,沒有轉換到二維視圖了
0.8,0.0,0.0, -20,-20,10,
0.8,0.0,0.0, -20,20,10,
0.8,0.0,0.0, 20,20,10,
};
static const GLint index_list[][4]=
{
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19,
20, 21, 22, 23,
};
static const GLint shadow_buffer_index_list[][4]=
{
20, 21, 22, 23,
};
GLuint vbuffer=0,
ibuffer=0,
shadow_buffer=0;//塗黑用的buffer
//GLeeInit();
glGenBuffers(1,&vbuffer);
glGenBuffers(1,&ibuffer);
glGenBuffers(1,&shadow_buffer);
glBindBuffer(GL_ARRAY_BUFFER,vbuffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_list),vertex_list,GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ibuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(index_list),index_list,GL_STATIC_DRAW);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glInterleavedArrays(GL_C3F_V3F,0,0);
glDrawElements(GL_QUADS,20,GL_UNSIGNED_INT,NULL);
glPushMatrix();
glTranslatef(0.0,-3.5,3.0);
glutSolidSphere(1,20,20);
glPopMatrix();
/////////test//檢測陰影體
//GLfloat temp_vertex[4][3]=
//{
// vertex_list[12][3], vertex_list[12][4],vertex_list[12][5],
// vertex_list[13][3], vertex_list[13][4],vertex_list[13][5],
// vertex_list[14][3], vertex_list[14][4],vertex_list[14][5],
// vertex_list[15][3], vertex_list[15][4],vertex_list[15][5],
//};
//glColor4f(1,1,0,1);
//shadow_volume(temp_vertex,LightPos);
///////////test//////////////////////////////////////
glPushAttrib(GL_ALL_ATTRIB_BITS);
glEnable(GL_CULL_FACE);
glDisable(GL_LIGHTING);
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
glDepthMask(GL_FALSE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glClearStencil(0);
glDepthFunc(GL_LESS);
glStencilFunc(GL_ALWAYS,1,1);
glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
glFrontFace(GL_CCW);
GLfloat temp_vertex[4][3]=
{
vertex_list[12][3], vertex_list[12][4],vertex_list[12][5],
vertex_list[13][3], vertex_list[13][4],vertex_list[13][5],
vertex_list[14][3], vertex_list[14][4],vertex_list[14][5],
vertex_list[15][3], vertex_list[15][4],vertex_list[15][5],
};
shadow_volume(temp_vertex,LightPos);
glStencilOp(GL_KEEP,GL_KEEP,GL_DECR);
glFrontFace(GL_CW);
shadow_volume(temp_vertex,LightPos);
//draw shadow
glStencilFunc(GL_NOTEQUAL,0,1);
glStencilFunc(GL_KEEP,GL_KEEP,GL_KEEP);
glDisable(GL_CULL_FACE);
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC0_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
//是不是每一次都要指定相應的vertex buffer ??但是前面指定過了,難道沒用?
glBindBuffer(GL_ARRAY_BUFFER,vbuffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_list),vertex_list,GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ibuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,24*sizeof(GLint),index_list+5,GL_STATIC_DRAW);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glInterleavedArrays(GL_C3F_V3F,0,0);
glDrawElements(GL_QUADS,4,GL_UNSIGNED_INT,NULL);
//glColor4f(0.8,0.0,0.0,0.9);
//glBegin(GL_QUADS);
//glVertex3f(20,-20,10);
//glVertex3f(-20,-20,10);
//glVertex3f(-20,20,10);
//glVertex3f(20,20,10);
//glEnd();
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glDisable(GL_STENCIL_TEST);
glPopAttrib();
glutSwapBuffers();
}
void keyboard(unsigned char key,int x,int y)
{
switch(key)
{
case 'w':
LightPos[2]=LightPos[2]-0.4;
glutPostRedisplay();
break;
case 's':
LightPos[2]=LightPos[2]+0.4;
glutPostRedisplay();
break;
case 'd':
LightPos[0]=LightPos[0]+0.4;
glutPostRedisplay();
break;
case 'a':
LightPos[0]=LightPos[0]-0.4;
glutPostRedisplay();
break;
case 27:
exit(0);
break;
default:
break;
}
}
int main(int argc,char** argv )
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGBA| GLUT_DOUBLE |GLUT_DEPTH |GLUT_STENCIL);
glutInitWindowSize(800,600);
glutInitWindowPosition(100,100);
glutCreateWindow("Shadow Volume");
init();
glutDisplayFunc(&display);
glutReshapeFunc(reshap);
glutKeyboardFunc(&keyboard);
//glutIdleFunc(keyboard);
glutMainLoop();
return 0;
}