您的位置:首页 > 其它

渲染器中的场景节点运动

2013-01-31 18:03 127 查看
一般渲染器都有场景节点的概念,用于挂载可渲染可运动的实体。

运动是相对的,这里主要写3种运动空间:

TS_LOCAL:本地空间

TS_PARENT:相对空间

TS_WORLD:世界空间,所有变换的起始空间,这不同于物理任何运动都是相对的

以下是场景节点的头文件:

#ifndef EGG_SCENENODE_H
#define EGG_SCENENODE_H

#include <vector>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
using namespace std;
using namespace glm;

enum TransformSpace{
TS_LOCAL,TS_PARENT,TS_WORLD
};

class SceneNode{
public:
void translate(const vec3& value,TransformSpace ts);
void setPosition(const vec3& value,TransformSpace ts);
void rotate(float degree,const vec3& axis,TransformSpace ts);

inline mat4 getRelTransform(){
mat4 ret=toMat4(mRelOrientation);
ret[3]=vec4(mRelPosition,1.0);
return ret;
}
protected:
void updateTransform();
protected:
SceneNode* mParent;
vector<SceneNode*> mChildren;
vec3 mRelPosition;
quat mRelOrientation;
mat4 mAbsTransform;
};

#endif


为节省内存,和运动相关的信息只存了相对的位置mRelPosition和朝向mRelOrientation,朝向用四元数表示。

为提高效率,需要存一下绝对变换mAbsTransform,因为这货每一帧都要提交到gpu,不能渲染前重复去计算。

这里没有写缩放变换,因为类似且比较简单。

复习一下矩阵变换的意义,先声明,这是非官方非严谨且是个人理解的M*P=Q

最简单的构造变换矩阵的方法是确定新的坐标系的xyz,也就是基,将它们逐列排成一个矩阵(这里用的是列优先矩阵,与glsl一致)

M就是把新坐标系中的P变换为父坐标系的Q

以下是SceneNode的实现:

#include "SceneNode.h"

void SceneNode::translate(const vec3& value,TransformSpace ts){
vec3 relDist;
if(ts==TS_LOCAL){
relDist=mRelOrientation*value;
}else if(ts==TS_PARENT){
relDist=value;
}else if(ts==TS_WORLD){
relDist=mRelOrientation*(inverse(mat3(mAbsTransform))*value);
}
setPosition(relDist,TS_PARENT);
}

void SceneNode::setPosition(const vec3& value,TransformSpace ts){
if(ts==TS_LOCAL){
mRelPosition=vec3(getRelTransform()*vec4(value,1.0));
}else if(ts==TS_PARENT){
mRelPosition=value;
}else if(ts==TS_WORLD){
vec4 localPos=inverse(mAbsTransform)*vec4(value,1.0);
mRelPosition=vec3(getRelTransform()*localPos);
}
updateTransform();
}

void SceneNode::rotate(float degree,const vec3& axis,TransformSpace ts){
vec3 rotAxis;
if(ts==TS_LOCAL){
rotAxis=mRelOrientation*axis;
}else if(ts==TS_PARENT){
rotAxis=axis;
}else if(ts==TS_WORLD){
rotAxis=mat3(inverse(mAbsTransform))*axis;
rotAxis=mRelOrientation*axis;
}
rotAxis=normalize(rotAxis);
mRelOrientation=angleAxis(degree,rotAxis)*mRelOrientation;
mRelOrientation=normalize(mRelOrientation);
updateTransform();
}

void SceneNode::updateTransform(){
mAbsTransform=mParent->mAbsTransform*getRelTransform();
for(int i=0;i<mChildren.size();i++){
mChildren[i]->updateTransform();
}
}


可见都是以父节点作为基准,因为SceneNode存的是相对位置与相对朝向,运动时需要改变这两个属性。

当value是本地坐标时需要把local位置转变为相对位置 mRelOrientation*value

当value是世界坐标时需要把世界坐标转化为本地坐标再转化为相对坐标 mRelOrientation*(inverse(mat3(mAbsTransform))*value)

这里只是演示一个可以正确运行的代码,实际代码并不会这么写,因为效率低,优化的可以参考ogre的Node和SceneNode
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: