初探Ogre 引擎!---- (3)OGRE初级教程

上一个教程运行完以后,可以看到一个OGRE的窗体,但是其中的代码到底是什么意思,这一章我们来详细讲解。

 

OGRE是怎样工作的

这是一个很广的题目,我们将从场景管理器开始然后进一步了解场景节点和实体。这三个类是所有OGRE程序的基石。

 

 

场景管理器基础

在屏幕上显示的所有东西都是由场景管理器来管理。当您在场景中添加物体时,场景管理器会记录这些物体的位置。当您添加摄像机来观看某个场景时,场景管理器会记录摄像机的位置。当您添加平面、广告牌、灯光时,场景管理器同样会管理他们。 OGRE里有很多种场景管理器。有的场景管理器渲染地面,有的场景管理器渲染BSP表等等。在后面,我们将进一步了解场景管理器。

 

 

实体基础

一个实体是可以在场景中渲染的物体之一。您可以把实体理解为任何一个3D模型。一个机器人可以是一个实体,一条鱼可以是一个实体,大地草原可以是一个非常大的实体。灯光,摄像机,粒子,广告牌等不能成为实体。
但在 Ogre 中你不能够直接将一个实体放入到场景中,而是将实体与场景节点绑在一起,这个场景节点则包括了实体的方位信息。

 

 

场景节点基础

场景节点将持续跟踪与它绑在一起的实体的方位。当你创建了一个实体时,它直到与一个场景节点绑定后才会被渲染。同样,一个场景节点也不能单独的在屏幕上显示出来,只有与一个实体绑定后才能在屏幕上显示。
场景节点可以绑定多个实体。例如在屏幕上有在行走的一个人物对象,并且希望这个对象产生发光效果,要实现这些,首先你需要创建一个场景节点,然后再创建一个人物对象的实体并与场景节点绑定在一起,之后你还需要创建一个光照模型也与这个场景节点绑定在一起。场景节点同样可以与其它场景节点绑定以描述更完整的对象。我们在后续的章节中介绍场景节点更多的用法。
在场景中,场景节点的位置总是与它的父节点相关。每一个场景管理器都包含一个根节点。

 

返回到我们刚才创建的代码,找到 TutorialApplication::createScene 成员函数。首先需要为整个场景设置环境光,这样才可以看到要显示的内容,通过调用setAmbientLight函数并指定环境光的颜色就可以做到这些。指定的颜色由红、绿、蓝三种颜色组成,且每种色数值范围在 0 到 1 之间。在createScene 中:

mSceneMgr->setAmbientLight( ColourValue( 1, 1, 1 ) );    //设置环境光

 

变量 mSceneMgr 是当前场景管理器的一个对象,createEntity 方法的第一个参数是为所创建的实体指定一个唯一的标识,第二个参数 "robot.mesh" 指明要使用的网格实体,"robot.mesh" 网格实体在 ExampleApplication 类中被装载。

Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" );

SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" );

这句代码首先调用场景管理器的 getRootSceneNode 方法来获取根节点,再使用根节点的 createChildSceneNode 方法创建一个名为 "RobotNode" 的场景节点。与实体一样,场景节点的名字也是唯一的。

最后,将实体与场景节点绑定在一起,这样机器人(Robot)就会在指定的位置被渲染:

 node1->attachObject( ent1 );

 

坐标和向量

在我们继续之前,需要先了解一下 OGRE 的坐标和向量。与其它图形引擎一样,OGRE 也使用 XZ 面作为其水平面,Y 轴作为纵轴。当你面对着屏幕时,从左至右的方向为 X 轴正方向,从下至上为 Y 轴正方向,从里至外为 Z 轴的正方向。

你现在可能注意到机器人正面朝 X 轴正方向,这也许是模型(mesh)设计时就预定好的方向。OGRE 并不会假定你的模型的方位,你所载入的每一个模型都可能有不同的“标准”方向。

OGRE 使用 Vectors 类来描述方向和位置,定义了2维、3维及4维向量,其中3维向量使用得最多。如果你对向量的知识不熟悉,那建议你在正式使用 OGRE 之前先学习一些向量的知识,因为在复杂的程序设计中向量是非常重要的。

 

添加其它的对象

现在我们已经了解了 OGRE 中的方位了,让我们再次回到代码部分你就会发现在代码中并没有指定机器人的方向。实际上在 OGRE 中大量的函数都有自己默认的参数值,例如SceneNode::createChildSceneNode成员函数有三个参数:场景节点的名称、位置及方向,该成员的位置默认值为(0,0,0)。让我们创建另一个场景节点,这次我们指定它的方位信息:

       Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
       SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode("RobotNode2", Vector3( 50, 0, 0 ) );
       node2->attachObject( ent2 );

这些代码看起来应该很熟悉,除了两个地方不同之外,其它部分与以前的代码都一样。首先我们创建的实体及场景节点的名称有细微的不同,其次创建场景节点时指定了节点的方位信息,我们让实体向 X 的正方向偏移了 50 个单位(这是相对与场景中的根节点而言,而所有的节点的位置都是相对于其父节点而言的)。编译并运行,你会发现屏幕上现在有两个[面对面]的机器人。


 

实体深入了解

实体类包含的内容相当的广,这里我不会向你介绍实体类的所有内容,只不过使你能够开始使用OGRE而已。不过我会向你介绍几个实体类有用的成员函数。

第一个是Entity::setVisible和Entity::isVisible。利用这个函数,你可以设置任何一个实体为可见或不可见。当你想暂时隐藏一个实体时,与其销毁这个实体然后再在用的时候重新创建,不如简单的调用一下这个函数。注意你不需要“省着”用这些实体,任何一个对象的材质和贴图只会被载入到内存里一次,所以当你省系统资源的时候,你并没有真正节省多少。你唯一省掉的只不过是实体创建和毁灭的时间。

getName函数返回实体的名称,getParentSceneNode函数返回这个实体绑定的节点。

 

场景节点深入了解

场景节点类同样相当复杂。场景节点能实现很多事情,所以我们只了解一些常用的。

你可以利用getPosition或setPosition得到或者设定场景节点的位置 (总是和父节点相对的)。你可以利用translate函数来移动对象。

场景节点不仅仅决定一个对象的位置,它还可以控制一个对象的缩放比例和旋转角度。你可以用scale函数来设置一个对象的缩放比例,还可以用yaw, roll, pitch函数来旋转对象。你可以用resetOrientation来还原你对对象进行的所有旋转。你还可以用setOrientation, getOrientation和rotate函数对对象进行高级旋转。四元数会在后面的教程里对大家讲解。

你现在已经见过attachObject函数了。当你想对绑定到场景节点上的对象进行操作时,这些函数会很有帮助:numAttachedObjects, getAttachedObject(这个函数有很多版本), detachObject(同样很多版本), detachAllObjects。还有一大堆函数是来处理父节点和子节点的。

由于所有的移动都是相对于父节点的,我们可以很容易的使两个节点一起移动。我们现在已经有这些代码在我们的程序里:

       Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" );
       SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" );
       node1->attachObject( ent1 );

       Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
       SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );
       node2->attachObject( ent2 );

假如我们将第5行从:

       SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );

改成:

       SceneNode *node2 = node1->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );

这样一来RobotNode2就变成了RobotNode的子节点。移动node1会使node2跟着移动,移动node2却不会影响node1。下面的代码就只会移动RobotNode2:

       node2->translate( Vector3( 10, 0, 10 ) );

下面的代码会移动RobotNode,既然RobotNode2是RobotNode的子节点,RobotNode2会跟着移动:

       node1->translate( Vector3( 25, 0, 0 ) );

最后说一下,你可以使用场景管理器的成员函数getSceneNode和getEntity来获取场景节点和实体,这样你就不用保留你创建他们时使用的指针了。但你仍然应该保留很常用的实体的指针。

 

额外尝试

到目前为止你应该已经掌握了实体,场景节点和场景管理器的基础了。现在你可以试验一下下面的代码段:

 

缩放

你可以用scale这个成员函数来缩放网格。试一下变换scale里面的值看看什么效果:

        Entity *ent = mSceneMgr->createEntity( "Robot", "robot.mesh" );
       SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" );
       node->attachObject( ent );

       node->scale( .5, 1, 2 ); 

       ent = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
       node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );
       node->attachObject( ent );

       node->scale( 1, 2, 1 );

 

旋转

你可以用yaw, pitch, roll来旋转对象。Yaw是Y轴的旋转,Pitch是X轴,Roll是Z轴。试一下变换角度(Degree)和合并多种旋转方式:

        Entity *ent = mSceneMgr->createEntity( "Robot", "robot.mesh" );
       SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode", Vector3( -100, 0, 0 )  );
       node->attachObject( ent );

       node->yaw( Degree( -90 ) );
 
       ent = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
       node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2");
       node->attachObject( ent );

       node->pitch( Degree( -90 ) );
 
       ent = mSceneMgr->createEntity( "Robot3", "robot.mesh" );
       node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode3", Vector3( 100, 0, 0 ) );
       node->attachObject( ent );

       node->roll( Degree( -90 ) );

 

小结

恭喜你,现在已经掌握了OGRE的基石——实体,场景节点,场景管理器。你并不需要对上面所讲的那些函数都了如指掌。由于他们是很基础的东西,我们会经常使用。随着对OGRE了解的深入,你应该可以自然而然的掌握他们(转自网络)。

你可能感兴趣的:(初探Ogre 引擎!---- (3)OGRE初级教程)