四元数问题的旋转骨骼

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;
}