Ogre 摄象机

Mage小组 著

Email: norman_chen@163.com  

renwind@163.com

QQ: 18725262

http://www.173d8.com

http://blog.csdn.net/pizi0475

 

摄象机

 OGRE中的摄象机支持透视投影(缺省投影方式、近大远小)和正射投影(大小与距离无关,是CAD设计中的常用投影方式)。摄象机还支持线画模式、纹理模式、灰度阴影模式等几种渲染模式。OGRE场景中可以有多台摄象机,可以将摄象机“看到”的结果渲染到多个窗口,甚至还能实现分屏和画中画功能。OGRE中的摄象机可以独立于场景节点树(摄象机本身也具有位置、旋转属性及控制方法),也可以被attach到场景节点上,通过对场景节点的控制来达到对摄象机的控制。

Camera类

 对摄象机的抽象。成员函数说明如下:

 标准构造函数

Camera(String name, SceneManager* sm);

标准析构函数

virtual ~Camera();

返回渲染该摄像机的scenemanager的指针

SceneManager* getSceneManager(void) const;

取得摄像机的名字

virtual const String& getName(void) const;

设定投影模式(正射或透视),缺省为透视

void setProjectionType(ProjectionType pt);

取得使用的投影模式的信息

ProjectionType getProjectionType(void) const;

设定该摄像机需要的渲染细节级别

void setDetailLevel(SceneDetailLevel sd);

取得该摄像机的渲染细节级别

SceneDetailLevel getDetailLevel(void) const;

设定摄像机的位置

void setPosition(Real x, Real y, Real z);

void setPosition(const Vector3& vec);

取得摄像机的位置

const Vector3& getPosition(void) const;

移动摄像机

void move(const Vector3& vec);

void moveRelative(const Vector3& vec);

设定摄像机的方向向量

void setDirection(Real x, Real y, Real z);

void setDirection(const Vector3& vec);

取得摄像机的方向

Vector3 getDirection(void) const;

这是一个辅助方法用来自动计算摄像机的方向向量,在当前位置和所看的点,参数targetPoint是一个向量指明所看的点。

void lookAt( const Vector3& targetPoint );

void lookAt(Real x, Real y, Real z);

将摄像机绕z轴逆时针旋转指定角度

void roll(Real degrees);

绕y轴逆时针旋转指定角度

void yaw(Real degrees);

绕x轴上下逆时针旋转

void pitch(Real degrees);

旋转任意角度

void rotate(const Vector3& axis, Real degrees);

使用四元组绕任意轴旋转

void rotate(const Quaternion& q);

指定摄像机是绕本地y轴还是指定的固定轴旋转

void setFixedYawAxis( bool useFixed, const Vector3& fixedAxis = Vector3::UNIT_Y );

设定y方向的视野域,水平方向的视野域将依此计算

void setFOVy(Real fovy);

取得y方向的视野域

Real getFOVy(void) const;

设定到近裁减面的距离

void setNearClipDistance(Real nearDist);

取得到近裁减面的距离

Real getNearClipDistance(void) const;

设定到远裁减面的距离

void setAspectRatio(Real ratio);

取得当前纵横比

Real getAspectRatio(void) const;

内部使用,取得该摄像机的投影矩阵

const Matrix4& getProjectionMatrix(void);

内部使用,取得该摄像机的观察矩阵

const Matrix4& getViewMatrix(void);

取得平截台体的特定面

const Plane& getFrustumPlane( FrustumPlane plane );

测试给定的包容器是否在平截台体中

bool isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy = 0);

bool isVisible(const Sphere& bound, FrustumPlane* culledBy = 0);

测试给定的顶点是否在平截台体中

bool isVisible(const Vector3& vert, FrustumPlane* culledBy = 0);

返回摄像机的当前方向

const Quaternion& getOrientation(void) const;

设定摄像机的方向

void setOrientation(const Quaternion& q);

输出流功能

friend std::ostream& operator<<(std::ostream& o, Camera& c);

取得摄像机继承的方向,包括从附着节点继承的任何旋转

Quaternion getDerivedOrientation(void);

取得继承的位置,包括从附着节点继承的任何平移

Vector3 getDerivedPosition(void);

取得继承的方向向量

Vector3 getDerivedDirection(void);

覆盖MovableObject的方法

void _notifyCurrentCamera(Camera* cam);

const AxisAlignedBox& getBoundingBox(void) const;

void _updateRenderQueue(RenderQueue* queue);

const String getMovableType(void) const;



使能/使不能自动跟踪scenenode

void setAutoTracking(bool enabled, SceneNode* target = 0, 

            const Vector3& offset = Vector3::ZERO);

 

Camera使用举例一

 打开OGRE提供的Demo_EnvMapping那个例子程序,运行之。对于这个例子我们应该很熟悉了,通过键盘和鼠标可以控制摄象机在场景中漫游,那么摄象机的创建代码在哪里呢?从EnvMapping.h和EnvMapping.cpp中都找不到创建摄象机的代码!不要忘了我们是基于OGRE的应用框架建立的这个例子,在OGRE应用框架的ExampleApplication.h里为我们创建了摄象机,打开ExampleApplication.h文件可以发现如下函数:

virtual void createCamera(void)

    {

        // 创建摄象机

        mCamera = mSceneMgr->createCamera(“PlayerCam”);

        // 将该摄象机放到0,0,500位置上

        mCamera->setPosition(Vector3(0,0,500));

        // 让摄象机“看”向Z轴负方向(从屏幕外向屏幕里)以模拟你的眼睛

        mCamera->lookAt(Vector3(0,0,-300));

        // 设置摄象机平截台体的“近面”距离

mCamera->setNearClipDistance(5);

}

每一个通过OGRE应用框架创建的应用程序都会拥有一个通过ExampleApplication类的createCamera函数创建出来的摄象机,该摄象机站在0,0,500位置上看向场景中心。

摄象机的创建代码有了,那通过鼠标和键盘控制摄象机在场景中漫游的代码在哪里呢?在OGRE应用框架中ExampleFrameListener类的frameStarted函数里。该函数又调用processUnbufferedInput函数,我们可以在processUnbufferedInput函数中发现如下代码:

mInputDevice->capture();

……(省略若干行)

mCamera->yaw(rotX);

mCamera->pitch(rotY);

mCamera->moveRelative(vec);

首先获取鼠标状态,而后根据该状态计算摄象机的旋转和移动量,最后通过Camera的几个控制方法控制其运动。

注意到这里的摄象机是独立于场景节点树之外的。我们已经了解到场景节点树上可以挂接Entity、摄象机和光。通过对场景节点的空间位置控制可以达到改变其下挂接的Entity、摄象机和光的位置的目的。但注意Entity和摄象机不一样。在OGRE引擎的设计中Entity是完全没有移动、旋转等能力的,所以它只能把这些任务交给场景节点来完成,而摄象机具有移动和旋转函数,所以它并不一定要完全靠场景节点来完成这些任务。这就引出一个有趣的话题,摄象机放到场景节点中和不放进去的区别究竟有多大。一般来讲,摄象机如果不放在场景节点中,它就非常自由,程序员可以用程序任意控制它,就象在这个例子中一样。想象一下在CS中,你牺牲后,你依然可以控制你的眼睛(灵魂?摄象机?)在场景中穿墙过屋,并为同伴通风报信,就可以体会到这种自由。而如果把摄象机挂接到场景节点中,那么摄象机就和此节点和同在本节点下的其它Entity绑在一起了,一般在这种情况下就不再直接操作摄象机移动位置,而是和Entity一样交给场景节点来做。墙上来回转动的监视器就是由挂接在同一节点下的Entity(监视器模型)和摄象机组成的。还有场景中的人,他们的身体(Entity)和眼睛(Camera)总是在一起,就因为他们同属于一个场景节点。

 

“人”的组织方法一

 

“人”的组织方法二

以上两图都将Camera放到了节点下,都可以实现身体和眼睛的同步。但第一种方法更好,因为第二种方法眼睛和身体同属于一个场景节点,它们之间无法实现相对位移,那么眼睛就可能会长在人的肚子里(节点的空间中心)。

将摄象机放到场景节点树中的做法使用也很普遍,下一个例子里我们将看到这样的情况。

Camera使用举例二

思路

 实现如下的场景节点树:

 

在该节点树中有一个食人魔、一个机器人和一架飞机。通过FrameListener来控制这三个Player都在自动旋转。通过按TAB键把Camera轮流挂接到三个Player所在的节点上,这样我们就会发现屏幕上会出现不同Player的以各自的视角所看到的世界。

为了便于对Player的控制,程序中使用一个std::map来保存Player列表,该列表中保存每个Player的所属节点名称和节点指针。

部分代码

// myExample.h

// 定义PlayerList

typedef std::map<std::string,SceneNode*> PlayerList;

// 由应用框架中的ExampleFrameListener派生出myFrameListener

class myFrameListener : public ExampleFrameListener

{

protected:

    // 接收myapp传过来的Player列表,以在这里控制其旋转

PlayerList *mPlayerList;

 // 保存当前Player的迭代子

PlayerList::iterator currentPlayer;

public:

    myFrameListener(RenderWindow* win, Camera* cam, PlayerList *pPlayerList)

        : ExampleFrameListener(win, cam)

    {

        mPlayerList = pPlayerList;

  // 缺省Player是列表中的第一人

  currentPlayer = mPlayerList->begin();

  // 将摄象机挂接到该Player所在的场景节点

currentPlayer->second->attachCamera(mCamera);

    }

    bool frameStarted(const FrameEvent& evt)

    {

  // 对TAB键的反应

  if (mInputDevice->isKeyDown(KC_TAB))

  {

   // 把摄象机从当前Player上卸下来

   currentPlayer->second->detachObject(mCamera->getName());

   // 切换当前Player

currentPlayer++;

   if(currentPlayer == mPlayerList->end())

    currentPlayer = mPlayerList->begin();

   // 再把摄象机挂接到当前Player上来。   

   currentPlayer->second->attachObject(mCamera);

  }

  

  // 让不同Player以不同的速度旋转  

  mPlayerList->find(“Robot”)->second->yaw(evt.timeSinceLastFrame * 30);

  mPlayerList->find(“Head”)->second->yaw(evt.timeSinceLastFrame * -60);

  mPlayerList->find(“Razor”)->second->yaw(evt.timeSinceLastFrame * 120);

  

  // 调用基类的frameStarted函数

        return ExampleFrameListener::frameStarted(evt);

    

    }

};

// 由应用框架的ExampleApplication派生出myApp

class myApp :public ExampleApplication

{

public:

 myApp(){}

protected:

 // Player列表

 PlayerList mPlayerList;

 // 创建场景

 void createScene(void)

 {

  SceneNode *pNodeRobot,*pNodeHead,*pNodeRazor;

  // 设置环境光

        mSceneMgr->setAmbientLight(ColourValue(1, 1, 1));

  // 创建天空盒

        mSceneMgr->setSkyBox(true, “Examples/SpaceSkyBox”, 50 );

  

  // 以下代码创建场景树

  // Create Robot Entity and attach it to a SceneNode 

  pNodeRobot = mSceneMgr->getRootSceneNode()->createChild(“Robot”);

  Entity *pEntityRobot = mSceneMgr->createEntity(“Robot”, “Robot.mesh”);

        pNodeRobot->attachObject(pEntityRobot);

  mPlayerList.insert(PlayerList::value_type(pNodeRobot->getName(),pNodeRobot));

  

  // Create OGREHead Entity and attach it to a SceneNode

  pNodeHead = mSceneMgr->getRootSceneNode()->createChild(“Head”);

  pNodeHead->translate(200,0,0);

  Entity *pEntityHead = mSceneMgr->createEntity(“Head”, “ogrehead.mesh”);

  pNodeHead->attachObject(pEntityHead);

  mPlayerList.insert(PlayerList::value_type(pNodeHead->getName(),pNodeHead));

  

  // Create OGREHead Entity and attach it to a SceneNode

  pNodeRazor = mSceneMgr->getRootSceneNode()->createChild(“Razor”);

        pNodeRazor->translate(-200,0,0);

  // Create head1 entity and attach it to pNodeHead1

  Entity *pEntityRazor = mSceneMgr->createEntity(“Razor”, “Razor.mesh”);

  pNodeRazor->attachObject(pEntityRazor);

  mPlayerList.insert(PlayerList::value_type(pNodeRazor->getName(),pNodeRazor));

  

 }

 //创建myFrameListener

    void createFrameListener(void)

    {

        mFrameListener= new myFrameListener(mWindow, mCamera, &mPlayerList);

        mRoot->addFrameListener(mFrameListener);

    }

 // 重新实现基类的createCamera函数,关键是让摄象机与其所在场景节点的相对位置为0,100,0。即高100个长度单位,防止摄象机在Entity的肚子里出现。

 virtual void createCamera(void)

    {

        // Create the camera

        mCamera = mSceneMgr->createCamera(“PlayerCam”);

        // 设置摄象机位置

        //mCamera->setPosition(Vector3(0,0,500));

  mCamera->setPosition(Vector3(0,100,0));

        // Look back along -Z

        mCamera->lookAt(Vector3(0,0,-300));

        mCamera->setNearClipDistance(5);

    }

};

 为了让例子简单一点,这里采用的是前面讲的第二种眼睛与身体的组合方法,摄象机与Entity的相对位置是靠摄象机的setPosition函数完成的,这样做并不是一个很好的方法。建议大家将本例改为前面讲的第一种眼睛与身体的组合方法,将摄象机与Entity的相对位置关系交给场景节点去做,那样摄象机的位置就可以设置为0,0,0。

 事情还没有结束,因为摄象机是属于Player的了,我们就不能让键盘再控制摄象机将他移出身体以外,所以需要更改ExampleFrameListener.h中frameStarted函数的代码,因为ExampleFrameListener.h是OGRE应用框架的一部分,所以请注意copy该文件,再更改。

 找到frameStarted函数中的如下代码:

     mCamera->yaw(rotX);

        mCamera->pitch(rotY);

        mCamera->moveRelative(vec);

 将最后一行注释掉,即可以让摄象机可以受鼠标控制旋转(东张西望?),但不能移动。

转载自:https://blog.csdn.net/onejavaer/article/details/6704729

You may also like...