Chinaunix首页 | 论坛 | 博客
  • 博客访问: 174362
  • 博文数量: 43
  • 博客积分: 827
  • 博客等级: 准尉
  • 技术积分: 487
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-26 19:19
文章分类

全部博文(43)

文章存档

2015年(1)

2014年(1)

2013年(5)

2012年(36)

我的朋友

分类: WINDOWS

2012-05-06 17:22:22

Irrlicht对于Shadow Volume的实现:

对于Shadow Volume技术:
1:先渲染出整个场景,构造出整个场景的Depth Buffer
2:对于每一个需要产生阴影的Object,使用他的Shadow Volume(注意,该Volume是依赖于Light的位置的,所以一个Shadow Volume对应一个Light)采用Z Pass或者Z Fail技术,构造出Stencl Buffer。在Stencil Buffer中 Non-Zero的地方为阴影,Zero的地方为非阴影,并且注意:阴影的地方一定是Front Cap多于Back Cap。所以在Stencil Buffer阴影区一定是全正或者全负(这取决于Front Cap的Stencil Func)。
3:如果是多个物体,重复2过程。
4:最后得到的Stencil buffer中Non-Zero区域就是阴影区。
5:绘制一个阴影Quad,该Quad覆盖在整个屏幕,且仅在阴影区域绘制。


上面的过程也就是Irrlicht实现的过程。
这种实现方法有一个问题,不能实现多个光源的阴影叠加,也即是所有的阴影都是一样的。

具体实现:
1:指定一个节点要产生阴影

Irrlicht中的阴影节点不是独立的,他必须依附在一个节点上。因此一个节点要产生阴影,就对该节点添加一个阴影节点:

点击(此处)折叠或打开

  1. virtual IShadowVolumeSceneNode* IAnimatedMeshSceneNode::addShadowVolumeSceneNode(const IMesh* shadowMesh=0, s32 id=-1, bool zfailmethod=true, f32 infinity=10000.0f) = 0;
  2. IShadowVolumeSceneNode* CAnimatedMeshSceneNode::addShadowVolumeSceneNode(const IMesh* shadowMesh,
  3.                                                  s32 id, bool zfailmethod, f32 infinity)
  4. {
  5.         if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))
  6.                 return 0;

  7.         if (Shadow)
  8.                 return Shadow;

  9.         if (!shadowMesh)
  10.                 shadowMesh = Mesh; // if null is given, use the mesh of node

  11.         Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id, zfailmethod, infinity);
  12.         return Shadow;
  13. }
将Shadow节点附到原节点上去。

2:实时生成Shadow Volume
注意:实时的生成Shadow Volume不是在OnAnimate和OnRegisterSceneNode中的而是在render中。
在CAnimatedMeshSceneNode::render中
        // If the node will generate shadow, update the shadow
        if (Shadow && PassCount==1)
                Shadow->updateShadowVolumes();
updataShadowVolumes生成了该帧状态的Shadow Volumes。

3:"渲染"Shadow Volume,生成Stencil Buffer。
这一步在Shadow Pass中完成,Shadow Pass在Solid Pass之后,在Transparent Pass之前。

点击(此处)折叠或打开

  1. // render shadows
  2.         {
  3.                 CurrentRendertime = ESNRP_SHADOW;
  4.                 Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);

  5.                 if(LightManager)
  6.                 {
  7.                         LightManager->OnRenderPassPreRender(CurrentRendertime);
  8.                         for (i=0; i<ShadowNodeList.size(); ++i)
  9.                         {
  10.                                 ISceneNode* node = ShadowNodeList[i];
  11.                                 LightManager->OnNodePreRender(node);
  12.                                 // Generate the shadow stencil buffer
  13.                                 node->render();
  14.                                 LightManager->OnNodePostRender(node);
  15.                         }
  16.                 }
  17.                 else
  18.                 {
  19.                         // Generate the stencil buffer one by one
  20.                         for (i=0; i<ShadowNodeList.size(); ++i)
  21.                                 ShadowNodeList[i]->render();
  22.                 }

  23.                 // Draw a quad to simulate the shadow
  24.                 if (!ShadowNodeList.empty())
  25.                         // Draw the shadow
  26.                         Driver->drawStencilShadow(true,ShadowColor, ShadowColor,
  27.                                 ShadowColor, ShadowColor);

  28.                 ShadowNodeList.set_used(0);

  29.                 if(LightManager)
  30.                         LightManager->OnRenderPassPostRender(CurrentRendertime);
  31.         }
因为在Solid Pass之后,整个场景的Depth Buffer已经构建好了,这里就直接用Shadow Volume去生成Stencil Buffer。最后在画一个覆盖整个屏幕的shadow quad,这里只会在shadow区域绘制。
因此Shadow Pass就贴上了Shadow。

Shadow Volume的具体过程
1:生成Shadow Volume
void CShadowVolumeSceneNode::updateShadowVolumes()在原结点的render中调用
这个过程,主要是根据Light的位置,将Front Cap和Front Cap直接投影到无穷远处的Cap而组成得到Shadow Volume。
    注意:
    1:他的做法和标准做法不太一样。他投影的是Front Cap。
    2:对于每一个Light生成一个Shadow Volume

2:生成Stencil Buffer的过程
Irrlicht中只实现了Z Pass的算法,没有实现Z Fail算法。

3:绘制过程
这里直接在Stencil Value > 0 的地方绘制

注意:
Irrlicht在生成Stencil Buffer和用Stencil Buffer绘制Quad是一个连续的过程,Stencil 在2中打开,3中是没有这个动作的。因此在2-3过程有其他的绘制过程绝对不能影响到Stencil Buffer也不能关闭Stencil。Irrlicht在绘制Stencil Buffer的Debug Data的代码有Bug。

如果要实现阴影的叠加效果,就应该单独的的一个光源处理,然后每一个光源叠加起来。
阅读(2016) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~