先说下开发环境.VS2013,C++空项目,引用glut,glew.glut包含基本窗口操作,免去我们自己新建win32窗口一些操作.glew使我们能使用最新opengl的API,因winodw本身只包含opengl 1.1版本的API,根本是不能用的.
其中矩阵计算采用gitHub项目openvr中的三份文件, Vectors.h ,Matrices.h, Matrices.cpp,分别是矢量与点类,矩阵类,我们需要的一些操作,矢量的叉乘和点乘,矩阵转置,矩阵的逆,矩阵与矢量相剩等.
这里主要简单介绍这二种阴影实现.Shadow Mapping简单来说,就是以灯光为视角,得到整个场景的深度图(深度图请看下面一段仔细说明).然后在正常视角下,把顶点转化成原灯光视角下的顶点,根据顶点位置找到深度纹理中存放的深度,如果顶点的深度值大于纹理中的深度值(说明在灯光视角中,顶点上有遮挡物,如下图),说明在阴影范围内.
(此图引用博友http://www.cnblogs.com/liangliangh/p/4131103.html中图片)
在这里,有必要讲一下深度图,不然有些位置大家可能理解不了,这个深度图是全屏渲染图,意思就是是场景中的三维物体经过投影成二维,这样就达到一种效果,纹理坐标与三维物体的坐标是有对应关系的,简单来说,三维物体经过投影后,我们经过(xyzw)/w,这样x,y,z 都在(-1,1)之间,再经过乘0.5加0.5后对应(0,1)之间,也就是深度图的纹理坐标,这过程和3D中物体由局部坐标到屏幕坐标的变换(屏幕Y是从上到下,还需要转换,这里先不说)一样.那么深度图一共包含了二样关系,一是纹理坐标st,对应3维中顶点xy.二是深度图本身保存的深度,这个深度是经过深度测试和深度写入(所以这二个GL_DEPTH_TEST, glDepthMask记的打开)的深度,默认的是深度比较算法是画家算法(GL_LESS,不要改),意思是深度度上全是最近的深度.
这样深度图就是一个三维场景,结合摄像机的设置,就可以把这个场景所有像素都重新投影到三维空间中去.
在附件中, Shadow Mapping主要有二种实现,一种是固定管线,一种是可编程管线,我们先看下固定管线的实现流程,再对比可编程管线的实现来理解整个过程.
如下是固定管线中纹理初始化的设置.
glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_2D); glGenTextures(1, &shadowTexture); glBindTexture(GL_TEXTURE_2D, shadowTexture); // 纹理和光照计算结果相乘 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //GL_LUMINANCE 把深度值替换到RGB三个分量上,GL_INTENSITY则替换到RGBA四个分量上.(深度值只有一个) //简单来说,就是定义深度如何保存,如果是GL_ALPHA,则替换到第四个分量上. //glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);// GL_LUMINANCE); // This is to allow usage of shadow2DProj function in the shader //纹理本身存的是深度值,而纹理坐标经过转换后成对应点的坐标. 纹理坐标R点比较纹理本身 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);//GL_NONE GL_COMPARE_R_TO_TEXTURE //比较方法,少于或等于是1,大于是0 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);//GL_GEQUAL,GL_LEQUAL //使用API自动生成的纹理坐标 glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_GEN_Q); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadowWidth, shadowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); //FBO 把桢缓冲区的深度输出到shadowTexture纹理中 glGenFramebuffers(1, &frameBuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowTexture, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0);