• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

Windogs

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

DirectX11笔记9:第一人称摄像机

 

 

效果

 

注意i,以下所有内容一定一定要结合我的源码阅读。

然后希望我的程序可以运行成功

 

1.个人坐标的创建:

 在第一人称摄像机中,摄像机需要以下几个元素去描述它的状态:

    DirectX::XMFLOAT4        Position;    //位置
    DirectX::XMFLOAT4        Pos_Up;    //指定向上的位置
    DirectX::XMFLOAT4        Target; //看的方向单位向量
    DirectX::XMFLOAT4        Pos_Right;//指定“右侧”的方向

 

记录相机相对于世界坐标的位置,向上的方向(现在没用),看向眼睛看向方向的单位向量,以及”右侧“的方向

 

他们的初始化赋值:

    //在这里初始化相机信息
    //建立,描述 摄像机与视角坐标
    Position = DirectX::XMFLOAT4(0.0f, 1.0f, 4.0f, 0.0f);
    Pos_Up = DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 0.0f);
    Target = DirectX::XMFLOAT4(0.0f, 0.0f, -1.0f,0.0f);
    Pos_Right = DirectX::XMFLOAT4(-1.0f, 0.0, 0.0, 0.0);

 

建立初始转化的矩阵:

View_World = DirectX::XMMatrixIdentity();//XMMatrixIdentity构建单位矩阵

View_View = DirectX::XMMatrixLookAtLH(DirectX::XMLoadFloat4(&Position), DirectX::XMLoadFloat4(&(Target + Position)), DirectX::XMLoadFloat4(&Pos_Up));
    //XMMatrixLookAtLH函数返回的是世界->视图变换矩阵

View_Projection = DirectX::XMMatrixPerspectiveFovLH(DirectX::XM_PIDIV2, 800 / (FLOAT)600, 0.01f, 100.0f);

 

这里需要自己写一个关于XMFLOAT4相加的重载函数:

/////////////
/////////////////////XNA_MATH_CODE////////////////////////////////////////////////////
DirectX::XMFLOAT4 operator+(DirectX::XMFLOAT4 &parameter1, DirectX::XMFLOAT4 &parameter2)
{
    return DirectX::XMFLOAT4(
        parameter1.x + parameter2.x,
        parameter1.y + parameter2.y,
        parameter1.z + parameter2.z,
        parameter1.w + parameter1.w
        );
}
/////////////////////////////////////////////////////////////////////////////////////////
////////////

 

这样,初始化好了摄像机,使用了一个位置,一个眼睛方向,一个向右方向,一个向上方向,固定了它。

相当于   位置坐标。Z轴,X轴,Y轴。在原世界坐标系中建立了一个新的坐标系,然后获得  世界————》局部  的坐标转换矩阵.

 

2.相机的旋转变幻

 旋转变化实际上就是2个绕着轴旋转。

左右旋转:眼睛看向的方向与向右的方向以Y轴为轴线旋转:

代码:

void FirstCamera::RotateBy_Y(float xx)
{
    DirectX::XMMATRIX Angle_RotateY = DirectX::XMMatrixRotationY(xx);//计算角度

    //角度变幻
    DirectX::XMVECTOR after = DirectX::XMVector4Transform(DirectX::XMLoadFloat4(&Target), Angle_RotateY);
    DirectX::XMStoreFloat4(&Target, after);

    //right 也要变化
    after = DirectX::XMVector4Transform(DirectX::XMLoadFloat4(&Pos_Right), Angle_RotateY);
    DirectX::XMStoreFloat4(&Pos_Right, after);

    IsCameraMove = TRUE;
}

 

  上下旋转:眼睛看向的方向与头顶的方向以 向右的方向为轴线旋转,

这里有一点注意,由于人的头是无法上下旋转超过90度的,就是说你无法通过向上转头,最后看到自己的脚后跟。需要一个限制。

void FirstCamera::RotateBy_X(float xx)
{
    DirectX::XMFLOAT4 Restrore = Target;

    //DirectX::XMFLOAT4 Restore = Target;//想一想又没其它办法?

    DirectX::XMMATRIX Angle_RotateX = DirectX::XMMatrixRotationAxis(XMLoadFloat4(&Pos_Right), xx);//计算角度a

    DirectX::XMVECTOR after = DirectX::XMVector4Transform(DirectX::XMLoadFloat4(&Target), Angle_RotateX);
    //使用
    //XMMatrixRotationAxis
    DirectX::XMStoreFloat4(&Target, after);

    //判断一下,锁定上下的视角
    if (Target.y<-0.9 || Target.y>0.9)
    {
        Target = Restrore;
        return;
    }

    /*
    if (Target.z<1.0 && Target.z>-1.0)
    {
        Target = Restore;
        return;
    }*/

    DirectX::XMVECTOR after_up = DirectX::XMVector4Transform(DirectX::XMLoadFloat4(&Pos_Up), Angle_RotateX);
    //注意这里
    
    //DirectX::XMStoreFloat4(&Pos_Up, after_up);

    IsCameraMove = TRUE;
}

 

 这里看会发现,我没有对像向上的方向进行变换,你们可以尝试打开那句注释,会有奇怪的事情发生。。。。我是发现为啥。。。

 

 

3.平移变换

很简单就可以想到,向前平移就是沿着眼睛方向在地面投影的方向移动(人不会飞,也不会钻地)。左右平移就是沿着向右方向在地面的投影方向平移。

代码:

void FirstCamera::MoveCamera_LeftRight(float xx)
{
    float normaliz_Length = (Pos_Right.x)*(Pos_Right.x) + (Pos_Right.z)*(Pos_Right.z);
    float normaliz_X = Pos_Right.x / normaliz_Length;
    float normaliz_Z = Pos_Right.z / normaliz_Length;

    horizonMove.x += normaliz_X*xx;
    horizonMove.z += normaliz_Z*xx;

    IsCameraMove = TRUE;
}
void FirstCamera::MoveCamera_UpDown(float xx)
{
    IsCameraMove = TRUE;
}
void FirstCamera::MoveCamera_ForwadBack(float xx)
{
    float normaliz_Length = (Target.x)*(Target.x) + (Target.z)*(Target.z);
    float normaliz_X = Target.x / normaliz_Length;
    float normaliz_Z = Target.z / normaliz_Length;
    
    horizonMove.x += normaliz_X*xx;
    horizonMove.z += normaliz_Z*xx;

    IsCameraMove = TRUE;
}

 

 没什么好说的。。。

最后是接受修改之后改变相机相对位置:

 

void FirstCamera::ApplyCameraChange()
{
    
    Position = DirectX::XMFLOAT4(
        Position.x + horizonMove.x,
        Position.y + horizonMove.y, 
        Position.z + horizonMove.z,
        Position.w );
            
    View_View = DirectX::XMMatrixLookAtLH(DirectX::XMLoadFloat4(&Position), DirectX::XMLoadFloat4(&(Target + Position)), DirectX::XMLoadFloat4(&Pos_Up));
    //update

    IsCameraMove = FALSE;
    horizonMove = DirectX::XMFLOAT3(0, 0, 0);

    //clean
}

 

 

 至此,所有的原理都解释完毕了。

 

然后是实现方式

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

键盘与鼠标的输入输出。

这里我本来是要使用DirectInput的功能区做键鼠模块的,后来google了一些资料,发现Iput系列也是用类似hook实现的,而且评价不好。

那就用windows自带的消息机制实现,也不难。

 

先是鼠标:

 鼠标接受一个MouseMove消息,并且将他固定在屏幕中央(使用SendInput),每次MouseMove后计算出它的移动值,以此来进行改变视角:

(这里有个没解决的问题,窗口化游戏时,鼠标移动太大会鼠标弹出窗口外。。。。)

case WM_MOUSEMOVE:
        POINT Mouse_Move;
   
        Mouse_Move.x = LOWORD(lParam);
        Mouse_Move.y = HIWORD(lParam);

        SendInput(1, &mouseInput, sizeof(INPUT));
        ClientToScreen(Main_Window_HWND, &Mouse_Move);
        //printf("Mouse Move :%ld, %ld\n", Mouse_Move.x - Window_Center_X, Mouse_Move.y - Window_Center_Y);
        Mouse_Move.x = Mouse_Move.x - Window_Center_X;
        Mouse_Move.y = Mouse_Move.y - Window_Center_Y;

        RotateBy_Y((float)(0.0015*Mouse_Move.x));
        RotateBy_X((float)(0.0015*Mouse_Move.y));
        return 0;

 

 在开始的时候使用ShowCursor(FALSE);把鼠标隐藏掉。

每次程序窗口移动都要重新计算窗口中心点:

case WM_MOVE:
  if (FALSE == SetMouseMoveCenter())
  printf("Error in Set Mouse Move Center\n");

  return 0;

 计算窗口中心的代码:

BOOL My_D3d_APP::SetMouseMoveCenter()
{
    BOOL ret;
    RECT wndRect1;
    ret = GetWindowRect(Main_Window_HWND, &wndRect1);
    printf("+====== SetMouseCenter =====+\n");
    printf("window Rect: %d, %d , %d, %d\n", wndRect1.bottom, wndRect1.left, wndRect1.right, wndRect1.top);
    Window_Center_X = wndRect1.left + 400;
    Window_Center_Y = wndRect1.top + 300;

    //Window_Center_X = 65535 * Window_Center_X / GetSystemMetrics(SM_CXSCREEN);
    //Window_Center_Y = 65535 * Window_Center_Y / GetSystemMetrics(SM_CYSCREEN);

    printf("Set Window Center :%d  %d\n", Window_Center_X, Window_Center_Y);

    mouseInput.mi.dx = 65535 * Window_Center_X / GetSystemMetrics(SM_CXSCREEN);
    mouseInput.mi.dy = 65535 * Window_Center_Y / GetSystemMetrics(SM_CYSCREEN);

    return ret;
}

 

 

 然后是键盘:

在Keydown的时候 记录一个按下的记号,在KeyUp的时候记录一个放开的记号,在每一帧之前计算一次是否移动,怎么移动。

    case WM_KEYDOWN:
        switch (wParam)
        {
        case 38://up
            if (Mouse_Move_Forwad_Back != 1)
                Mouse_Move_Forwad_Back = 1;
            //MoveCamera_ForwadBack((float)+0.2);
            //RotateBy_X((float)+0.05);

            break;
        case 40://down
            if (Mouse_Move_Forwad_Back != -1)
                Mouse_Move_Forwad_Back = -1;
            //MoveCamera_ForwadBack((float)-0.2);
            //RotateBy_X((float)-0.05);

            break;
        case 37://left
            if (Mouse_Move_Righr_Left != -1)
                Mouse_Move_Righr_Left = -1;
            //MoveCamera_LeftRight((float)-0.2);
            break;
        case 39://right
            if (Mouse_Move_Righr_Left != 1)
                Mouse_Move_Righr_Left = 1;

            //MoveCamera_LeftRight((float)+0.2);
            break;
        default:
            break;
        }
        return 0;

    case WM_KEYUP:
        switch (wParam)
        {
        case 38://up
            if (Mouse_Move_Forwad_Back != -1)
                Mouse_Move_Forwad_Back = 0;
            break;
        case 40://down
            if (Mouse_Move_Forwad_Back != 1)
                Mouse_Move_Forwad_Back = 0;
            break;
        case 37://left
            if (Mouse_Move_Righr_Left != 1)
            Mouse_Move_Righr_Left = 0;
            break;
        case 39://right
            if (Mouse_Move_Righr_Left != -1)
            Mouse_Move_Righr_Left = 0;
            break;
        default:
            break;
        }
        return 0;

 

 

 还有:

if (Mouse_Move_Forwad_Back != 0)
       MoveCamera_ForwadBack(Mouse_Move_Forwad_Back*((float)+0.04));

if (Mouse_Move_Righr_Left != 0)
       MoveCamera_LeftRight(Mouse_Move_Righr_Left*((float)+0.04));
RenderScene();
Sleep(15);

 

 至此,所有的内容完毕。

后面是一些注意点。

 1.顶点输入顺序。

在输入顶点时以先下后上的方式输入。

比如我的程序中有一个箱子与一个100X100的草地,那么箱子在草地上面,必须先输入草地的顶点,再输入箱子的顶点

 

2.Y轴的问题:

这个程序中相机的Y轴不会改变,否则会出现奇怪的事情。。。。

工程代码

 

posted on 2015-12-16 14:49  Windogs  阅读(1387)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3