四元数问题的旋转骨骼
rotating bone with quaternion issue
我需要旋转骨骼的骨骼,我已经有每个关节对应的四元数;旋转时我很困惑。
Skeleton to move 是我需要移动的 opengl 场景。
我的问题是我无法旋转关节;谁能帮忙
下面是我的代码
//我评估每个关节以获得平移和旋转。
void Node::EvaluatePoi(std::vector<POI> pois, const Vector &par_pos,
const Quaternion &par_rot, Vector Node::*world_pos,std::vector<Arti> joints)
{ Vector poi=this->PoiVec ;
Quaternion rot;
if (pois.empty()){
this->*world_pos= this->rest_position ;//OFFSET
rot= this-> rest_rotation ;//identity
}else{
if(this->name=="Hips")
{
this->*world_pos = eval_instant_positionPOI(poi);
rot= this-> rest_rotation ;// do not rotate
}else if(this->name=="LeftUpLeg")
{
this->*world_pos = this->rest_position;// set to OFFSET
rot= this-> rest_rotation ;// do not rotate
}else if(this->name=="RightUpLeg")
{
this->*world_pos = this->rest_position;
rot= this-> rest_rotation ;
}
else
{
this->*world_pos= this->rest_position;
rot= eval_instant_rotationPOI(joints);
}
}
//Applying transformation on the global position with rot =qparent * qchild
(this->*world_pos).rotate(par_rot);
this->*world_pos += par_pos;
rot = par_rot * rot;
// draw joint's subhierarchy
for (int i = 0; i < n_children; i++)
child[i]->EvaluatePoi(pois, this->*world_pos, rot, world_pos,joints);
}
编辑:
//这里我得到每个关节的局部旋转,然后创建相当于单个欧拉旋转的四元数,然后组合成一个旋转
Vector x_vector(1.0, 0.0, 0.0),
y_vector(0.0, 1.0, 0.0),
z_vector(0.0, 0.0, 1.0);
Quaternion Node::eval_instant_rotationPOI( std::vector<Arti> joints)
{
Quaternion roto;//= new Quaternion();
Quaternion sample;
double t= 0.02;
Vector v;
Vector Euler(0,0,0);;
string x =this->name;
if(x== "Head"){
Euler=GetEulers(joints,JOINT_HEAD);
}else if(x== "Neck"){
Euler=GetEulers(joints,JOINT_NECK);
}
else if(x== "LeftUpArm"){
Euler=GetEulers(joints,JOINT_LEFT_SHOULDER);
}
else if(x== "RightUpArm"){
Euler=GetEulers(joints,JOINT_RIGHT_SHOULDER);
}
else if(x== "LeftLowArm"){
Euler=GetEulers(joints,JOINT_LEFT_ELBOW);
}
else if(x== "LeftHand"){
Euler=GetEulers(joints,JOINT_LEFT_HAND);
}
else if(x== "RightLowArm"){
Euler=GetEulers(joints,JOINT_RIGHT_ELBOW);
}
else if(x== "RightHand"){
Euler=GetEulers(joints,JOINT_RIGHT_HAND);
}
else if(x== "Hips"){
Euler=GetEulers(joints,JOINT_TORSO);
}
else if(x== "LeftUpLeg"){
Euler=GetEulers(joints,JOINT_LEFT_HIP);
}
else if(x== "RightUpLeg"){
Euler=GetEulers(joints,JOINT_RIGHT_HIP);
}
else if(x== "LeftLowLeg"){
Euler=GetEulers(joints,JOINT_LEFT_KNEE);
}
else if(x== "LeftFoot"){
Euler=GetEulers(joints,JOINT_LEFT_FOOT);
}
else if(x== "RightLowLeg"){
Euler=GetEulers(joints,JOINT_RIGHT_KNEE);
}
else if(x== "RightFoot"){
Euler=GetEulers(joints,JOINT_RIGHT_FOOT);
}
Quaternion qx(x_vector, (Euler.x ));
Quaternion qy(y_vector, (Euler.y ));
Quaternion qz(z_vector, (Euler.z ));
sample = qz * qy * qx;
roto= slerp(qTemp, sample, t);
qTemp=roto;
return roto ;
}
/*这里我将关节和它的父关节相乘得到欧拉角;是否有必要转换成
欧拉角?/
Vector Node::GetEulers(std::vector<Arti> joints, const int idx) {
// Get the quaternion of its parent.
Quaternion q_parent;
Quaternion q_current;
if (idx == JOINT_TORSO) {
q_parent.identity();
}
/////
{
q_parent = Quaternion(joints[parent_joint_map[idx]].quat.x,
joints[parent_joint_map[idx]].quat.y,
joints[parent_joint_map[idx]].quat.z,
joints[parent_joint_map[idx]].quat.w);
}
// Get the quaternion of the joint.
q_current = Quaternion(joints[idx].quat.x, joints[idx].quat.y,
joints[idx].quat.z, joints[idx].quat.w);
// Calculate the relative quaternion.
Quaternion q_delta = quat_left_multiply(q_current , quat_inverse(q_parent));
Vector angle = euler_from_quat(q_delta);
// cout<<this->name<<" "<<angle<<" ";
return angle;
}
Quaternion quat_left_multiply(Quaternion l, Quaternion r) {
Quaternion q = {r.w * l.x + r.x * l.w + r.y * l.z - r.z * l.y,
r.w * l.y + r.y * l.w + r.z * l.x - r.x * l.z,
r.w * l.z + r.z * l.w + r.x * l.y - r.y * l.x,
r.w * l.w - r.x * l.x - r.y * l.y - r.z * l.z};
return q;
}
Vector& Vector::rotate(const Quaternion& q)
{
Quaternion p(x, y, z, 0.0f);
Quaternion qc(q);
qc.conjugate();
Quaternion pp(q * p * qc);
x = pp.x;
y = pp.y;
z = pp.z;
return *this;
}
旋转四元数实际上是将一个四元数乘以另一个。给定表示对象当前旋转的四元数 qA
和表示要应用(添加)到该对象的旋转量的四元数 qB
,该对象的新旋转结果计算如下(伪代码):
qA = qA * qB;
或者,您可以通过交换操作数在所谓的“对象”或“局部”转换中应用(添加)此旋转space:
qA = qB * qA
每个关节都应持有(通常作为 class 成员)一个四元数,表示其在局部 space 中的当前旋转。这可能是您已经完成的。如果您想对该关节应用旋转,那么您只需将关节四元数乘以另一个表示要应用的旋转量的四元数。一个四元数旋转方法可以是这样的(伪代码):
Joint::Rotate(const quaterion& amount, bool local)
{
if(local) {
this->rotation = amount * this->rotation;
} else {
this->rotation = this->rotation * amount;
}
this->rotation.normalize();
}
这就是旋转部分所需要的,没有别的。之后,您需要将联合四元数转换为旋转矩阵,以便与其他联合变换(平移、缩放等)相结合。这是四元数到旋转矩阵转换(伪代码)的一个实现:
Matrix3 QuaternionToMatrix(const quaternion& q)
{
float x2 = q.x + q.x;
float y2 = q.y + q.y;
float z2 = q.z + q.z;
float xx = q.x * x2;
float xy = q.x * y2;
float xz = q.x * z2;
float yy = q.y * y2;
float yz = q.y * z2;
float zz = q.z * z2;
float wx = q.w * x2;
float wy = q.w * y2;
float wz = q.w * z2;
Matrix3 m; //< 3x3 matrix
m[0] = (1.0f - (yy + zz));
m[1] = (xy - wz);
m[2] = (xz + wy);
m[3] = (xy + wz);
m[4] = (1.0f - (xx + zz));
m[5] = (yz - wx);
m[6] = (xz - wy);
m[7] = (yz + wx);
m[8] = (1.0f - (xx + yy));
return m;
}
您可能最终需要的是使用欧拉角而不是四元数值来输入旋转。事实上,当从人类的角度应用旋转时,欧拉角更容易处理和理解。在这种情况下,您需要将输入欧拉角转换为四元数。这是一个欧拉角到四元数转换的可能实现:
Quaternion EulerToQuaternion(float x, float y, float z)
{
float sx = sinf(x * -0.5f);
float cx = cosf(x * -0.5f);
float sy = sinf(y * -0.5f);
float cy = cosf(y * -0.5f);
float sz = sinf(z * -0.5f);
float cz = cosf(z * -0.5f);
Quaternion q;
q.x = sx * cy * cz + cx * sy * sz;
q.y = cx * sy * cz - sx * cy * sz;
q.z = cx * cy * sz + sx * sy * cz;
q.w = cx * cy * cz - sx * sy * sz;
return q;
}
我需要旋转骨骼的骨骼,我已经有每个关节对应的四元数;旋转时我很困惑。
Skeleton to move 是我需要移动的 opengl 场景。
我的问题是我无法旋转关节;谁能帮忙
下面是我的代码
//我评估每个关节以获得平移和旋转。
void Node::EvaluatePoi(std::vector<POI> pois, const Vector &par_pos,
const Quaternion &par_rot, Vector Node::*world_pos,std::vector<Arti> joints)
{ Vector poi=this->PoiVec ;
Quaternion rot;
if (pois.empty()){
this->*world_pos= this->rest_position ;//OFFSET
rot= this-> rest_rotation ;//identity
}else{
if(this->name=="Hips")
{
this->*world_pos = eval_instant_positionPOI(poi);
rot= this-> rest_rotation ;// do not rotate
}else if(this->name=="LeftUpLeg")
{
this->*world_pos = this->rest_position;// set to OFFSET
rot= this-> rest_rotation ;// do not rotate
}else if(this->name=="RightUpLeg")
{
this->*world_pos = this->rest_position;
rot= this-> rest_rotation ;
}
else
{
this->*world_pos= this->rest_position;
rot= eval_instant_rotationPOI(joints);
}
}
//Applying transformation on the global position with rot =qparent * qchild
(this->*world_pos).rotate(par_rot);
this->*world_pos += par_pos;
rot = par_rot * rot;
// draw joint's subhierarchy
for (int i = 0; i < n_children; i++)
child[i]->EvaluatePoi(pois, this->*world_pos, rot, world_pos,joints);
}
编辑: //这里我得到每个关节的局部旋转,然后创建相当于单个欧拉旋转的四元数,然后组合成一个旋转
Vector x_vector(1.0, 0.0, 0.0),
y_vector(0.0, 1.0, 0.0),
z_vector(0.0, 0.0, 1.0);
Quaternion Node::eval_instant_rotationPOI( std::vector<Arti> joints)
{
Quaternion roto;//= new Quaternion();
Quaternion sample;
double t= 0.02;
Vector v;
Vector Euler(0,0,0);;
string x =this->name;
if(x== "Head"){
Euler=GetEulers(joints,JOINT_HEAD);
}else if(x== "Neck"){
Euler=GetEulers(joints,JOINT_NECK);
}
else if(x== "LeftUpArm"){
Euler=GetEulers(joints,JOINT_LEFT_SHOULDER);
}
else if(x== "RightUpArm"){
Euler=GetEulers(joints,JOINT_RIGHT_SHOULDER);
}
else if(x== "LeftLowArm"){
Euler=GetEulers(joints,JOINT_LEFT_ELBOW);
}
else if(x== "LeftHand"){
Euler=GetEulers(joints,JOINT_LEFT_HAND);
}
else if(x== "RightLowArm"){
Euler=GetEulers(joints,JOINT_RIGHT_ELBOW);
}
else if(x== "RightHand"){
Euler=GetEulers(joints,JOINT_RIGHT_HAND);
}
else if(x== "Hips"){
Euler=GetEulers(joints,JOINT_TORSO);
}
else if(x== "LeftUpLeg"){
Euler=GetEulers(joints,JOINT_LEFT_HIP);
}
else if(x== "RightUpLeg"){
Euler=GetEulers(joints,JOINT_RIGHT_HIP);
}
else if(x== "LeftLowLeg"){
Euler=GetEulers(joints,JOINT_LEFT_KNEE);
}
else if(x== "LeftFoot"){
Euler=GetEulers(joints,JOINT_LEFT_FOOT);
}
else if(x== "RightLowLeg"){
Euler=GetEulers(joints,JOINT_RIGHT_KNEE);
}
else if(x== "RightFoot"){
Euler=GetEulers(joints,JOINT_RIGHT_FOOT);
}
Quaternion qx(x_vector, (Euler.x ));
Quaternion qy(y_vector, (Euler.y ));
Quaternion qz(z_vector, (Euler.z ));
sample = qz * qy * qx;
roto= slerp(qTemp, sample, t);
qTemp=roto;
return roto ;
}
/*这里我将关节和它的父关节相乘得到欧拉角;是否有必要转换成 欧拉角?/
Vector Node::GetEulers(std::vector<Arti> joints, const int idx) {
// Get the quaternion of its parent.
Quaternion q_parent;
Quaternion q_current;
if (idx == JOINT_TORSO) {
q_parent.identity();
}
/////
{
q_parent = Quaternion(joints[parent_joint_map[idx]].quat.x,
joints[parent_joint_map[idx]].quat.y,
joints[parent_joint_map[idx]].quat.z,
joints[parent_joint_map[idx]].quat.w);
}
// Get the quaternion of the joint.
q_current = Quaternion(joints[idx].quat.x, joints[idx].quat.y,
joints[idx].quat.z, joints[idx].quat.w);
// Calculate the relative quaternion.
Quaternion q_delta = quat_left_multiply(q_current , quat_inverse(q_parent));
Vector angle = euler_from_quat(q_delta);
// cout<<this->name<<" "<<angle<<" ";
return angle;
}
Quaternion quat_left_multiply(Quaternion l, Quaternion r) {
Quaternion q = {r.w * l.x + r.x * l.w + r.y * l.z - r.z * l.y,
r.w * l.y + r.y * l.w + r.z * l.x - r.x * l.z,
r.w * l.z + r.z * l.w + r.x * l.y - r.y * l.x,
r.w * l.w - r.x * l.x - r.y * l.y - r.z * l.z};
return q;
}
Vector& Vector::rotate(const Quaternion& q)
{
Quaternion p(x, y, z, 0.0f);
Quaternion qc(q);
qc.conjugate();
Quaternion pp(q * p * qc);
x = pp.x;
y = pp.y;
z = pp.z;
return *this;
}
旋转四元数实际上是将一个四元数乘以另一个。给定表示对象当前旋转的四元数 qA
和表示要应用(添加)到该对象的旋转量的四元数 qB
,该对象的新旋转结果计算如下(伪代码):
qA = qA * qB;
或者,您可以通过交换操作数在所谓的“对象”或“局部”转换中应用(添加)此旋转space:
qA = qB * qA
每个关节都应持有(通常作为 class 成员)一个四元数,表示其在局部 space 中的当前旋转。这可能是您已经完成的。如果您想对该关节应用旋转,那么您只需将关节四元数乘以另一个表示要应用的旋转量的四元数。一个四元数旋转方法可以是这样的(伪代码):
Joint::Rotate(const quaterion& amount, bool local)
{
if(local) {
this->rotation = amount * this->rotation;
} else {
this->rotation = this->rotation * amount;
}
this->rotation.normalize();
}
这就是旋转部分所需要的,没有别的。之后,您需要将联合四元数转换为旋转矩阵,以便与其他联合变换(平移、缩放等)相结合。这是四元数到旋转矩阵转换(伪代码)的一个实现:
Matrix3 QuaternionToMatrix(const quaternion& q)
{
float x2 = q.x + q.x;
float y2 = q.y + q.y;
float z2 = q.z + q.z;
float xx = q.x * x2;
float xy = q.x * y2;
float xz = q.x * z2;
float yy = q.y * y2;
float yz = q.y * z2;
float zz = q.z * z2;
float wx = q.w * x2;
float wy = q.w * y2;
float wz = q.w * z2;
Matrix3 m; //< 3x3 matrix
m[0] = (1.0f - (yy + zz));
m[1] = (xy - wz);
m[2] = (xz + wy);
m[3] = (xy + wz);
m[4] = (1.0f - (xx + zz));
m[5] = (yz - wx);
m[6] = (xz - wy);
m[7] = (yz + wx);
m[8] = (1.0f - (xx + yy));
return m;
}
您可能最终需要的是使用欧拉角而不是四元数值来输入旋转。事实上,当从人类的角度应用旋转时,欧拉角更容易处理和理解。在这种情况下,您需要将输入欧拉角转换为四元数。这是一个欧拉角到四元数转换的可能实现:
Quaternion EulerToQuaternion(float x, float y, float z)
{
float sx = sinf(x * -0.5f);
float cx = cosf(x * -0.5f);
float sy = sinf(y * -0.5f);
float cy = cosf(y * -0.5f);
float sz = sinf(z * -0.5f);
float cz = cosf(z * -0.5f);
Quaternion q;
q.x = sx * cy * cz + cx * sy * sz;
q.y = cx * sy * cz - sx * cy * sz;
q.z = cx * cy * sz + sx * sy * cz;
q.w = cx * cy * cz - sx * sy * sz;
return q;
}