OGRE 3D程序设计(4)

    代码4-7:手动启动Ogre应用程序
    #include “Ogre.h”
    //建立一个没有配置文件的Root实例
    Root *root = new Root(“”, ””);
    //载入渲染系统插件
    root->loadPlugin(“RenderSystem_Direct3D9”);
    root->loadPlugin(“RenderSystem_GL”);
    //在这里我们伪装成用户已经选择了OpenGL渲染器
    String rName(“OpenGL Render Subsystem”);
    RenderSystemList * rList = root->getAvailableRenderers();
    RenderSystemList::iterator it = rList->begin();
    RenderSystem *rSys = 0;
    while(ii != rList->end()){
    rSys = * (it++);
    if(rSys->getName() == rName){
    //设置渲染器,并结束循环
    root->setRenderSystem(rSys);
    break;
    }
    }
    //如果没有找到一个可用的OpenGL渲染器,就在这里结束程序。
    if(root->getRenderSystem() == NULL){
    delete root;
    return -1;
    }
    //root初始化的时候,我们可以传入一个false值来告知Root不用给我们创建渲染窗口。
    root->initialise(false);
    //在这里我们仍然使用默认的参数来创建渲染窗口
    RenderWindow *window = rSys->createRenderWindow(
    “Manual Ogre Window”, //窗口的名字
    800, //窗口的宽度(像素)
    600, //窗口的高度(像素)
    false, //是否全屏显示
    0); //其他参数,使用默认值
    //在这之后你就可以向之前所说的一样创摄像机和视口了。
    在上面的代码中没有做任何值得让人觉得惊奇的事情;我们假设已经从其他的代码片断中(比如游戏的显示控制)得到了参数来构建渲染窗口。如果我们不在参数列表(构造函数最后一个参数)中做特殊设置,那么窗口名称和窗口标题(等同于窗口的标题栏和系统的组件栏)为相同字符串。
    注意:RenderWindow(渲染窗口)对象是对RenderTarget(渲染目标)接口的一个实现,它被接口抽象成一个渲染表面(Rendering Surface)。在我们在我们要把场景渲染到没有帧缓冲(non-frame-buffer)的贴图之类的渲染目标的时,这种抽象就显得非常有用。所有的RenderTarget实例都可以通过名称到相应的工厂方法(Ogre使用的一种设计模式)中来构造,在我们上面调用createRenderWindow()方法的第一个参数就是我们需要的名称。
    在上面的例子createRenderWindow()方法中,所有参数我们都是用了最常用的设置,你可以通过线上API手册或者直接去看OgreRenderSystem.h文件去了解参数每一项的具体含义(在这里我建议你先去看看,因为参数中的很多都经常被改变。我在这里介绍了的一些,可能在不久也会过时。)
    如果你希望让渲染窗口显示在屏幕的左上角,或者想要让渲染窗口的名称和渲染窗口的标题是用不一样的字符串。那么你就需要用到NameValuePairList类(参数列表)的支持,其实这不过是一个标准模板库(STL)的map对象,被createRenderWindow方法作为最后一个参数传入,你只要把你希望改变的属性写在这个map中,系统就把会这些设置过滤出来,其他你没有填入的选项仍采用默认设置(参考代码4-7)。
    NameValuePairList params;
    params[“left”] = “0”;
    params[“top”] = “0”;
    params[“title”] = “Alternate Window Title”;
    RenderWindow *window = rSys->createRenderWindow(
    “MainWindow”, //渲染目标的名字
    800, //窗口的宽度(像素)
    600, //窗口的高度(像素)
    false, //是否全屏显示
    ?ms); //其他参数,这次我们在上面已经设置了
    上面的代码创建了一个在屏幕的左上角现实的窗口,名称为“MainWindow”而标题是“Alternate Window Title”。
    到现在为止,还没有很完美的方法来重新设置渲染窗口或者渲染系统。举例来说,如果你希望在程序运行的时候从Direct3D渲染系统转换到OpenGL系统,就只能关闭当前渲染系统,然后初始化一个新的OpenGL的渲染系统。虽然你也可以对渲染窗口做一些简单的改变,比如改变大小(宽度和高度),移动它在屏幕中的位置。但是如果要改变一些复杂的属性,比如全屏反锯齿效果,就需要释放这个窗口再去创建新的。
    正如同我们之前提到的,有些时候你可能需要多个渲染窗口同时运行。在类似关卡编辑器中经常会出现这种情况,提供不同的显示区域来展示你的场景。这和简单的使用多个视口来渲染不一样,可以让每个视口充满整个上层Windows。
    你也可以把Ogre的渲染窗口插入到一些窗口系统或者组件系统中来(比如Qt或者wxWidgets)。通过RenderWindow的getCustomAttribute()方法来得到当前渲染窗口在系统中的句柄。相应的,你也可以让Ogre使用你提供给它的窗口来作为渲染的父窗口。如下边的代码:
    //hWnd是一个Win32系统中存在的窗口的句柄。
    //渲染系统的指针所指的是一个初始化过的D3D9RenderSystem的实例。
    NameValuePairList opts;
    opts[“parentWindowHandle”] = StringConverter::toString(hWnd);
    //everything but “opts” is somewhat irrelevant in the context of an
    //explicitly parented window
    RenderWindow *window = RenderSystem->createRenderWindow(
    “WindowName”,
    800, 600,
    false, &opts);
    使用上面的代码,允许让你把Ogre渲染窗口插入到一个已经存在的窗口系统中。但同时要注意一些事情,Ogre的窗口消息处理函数在这里被忽略了,因此你要手动处理Ogre相关的消息,比如当用户点击关闭按键时候,清理Ogre渲染窗口。
    摄像机和场景管理(Camera and SceneManager)
    在这里为了避免过多地介绍这两个对象,我会尽量只介绍那些能足够展示它们功能的方法。尽管如此,还是希望你能了解,是这两个类渲染了你的场景。
    场景管理器(SceneManager)
    我们会在下一个章节中详细介绍这个类,所以我尽量避免在这里过多的深入。尽管如此,为了目前章节的完整性,我还是需要讲一下最基础的知识,来帮助你建立一个应用程序所使用的场景管理系。
    在你是用场景管理器之前,你首先需要构建一个相应的实例。
    SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC, “MySceneManager”);
    在我们之前提到的插件里面,其中有一类是场景管理器的构造器,当Ogre载入它们时,这些构造器就把自己注册到系统中,并同时绑定到某一种场景管理器类型:
    ·ST_GENERIC:最简单的场景管理器的构造器类型,其场景管理器没有对场景内容和结构做任何优化。在极其简单的场景中(例如菜单界面)中才有其价值。
    ·ST_INTERIOR:这种场景管理器的构造器所产生的管理器,优化了室内近距离的渲染,比较适合高密度的场景。
    ·ST_EXTERIOR_CLOSE:优化了室外场景里面的中近距离可视体,比较适合用一个简单模型或者高度场产生的场景地图。
    ·ST_EXTERIOR_FAR:Ogre历史遗留的错误,已经不需要在考虑使用它。在需要的时候用ST_EXTERIOR_CLOSE和ST_EXTERIOR_REAL_FAR来代替这个选项。
    ·ST_EXTERIOR_REAL_FAR:这种类型的场景管理器特别适合那种需要动态加载的地形或者场景。动态加载的地形通常都非常巨大,甚至可能描绘了一个星球的地貌。
    在前面的例子中,我们创建了一个ST_GENERIC类型的场景管理器。如果我们要载入一个雷神之锤III(Quake3)的场景,使用ST_INTERIOR来载入场景管理器就没错,因为BSPSceneManager插件已经把置身注册到ST_INTERIOR类型中。如果你希望建立一个以高度场为基础的地形,你就需要载入TerrainSceneManager(TSM)插件所带来的场景管理器,我们可以通过ST_EXTERIOR_CLOSE类型来创建。ST_GENERIC类型中并没有一个特定的场景管理插件,但是当你载入OctreeSceneManager插件之后,它就会接管ST_GENERIC的职责。
    摄像机(Camera)
    摄像机的概念和真实世界中的一样,会放在一个合适的位置(这意味着它拥有位置方向的属性),为你的场景进行“拍摄” 每一帧的工作。同时它是一个不能被渲染的物体,所以就算你的一台摄像机的拍摄范围你有另外一台摄像机出现,另外一台也不会被“拍摄”渲染出来(参考下面的摄像机视截体3D模型,你会更好理解我说的意思的)。摄像机(以及我们之后要提及的灯光)既可以挂在场景节点上面(这就意味着它可以被动画控制器操作),也可以直接放到空间中(你可以通过手动的方法改变其位置和方向)。就像前面说的,摄像机有一个“可视区域”的概念,它的形状是一个顶点在摄像机位置上的棱锥,并被远截面和近截面所切割成的视截体。如下图4-2所示。


    图4-2:摄像机视截体
    在图示中,(x, y, z)代表摄像机所在点的坐标。X和Y分别代表近截面的宽度和高度,Z代表从摄像机到近截面的距离。X’和Y’代表远截面的宽度和高度,其中(Z+Z’)的和代表着从摄像机到远截面的距离。其他的一些信息有,近截面和远截面的距离,摄像机的纵宽比率(X/Y),代表视方向和视截体下截面(或者上截面)的夹角W(代表了在Y轴的可视范围),以及可以由摄像机类计算出来的与地平线的夹角等。
    现在假设我们需要建立一个这样的摄像机:它拥有标准3:4的纵宽比;近截面距离摄像机5单位,远截面距离1000单位;视线方向和视截体的下平面(以及上平面)拥有30度夹角(换句话说,就是上图的W等于30°)。下面的代码实现了以上工作:
    //sceneManager是一个已经存在的场景管理器实例的指针。
    //我们在这里构建名称为“MainCam”的摄像机。
    Camera *camera = sceneMgr->createCamera(“MainCam”);
    //并不需要计算什么,可以直接从视口中得到这个尺寸
    camera->setAspectRatio(1.333333f);
    //30度角可以让我们看到一个长而远的视野
    camera->setFOVy(30.0f);
    camera->setNearClipDistance(5.0f);
    camera->setFarClipDistance(1000.0f);
    系统根据摄像机的设置而产生的视截体,然后再剔除在视截体六个面外面的几何体(意味着把这些集合体从当前帧的渲染列表中清除)。
    渲染模式(Rendering Modes)
    我们的摄像机支持3种不同的渲染模式:边框,实体,“点”(只渲染顶点)。
    camera->setPolygonMode(PM_WIREFRAME);
    camera->setPolygonMode(PM_POINTS);
    camera->setPolygonMode(PM_SOLOD);
    PolygonMode mode = camera->getPolygonMode();
    这些设置可以一直持续到下一次重新设置的时候(也就是说,不用在每一帧都调用这些函数)。系统默认的参数是PM_SOLOD。
    位置和变换(Position and Translation)
    摄像机(视截体)是一个MovableObject(活动对象)接口的实现,因此也具有这对象的所有方法和特性。MovableObject的大部分属性都是为了可以让它挂接到一个场景节点上面,并“背着”摄像机到处“拍摄”可以被渲染的物体。当你想要有一些不同的摄像机“追尾”技术的时候,你就会觉得这是一个不错的方法,在后面的章节中你会看到一些第三人称“追尾”技术的实现;不过在这里,我们首先来了解一下它内在用来改变位置和方向的方法。
    //确认我们已经有一个指向“Camera”类型实例的指针camera。
    camera->setPosition(200, 10, 200);
    //也可以用一个三维向量来设置摄像机坐标,在我们得到场景坐标时候这么做会方便一些
    //camera->setPosition(Vector3(200, 10, 200));
    上面的代码把摄像机设置到世界坐标系的绝对点(200,10,200)上。这和我们使用move()方以及moveRelative()方法有很大的区别,后面两种方法会把摄像机移动到当前位置的相对位置上。
    //假设摄像机还在我们之前设置的200, 10, 200空间位置上。
    camera->move(10, 0, 0); //摄像机移动到210, 10, 200
    camera->moveRelative(0, 0, 10); //摄像机移动到210, 10, 210
    在这里要注意一下moveRelative()方法。它关注的是本地坐标系,也就是说变换是根据当前摄像机所朝向的方向。在前面的例子中,摄像机仍然垂直地面,面向Z轴正方向。假如我们现在把摄像机向右转90度角。这时候摄像机本地坐标和世界的绝对坐标就不同了,如果我们这时候再调用moveRelative(0, 0, 10),在本地坐标移动10个z方向,但在世界坐标却是移动x方向,最后摄像机在世界坐标系的位置是(220, 10, 200)。
    指向,方向,和“着眼点”(Direction,Orientation,and“Look-At”)

注:转载源地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值