Une caméra à la première personne (FPS)

Intro :

camera

Pour pouvoir observer nos entités dans une scène 3D, il faut pour cela mouvoir une caméra que l’on peut bouger aisément.

Prérequis :

– Savoir lire et écrire du C++

– Savoir utiliser les objets et fonctions mathématiques de la librairie D3DX

– Savoir ce qu’est une matrice

– Savoir ce que sont les transformations de repère

– Savoir utiliser la classe InputManager utilisée dans le code présenté

– Savoir utiliser les mathématiques des vecteurs (produit vectoriel et produit scalaire)

Explications :

Voici le code tout simplement de notre caméra à la première personne.

A noté l’utilisation de la classe InputManager avec les appels à INPUT_MANAGER dans le code.

Voici le code pour le fichier FPSCamera.h :

#ifndef FPS_CAMERA_H
#define FPS_CAMERA_H

#include <d3d10.h>
#include <d3dx10.h>

class FPSCamera
{
public:
    FPSCamera();
    virtual ~FPSCamera();

    D3DXMATRIX* GetViewMatrix();
    D3DXMATRIX* GetProjectionMatrix();

    D3DXVECTOR3 GetPosition();

    void LookAt(D3DXVECTOR3& target);

    void UpdateLens(float fFov, float fAspect, float fNearZ, float fFarZ);
    
    void SetSpeed(float fSpeed);

    void Update(float dt);

    void Walk(float d);

    void Strafe(float d);

private:
    void BuildView();

private:
    D3DXMATRIX m_view;
    D3DXMATRIX m_proj;

    D3DXVECTOR3 m_pos;

    D3DXVECTOR3 m_right;
    D3DXVECTOR3 m_up;
    D3DXVECTOR3 m_lookAt;

    float m_fSpeed;
};

 

Voici le code pour le fichier FPSCamera.cpp :

#include "FPSCamera.h"

FPSCamera::FPSCamera() :
m_fSpeed(30)
{
    D3DXMatrixIdentity(&m_view);
    D3DXMatrixIdentity(&m_proj);

    m_pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    m_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
    m_up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    m_lookAt = D3DXVECTOR3(0.0f, 0.0f, 1.0f);

    UpdateLens((float)D3DX_PI * 0.25f, (float)D3D10_RENDERER->GetViewportWidth() / (float)D3D10_RENDERER->GetViewportHeight(), 0.1f, 100.0f);
}

FPSCamera::~FPSCamera()
{
}

D3DXMATRIX* FPSCamera::GetViewMatrix()
{
    return &m_view;
}

D3DXMATRIX* FPSCamera::GetProjectionMatrix()
{
    return &m_proj;
}

D3DXVECTOR3 FPSCamera::GetPosition()
{
    return m_pos;
}

void FPSCamera::LookAt(D3DXVECTOR3& target)
{
    D3DXVECTOR3 L = target - m_pos;
    D3DXVec3Normalize(&L, &L);

    D3DXVECTOR3 R;
    D3DXVec3Cross(&R, &m_up, &L);
    D3DXVec3Normalize(&R, &R);

    D3DXVECTOR3 U;
    D3DXVec3Cross(&U, &L, &R);
    D3DXVec3Normalize(&U, &U);

    m_pos = m_pos;
    m_right = R;
    m_up = U;
    m_lookAt = L;

    BuildView();
}

void FPSCamera::UpdateLens(float fFov, float fAspect, float fNearZ, float fFarZ)
{
    D3DXMatrixPerspectiveFovLH(&m_proj, fFov, fAspect, fNearZ, fFarZ);
}
    
void FPSCamera::SetSpeed(float fSpeed)
{
    m_fSpeed = fSpeed;
}

void FPSCamera::Walk(float d)
{
    m_pos += d * m_lookAt;
}

void FPSCamera::Strafe(float d)
{
    m_pos += d * m_right;
}

void FPSCamera::Update(float dt)
{
    if (INPUT_MANAGER->IsKeyDown('Z'))
    {
        Walk(dt * m_fSpeed);
    }

    if (INPUT_MANAGER->IsKeyDown('S'))
    {
        Walk(dt * -m_fSpeed);
    }

    if (INPUT_MANAGER->IsKeyDown('D'))
    {
        Strafe(dt * m_fSpeed);
    }

    if (INPUT_MANAGER->IsKeyDown('Q'))
    {
        Strafe(dt * -m_fSpeed);
    }

    float x = 0.0f;
    float y = 0.0f;

    INPUT_MANAGER->GetMouseRelativePosition(x, y);

    float pitch = y * dt * 1800;
    float yAngle = x * dt * 1800;
    
    /* Contrainte du pitch */

    D3DXMATRIX R;
    D3DXMatrixRotationAxis(&R, &m_right, pitch);

    D3DXVECTOR3 tmpVect;
    D3DXVec3TransformCoord(&tmpVect, &m_up, &R);

    D3DXVECTOR3 axisY(0.0f, -1.0f, 0.0f);

    if (D3DXVec3Dot(&tmpVect, &axisY) < 0.0f)
    {
        D3DXVec3TransformCoord(&m_up, &m_up, &R);
        D3DXVec3TransformCoord(&m_lookAt, &m_lookAt, &R);    
    }

    /* Rotate Y */

    D3DXMatrixRotationY(&R, yAngle);
    D3DXVec3TransformCoord(&m_right, &m_right, &R);
    D3DXVec3TransformCoord(&m_up, &m_up, &R);
    D3DXVec3TransformCoord(&m_lookAt, &m_lookAt, &R);

    BuildView();

    INPUT_MANAGER->CenterMouseCursor();
}

void FPSCamera::BuildView()
{
    D3DXVec3Normalize(&m_lookAt, &m_lookAt);

    D3DXVec3Cross(&m_up, &m_lookAt, &m_right);
    D3DXVec3Normalize(&m_up, &m_up);

    D3DXVec3Cross(&m_right, &m_up, &m_lookAt);
    D3DXVec3Normalize(&m_right, &m_right);

    float x = -D3DXVec3Dot(&m_pos, &m_right);
    float y = -D3DXVec3Dot(&m_pos, &m_up);
    float z = -D3DXVec3Dot(&m_pos, &m_lookAt);

    m_view(0,0) = m_right.x;
    m_view(1,0) = m_right.y;
    m_view(2,0) = m_right.z;
    m_view(3,0) = x;

    m_view(0,1) = m_up.x;
    m_view(1,1) = m_up.y;
    m_view(2,1) = m_up.z;
    m_view(3,1) = y;

    m_view(0,2) = m_lookAt.x;
    m_view(1,2) = m_lookAt.y;
    m_view(2,2) = m_lookAt.z;
    m_view(3,2) = z;

    m_view(0,3) = 0.0f;
    m_view(1,3) = 0.0f;
    m_view(2,3) = 0.0f;
    m_view(3,3) = 1.0f;
}

#endif

 

Comment se servir du code précédent ? 

Ce qui nous intéresse dans cette implémentation de caméra ce sont la matrice de vue (m_view) et la matrice de projection (m_proj).

Dans votre propre code, avant d’effectuer le rendu de vos modèles 3D vos devez spécifier ces matrices au shader correspondant :

m_pViewShaderVariable->SetMatrix((float*)CAMERA->GetViewMatrix());
m_pProjShaderVariable->SetMatrix((float*)CAMERA->GetProjectionMatrix();

 

Et voilà c’est tout simple !

 

Résumé :

Nous avons implémenté une caméra à la première personne (FPS) qui nous permet de naviguer facilement dans notre scène 3D.

Nous accédons aux matrices de vue et de projection avec les méthodes GetViewMatrix() et GetProjectionMatrix().

Références :

– Introduction to DirectX 9.0c – A shader approach

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *