先说下开发环境.VS2013,C++空项目,引用glut,glew.glut包含基本窗口操作,免去我们自己新建win32窗口一些操作.glew使我们能使用最新opengl的API,因winodw本身只包含opengl 1.1版本的API,根本是不能用的.

  其中矩阵计算采用gitHub项目openvr中的三份文件, Vectors.h ,Matrices.h, Matrices.cpp,分别是矢量与点类,矩阵类,我们需要的一些操作,矢量的叉乘和点乘,矩阵转置,矩阵的逆,矩阵与矢量相剩等.

  这里主要简单介绍这二种阴影实现.Shadow Mapping简单来说,就是以灯光为视角,得到整个场景的深度图(深度图请看下面一段仔细说明).然后在正常视角下,把顶点转化成原灯光视角下的顶点,根据顶点位置找到深度纹理中存放的深度,如果顶点的深度值大于纹理中的深度值(说明在灯光视角中,顶点上有遮挡物,如下图),说明在阴影范围内.

OpenGL 阴影之Shadow Mapping和Shadow Volumes

  (此图引用博友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);
阴影纹理初始化

相关文章: