看了简单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;
}