저번에 했던 플레이어 회전을 오일러에서 쿼터니온으로 변경하였다. 

이유는.


DirectX에서 구면 선형 보간 함수인 D3DXQuaternionSLerp 함수를 제공해주기 때문이다


D3DXQUATERNION *D3DXQuaternionSlerp(      

    D3DXQUATERNION *pOut,     CONST D3DXQUATERNION *pQ1,     CONST D3DXQUATERNION *pQ2,     FLOAT t );

참고 : Microsoft DirectX 9.0



일단 영상을 보자




구면선형보간과 그냥 선형보간의 다른 점을 찾아봤는데 

만약 정원에 있는 


두 점을 


선형보간을하게 되면 원의 표면을 따라가는 곡선이 아니라 원이 관통되어 직선이 그어진다


구면 선형 보간을 하게되면 원의 표면을 따라가는 곡선이 그어져 더 부드럽게 보간된다고 한다.


나는 카메라가 그냥 직선으로 보간되어 회전되는것 보단 구면 선형 보간을 사용해 원을 그리듯이 회전하면 더욱 부드러울꺼 같아서 사용했다.


일단 PlayerAIrPlane 클래스에 부모클래스인 AirPlane 클래스에다가 쿼터니온을 사용하여 비행기를 회전하는 함수를 만들었다 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//AirPlane.cpp
//Quaternion == D3DXQUATERNION
void AirPlane::YawRotation(float fAngle)
{
    Quaternion qRot;
    D3DXQuaternionRotationAxis(&qRot, &vAxis[E_AXIS_UP], fAngle);
    transform->qRot = transform->qRot * qRot;
    
    Matrix matRot;
    D3DXMatrixRotationQuaternion(&matRot, &qRot);
    D3DXVec3TransformNormal(&vAxis[E_AXIS_RIGHT], &vAxis[E_AXIS_RIGHT], &matRot);
    D3DXVec3TransformNormal(&vAxis[E_AXIS_FORWARD], &vAxis[E_AXIS_FORWARD], &matRot);
}
void AirPlane::RollRotation(float fAngle)
{
    Quaternion qRot;
    D3DXQuaternionRotationAxis(&qRot, &vAxis[E_AXIS_FORWARD], fAngle);
    transform->qRot = transform->qRot * qRot;
    Matrix matRot;
    D3DXMatrixRotationQuaternion(&matRot, &qRot);
    D3DXVec3TransformNormal(&vAxis[E_AXIS_RIGHT], &vAxis[E_AXIS_RIGHT], &matRot);
    D3DXVec3TransformNormal(&vAxis[E_AXIS_UP], &vAxis[E_AXIS_UP], &matRot);
}
void AirPlane::PitchRotation(float fAngle)
{
    Quaternion qRot;
    D3DXQuaternionRotationAxis(&qRot, &vAxis[E_AXIS_RIGHT], fAngle);
    transform->qRot = transform->qRot * qRot;
    Matrix matRot;
    D3DXMatrixRotationQuaternion(&matRot, &qRot);
    D3DXVec3TransformNormal(&vAxis[E_AXIS_FORWARD], &vAxis[E_AXIS_FORWARD], &matRot);
    D3DXVec3TransformNormal(&vAxis[E_AXIS_UP], &vAxis[E_AXIS_UP], &matRot);
}
void AirPlane::SetAirPlaneMatrix()
{
    if (fRollAngle)
        AirPlane::RollRotation(fRollAngle);
    if (fYawAngle)
        AirPlane::YawRotation(fYawAngle);
    if (fPitchAngle)
        AirPlane::PitchRotation(fPitchAngle);
 
    if (fRollAngle || fYawAngle || fPitchAngle)
        fRollAngle = fYawAngle = fPitchAngle = 0.f;
 
    D3DXMatrixTranslation(&transform->matPos, transform->pos.x, transform->pos.y, transform->pos.z);
    D3DXMatrixScaling(&transform->matScale, transform->scale.x, transform->scale.y, transform->scale.z);
}
 
cs

각각 비행기의 YawRotation RollRotation PitchRotation 함수를 구현해놨다. 


AirPlane 클래스에 멤버변수에는


1
2
3
    float fYawAngle;
    float fPitchAngle;
    float fRollAngle;
cs


이 값을 변경해주면 


1
2
3
4
5
6
7
8
9
10
11
12
void AirPlane::SetAirPlaneMatrix()
{
    if (fRollAngle)
        AirPlane::RollRotation(fRollAngle);
    if (fYawAngle)
        AirPlane::YawRotation(fYawAngle);
    if (fPitchAngle)
        AirPlane::PitchRotation(fPitchAngle);
 
    if (fRollAngle || fYawAngle || fPitchAngle)
        fRollAngle = fYawAngle = fPitchAngle = 0.f;
 
cs

이 함수에서 위 변수의 값으로 각각의 회전함수를 써서 비행기를 회전한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//PlayerAirPlane.cpp
void PlayerAirplane::Update()
{
    InputMouse();
    InputKeyboard();
 
    if (fSpeed > fMaxSpeed)
        fSpeed -= PlayerUnAccel;
 
    transform->pos += vAxis[E_AXIS_FORWARD] * (fSpeed * Et);
 
    AirPlane::SetAirPlaneMatrix();
 
    if (KEYDOWN(VK_SPACE))
    {
        Vector3 LeftFirePos = Vector3(-20.f, 0.f, 30.f);
        Vector3 RightFirePos = Vector3(20.f, 0.f, 30.f);
 
        D3DXMATRIX matRot;
        D3DXMatrixRotationQuaternion(&matRot, &transform->qRot);
        memcpy(&matRot._41, transform->pos, sizeof(Vector3));
 
        D3DXVec3TransformCoord(&LeftFirePos, &LeftFirePos, &matRot);
        D3DXVec3TransformCoord(&RightFirePos, &RightFirePos, &matRot);
 
        OBJECT.AddObject<PlayerBullet>()
            ->SetBullet(LeftFirePos, transform->qRot, 2000.f, 1.f);
        OBJECT.AddObject<PlayerBullet>()
            ->SetBullet(RightFirePos, transform->qRot, 2000.f, 1.f);
    }
 
    CamreaSetting();
}
 
void PlayerAirplane::InputMouse()
{
    Vector2 vGap(INPUT.GetMouseGap());
 
    float XGap = D3DXToRadian(vGap.x) * 0.05f;
    float YGap = D3DXToRadian(vGap.y) * 0.05f;
 
    if (XGap)
        fRollAngle = (XGap * -1);
    if (YGap)
        fPitchAngle = (YGap * -1);
}
 
void PlayerAirplane::InputKeyboard()
{
    if (KEYPRESS(VK_UP))
        fPitchAngle = PlayerPitchAngle;
    if (KEYPRESS(VK_DOWN))
        fPitchAngle = -PlayerPitchAngle;
    
    if (KEYPRESS(VK_LEFT))
        fRollAngle = PlayerRollAngle;
    if (KEYPRESS(VK_RIGHT))
        fRollAngle = -PlayerRollAngle;
 
    if (KEYPRESS('A'))
        fYawAngle = -PlayerYawAngle;
    if (KEYPRESS('D'))
        fYawAngle = PlayerYawAngle;
 
    if (KEYPRESS('C'))
        bCameraBack = true;
    else if (KEYPRESS(VK_MBUTTON))
        bCameraBack = true;
    else
        bCameraBack = false;
 
    if (KEYPRESS('W'))
    {
        if(fSpeed < fMaxSpeed)
            fSpeed += PlayerAccel;
    }
    
    if (KEYPRESS('S'))
    {
        if(fSpeed > 0.f)
            fSpeed -= PlayerUnAccel;
    }
 
    if (KEYPRESS(VK_LSHIFT))
        fMaxSpeed = 800.f;
    else
        fMaxSpeed = 500.f;    
}
 
void PlayerAirplane::CamreaSetting()
{
    Vector3 vCameraDir = Vector3(0.f, 0.f, bCameraBack ? 1.f : -1.f);
 
    D3DXQuaternionSlerp(&qCameraRot, &qCameraRot, &transform->qRot, 0.1f);
 
    D3DXMATRIX matCamreaRot;
    D3DXMatrixRotationQuaternion(&matCamreaRot, &qCameraRot);
    
    
    //vCameraPos;
    D3DXMATRIX matInitCameraRot;
    D3DXMatrixRotationX(&matInitCameraRot, bCameraBack ? -fCameraAngle : fCameraAngle);
 
    D3DXVec3TransformNormal(&vCameraDir, &vCameraDir, &matInitCameraRot);
    D3DXVec3TransformNormal(&vCameraDir, &vCameraDir, &matCamreaRot);
    vCameraPos = transform->pos + vCameraDir * fCameraDistance;
 
    //Up
    vCameraUp = Vector3(0.f, 1.f, 0.f);
    D3DXVec3TransformNormal(&vCameraUp, &vCameraUp, &matCamreaRot);
 
    //LookAt
    vCameraLookAt = Vector3(0.f, 50.f, 0.f);
    memcpy(&matCamreaRot._41, &transform->pos, sizeof(Vector3));
    
    D3DXVec3TransformCoord(&vCameraLookAt, &vCameraLookAt, &matCamreaRot);
    
 
    CAMERA.SetCameraPos(vCameraPos, false0.f);
    CAMERA.SetCameraLookAt(vCameraLookAt, false0.f);
    CAMERA.SetCameraUp(vCameraUp, false0.f);
}
cs


PlayerAirPlane 클래스를 보면 입력에 따라 fYawAngle, fPitchAngle, fRollAngle 변수를 변경해서


80 번째줄 AirPlane::SetAirPlaneMatrix(); 으로 회전을 반영시켰다.


그리고


162번째 줄 

1
void PlayerAirplane::CamreaSetting()
cs


함수에서 구면보간 함수인 D3DXQuaternionSlerp 함수를 써서 카메라의 회전을 구면 보간하는것을 알 수있다.






 

드디어 오늘 플레이어 이동 & 카메라 구현을 완료했다. 


처음 만드는 3D게임이여서 어려웠던거 같다. 

그래도 만들면서 카메라 회전이나 회전행렬등을 이해할 수 있었다



구현 영상

조작은 마우스로도 할 수 있고 키보드 방향키로도 할 수 있다


구현 코드 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
void PlayerAirplane::InputMouse()
{
    // vGap : 전프레임과 현재 프레임의 마우스 위치의 차이.
    Vector2 vGap(INPUT.GetMouseGap());
 
    //fForwardAngle : Forward축으로 돌릴 회전값
    float fForwardAngle = -(D3DXToRadian(vGap.x) * 0.05f);
    //fRightAngle : Right축으로 돌릴 회전값
    float fRightAngle = -(D3DXToRadian(vGap.y) * 0.05f);
 
    //키보드 사용시 
    if (KEYPRESS(VK_UP))
        fRightAngle += D3DXToRadian(3.f);
    if (KEYPRESS(VK_DOWN))
        fRightAngle -= D3DXToRadian(3.f);
 
    if (KEYPRESS(VK_RIGHT))
        fForwardAngle -= D3DXToRadian(3.f);
    if (KEYPRESS(VK_LEFT))
        fForwardAngle += D3DXToRadian(3.f);
 
    // 각 축의 회전행렬과 초기화
    D3DXMATRIX matForwardRot;
    D3DXMatrixIdentity(&matForwardRot);
 
    D3DXMATRIX matRightRot;
    D3DXMatrixIdentity(&matRightRot);
 
 
#pragma region AIRPLANE ROTATION
    // 플레이어를 회전 코드
 
    if (fForwardAngle)
    {
        // Forward 축으로 회전한 Matrix를 다른축에다 적용시켜준다 
        D3DXMatrixRotationAxis(&matForwardRot, &vAxis[E_AXIS_FORWARD], fForwardAngle);
        
        D3DXVec3TransformNormal(&vAxis[E_AXIS_RIGHT], &vAxis[E_AXIS_RIGHT], &matForwardRot);
        D3DXVec3TransformNormal(&vAxis[E_AXIS_UP], &vAxis[E_AXIS_UP], &matForwardRot);
    }
 
    if (fRightAngle)
    {
        // Right 축으로 회전한 Matrix를 다른축에다 적용시켜준다 
        D3DXMatrixRotationAxis(&matRightRot, &vAxis[E_AXIS_RIGHT], fRightAngle);
        
        D3DXVec3TransformNormal(&vAxis[E_AXIS_FORWARD], &vAxis[E_AXIS_FORWARD], &matRightRot);
        D3DXVec3TransformNormal(&vAxis[E_AXIS_UP], &vAxis[E_AXIS_UP], &matRightRot);
    }
 
    if (fRightAngle != 0 || fForwardAngle != 0)
        transform->matRot = transform->matRot * matForwardRot * matRightRot;    
 
#pragma endregion AIRPLANE ROTATION
 
#pragma region CAMERA_SETTING    
    // 카메라를 회전 코드
    // 구한 회전 행렬을 플레이어와 카메라위치, 카메라LookAt위치에 대한 방향에 적용시켜준다. 
    if (fForwardAngle)
    {
        D3DXVec3TransformNormal(&vCameraLookAtDir, &vCameraLookAtDir, &matForwardRot);
        D3DXVec3TransformNormal(&vCameraDir, &vCameraDir, &matForwardRot);
    }
 
    if (fRightAngle)
    { 
        D3DXVec3TransformNormal(&vCameraLookAtDir, &vCameraLookAtDir, &matRightRot);
        D3DXVec3TransformNormal(&vCameraDir, &vCameraDir, &matRightRot);
    }
    
    //카메라 위치 셋팅
    vCameraPos = transform->pos + (vCameraDir * fCameraDistance);
    vCameraLookAt = transform->pos + (vCameraLookAtDir * fCameraLookAtDistance);
 
    CAMERA.SetCameraInfo(vCameraPos, vCameraLookAt, vAxis[E_AXIS_UP]);
#pragma endregion CAMERA_SETTING
 
}
 
void PlayerAirplane::InputKeyboard()
{
    if (KEYPRESS('W'))
        transform->pos += vAxis[E_AXIS_FORWARD] * (200 * Et);
    if (KEYPRESS('S'))
        transform->pos -= vAxis[E_AXIS_FORWARD] * (200 * Et);
 
}
 
 
 
cs

각 코드의 설명은 주석으로 작성해놨다.


알아두고 가야할 요소들.


* 값의 변화를 잘 알아두고 작업하자 


144줄과 150줄 사이에서 Forward벡터를 축으로한 회전이 이루어진다

그리고 153줄과 160줄 사이에서 Right축으로한 회전이 이루어지면서

Forward벡터가 변경되는데

여기서 입력이 동시에 이루어진다면 Forward벡터는 변경되기 전 값으로 

계속 먼저 회전되고나서 값의 변경이 이루어진다. 

이 값이 계속 누적되면 카메라가 플레이어를 똑바로 바라보지않고 오차가 생기게된다


--------------------------------------------------------------------------

만들때는 어려웠지만 만들고 보닌까 이해가 됐다. 



+ Recent posts