GRE的空間變換,對(duì)于新手來說是一個(gè)頭疼的問題,而其中的陷阱也是一堆一堆,即使我已經(jīng)爬出這些陷阱,我還是覺得有必要講一下.
translate()這個(gè)神秘的函數(shù).網(wǎng)上發(fā)現(xiàn)一個(gè)朋友中了陷阱:
以下黑體字為論壇某個(gè)網(wǎng)友的錯(cuò)誤理解:
----------------------------------------------------------------------------------------------------------------------------------
在CSDN和gameres上都發(fā)了這個(gè)問題,一直沒人解答,不知道這兒怎么樣?
void createScene() {
... //第一個(gè)物體(坐標(biāo)原點(diǎn))
Entity* head = mSceneMgr->createEntity("object1", "ogrehead.mesh");
head->setMaterialName("Examples/Rockwall");
SceneNode* node1 = rootNode->createChildSceneNode();
node1->attachObject(head);
//第二個(gè)物體
head = mSceneMgr->createEntity("object2", "ogrehead.mesh");
SceneNode* node2 = node1->createChildSceneNode();
node2->attachObject(head);
node2->translate(Vector3(50, 0, 0), SceneNode::TS_PARENT);
//第三個(gè)物體
head = mSceneMgr->createEntity("object3", "ogrehead.mesh");
SceneNode* node3 = node2->createChildSceneNode();
node3->attachObject(head);
node3->translate(Vector3(0, 50, 0), SceneNode::TS_WORLD);
node3->yaw(Degree(90), SceneNode::TS_LOCAL);
}
按照空間變換分析,node1在原點(diǎn),node2的世界坐標(biāo)是(50, 0, 0),node3的世界坐標(biāo)是(0, 50, 0),
但是從顯示的結(jié)果來看node3是(50, 50, 0),
無論用哪個(gè)TransformSpace值,結(jié)果都是一樣的:translate都是相對(duì)于parent,旋轉(zhuǎn)都是在loacal中。
我對(duì)這個(gè)參數(shù)的理解是:
TS_WORLD:不管當(dāng)前節(jié)點(diǎn)是在哪個(gè)節(jié)點(diǎn)下,他的操作都是相對(duì)于世界坐標(biāo)系的原點(diǎn)的。
如node3是在node2下面,所以他的初始世界坐標(biāo)應(yīng)該是(50,0,0),
如果它translate(Vector3(0, 50, 0), SceneNode::TS_LOCAL), 世界坐標(biāo)應(yīng)該是(50, 50, 0)
如果它translate(Vector3(0, 50, 0), SceneNode::TS_WORLD), 相對(duì)于世界坐標(biāo)原點(diǎn)的平移,世界坐標(biāo)應(yīng)該是(0, 50, 0)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
這個(gè)網(wǎng)友遇到的問題,其實(shí)就是translate變換的問題,為什么他會(huì)這么理解,我們慢慢分析:
translate()這個(gè)函數(shù),其實(shí)是相對(duì)于父節(jié)點(diǎn)的相對(duì)移動(dòng),而不是那個(gè)網(wǎng)友理解的絕對(duì)移動(dòng).
但是如果是相對(duì)于父節(jié)點(diǎn)的相對(duì)移動(dòng),還要選擇3個(gè)參考空間干嘛???
這就是關(guān)鍵所在,這3個(gè)參考空間,是決定相對(duì)移動(dòng)量的方向的,而不是那個(gè)網(wǎng)友理解成的移動(dòng)的位置!!!
為什么他的代碼中的第3個(gè)節(jié)點(diǎn)無論怎么改變參考空間結(jié)果都一樣?如果理解成方向,就豁然開朗了.
因?yàn)槭澜缭c(diǎn),父節(jié)點(diǎn),他本身,這3個(gè)節(jié)點(diǎn)的面向的方向都是一樣的(默認(rèn)-Z方向),所以相對(duì)移動(dòng)的值是一樣,都是Y方向相對(duì)移動(dòng)50
如果世界原點(diǎn),父節(jié)點(diǎn)都是默認(rèn)面向Z正方向,本身節(jié)點(diǎn)旋轉(zhuǎn)90度面向Y的方向
如果他在本地空間translate(Vector3(0, 50, 0),SceneNode::TS_LOCAL),
因?yàn)樗旧沓験, 在本地空間Y方向移動(dòng)50, 在世界空間,其實(shí)就是-Z方向移動(dòng)了50.
一句話:都是相對(duì)父節(jié)點(diǎn)移動(dòng)50單位,而參考空間只是決定這50個(gè)單位移動(dòng)的方向!
所以參考空間理解成朝向,就對(duì)了
世界空間----------認(rèn)定世界原點(diǎn)的朝向?yàn)闃?biāo)準(zhǔn)朝向,默認(rèn)-Z方向
父節(jié)點(diǎn)空間----------認(rèn)定父節(jié)點(diǎn)的朝向?yàn)闃?biāo)準(zhǔn)朝向
本地空間--------------認(rèn)定本身節(jié)點(diǎn)的朝向?yàn)闃?biāo)準(zhǔn)朝向
那個(gè)網(wǎng)友的理解,其實(shí)合情合理,他的理解其實(shí)就是下面假想的函數(shù):
setPosition(TS_WORLD);
setPosition(TS_PARENT);
setPosition(TS_LOCAL);
可惜這只是假想,設(shè)置位置的函數(shù)并沒有參考空間可以選~~~
OGRE為什么不設(shè)置這樣的函數(shù)呢?因?yàn)槠鋵?shí)已經(jīng)有替代的函數(shù)了:
setPosition() // 相對(duì)父空間坐標(biāo)
setDerivedPosition() // 世界空間絕對(duì)坐標(biāo)
而本地空間的設(shè)置位置函數(shù)根本不需要....
最后,回頭一想,為什么容易把translate()理解錯(cuò)....原因就是translate(TS_PARENT)相對(duì)父空間移動(dòng)
的時(shí)候,如果把參考空間父空間理解成方向或者位置,2種情況下結(jié)果是正好是一樣的,巧合啊
就是這種巧合,暗藏了一個(gè)陷進(jìn),讓很多人認(rèn)為理解成位置正好是對(duì)的...
不信看源碼:
void Node::translate(const Vector3& d, TransformSpace relativeTo)
{
switch(relativeTo)
{
case TS_LOCAL:
// position is relative to parent so transform downwards
mPosition += mOrientation * d;
break;
case TS_WORLD:
// position is relative to parent so transform upwards
if (mParent)
{
mPosition += (mParent->_getDerivedOrientation().Inverse() * d)
/ mParent->_getDerivedScale();
}
else
{
mPosition += d;
}
break;
case TS_PARENT:
mPosition += d;
break;
}
needUpdate();
}
因?yàn)閙Position相對(duì)于父節(jié)點(diǎn)的位置和方向,
所以計(jì)算結(jié)果都要換算成父空間相加.
本地空間,朝本身節(jié)點(diǎn)的朝向移動(dòng)d,換到mPosition的所在的父空間只需要旋轉(zhuǎn)一定角度,
這個(gè)角度應(yīng)該是本身節(jié)點(diǎn)方向和父節(jié)點(diǎn)的方向的夾角,正好是mOrientation
mPosition += mOrientation * d;
世界空間,朝世界原點(diǎn)的朝向移動(dòng)d ,換到mPosition的所在父空間只需要旋轉(zhuǎn)一定角度,
這個(gè)角度應(yīng)該是世界原點(diǎn)方向和父節(jié)點(diǎn)的方向的夾角,正好是父節(jié)點(diǎn)世界絕對(duì)角度再取反.
(為什么要取反,2個(gè)四元數(shù)相乘是不能交換的,因?yàn)槭澜绻?jié)點(diǎn)和父節(jié)點(diǎn)夾角 != 父節(jié)點(diǎn)和世界節(jié)點(diǎn)夾角,正好相反)
mPosition += (mParent->_getDerivedOrientation().Inverse() * d)
/ mParent->_getDerivedScale();
父節(jié)點(diǎn)空間,朝父節(jié)點(diǎn)的朝向移動(dòng)d ,換到mPosition的所在父空間只需要旋轉(zhuǎn)一定角度,
父空間轉(zhuǎn)到父空間....其實(shí)角度就是一致的
應(yīng)該是mPosition += Quaternion::IDENTITY * d;
等價(jià)于mPosition += d;