OGRE 投影贴图

 

投影贴图

声明:本系列手札是对中级教程的摘要,本节已经是中级教程的第六节了,详细的原材料,请到OGRE3D开放资源地带下载

OGRE手札-27 <wbr>投影贴图

开始前请将这两幅(右边的也是一幅图啊)图放到media/materials/textures目录下。

照例的框架程序

#include "ExampleApplication.h"
   
    // A FrameListener that gets passed our projector node and decal frustum so they can be animated
    class ProjectiveDecalListener : public ExampleFrameListener
    {
    public:
            ProjectiveDecalListener(RenderWindow* win, Camera* cam, SceneNode *proj, Frustum *decal)
                    : ExampleFrameListener(win, cam), mProjectorNode(proj), mDecalFrustum(decal), mAnim(0)
            {
            }
   
            bool frameStarted(const FrameEvent& evt)
            {
                    return ExampleFrameListener::frameStarted(evt);
            }
   
    protected:
            SceneNode *mProjectorNode;
            Frustum *mDecalFrustum;
            float mAnim;
    };
   
    class ProjectiveDecalApplicati on : public ExampleApplication
    {
    protected:
            SceneNode *mProjectorNode;
            Frustum *mDecalFrustum;
            Frustum *mFilterFrustum;
   
            void createScene()
            {
                    // Set ambient light
                    mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.2));
   
                    // Create a light
                    Light* l = mSceneMgr->createLight("MainLight");
                    l->setPosition(20,80,50);
   
                    // Position the camera
                    mCamera->setPosition(60, 200, 70);
                    mCamera->lookAt(0,0,0);
   
                    // Make 6 ogre heads (named head0, head1, etc.) arranged in a circle
                    Entity *ent;
                    for (int i = 0; i < 6; i++)
                    {
                            SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
                            ent = mSceneMgr->createEntity("head" + StringConverter::toString(i), "ogrehead.mesh");
                            headNode->attachObject(ent);
                            Radian angle(i * Math::TWO_PI / 6);
                            headNode->setPosition(75 * Math::Cos(angle), 0, 75 * Math::Sin(angle));
                    }
            }
   
            // The function to create our decal projector
            void createProjector()
            {
            }
   
            // A function to take an existing material and make it receive the projected decal
            void makeMaterialReceiveDecal (const String &matName)
            {
            }
   
            // Create new frame listener
            void createFrameListener(void)
            {
                    mFrameListener= new ProjectiveDecalListener(mWindow, mCamera, mProjectorNode, mDecalFrustum);
                    mRoot->addFrameListener(mFrameListener);
            }
    };
   
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    #define WIN32_LEAN_AND_MEAN
    #include "windows.h"
    #endif
   
    #ifdef __cplusplus
    extern "C" {
    #endif
   
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
    #else
            int main(int argc, char **argv)
    #endif
            {
                    // Create application object
                    ProjectiveDecalApplicati on app;
   
                    try {
                            app.go();
                    } catch(Exception& e) {
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
                            MessageBoxA(NULL, e.getFullDescription().c_str(),
                                    "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
    #else
                            std::cerr << "An exception has occurred: " << e.getFullDescription();
    #endif
                    }
   
                    return 0;
            }
   
    #ifdef __cplusplus
    }
    #endif

 

运行这个程序,你应该可以看见六个Ogre头颅。

 

下面开始添加代码:

 

一、投影贴图

平截头体(Frustums)

    一个平截头体(Frustums)表示一个头部被截取的棱椎,代表了一个可视区域或是一个投影。Ogre使用它来表示一个camera(Camera类直接继承了Frustum类)。在这一课里,我们将使用一个平截头体来把贴花投影到场景中的网格(mesh)上。

创建一个投影器(projector),即创建代表它的平截头体,并绑定到场景节点上。

createProjector函数中添加代码:

      mDecalFrustum = new Frustum();
      mProjectorNode = mSceneMgr->getRootSceneNode()->createChildSceneNode               

              ("DecalProjectorNode");
      mProjectorNode->attachObject(mDecalFrustum);
      mProjectorNode->setPosition(0,5,0);

这样一个投影器就创建好了,当你离得越远时,这个贴图会变得更大.

如果你想不论多远它总是保持恒定的大小和形状,请添加如下代码:

 // 这里无需添加,这两句通过设置正射投影的视线范围、横纵比、近截取距离,决定了这个帖图
 // 的大小和形状,使得无论投影器有多远,它总是保持恒定。
 mDecalFrustum->setProjectionType(PT_ORTHOGRAPHIC); mDecalFrustum->setNearClipDistance(25);
请先搞清楚我们的平截头体将要把贴图投影到的地点。在这个程序中,有一圈Ogre头颅,而这个平截头体处于它们的正中心(虽然微微向上抬起了5个单位),并指向-Z方向(由于我们没有改变朝向,是一个默认值)。这就意味着,最终我们运行程序时,贴图将投向后面的Ogre人头。 
 

二、 修改材质

    为了让贴图最终显示在物体上,它使用的材质必须能够接收贴图。为此我们创建一个新的通路,在常规纹理上面渲染贴图。这个平截头体决定了这个投影贴图的位置、大小和形状。在这个Demo里面,我们将直接修改这个材质来接收贴图,但在实际的应用中,你应该创建这个材质的一个拷贝,再修改它,这样你就可以将材质切换回原来的。

makeMaterialReceiveDecal中添加代码:

     //获得这个材质

     MaterialPtr mat = (MaterialPtr)MaterialManager::getSingleton().getByName(matName);

     Pass *pass = mat->getTechnique(0)->createPass();//为它创建一个新的通路

//设置混合和光照

新添加的纹理必须与当前的纹理正确地混合。为此我们将把场景混合(scene blending)设置成alpha透明

把深度偏移(depth bias)设置成1(也就是贴图不透明)。

最后我们把材质的光照关闭,这样不论场景使用何种光照,它总是显示的。如果你想在程序里让这个贴图受光照的影响,你就不必加上最后的函数调用:

 

          pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
          pass->setDepthBias(1);
          pass->setLightingEnabled(false);

 

         //用我们的decal.png图像来创建一个新的纹理单元状态

          TextureUnitState *texState = pass->createTextureUnitState("decal.png");

          texState->setProjectiveTexturing(true, mDecalFrustum);//打开了投影纹理
          texState->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);//设置过滤和寻址模式

          //把纹理的寻址模式设置成clamp,这样贴图就不会在物体上不停地循环了
          texState->setTextureFiltering(FO_POINT, FO_LINEAR, FO_NONE);
          //当对象放大时,使用标准线性滤波,但对于缩小的情况,我们只是关闭过滤
          //并且完全关闭mip贴图。这样避免了当缩小时,贴图的边缘不能很好地融入纹理的其它部分。

三、调用函数

我们已经建立好了函数,为了设置投影器和材质还必须调用它们

 createScene最后面添加代码:

          createProjector();
          for (unsigned int i = 0; i < ent->getNumSubEntities(); i++)
                  makeMaterialReceiveDecal (ent->getSubEntity(i)->getMaterialName());

注意,在前面的循环里,ent变量已经保存有了Ogre头颅。由于所有的Ogre头颅使用相同的材质,我们只需要随机选择他们中的一个来获取材质名称。

 

 四、消除反向投影

为了过滤掉反向投影,我们需要一个新的平截头体,让它指向我们想要过滤的方向。

在createProjector方法中添加以下代码:

        mFilterFrustum = new Frustum();
        mFilterFrustum->setProjectionType(PT_ORTHOGRAPHIC);
        SceneNode *filterNode = mProjectorNode->createChildSceneNode("DecalFilterNode");
        filterNode->attachObject(mFilterFrustum);
        filterNode->setOrientation(Quaternion(Degree(90),Vector3::UNIT_Y));

唯一的区别就是我们把这个节点旋转了90度以朝向背面

修改材质

下面我们添加另一个纹理状态到材质的通路上。

makeMaterialReceiveDecal中添加代码:

         texState = pass->createTextureUnitState("decal_filter.png");
         texState->setProjectiveTexturing(true, mFilterFrustum);
         texState->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
         texState->setTextureFiltering(TFO_NONE);

注意,我们正在使用过滤纹理,过滤平截头体,并关闭了过滤。编译并运行程序。你应该可以看到只有朝向前面的投影贴图。

 

简单的旋转

旋转这个投影并更新它的视线范围(Field of View)。

为了旋转这个投影器,只需要把下面几行代码添加到frameStarted方法里:

 mProjectorNode->rotate(Vector3::UNIT_Y, Degree(evt.timeSinceLastFrame * 10));

编译并运行程序。你将会看到贴图沿着一个圈被投影到Ogre人头。

修改视线范围

接下来,我们将修改这个投影器的视线范围(field of view)。由于我们不使用正射投影器,我们可以修改视力范围来增加或缩小投影的大小。作为演示,我们将把FOVy(field of view Y)设置成15到25度的夹角。下面的代码将增加或缩小贴图的大小(添加到frameStarted方法):

 mAnim += evt.timeSinceLastFrame / 2;
 if (mAnim >= 1)
 mAnim -= 1;
 mDecalFrustum->setFOVy(Degree(15 + Math::Sin(mAnim * Math::TWO_PI) * 10));

编译并运行程序。

最后要注意的

最后要注意到是,如果你在程序里使用贴图,必须保证贴图的边缘像素都是完全透明的(alpha为0)。否则,出于纹理clamping的工作机制,贴图会拖泥带水。

 

 

这些东西什么时候会用到呢》?程序添加的代码十分简单,就不提供终级代码了,不过贴一张运行的贴图吧

这张是由于没有将那两张图片放到texture下的缘故

E,有点过于艺术了!

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值