OGRE教程:SceneNode, Entity, SceneManager


   OGRE教程(一)
   OGRE教程(二)


注意:这篇文章是针对OGRE 1.0.0,如果你使用其他的版本,如果遇到问题请到 论坛中讨论

目录

1 读者对象
2 简介
3 如何开始
4 OGRE如何工作
  4.1 SceneManager 基础
  4.2 Entity 基础
  4.3 SceneNode 基础
5 你的第一个OGRE程序
6 坐标和向量
7 添加另一个物体
8 Entities more in Depth
9 SceneNodes more in Depth
10 尝试
  10.1 Scale(变换)
  10.2 旋转
11 总结
12 你的想法?


1 读者对象

  这篇文章是假设你有C++编程知识,并设置了OGRE在编译器中,(如果你不知道如何设置,请参看《OGRE初学者引导》),对OGRE一无所知的情况下。


2 简介

  在这篇教程中,我将介绍一些基本的OGRE结构:SceneManager, SceneNode, and Entity 。在这篇文章中,我不会使用太多的代码,而是讲解一些基本的理论。
  通过这篇文章,你将慢慢的添加代码到你的程序中,并观察他的运行结果。对于这些理论,并没有固定的代码,你也可以通过这些理论写出其他的代码。


3 如何开始

  对于这篇教程,我们使用了一段固定的代码,(也许你在《OGRE初学者引导》见过)。在这段代码中,你可以忽视其他的代码,但createScene中的代码应注意。在下一篇教程中,我们将深入讲解OGRE是如何工作的,因此这里的基本知识很重要。添加下面的代码到你的编译器中:

#include "ExampleApplication.h"

class TutorialApplication : public ExampleApplication
{
protected:
public:
    TutorialApplication()
    {
    }

    ~TutorialApplication() 
     {
     }
protected:
     void createScene(void)
     {
     }
};

#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
     // Create application object
     TutorialApplication app;

     try {
         app.go();
     } catch( Exception& e ) {
#if OGRE_PLATFORM == PLATFORM_WIN32
         MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_IConERROR | MB_TASKMODAL);
#else
         fprintf(stderr, "An exception has occured: %s\n", e.getFullDescription().c_str());
#endif
     }

     return 0;
}

  如果你是使用WINDOWS下的OGRESDK,请确定添加"[OgreSDK_DIRECTORY]\samples\include"这个目录到这个项目中。如果是使用OGRE的源代码,请添加"[OgreSource_DIRECTORY]\Samples\Common\include"这个目录。然后,就可以编译和运行了,如果还遇到问题,请参看WIKI的有关这些信息的页面,如果任然不行,请到论坛中讨论。

  程序控制:用WASD移动,鼠标确定方向。ESC键退出。


4 OGRE如何工作

  一个很宽的主题。我们将从他的基础 The SceneNode, Entity, and SceneManager 讲解。

4.1 SceneManager 基础

  SceneManager管理出现在屏幕上的所有物体。当你把一个物体放到场景中,SceneManager就将对这个物体的坐标进行跟踪。当你建立一个摄象机时,SceneManager就将对他们所有东西进行跟踪。当你建立木块,广告牌,灯光...,SceneManager 还是会对他们进行跟踪。

  SceneManager也有许多的类型,有渲染地形的,有渲染BSP树的,等等。在这篇文章中,你将学到许多类型的SceneManager。

4.2 Entity 基础

  Entity是你在场景中渲染的物体的形状。你能把他想象成3D网格。一个机器人有网格,一条鱼有网格,你的角色行走的地形有一个大的网格。而如灯光,广告牌(Billboards),粒子,摄象机等没有Entity。

  值得注意的一件事是,OGRE是根据物体的坐标和方向的可渲染性分别进行渲染的。这就意味着你不能直接到场景中的一个网格进行渲染。你必须将要渲染的物体的网格给予SceneNode ,SceneNode 包含诸如坐标和方向等信息。

4.3 SceneNode 基础

  在上面已经提到,SceneNode 用于保持对所有与它联系的物体的坐标和方向进行跟踪。当你建立一个网格,他并不会在场景中进行渲染,除非你将这个网格赋予SceneNode 。相似的,SceneNode 不是你要在屏幕上显示的物体,只有当你建立一个SceneNode,并将一个网格赋予他,他才会在屏幕上显示一个物体。

  SceneNode 能将许多的物体赋予他。例如,你在屏幕上有一个行走的物体,并且你想产生一个灯光环绕着他。首先,你需要建立一个SceneNode ,然后建立角色的网格,并将他赋予SceneNode 。下一步建立灯光,并将他赋予SceneNode 。SceneNode也允许你将他赋予其他SceneNode,这样就建立了一个有等级的节点系统。在下一篇文章中,我们将更详细的讲解SceneNode的功能。

  一个重要的概念是,SceneNode的位置总是和他的父SceneNode有关,并且SceneManager包含所有被赋值的SceneNodes的根节点。


5 你的第一个OGRE程序

  现在回到我们开始建立的代码中,找到TutorialApplication::createScene 成员函数。在这篇教程中,我们只将对这个函数进行操作。我们要做的第一件事是建立网格。我们可以通过调用 SceneManager's createEntity 函数来实现。添加下面的行到createScene :

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

  到这里,有几个问题将弹出。首先,mSceneMgr来自哪里,我们用什么值去调用这个函数?
  mSceneMgr常量包含当前SceneManager物体(这是通过ExampleApplication类来实现的)。第一个参数是我们通过createEntity建立的网格的名字。所有的网格必须有唯一的名字。如果你试图建立两个有相同名字的网格,你将得到错误。“robot.mesh" 唯一表明了我们要使用的网格的名字。在这里,我们使用的网格是通过ExampleApplication类导入的。

  到现在,我们建立了网格,我们还需要用SceneNode 与他关联。又因为每个SceneManager有一个根 SceneNode,我们将用下面的代码建立他的一个子节点:

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

  这条长长的语句首先调用当前SceneManager的getRootSceneNode方法。然后,他又调用根SceneNode的createChildSceneNode方法。createChildSceneNode方法中的参数是我们建立的SceneNode的名字。像前面所述一样,SceneNode也不允许名字相同。

  最后,我们需要将网格赋予SceneNode,以便于ROBOT有渲染的坐标:

node1->attachObject( ent1 );

  一切OK!编译并运行,你将在屏幕上看到一个机器人。


6 坐标和向量

  在我们开始讲解之前,我们有必要讨论一下屏幕坐标和OGRE向量。OGRE像其他的图象引擎一样,用X,Z轴表示水平的面,Y表示垂直的轴。正如你看屏幕一样,X轴表示你的屏幕的左,右,右面是X轴正方向。Y轴表示你的屏幕的下到上,上面是Y轴正方向。Z轴表示你的屏幕的由里到外,外面是Z轴正方向。

  注意,我们的机器人如何面对X轴的正方向?这是网格的一个属性,他是如何设计的呢?OGRE并没有规定你的初始模型的方向,所以你导入的每个物体网格的方向都是不一定的。

  OGRE用向量类来表示坐标和方向。这些向量vectors可以定义为2(Vector2),3(Vector3),4(Vector4)维,其中Vector3最常用,如果你对向量不熟悉,我建议你看一下下面的网站:

http://en.wikipedia.org/wiki/Vector_%28spatial%29  

笔者注:我建议大家在学习3D编程知识之前,看一下《线形代数》,《空间解析几何》。

关于向量的数学知识在以后的学习中将发挥重要的作用。


7 添加另一个物体

  前面,我们讲解了坐标系统的作用,下面我们回到我们的代码中来。在前面,我们添加的代码中,我们并没有明确表明我们的机器人出现的坐标。但在OGRE中,有许多的函数可以初始坐标。例如:
  SceneNode::createChildSceneNode 成员函数中,有三个参数:SceneNode的名字,SceneNode的坐标,和SceneNode的基本旋转。对于坐标,正如你所看到的,我们初始时为(0,0,0)。现在,我们建立另一个SceneNode,但是这次我们表明他的坐标为另一个值:

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

  这看起来和前面我们定义的一样,只是有轻微的变化。首先,我们命名网格和SceneNode时,有很小的不同。第二件不同的是我们初始网格开始的位置偏离了根SceneNode50个单位(记住SceneNode所有的坐标和他们的父节点有关)。编译并运行,现在,你有两个机器人。


8 Entities more in Depth(深入Entities)

  Entities类功能非常强大,我在这里并不想覆盖的太宽,只讲一下他的基本。首先,我们不得不提一下Entities中的一些功能强大的成员函数。

  第一个是Entity::setVisible 和Entity::isVisible.你能把任何Entity 设置成可视。如果你需要先隐藏Entity ,然后又显示它,你可以不调用这个函数,而采用先删除Entity ,显示时又从新建立他。物体的网格和文理自动拷贝到内存中,不需要你自己保存他们。你需要保存的是Entity物体的建立和删除的东西。

  getName函数用于返回Entity的名字,getParentSceneNode函数用于返回与Entity相关的SceneNode。


9 SceneNodes more in Depth

  SceneNode 类非常复杂。在SceneNodes 上有许多的事情可以做,因此,我们在这里只覆盖到一些通用的功能。

  你能用SceneNode的getPosition,setPosition函数得到和设置SceneNode的点(通常和SceneNode的父节点有关)。你能用translate函数移动物体。

  SceneNode不仅能设置位置,还能管理物体的变换大小和旋转。你能设置变换大小用scale函数。你能用yaw,roll,pitch旋转物体。你也能用resetOrientation函数设置物体的旋转参数。你还能用setOrientation,getOrientation,rotate函数实现高质量的旋转。

  我们再来看一下attachObject函数,如果你想把物体系到SceneNode点上,这些相关函数将非常有用:numAttachedObjects,getAttachedObject(这个函数有多个版本),detatchObject(也有多个版本),detatchAllObjects.也有整个一组函数处理父和子SceneNode。

  所有的坐标和父SceneNode节点相关,我们能做两个相互移动的SceneNode节点。下面有一段代码:

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 );

如果我们把第六行:
SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );

变化如下:

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

然后把RobotNode2 作为RobotNode的子节点。如果你移动node1就将移动node2.例如,下面的代码移动RobotNode2 :
node2->translate( Vector3( 10, 0, 10 ) );

下面的代码可以移动RobotNode,因为RobotNode2是RobotNode的子节点,RobotNode2也将跟随移动:
node1->translate( Vector3( 25, 0, 0 ) );

  如果,你在这里有麻烦,我们可以理解成从根SceneNode 自顶向下。在这里,我们开始node1从(0,0,0),然后转移到(25,0,0),因此,node1的坐标为(25,0,0)。node2开始在(50,0,0),加上(10,0,0)。因此新坐标为(60,0,10)。

  现在,让我们来计算这些物体的真正坐标。从根SceneNode 节点开始,他的位置总是(0,0,0)。现在,node1的坐标为(root+node1):(0,0,0)+(25,0,0)=(25, 0, 0). 而node2是node1子节点,因此,他的坐标为(root + node1 + node2): (0, 0, 0) + (25, 0, 0) + (60, 0, 10) = (85, 0, 10). 这只是描述关于SceneNode坐标层次的一个例子。

  你能通过getSceneNode,getEntity函数得到SceneNodes and Entities 的名字。这两个函数是SceneManager中的方法,因此,你不需要在你建立的每个SceneNode中保持一个指针。你只需要悬挂你经常用的那个。


10 尝试

  通过这章的学习,我们学习了Entities, SceneNodes, and the SceneManager. 的基本知识。下面,我将给出他们的一些例程。在这个例子中,我们在场景中建立一组机器人。

Scale 

下面的代码是通过SceneNode中的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 );

旋转

下面的代码是通过角度和半径,用the yaw, pitch, and roll 函数旋转物体。

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 ) );


11 总结

  在一章中,我们学习了一些关于SceneManager, SceneNode, and Entity 类的基本知识。但对文中提到的函数,不是很了解,在下一章中,我们将详细讲解这些函数。


  OGRE教程(一)
  OGRE教程(二)


1)前提
2)简介
3)开始
4)摄象机
4.1 OGRE 摄象机
4.2 建立摄象机
5)视口(viewports)
5.1 OGRE 视口
5.2 建立一个视口
6)灯光和阴影
6.1 OGRE支持的阴影类型
6.2 在OGRE中使用阴影
6.3 灯光的种类
6.4 建立灯光
7)尝试
7.1 不同的阴影类型
7.2 灯光渐淡
7.3 SceneManager::setAmbientLight
7.4 视口背景颜色
7.5 Camera::setFarClipDistance
7.6 Planes(程度)
8)你的想法?


1)前提

  这篇文章是假设你有C++编程知识并在编译器中安装和设置了OGRE。这篇文章是建立在第一篇教程的基础上的。

2)简介

  在这篇教程中,我们将讲解更多的关于OGRE的结构。这篇教程主要是关于灯光物体的,并讲解了他们在OGRE中是如何建立阴影的。我们也将复习一下关于摄象机的知识。

  通过这篇教程的学习,你将添加更多的代码到你的程序中,并看见他们的结果。

3)开始

  和第一篇教程一样,我们将使用一些准备好了的代码。我们将在我们的TutorialApplication 类中添加两个方法:createViewport and createCamera。这两个函数也是在基本的ExampleApplication中定义了的,但是,在这篇教程中,我们将看一下摄象机和视口在实际中是如何工作的。

在编译器中建立一个工程,添加下面的代码到里面:

#include "ExampleApplication.h"

class TutorialApplication : public ExampleApplication
{
protected:
public:
    TutorialApplication()
    {
    }

    ~TutorialApplication() 
    {
    }
protected:
    virtual void createCamera(void)
    {
    }

    virtual void createViewports(void)
    {
    }

    void createScene(void)
    {
        Entity *ent;
        Light *light;
    }
};

#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
    // Create application object
    TutorialApplication app;

    try {
        app.go();
    } catch( Exception& e ) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_IConERROR | MB_TASKMODAL);
#else
        fprintf(stderr, "An exception has occured: %s\n", e.getFullDescription().c_str());
#endif
    }

    return 0;
}

  如果你在WINDOWS下使用OGRE SDK,请添加"[OgreSDK_DIRECTORY]\samples\include" 下的目录到这个工程。如果你使用OGRE源代码,请更换成下面的路径"[OgreSource_DIRECTORY]\Samples\Common\include" 。请在继续下一段时,确信你的程序能运行,运行时,显示一个黑色的窗口。如果你在这里有问题,请参考《OGRE初学者引导》。

  程序控制: 用WASD键移动,鼠标能环绕四周。ESCAPE键退出。


4)摄象机

4.1 OGRE 摄象机

  下面,我们来建立查看场景的摄象机。摄象机是一个特别的物体,有点象SceneNode 。摄象机有setPosition,yaw,roll ,pitch4个函数,你能把他给予任何一个SceneNode 。和SceneNode 一样,摄象机的位置和他的父节点有关。对于移动和旋转摄象机,你应该象考虑SceneNode 一样考虑他。

  值得注意的是,你可能想只能在场景中建立一个摄象机,这就是说,我们不能建立一个用于场景一部分的摄象机,而另一个用于场景的另一部分,然后根据场景的显示来确定使用哪个摄象机。相反,OGRE能建立很多的摄象机,把这些摄象机建立到SceneNode 中,然后根据场景显示时,根据他的SceneNode 点中的摄象机来决定场景的显示。我们将在FrameListener 教程中详细讨论他。

4.2 建立摄象机

我们将改变在ExampleApplication 中关于摄象机的方法。

找到TutorialApplication::createCamera 函数,因为摄象机是SceneManager 中的功能,因此,我们用SceneManager 来建立他们。添加下面的代码到程序中:

  // create the camera
  mCamera = mSceneMgr->createCamera("PlayerCam"); 

  我们建立一个名叫PlayerCam"的摄象机。你能通过摄象机的名字,使用SceneManager 中的getCamera函数来得到摄象机,如果你不想把摄象机的指针给SceneManager 的话。

  下面,我们将设置摄象机的位置和他的方向。因为物体初始时是在原点,因此我们的摄象机应该在+Z轴上变换,并使摄象机面向原点。添加下面代码到程序中:

  // set its position, direction
  mCamera->setPosition(Vector3(0,10,500));
  mCamera->lookAt(Vector3(0,0,0));

你能使摄象机面向你调用yaw, rotate, and pitch 函数后的任何位置。
lookAt()在游戏中精确定位很重要。

  它使SceneNodes在游戏中调整摄象机的方向很容易。最后,我们设置剪切距离为5。摄象机的剪切距离是指在你面前,你能看见的近处的或远处的物体。如果你把剪切距离的值设置的很小,你靠近物体,你不能全部看见,你只能看见物体的一小部分,相反,你将看见物体的全部。这在你将渲染大量的物体时很有用。下面代码设置最近的剪切距离:

  mCamera->setNearClipDistance(5);

设置最远的剪切距离可以用setFarClipDistance。


5)视口(viewports)

5.1 OGRE 视口

  当我们开始处理多个摄象机时,视口的概念就显的很重要。我认为明白OGRE是如何用摄象机来渲染场景是很重要。在OGRE中,多个SceneManagers 一起运行是可能的。他们也可能把屏幕分成多个方块,并且在屏幕上分别用摄象机渲染他们(例如,可以想象成有两个人在玩游戏)。关于这些是如何完成的,我们将在后面的教程中讲解。

  要明白OGRE是如何渲染场景的,要明白OGRE的三个结构:the Camera, the SceneManager, and the RenderWindow。RenderWindow 我们以前没有讲过,但非常的简单,就是显示物体的窗口。

  而SceneManager用于建立摄像机来显示场景。你必须告诉RenderWindow应该在屏幕上显示那个摄像机,在窗口中什么部分显示他。而你告诉RenderWindow显示摄像机的那个窗口部分就是你的视口。在OGRE的通常的用法中,你只需要建立一个摄像机,而这个摄像机就占有整个RenderWindow,这样就只有一个视口。

  而在这章中,我们会讲解如何通过摄像机建立视口。我们能用这个视口去设置我们渲染的场景的背景颜色。

5.2 建立一个视口

  让我们回到用ExampleApplication建立视口这个部分,找到TutorialApplication::createViewports 这个方法。要建立视口,我们只需要简单地调用RenderWindow中的addViewport方法,并用我们用的摄像机去支持它。mWindow类是从ExampleApplication类中派生出来的,因此,我们添加下面的代码:

// Create one viewport, entire window
Viewport* vp = mWindow->addViewport(mCamera);

  现在,我们有了我们自己的视口,我们能用它做什么呢?最为重要的就是用它去调用setBackgroundColour方法去设置背景颜色。在这里,因为我们要处理灯光,因此,我们设置背景颜色为黑色。

vp->setBackgroundColour(Colourvalue(0,0,0));

  注意颜色值是用红,绿和蓝来表示的,而这些值的取值范围为0到1。最后,我们要做的最为重要的事是设置摄像机的高宽比 。如果你设置标准全屏幕视口以外的模式,将得到一个很奇怪的场景。在这里,我们设置初始的高宽比:

// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));


6)灯光和阴影

6.1 OGRE支持的阴影类型

OGRE当前支持三种阴影:

1。调整纹理阴影(SHADOWTYPE_TEXTURE_MODULATIVE) - 是用的最常用的。这个用于建立黑白纹理阴影。

2,调整模版阴影(SHADOWTYPE_STENCIL_MODULATIVE) - 这种技术用于渲染所有在场景中不透明的物体。这种方法和附加模板阴影一样,不常用,但是很精确。

3,附加模板阴影 (SHADOWTYPE_STENCIL_ADDITIVE) - 这种技术用于渲染场景中、每个光线。这在图形卡上是非常困难的。

OGRE不将软阴影作为引擎的一部分。

6.2 在OGRE中使用阴影

  在OGRE中使用阴影比较简单,SceneManager 中提供setShadowTechnique成员来设置阴影的类型。你也能用seCastShadows成员来设置投射阴影。
  在这里,我们设置环境光为黑色,然后设置阴影。找到TutorialApplication::createScene成员,添加下面的代码:

mSceneMgr->setAmbientLight( Colourvalue( 0, 0, 0 ) );
mSceneMgr->setShadowTechnique( SHADOWTYPE_STENCIL_ADDITIVE );

现在 SceneManager用调整纹理阴影,下面,我们建立一个物体,并使它投射阴影:

ent = mSceneMgr->createEntity( "Ninja", "ninja.mesh" );
ent->setCastShadows( true );
mSceneMgr->getRootSceneNode()->createChildSceneNode( )->attachObject( ent );

在这里,ninja.mesh被 ExampleApplication预先导入。为了让ninja.mesh能显示投射阴影,我们必须为它建立一个支持阴影的面。
(译者注:下面这段文字可能有问题,原文如下:
Again, the ninja.mesh has been preloaded for us by the ExampleApplication. We also need something for the Ninja to stand on (so that he has something to cast shadows onto). To do this we will create a simple plane for him to stand on. This is not meant to be a tutorial on using MeshManager, but we will go over the very basics since we have to use it to create a plane. First we need to define the Plane (http://www.ogre3d.org/docs/api/html/classOgre_1_1Plane.html) object itself, which is done by supplying a normal and the distance from the origin. We could (for example) use planes to make up parts of world geometry, in which case we would need to specify something other than 0 for our origin distance. For now we just want a plane to have the positive y axis as its normal (that means we want it to face up), and no distance from the origin: 

  首先,我们必须为它建立一个PLANE对象,这个对象支持法线和来自原点的距离。我们可以使面成为世界几何物体的一部分,在这种情况下,我们将表明我们到原点的距离。但是现在,我们使面的法线的轴指向正Y轴,没有到原点的距离:

Plane plane( Vector3::UNIT_Y, 0 );

  现在,我们将存储这个面,以便宜我们在程序中用到它。而MeshManager 类用于保持我们导入场景中的所有网格的路径。createPlane方法用于生成Plane的定义,并按照这些定义值生成网格。代码如下:

MeshManager::getSingleton().createPlane("ground",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
1500,1500,20,20,true,1,5,5,Vector3::UNIT_Z);

  现在,也许你想知道MeshManager是如何使用的,但我现在并不想讲解。在这里,我们存储我们的面为1500X1500的网格,命名为“ground".下面建立这个网格并在场景中固定它:

ent = mSceneMgr->createEntity( "GroundEntity", "ground" );
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);

  在我们完成地面之前,我们需要做两件重要的事情。第一件事是告诉SceneManager 我们不想在不知道用什么阴影的情况下投射阴影。第二件事是地面的纹理。我们的机器人肯ninja 已经有材质脚本定义了。当我们建立我们的地面时,并没有为他建立文理。在这里我们用OGRE中的"Examples/Rockwall"中的材质脚本:

ent->setMaterialName("Examples/Rockwall");
ent->setCastShadows(false);

  现在,我们在场景中有Ninja和地面,让我们编译和运行它。我们并没有看见任何物体!WHY?
那是因为我们没有添加灯光,下面让我们添加灯光。

6.3 灯光的种类

在OGRE中提供了三种灯光:

1,点(LT_POINT) - 从一点发出,向四面发散。

2,点光(LT_SPOTLIGHT) - 点光有点象电筒光。光从一点开始,然后朝一个方向发散。你也能定义点光的圆的角度,分为光源部分和散射部分。

3,方向光(LT_DIRECTIONAL) - 方向光有点象镜面反射光。例如,你建立了一个夜晚的场景,你想模拟月光。在这里,你能根据月亮的发射方向使用方向光来模拟月光。

  光有许多参数,其中最为重要的是diffuse 和specular.每个材质脚本定义材质能反射多少diffuse 和specular光,在下面的章节中,我们将详细学习。

6.4 建立灯光

  在OGRE中建立灯光,我们需要调用SceneManager中createLight方法。建立了灯光后,我们能设置它的位置或将它和SceneNode 联系起来,以便于移动。灯光只有两个函数用于设置:setPosition 和setDirection.因此,你想建立固定光,只需要调用setPosition方法。如果你想建立活动光,只需要将它和SceneNode联系起来。

下面,我们来建立点,第一步是建立灯光,设置类型和位置:

light = mSceneMgr->createLight( "Light1" );
light->setType( Light::LT_POINT );
light->setPosition( Vector3(0, 150, 250) );

我们已经建立了灯光,下面我们需要设置diffuse and specular颜色,这里设置红色:

light->setDiffuseColour( 1.0, 0.0, 0.0 );
light->setSpecularColour( 1.0, 0.0, 0.0 );

现在编译,运行,这时你能看见Ninja和它的阴影。注意我们并不能看见光源,而只能看见光源产生的结果。

下面,我们将建立方向光,在这里我们建立了黄色的方向光:

light = mSceneMgr->createLight( "Light3" );
light->setType( Light::LT_DIRECTIONAL );
light->setDiffuseColour( Colourvalue( .25, .25, 0 ) );
light->setSpecularColour( Colourvalue( .25, .25, 0 ) );

前面我们并没有设置方向光的方向,而只设置了它的位置,下面,我们将设置它的方向:指向正Z轴和负Y轴:

light->setDirection( Vector3( 0, -1, 1 ) );

编译和运行,你将在场景中得到两个阴影。最后,我们来建立点光:

light = mSceneMgr->createLight( "Light2" );
light->setType( Light::LT_SPOTLIGHT );
light->setDiffuseColour( 0, 0, 1.0 );
light->setSpecularColour( 0, 0, 1.0 );

我们同样需要设置点光的位置和方向:

light->setDirection( -1, -1, 0 );
light->setPosition( Vector3( 300, 300, 0 ) );

想象一下,电筒可以通过调节光圈来控制光的范围。同样点光可以通过setSpotlightRange方法来调节:

light->setSpotlightRange( Degree(35), Degree(50) );


7)尝试

7.1 不同的阴影类型

  在这篇文章中,我们只使用了阴影的SHADOWTYPE_STENCIL_ADDITIVE类型,请你试试其他两种类型,看看发生了什么。在SceneManager中还有许多其他关于阴影的函数。试试它们,看看你能否使用它们。

7.2 灯光渐淡

灯光中还提供了一个setAttenuation,用于减淡灯光。将这个函数加入你的点光中,设置不同的值,看看有什么效果。

7.3 SceneManager::setAmbientLight

测试一下SceneManager中的setAmbientLight方法。

7.4 视口背景颜色

在createViewports函数中改变颜色值。学会如何去改变它,以便于更好的使用它。

7.6 Planes(程度)

在这章中,我们并没有详细讲解Planes(程度),我们将在下一课讲解它。在这里,你主要是学会如何使用createPlane函数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值