2.1对于Ogre摄像机来说,有一点跟你想象不一样的是同一个时间内你只能使用一台摄像机(当前)。这就是说,我们不能创建其中一个摄像机去观察场景的一部分,而用第二个去观察场景的另一部分,并且仅通过打开或关闭某一个摄像机就可以来呈现我们想要的一部分场景。不过要完成这一步有一个替代方式就是通过一些创建场景节点来扮演“摄像机句柄”。这些场景节点简单的参加到场景中指向摄像机可能要捕捉的位置。当只需要展示一部分场景时,摄像机只需要简单的把自己绑定到适当的节点上就可以了。
2.2当你开始处理多个摄像机时,视口类的概念就变得对你很有用了。我提出这个主题的原因是,我认为了解当渲染一个场景时Ogre是如何决定使用哪个摄像机的是非常重要的。在同一个时间内Ogre中可能有多个场景管理器在运行。也有可能把场景分为多个区域,然后用分开的摄像机去渲染场景中不同的区域(例如:思考一下控制台游戏中两个人的观察)。
2.3 Ogre currently supports three types of Shadows: Ogre目前支持3种类型的阴影(当前版本1.4.x目前已经支持四种阴影类型):
- 调制纹理阴影 (SHADOWTYPE_TEXTURE_MODULATIVE) - 是这3种中最节省资源的。她创建一个阴影投射者的黑与白渲染到纹理,然后用于场景中。
- 调制模板阴影 (SHADOWTYPE_STENCIL_MODULATIVE) - 这项技术是在所有的非透明体被渲染到场景以后再渲染所有的阴影体来调制阴影,她耗费资源没有加成模板阴影强,但是也不是非常精确。
- 加成模板阴影 (SHADOWTYPE_STENCIL_ADDITIVE) - 这项技术是渲染每一个光源作为分离的部分附加到场景中。 对显卡来说这是比较麻烦的,因为在场景中每一个附加的光源需要一个附加渲染通路。
2.4 。SceneManager类中有一个setShadowTechnique成员函数,我们可以使用她设置我们想要的阴影类型。
2.5光源有一个范围属性描述光看上去的样子。两个更重要的属性是光的漫反射颜色和镜面反射颜色(diffuse and specular color)。每一个材质脚本定义有多少漫反射和镜面反射光线由材质反射来
2.6在Ogre中创建一个光源需要调用场景管理器中的createLight方法然后提供光源的名称,就跟我们创建实体和摄像机一样,光源仅仅有两个方法setPosition和setDirection(而且并不是像旋转,偏移,滚动一样有全套的移动函数)。如果你需要移动光源(例如创建一个光源跟随角色),你需要把光源绑定到场景节点上。
3.1 首先我们需要让OGRE创建一个地面。我们必须将场景管理器设为地面的场景管理器,而不是ExampleApplication里默认的那个。将下面的代码添加到chooseSceneManager函数里:
mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);
3.2 场景管理器不是单例,您想创建多少就创建多少。与场景节点/灯光等不同的是,您可以直接使用 "new SceneManager()" 这种语句来直接创建它(而不必使用Root的createSceneManager方法,但你的确应该这么用)。你可以有多个场景管理器,来同时装载多个独立的几何体和实体。你可以通过重建视口的方式在任何时间交换这些场景管理器
3.3用哪个好?
究竟用哪种天空完全取决于你的程序。假如你需要看到周围所有的东西,包括负y的方向,那你可能只有天空盒这个选择了。假如你有一个地面或者类似地板的东西可以遮住负y方向,那么使用天空穹会有一个更真实的效果。假如你看不到海平线(比如一个峡谷,一个监狱,或者城堡中间的院子),天空面会有一个很好的效果同时只有很低的GPU使用量。我们会在下一节中来解释用天空面的最重要的一个原因,是因为它能和云雾很好地结合在一起。
这些只是建议。当你自己写程序的时候应该把这几种都试试然后看看哪种最好再来决定最终用哪个。
3.4 雾化效果在OGRE里非常简单。但是在你使用它之前要注意,当你使用地面管理器(TerrainSceneManager)的时候,一定要在调用setWorldGeometry函数之前调用setFog函数。(别的场景管理器里没关系的)。
3.5你需要知道关于设置雾化效果的很重要的一点是他并不像你想象的那样在空的地方创建“雾”的实体。实际上雾只是当你观看物体时的一个滤镜。当你面前没有任何物体时,你是看不见雾的。事实上,你只会看到视口(viewport)的背景色。所以想要使雾逼真,我们必须将视口的背景色设成雾的颜色。
3.6在Ogre中我们可以注册一个类去接收消息当一帧被渲染到屏幕之前和之后。FrameListener接口定义两个函数:
bool frameStarted(const FrameEvent& evt)
bool frameEnded(const FrameEvent& evt)
Ogre的主循环(Root::startRendering)类似这样:
- Root object调用frameStarted方法在所有已经注册的FrameListeners中。
- Root object渲染一帧。
- Root object调用frameEnded方法在所有已经注册的FrameListeners中。
这个循环直到有任意一个FrameListener的frameStarted或者frameEnded函数返回false时终止。这些函数返回值的主要意思是“保持渲染”。如果你返回否,这个程序将退出。FrameEvent实体包含两个参数,但是只有timeSinceLastFrame在帧监听中是有用的。这个变量了解frameStarted或者frameEnded最近被激活时间。注意在frameStarted方法中,FrameEvent::timeSinceLastFrame将包含frameStarted时间最近被激活的时间(而不是最近被激活的frameEnded方法)。
3.7一个重要的概念你不能决定那个FrameListener被调用的次序。如果你需要确定FrameListeners被调用的准确顺序,你应该只注册一个FrameListener,然后用适当的顺序来调用所有的物体。
3.8 这个ExampleFrameListener(我们的TutorialFrameListener是从它继承来的),还提供了一个showDebugOverlay(bool)方法,用来告诉ExampleApplication是否要在左下角显示帧率的提示框。我们会把它打开:
// Show the frame stats overlay
mFrameListener->showDebugOverlay(true);
3.9我们使用FrameListener来做的第一个事情就是,让鼠标左键来控制灯的开关。通过调用InputReader的getMouseButton方法,并传入我们要查询的按钮,我们能够检查这个按钮是否被按下。通常0是鼠标左键,1是右键,2是中键。在某些系统里1是中键、2是右键。
3.10 当你对一个节点进行平移,或者是绕某个轴进行旋转,你都能指定使用哪一个“变换空间”来移动它。一般你移动一个对象时,不需要指定这个参数。它默认是TS_PARENT,意思是这个对象使用的是父节点所在的变换空间。在这里,父节点是场景的根节点。
3.11Ogre定义了三种变换空间:TS_LOCAL, TS_PARENT, 和TS_WORLD。
6.1 Crazy Eddies GUI系统是一个为不具备或缺乏用户界面制作功能的图形API或引擎提供免费用户界面支持的开源的库。这个使用c++编写的库是针对那些想制作优秀的游戏却又没有GUI(图形用户界面)子系统的专业游戏开发者。
6.2 创建CEGUI日志的地方,一般都设置为Informative模式的。其具有四种模式:Standard, Errors, Informative 和 Insane。
6.33本质上CEGUI是通过向窗口添加第二个场景,这个场景是在Ogre的基本渲染队列完成后才渲染的。这个场景仅仅是由一系列3D矩形对象组成的。(也就是两个多边形沿着其边压制到一起)。
6.4CEGUI中巧妙的推出设置
// Tell the frame listener to exit at the end of the next frame
void requestShutdown(void)
{
mShutdownRequested = true;
}
bool frameEnded(const FrameEvent& evt)
{
if (mShutdownRequested)
return false;
else
return ExampleFrameListener::frameEnded(evt);
}
7.1CEGUI是一个功能全面的GUI库,它能够植入像Ogre这样的3D应用程序(当然也支持纯DirectX和OpenGL)。就像Ogre只是一个图像库一样(不做声音、物理等其它的事情),CEGUI只是一个GUI库,意味着既不自己做渲染,也不与任何鼠标键盘事件挂钩。实际上,为了让CEGUI能渲染,你必须提供一个渲染器给它(包含在SDK的OgreGUIRenderer库)。而为了让它能够理解鼠标键盘事件,你必须手工地把它们注入系统。这也许一开始看起来觉得痛苦,但其实只需要一丁点代码就能实现。这样,你就可以对渲染和输入进行全面控制,CEGUI根本不会防碍你。
7.2OIS和CEGUI为键盘输入使用相同的键码。但鼠标按钮却不是这样。在按下鼠标按钮注入到CEGUI时,我们需要写一个函数来把OIS的按钮ID转换为CEGUI的按钮ID。在你代码顶部附近,TutorialListener类前面,添加以下函数:
CEGUI::MouseButton convertButton(OIS::MouseButtonID buttonID)
{
switch (buttonID)
{
case OIS::MB_Left:
return CEGUI::LeftButton;
case OIS::MB_Right:
return CEGUI::RightButton;
case OIS::MB_Middle:
return CEGUI::MiddleButton;
default:
return CEGUI::LeftButton;
}
}
7.3CEGUI最常用的用法是,你不必在代码里创建每一个单独的对象。而你可以通过一个像CEGUI Layout Editor这样的编辑器,来为你的程序创建一个GUI布局。根据你的喜好,放置你的窗口、按钮以及其它部件到屏幕上之后,编辑器会把布局保存到一个文本文件里。你就可以之后加载这个布局到GUI sheet里面(它也是CEGUI::Window的一个子类)。