纹理阴影实现笔记

来源:互联网 发布:数据分析与商务智能 编辑:程序博客网 时间:2024/04/28 19:50
纹理阴影实现的三种方法


ogre包含两种阴影实现技术,阴影体与阴影纹理.
阴影体需要主cpu计算出物体的轮廓,同时生成更多的多边形,会降低帧率
并且在骨骼蒙皮时,会存在问题(如果采用硬蒙皮,无法计算轮廓,软蒙皮则降低帧率)

纹理阴影只需要将几何体按不同的视角绘制(往显卡数据传输量增加),对帧率影响较小.


纹理阴影实现的原理.


假设场影中有一个点光源,首先以点光源的位置为视点(Vl),将场景绘制一遍,
得到的绘制结果保存在一个纹理之中,我们记做shadowmap
(可以以拷贝的形体得到此纹理glCopyTexSubImage2D,
或者将渲染输出绑定到纹理glBindFramebufferEXT)


此时将得到一个深度缓冲,记录以光源为视点,观察得到的场影投影中,每个像素的深度值


然后切换到观察者的视点(Ve),绘制阴影.
此时需要做处理,在绘制每个像素时,需要将它们转换至光源为视点时(Ve)对应的纹理位置,
并得出相应的深度,如果深度小到shadowmap中的值,表明不处在该光源的阴影中,反之,则处在
阴影之中
(通过替换变换矩阵来实现相应的纹理坐标转换,以及深度转换,但每个顶点的变换矩阵仍然
是当前观察点的,呵呵,拗口吧.)
实际就是顶点的位置由当前的观察视点变换矩阵来计算,但纹理映射与深度则由光源的变换矩阵来计算


实现的方式,可以通过纹理矩阵,投影纹理,或者以顶点着色器,像素着色器来实现.
相应的由着色器的方式来实现,代码比较好理解.(ogre里提供的相应的glsl以及hlsl脚本代码)


这里说明一下以投影纹理 或者纹理矩阵来实现(原理一样,但代码不是很好理解)






用投影纹理来实现
首先是将以光源为视点渲染得到的场影缓存拷贝至纹理(glCopyTexSubImage2D)


这样就得到了以光源为视点的深度缓存


然后绘制阴影
要提供纹理的变换矩阵


MATRIX4X4 textureMatrix=biasMatrix*lightProjectionMatrix*lightViewMatrix;
lightProjectionMatrix 表示光源为视点的投影矩阵
lightViewMatrix 表示光源为视点的变换矩阵


然后是纹理投影坐标的计算方法
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_S, GL_EYE_PLANE, textureMatrix.GetRow(0));
glEnable(GL_TEXTURE_GEN_S);


glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_T, GL_EYE_PLANE, textureMatrix.GetRow(1));
glEnable(GL_TEXTURE_GEN_T);


glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_R, GL_EYE_PLANE, textureMatrix.GetRow(2));
glEnable(GL_TEXTURE_GEN_R);


glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_Q, GL_EYE_PLANE, textureMatrix.GetRow(3));
glEnable(GL_TEXTURE_GEN_Q);


其中 s与t分别对应纹理映射, r就表示该点的深度
然后就有以下代码,这时需要对纹理深度比较,判断哪些是阴影,哪些不是
//Enable shadow comparison
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);


//Shadow comparison should be true (ie not in shadow) if r<=texture
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);


//Shadow comparison should generate an INTENSITY result
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);


然后,没有然后了,
这是投影阴影纹理的网上能找到的流行实现方式,完整,相对好理解
========================== 我是分割线 =============================


用纹理矩阵来实现
ogre的纹理阴影又是如何做的呢?
笔者反复读了代码,判断出ogre可能是采用纹理矩阵来实现的(ogre代码太绕...)
在ogre的render大循环中
step1: prepareShadowTextures此函数负责绘制以光源为视点的场影,并将绘制结果输出至缓存
(GLFrameBufferObject::bind-->glBindFramebufferEXT)
step2: 然后调用renderTextureShadowCasterQueueGroupObjects,将所有的物体画一遍
step3: 然后会调用这个函数renderModulativeTextureShadowedQueueGroupObjects画阴影
(我们先关注调制模式,原理其实一样)


七转八绕我们会关注到Pass这个类


Pass包含了光源的位置信息(也就是可以得到光源的投影矩阵与变换矩阵)
Pass包含了shadowmap,它是由prepareShadowTextures阶段绘制得到的.
然后我们可以看到SceneManager::_setPass这个函数,
(在step3 renderModulativeTextureShadowedQueueGroupObjects 函数中,会调用到)


SceneManager::_setPass-->RenderSystem::_setTextureUnitSettings--->GLRenderSystem::_setTextureCoordCalculation
--->会有这样一个switch case分支
        case TEXCALC_PROJECTIVE_TEXTURE:
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGenfv(GL_S, GL_EYE_PLANE, eyePlaneS);
            glTexGenfv(GL_T, GL_EYE_PLANE, eyePlaneT);
            glTexGenfv(GL_R, GL_EYE_PLANE, eyePlaneR);
            glTexGenfv(GL_Q, GL_EYE_PLANE, eyePlaneQ);
            glEnable(GL_TEXTURE_GEN_S);
            glEnable(GL_TEXTURE_GEN_T);
            glEnable(GL_TEXTURE_GEN_R);
            glEnable(GL_TEXTURE_GEN_Q);


            mUseAutoTextureMatrix = true;


            // Set scale and translation matrix for projective textures
            projectionBias = Matrix4::CLIPSPACE2DTOIMAGESPACE;


            projectionBias = projectionBias * frustum->getProjectionMatrix();
            projectionBias = projectionBias * frustum->getViewMatrix();
            projectionBias = projectionBias * mWorldMatrix;


            makeGLMatrix(mAutoTextureMatrix, projectionBias);
            break;
我们会发现eyePlaneS eyePlaneT eyePlaneR eyePlaneQ其实没有做任何处理,
frustum保存的光源的信息,即可以得到光源的投影与变换矩阵
真正的信息存在mAutoTextureMatrix这位大佬里面
之后会调用GLRenderSystem::_setTextureMatrix设置纹理矩阵
这样就实现替换成光源的投影与变换矩阵.
比较时,只要开启深度测试即可.
(这是笔者存疑的一个地方,但在ogre中找不到glTexParameteri相应的调用参数,只能是这么认为了)


========================== 我是分割线 =============================

用着色器脚本来实现
ogre提供了相应的脚本
media\materials\programs目录下面
DepthShadowmap.hlsl Dx的脚本
DepthShadowmapxxxx.glsl opengl的脚本


脚本分为阴影产生脚本,阴影接受脚本.
阴影产生caster就是生成shadowmap的脚本代码
阴影接受receiver就是画阴影的代码
castere脚本做的事情,是把深度值保存下来
receiver则做矩阵变换,查找对应shadowmap的深度值,来判断是否处理阴影当中
代码相对与投影纹理,纹理矩阵实现方式要容易理解。不详述了。
0 0