65650
タイトル通り3Dアクションゲームでよくあるカメラがキャラクターの周りを回転してプレイヤーを中心にカメラ移動するプログラムを作りたいのですが
"プレイヤーを中心に"という処理の値の作り方？がわからない。クォータニオンを使いました。

またコンソールアプリで縦横の値の動きを調べましたがどうやら前の回転するときにX軸とZ軸が  
(X,Yの座標系だと考えた時)に今向いている座標の方に向かって原点0,0を通ろるはずですがX軸の値が上がらないためおかしいと思うのですがこれはどうしてなのでしょうか？  
三次元の場合ではXの値が上がってYつまりZ軸の値も上がりながらY軸の値も上がるはずです。  
初期座標(-6,5,-6)当の場合

参考サイトの③の式を参考にしました。  
参考サイト: <http://marupeke296.com/DXG_No10_Quaternion.html>

[![画像の説明をここに入力](https://i.stack.imgur.com/4bk50.png)](https://i.stack.imgur.com/4bk50.png)

```

    #include "Vector.hpp"
    #include <iostream>
    using namespace std;
    #define PI_F 3.14159265358979323846264338327950288f
    
    
    /*回転する座標、ラジアン、中心、軸*/
    Vector rotate(Vector pos, const float ang, const Vector targetV, const Vector axis)
    {
    
        //Quaternion Prev(0, Vector(*x - mx, *y - my, *z - mz));
    
    //  Quaternion P(0, Vector(pos.x, pos.y, pos.z));//回転させる点
        Quaternion P2(0, Vector(pos.x - targetV.x, pos.y - targetV.y, pos.z - targetV.z));//回転させる点
    //  Quaternion PP(0, Vector(targetV.x - pos->x, targetV.y - pos->y, targetV.z - pos->z));//回転させる点
    //  Quaternion PP3(0, Vector(targetV.x, targetV.y, targetV.z));//回転させる点
    
        Quaternion Q(cos(ang / 2), Vector(axis.x * sin(ang / 2), axis.y * sin(ang / 2), axis.z * sin(ang / 2)));
        Quaternion R(cos(ang / 2), Vector(-axis.x * sin(ang / 2), -axis.y * sin(ang / 2), -axis.z * sin(ang / 2)));
    
        Quaternion result = (R * P2) * Q;//順番が大事
    
        pos.x = result.v.x + targetV.x;
        pos.y = result.v.y + targetV.y;
        pos.z = result.v.z + targetV.z;
    
        return pos;
    
    }
    
    
    Vector cameraPos(-6, 5, -6.0f);//カメラ座標
    //Vector targetPos(0, 5, 12.5f);//注視点座標
    //Vector targetPos(0, 5, 4);//注視点座標
    Vector targetPos(0,0,0);//注視点座標
    const float ROTATE_SPEED = PI_F / 90;//回転スピード
    Vector v = cameraPos;
    
    int main()
    {
    
        printf("cemeraPos 初期座標: %.2f , %.2f , %.2f \n", cameraPos.x, cameraPos.y, cameraPos.z);
    
        for(int i = 0; i < 100; i++){
            v = rotate(v, ROTATE_SPEED, targetPos, Vector(-1, 0, 0));
    //      v = rotate(v,ROTATE_SPEED,targetPos,Vector(0,-1,0));
            printf("cameraPos 移動後座標: %d , %d , %d \n",(int)v.x, (int)v.y, (int)v.z);
        }
    
    
    
    
    
        int _ch = getchar();
        return 0;
    }
    
```

Vector.hpp

```

    #ifndef ___Vector_H
    #define ___Vector_H
    
    //template<typename type>
    class Vector
    {
    private:
    public:
    
        float x;
        float y;
        float z;
    
        Vector(float xx, float yy, float zz);
        Vector();
    
        static Vector cross(Vector a, Vector b);//外積
        static float dot(Vector a, Vector b);//内積
        static Vector mul_scalar(float a, Vector v);//スカラーとベクトルの掛け算
    
        Vector operator = (Vector v);
        Vector operator + (Vector v);
    
    };
    
    //template<typename type>
    class Quaternion
    {
    public:
    
        /*実部*/
        float a;
    
        /*虚部*/
    //  Vector<type> v;
        Vector v;
    
        Quaternion(float aa, Vector vv);
        Quaternion();
    
        Quaternion operator * (Quaternion t);
    
    };
    
    
    
    
    #endif
    
    
```

Vector.cpp

```

    include "Vector.hpp"
    
    
    /*ベクトル　ｘ　スカラーの掛け算*/
    Vector Vector::mul_scalar(float a, Vector v)
    {
        return Vector(a * v.x,a * v.y,a * v.z);
    }
    
    
    /*外積 ベクトル　ｘ　ベクトル*/
    Vector Vector::cross(Vector a,Vector b)
    {
        return Vector( (a.y * b.z) - (a.z * b.y), 
                            (a.z * b.x) - (a.x * b.z), 
                                (a.x * b.y) - (a.y * b.x));
    
    }
    
    /*内積*/
    float Vector::dot(Vector a, Vector b)
    {
        return (a.x * b.x) + (a.y * b.y) + (a.z * a.z);
    }
    
    /*コンストラクタ*/
    Vector::Vector(float xx,float yy,float zz)
    {
        x = xx;
        y = yy;
        z = zz;
    }
    
    /*コンストラクタ*/
    Vector::Vector()
    {
        x = 0.0f;
        y = 0.0f;
        z = 0.0f;
    }
    
    /*オペレーター = */
    Vector Vector::operator = (Vector v)
    {
        this->x = v.x;
        this->y = v.y;
        this->z = v.z;
    
        return *this;
    }
    
    /*オペレーター + */
    Vector Vector::operator + (Vector v)
    {
        Vector t;
        t.x = this->x + v.x;
        t.y = this->y + v.y;
        t.z = this->z + v.z;
    
    
        return t;
    }
    
    
    
    
    /*コンストラクタ　実部、虚部(Vector) */
    Quaternion::Quaternion(float aa, Vector vv)
    {
        a = aa;//実部
    
        /*虚部*/
        v = vv;
    }
    
    
    /*コンストラクタ　引数なし*/
    Quaternion::Quaternion()
    {
    
        a = 0.0f;//実部
    
        /*虚部*/
        v.x = 0.0f;
        v.y = 0.0f;
        v.z = 0.0f;
    }
    
    /*クォータニオン同士の掛け算
    
    A = (a; U)
    B = (b; V)
    AB = (ab - U・V; aV + bU + U×V)
    */
    Quaternion Quaternion::operator * ( Quaternion t )
    {
        return Quaternion(this->a * t.a - Vector::dot(this->v,t.v),Vector(Vector::mul_scalar(this->a,t.v) + Vector::mul_scalar(t.a,this->v) + Vector::cross(this->v,t.v)));
    }
    
    
    
```

[![画像の説明をここに入力](https://i.stack.imgur.com/PuntC.png)](https://i.stack.imgur.com/PuntC.png)
CC BY-SA 4.0
2020-04-05T10:42:53.800
65364
2020-04-15T12:36:09.527
2020-04-15T06:41:43.260
question
c++ directx
クォータニオンによる中心点を中心にラジアン度カメラを回転する視点移動を実装したい。
```

    Quaternion r = Quaternion::mul(R , Q);
    Quaternion result = Quaternion::mul(r , P);
    
```

流れ全体を追ってませんが、これは参考サイトを見る限り

```

    Quaternion r = Quaternion::mul(R, P);
    Quaternion result = Quaternion::mul(r, Q);
    
```

ではないですか?  
四元数の積は交換法則を満たしません。  
R・Q・Pは必ずしもR・P・Qとは一致しないので、R・P・Qを計算したいのであれば、その順序を入れ替えてはいけません。

* * *

>
> 左右に一次変換の関数rotate();を実行して、上下に動かすrotate_x();関数を実行するとカメラがその座標を中心に回ってしまいクォータニオンされません。どういうことなのでしょうか？

このコメントだけだとどうなるはずのところがどうなったのかがよくわかりません。  
いずれにしてもrotate_xのコードには、試行錯誤の結果と思われるコメントアウトされてるコードや参考サイトのやり方から追加されてるコードがあるので、それも戻さないと正しく動きません。
CC BY-SA 4.0
2020-04-05T13:26:47.590
65370
2020-04-06T02:20:08.013
2020-04-06T02:20:08.013
33033
33033
65364
answer
グローバル座標とローカル座標というものがあります。  
グローバル座標はある空間内で共通の座標です。一方ローカル座標は空間内の物体、例えばカメラやNPCなどから見た座標です。3D処理を考える上では必須の概念なのでもし知らなければ勉強してください。

ある空間に対して複数の座標系を考えることができ、同じ地点でもグローバル座標系、ローカル座標系A, ローカル座標系B, ... によって異なる (x,y,z)
で表現できます。  
逆に言うと同じ (x,y,z) であっても座標系が異なると別の地点を指すことになります。

質問中の回転軸 (-1, 0, 0) はカメラ中心のローカル座標を意識していると思います。  
しかしカメラの座標 `v` およびターゲットの座標 `targetPos` はグローバル座標です。  
そのためカメラ座標を変更する処理はグローバル座標系で取り扱われ、(-1, 0, 0) もグローバル座標と見なされ、意図異なる動作になっています。  
カメラ位置がどこにあってもグローバルx軸周りを回転させる、というのが現在の動作です。

解決には回転軸の (x,y,z) をグローバル座標で取得すればよいです。  
ベクトル u を v 方向に回転させる回転軸は u, v 両方に垂直なベクトルであり、これは外積で取得できます。  
今回はカメラ座標を鉛直上に向けて回転させようとしているので次のようにしてみました。

```

    void normalize(Vector* v) {
        float magnitude = sqrt(Vector::dot(*v, *v));
        v->x /= magnitude;
        v->y /= magnitude;
        v->z /= magnitude;
    }
    
    Vector pitch_axis(Vector camera, Vector target) {
        Vector rel = Vector(
            camera.x - target.x,
            camera.y - target.y,
            camera.z - target.z
        );
    
        Vector y = Vector(0, 1, 0);
    
        Vector n = Vector::cross(y, rel);
        normalize(&n);
    
        return n;
    }
    
    int main()
    {
        printf("cemeraPos 初期座標: %.2f , %.2f , %.2f \n", cameraPos.x, cameraPos.y, cameraPos.z);
    
        for (int i = 0; i < 100; i++) {
            Vector axis = pitch_axis(v, targetPos);
            v = rotate(v, ROTATE_SPEED, targetPos, axis);
    
            printf("cameraPos 移動後座標: %6.2f, %6.2f, %6.2f, %6.2f \n", v.x, v.y, v.z, v.z / v.x);
        }
    
        int _ch = getchar();
        return 0;
    }
    
```

カメラがターゲットのちょうど真上や真下にあるときは NaN
が出ると思いますが、実際そのような状態ではどのように回転すべきなのか決められないのでしょうがないです。例外処理を入れたり、そもそもそういう状態にならないように工夫してください。
CC BY-SA 4.0
2020-04-15T12:36:09.527
65650
2020-04-15T12:36:09.527
20885
65364
answer