OGRE3D17BeginnersGuide第二章Word文档下载推荐.docx
《OGRE3D17BeginnersGuide第二章Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《OGRE3D17BeginnersGuide第二章Word文档下载推荐.docx(20页珍藏版)》请在冰点文库上搜索。
3.然后添加以下两行;
这两行代码的添加顺序对场景的效果没有影响。
addChild(node);
node->
4.编译并且运行应用程序。
5.当你运行应用程序,你应会看到和你第一章一样的场景。
刚刚发生了什么?
我们创建了一个新的命名为Node1的场景结点。
然后我们添加这个场景结点到根结点。
在这之后,我们绑定之前的3D模型到我们新创建的结点上面,这样就可以看到效果了。
如何使用场景根结点
调用mSceneMgr->
getRootSceneNode()函数将会返回场景的根结点。
这个场景结点是场景管理器的一个成员变量。
当我们想要什么显示的时候,我们需要以一种方式把它绑定到场景根结点上或者一个派生类或子类的结点上。
简而言之,子结点需要和根结点保持结点与结点之间的联系,否则的话,子结点上的模型就得不到渲染。
就像变量名所暗示的那样,场景根结点是场景的根。
因此整个场景将会以某种方式绑定到场景的根结点上。
Ogre3D使用了所谓的场景绘图的方式来组织场景。
这个绘图的方式就好像一棵树一样。
它只有一个根(也就是场景根结点),并且每个结点都可以有子结点。
我们已经使用了这一特性当我们调用mSceneMgr->
addChild(node)的时候。
我们把创建好的场景结点当做场景根结点的子结点。
在这之后,我们添加了node->
attachObject(ent)使另一种类型添加到场景结点。
这里,我们添加一个实体到场景结点。
我们现在有两种不同的可以添加到场景结点的对象。
首先,创建可以被添加为子结点或者可以继续为自己添加子结点的结点。
然后,我们我们创建我们想要渲染的实体。
实体不是子结点,它们也不可能有子结点。
它们有数据对象可以被关联上结点也可以被认为是树上的叶子。
我们稍晚将会学习它们到底是什么和如何使用它们。
现在,我们只需要实体。
我们当前的场景绘图方式就好像下图一样。
首先我们需要理解的是什么是场景结点并且他们是干什么的。
一个场景绘图就是用来代替在3D空间中不同部分的场景是如何相互关联在一起的。
3D空间
Ogre3D是一个3D渲染引擎,所以我们需要一些基本的3D概念。
在3D上最基础的结构就是向量,这个向量中有次序的包含着(x,y,z)。
在3D空间的每个位置都可以被欧几里德三维坐标系的一个三维坐标表示。
我们要强调一下在3D表示方面有不同的坐标体系。
而这些体系之间的不同就是主轴的方向和选装的正方向。
现在主流的有两种体系,左手坐标系和右手坐标系。
在下面的图中,我们看到两种体系—在左边我们看到的是左手坐标系;
在右边我们看到的是右手坐标系。
左手和右手坐标系名字的由来是基于他们主轴方向的不同,方向是左手和右手来决定创建的。
大拇指是X轴,食指是Y轴,而中指是Z轴。
我们需要张开手使大拇指,食指和中指之间保持90度角。
当使用右手时,我们就可以得到一个右手坐标系。
当使用左手时,我们就可以得到一个右手坐标系。
gre使用的是右手坐标系,但是旋转坐标系使X轴的正半轴指向右并且X轴的负半轴指向左。
Y轴(正半轴)指向上,Z轴(正半轴)垂直于屏幕向外,这被称为y-upconvention。
这开始会使我们很不适应。
但是我们不久将会在这样的坐标系下学习研究Ogre。
这个网址http:
//viz.aset.psu.edu/gho/sem_notes/3d_fundamentals/html/3d_coordinates.html
有能更好展示不同坐标系之间的不同和联系的图片解释。
场景图
场景绘图是在图形化编程领域最被广泛使用的概念之一。
简单的说,这是存储场景信息的一种方式。
我们已经讨论过场景绘图必须有一个根结点而且是树形结构的。
但是我们还没有涉及场景绘图中最重要的函数。
每个场景结点既可有子结点也可以在3D空间变换的函数。
这些变换可以说由三方面组成,就是——位置变换(position),旋转变换(rotation)和缩放(scale)变换。
坐标点的(x,y,z),明确的描述了结点在场景中的位置。
旋转是使用四元数来储存的,四元数是3D空间存贮旋转的一个数学概念,但是我们可以认为旋转就是每个坐标轴一个的浮点数,描述了结点以弧度为单位的旋转。
缩放就比较简单了,同样的,就是它使用了一个(x,y,z)的数组,并且数组的每一部分就是对应坐标轴的缩放比例。
关于场景绘图很重要的一件事是相对于父结点的变换。
如果我们修改了父结点的方向,子结点也会受其影响发生改变。
当我们把父结点沿X轴移动十个单位,所有的子结点也将会沿X轴移动十个单位。
最后子结点的方向会根据所有父结点的方向而计算出来。
这个概念将会在下面的图标中展示的更加清楚。
MyEntity的在场景中的位置将会是(10,0,0)并且MyEntity2将会在位置(10,10,20)。
然后让我们在Ogre3D中尝试一下这个实验
简单测试——找到场景结点的位置
1.观察下面的树形结构并且判定MyEntity和MyEntity2的最终位置
a.MyEntity(60,60,60)andMyEntity2(0,0,0)
b.MyEntity(70,50,60)andMyEntity2(10,-10,0)
c.MyEntity(60,60,60)andMyEntity2(10,10,10)
设置场景结点的位置
现在,对比上幅图片我们将会尝试创建如图表中描述的场景。
实践时刻——设置场景结点的位置
1.在创建场景结点后添加以下一行代码:
setPosition(10,0,0);
2.在createScene()函数结尾中添加下面一行代码以创建第二个实体。
Entity*ent2=mSceneMgr->
MyEntity2"
3.然后创建第二个场景结点。
SceneNode*node2=mSceneMgr->
Node2"
4.把第二个结点添加到第二个结点上node->
addChild(node2);
5.设置第二个结点的位置
node2->
setPosition(0,10,20);
6.把第二个实体关联到第二个结点上面:
attachObject(ent2);
7.编译这个程序然后你就会看到两个Sinbad实例了:
我们创建了一个和之前图解中相匹配的场景。
我们在第一步使用的首个新函数。
能够很容易的猜到,setPosition(x,y,z)函数是根据数组来设置结点位置的。
记住这个位置是相对于父结点的位置。
我们想要MyEntity2在位置(10,10,20),因为我们添加了关联MyEntity2的node2结点,并且node2的父结点已经在位置(10,0,0)了。
我们只需要设置node2的位置到(0,10,20)。
当两个位置结合到一起,MyEntity2就在(10,10,20)了。
简单测试——使用场景节点
1.我们现在有场景结点node1在(0,20,0)并且我们有个场景子结点在node2并且已经有一个提示关联上去了。
如果我们想要实体在(10,10,10)位置被渲染,那么我们应该把node2设置在什么位置?
a.(10,10,10)b.(10,-10,10)c.(-10,10,-10)
让英雄动起来——添加一个Sinbad
添加Sinbad的第三个实例兵器让他在位置(10,10,30)受渲染
旋转一个场景结点
我们已经知道如何设置一个场景结点的位置。
现在,我们将会学习如何去旋转一个场景结点并且如何以另一种方式去修改一个场景结点。
实践时刻——旋转一个场景结点
我们将会使用之前的代码,但是为createScene()函数创建全新的代码。
1.移走createScene()函数中的所有代码。
2.首先创建一个Sinbad.mesh的实例并且然后创建一个新的场景结点。
设置场景结点的位置到(10,10,0),在最后关联实体到结点,并且添加一个结点为场景根结点的子结点。
setPosition(10,10,0);
3.同样的,创建一个新的实例模型,然后是一个新的场景结点,并且设置点到(10,0,0)
4.现在添加以下两行到旋转模型并且关联实体到场景结点
pitch(Ogre:
Radian(Ogre:
Math:
HALF_PI));
5.同样的,但是这次使用yaw函数代替pitch函数并且使用translate函数代替setPosition函数
Entity*ent3=mSceneMgr->
MyEntity3"
SceneNode*node3=mSceneMgr->
Node3"
);
addChild(node3);
node3->
translate(20,0,0);
yaw(Ogre:
Degree(90.0f));
attachObject(ent3);
7.同样再次代替yaw()和pitch()函数使用roll()函数旋转
Entity*ent4=mSceneMgr->
MyEntity4"
SceneNode*node4=mSceneMgr->
Node4"
addChild(node4);
node4->
setPosition(30,0,0);
roll(Ogre:
attachObject(ent4);
8.编译并且运行程序,并且你将会看到下面的截图
刚刚发生了什么?
我们几乎重复了4遍我们的代码并且总是改变一些小细节。
第一次的代码没有什么特别的。
它仅是我们之前写过的一样的代码,但是这个实例会作为我们的参照物,用来对于其他三个实例在改变之后发生了什么。
在第四步中,我们添加了下面一行代码:
函数pitch(Ogre:
HALF_PI))用来绕X轴旋转结点。
就如我们之前所说的一样,这个函数接受一个弧度单位作为参数并且我们使用π/2,也就是旋转90度。
在第五步中,我们代替setPosition(x,y,z)函数调用了translate(x,y,z)函数。
setPosition(x,y,z)和translate(x,y,z)函数之间的不同setPosition()函数仅是设置点,没什么可说。
Translate()以给定的值设置点的位置,但是它是相对于现在的位置变换。
如果一个场景结点在位置(10,20,30)并我们调用setPosition(30,20,10),那个结点在就在世界空间的位置(30,20,10)。
另一方面,如果我们调用translate(30,20,10),结点就会在位置(40,40,40)。
这点区别虽小,但是相当重要。
如果我们在正确的环境中使用这两个函数他们都是有效的,比如当我们想要设置结点在场景中的位置,我们将会使用setPosition(x,y,z)函数。
然而,当我们想要移动一个已经在场景中设置好的结点,我们将会使用translate(x,y,z)。
同样的,我们用yaw(Ogre:
Degree(90.0f))代替pitch(Ogre:
HALF_PI))函数。
yaw()函数绕Y轴旋转场景结点。
我们使用Ogre:
Degree()来代替Ogre:
Radian(),当然,Pitch和yaw仍然需要一个使用弧度参数。
然而,Ogre3D提供了一个可以使编译器自动转换角度到弧度的操作的Degree()类。
因此,程序员可以随心所欲使用一个弧度或角度单位来旋转场景结点了。
不同的类命令确保类的使用是清楚的,这样防止使用混淆或者可能出错的资源。
第六步介绍了三个不同旋转结点函数的最后一个函数,即是roll()函数。
这个函数绕Z轴旋转场景结点。
同样的,我们可以使用roll(Ogre:
Degree(90.0f))来代替roll(Ogre:
HALF_PI))。
当程序运行我们可以一个不同和三个已经旋转过了的模型。
最左边的模型没有转动,左边模型的右边那个是绕X轴转动过的,最右边的模型左边的那个模型是绕Y轴旋转过的,最右边的模型是绕Z轴旋转过的。
三个实例都显示了不同旋转函数的效果。
简而言之,pitch()函数绕X轴旋转,yaw()函数绕Y轴旋转,roll()函数绕Z轴旋转。
我们可以使用Ogre:
Degree(degree)或者Ogre:
Radian(radian)其中一个来明确指明我们想要旋转的程度。
简单测试——旋转一个场景结点
1.哪个是旋转结点的三个函数?
a.pitch,yawn,rollb.pitch,yaw,rollc.pitching,yaw,roll
让英雄动起来——使用Ogre:
Degree
修改我们之前输入的代码段,替换现有的Ogre:
Radian为Ogre:
Degree或反之亦然,旋转将会保持不变
缩放一个场景结点
我们已经使用了最基本的三个操作中的两个,用来控制场景图,现在我们来使用最后一个操作,缩放功能.
实践时刻——缩放一个场景结点
再一次,以我们之前用过的程序段作为我们的开始
1.移走createScene()函数中所有代码并插入以下代码段
2.同样的,创建一个新实体:
Sinbad.
3.现在我们使用一个函数创建一个场景结点并且把它添加为一个子结点。
然后同样我们照原来所做。
SceneNode*node2=node->
createChildSceneNode("
node2"
4.现在,在setPosition()函数之后,调用下面的一行来缩放模型:
scale(2.0f,2.0f,2.0f);
5.创建一个新的实体:
6.现在我们调用第三步中同样的函数,但是添加一个新增的参数
SceneNode*node3=node->
node3"
Ogre:
Vector3(20,0,0));
7.在调用完函数之后,插入这一行以缩放模型:
scale(0.2f,0.2f,0.2f);
9.编译程序并运行,然后就就看到下面的图片了:
我们创建一个有缩放模型的场景。
在第三步之前没有什么特别的发生。
然后我们使用了一个新函数,即是——node->
)。
这个函数是场景结点的一个成员函数并且用给定的名字创建新的场景结点,并且当调用函数时,直接添加新结点到指定的父结点。
因此,node2被添加为node的子结点。
在第四步中,我们对场景结点使用了scale()函数。
函数使用数组(x,y,z)来表示场景结点是如何缩放的。
想,x,y,z轴都是参数因子,如(0.5,1.0,2.0)表示场景结点应该在X轴上缩小一半,在Y轴保持不变,在Z轴放大一倍。
当然,从严格意义上说,场景结点是不能缩放的,它只保存着不被渲染元数据。
更严格的说每个渲染的对象将会代替原有结点造成缩放。
所以说,结点只是一个关联子结点和渲染对象的容器和参考框架。
在第六步中,我们又使用了createChildSceneNode()函数,但是这次有更多的参数。
在这个函数中的第二个参数接收一个我们常用的数组(x,y,z)。
Ogre3D也有自己调用(x,y,z)的类Ogre:
Vector3。
除了储存数组,这个类提供了实现基础操作的函数。
使它们可以使用线代中的三维向量。
这个向量描述了当场景结点被创建起来时,结点的变换。
createChildSceneNode()函数使用代替了以下代码:
或者甚至是
node->
node2->
setPosition(20,0,0);
在最后的一段代码可以被替换成
如果我们省略Vector3参数,我们替换第一段代码。
这个函数还有很多版本,我们将会稍后展示。
如果您有点迫不及待了,请浏览Ogre3D的网上文档http:
//www.ogre3d.org/docs/api/html/index.html
。
除了scale()函数,也有一个setScale()函数。
这两个函数之间的不同就好像setPosition()和translate()函数一样。
简单测试——创建一个场景子结点
1.说出调用createChildSceneNode()函数的两种不同方式
2.一下这行如果不用createChildSceneNode()用何代码来代替?
node1"
Vector3(10,20,30));
这行代码可以用三行代码来代替。
第一行是创建场景结点,第二行是变换结点,第三行是把它绑定到node结点上。
translate(Ogre:
让英雄动起来——使用createChildSceneNode()函数
使用createChildSceneNode()函数来重构这章你写的所有代码。
用聪明的方式使用场景图
在这部分,我们将会学习如何使用场景绘图的一些特性使得场景绘图更加简单。
这也将会扩展我们关于场景图的认识
实践时刻——使用场景结点创建树。
这次,我们将会使用除Sinbad的另一个模型。
1.移去createScene()函数中的所有代码。
2.用我们之前的方法创建一个Sinbad。
3.现在创建一个可以到处跟着Sinbad移动的ninja。
(译者注:
Sinbad是天方夜谭中的水手辛巴达,ninja是日本忍者。
)
MyEntitysNinja"
ninja.mesh"
setScale(0.02f,0.02f,0.02f);
attachObject(ent