原文:Directx11教程(8) 一个新的camera类

     本章我们将替换掉CameraClass类,实现一个稍微靠谱点的摄像机类。并通过Q,W,E,A,S,D,Z,X,C等按键实现摄像机的控制。

     该类的主要功能就是根据指定的摄像机位置,up方向以及lookat方向,得到最终的视图矩阵,所谓视图矩阵就是把世界坐标系的顶点位置转化到视点(或者说摄像机)空间的矩阵。该类可以实现两种模式的摄像机操作,一类是AIRCRAFT摄像机,允许摄像机在空间自由运动,具有6个自由度。另一种是LANDOBJECT摄像机,只允许沿着某些特定的轴运动。

    

     下面简单看下计算ViewMatrix的原理:

      

     假设摄像机的局部坐标系为(S,W,Q),S, W, Q它们都是归一化向量, S是局部坐标的右(right)向轴(类似世界坐标系的x轴),W是局部坐标系的上(up)向轴(类似世界坐标系y轴),Q是前(lookat)向轴(类似于世界坐标系的z轴),S向量表示为(Sx, Sy, Sz), up 向量表示为(Wx, Wy, Wz), Q向量表示为(Qx, Qy, Qz),摄像机的位置为(Px, Py, Pz)。

    我们现在要把世界坐标系中的顶点A(x, y, z),如果要转化到视觉坐标系(right,up, lookat)则需要做下面两步:

1、把视点移回原点,则转化矩阵为:

2.我们要把一个世界坐标系点K(Kx, Ky, Kz),表示成(S,W,Q)坐标系的点(假设此时,已经经过平移操作,摄像机在世界坐标系的原点),则其公式为:

Lx = Kx * Sx + Ky * Sy + Kz * Sz;

Ly = Kx * Wx + Ky * Wy + Kz * Wz;

Lz = Kx * Qx + Ky * Qy + Kz * Qz

     则转为矩阵为:

则世界坐标系到视觉坐标系的转化矩阵为上面两个矩阵相乘,最终的矩阵为:

      通常在摄像机类中,都是给定up, lookat,或者lookat, right之后,通过差积的方式求得right或者up,然后再差积,最后求得正交归一化的的坐标系(S, W, Q)。

   在本Camera类中,就是通过这种方式:

//根据lookup, right求得正交的up

D3DXVec3Cross(&_up, &_look, &_right);
//up归一化

D3DXVec3Normalize(&_up, &_up);

//根据up和lookup,求得正交的right

D3DXVec3Cross(&_right, &_up, &_look);

//righ再归一化
D3DXVec3Normalize(&_right, &_right);

 

CameraClass.h的代码如下:

#pragma once

#include <d3dx10math.h>

class CameraClass
    {
    //支持两种摄像机模型 AIRCRAFT 允许在空间自由运动,具有6个自由度
    // LANDOBJECT 沿某些特定轴进行移动
    public:
        enum CameraType { LANDOBJECT, AIRCRAFT };

    public:
        CameraClass(void);
        CameraClass(const CameraClass&);
        ~CameraClass(void);

        void strafe(float units); // 左右
        void fly(float units);   // 上下
        void walk(float units);   // 前后

        void pitch(float angle); // 旋转view坐标系right向量
        void yaw(float angle);  // 旋转up向量
        void roll(float angle); // 旋转look向量

        void getViewMatrix(D3DXMATRIX* V);
        void setCameraType(CameraType cameraType);
        void getPosition(D3DXVECTOR3* pos);
        void setPosition(D3DXVECTOR3* pos);

        void getRight(D3DXVECTOR3* right);
        void getUp(D3DXVECTOR3* up);
        void getLook(D3DXVECTOR3* look);
    private:
        CameraType  _cameraType;
        D3DXVECTOR3 _right;
        D3DXVECTOR3 _up;
        D3DXVECTOR3 _look;
        D3DXVECTOR3 _pos;

    };

CameraClass.cpp代码如下:

#include "CameraClass.h"

CameraClass::CameraClass(void)
    {
    _cameraType = AIRCRAFT;

    _pos   = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    _right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
    _up    = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    _look  = D3DXVECTOR3(0.0f, 0.0f, 1.0f);

    }

CameraClass::CameraClass(const CameraClass& other)
    {
    }

CameraClass::~CameraClass(void)
    {
    }

void CameraClass::getPosition(D3DXVECTOR3* pos)
    {
    *pos = _pos;
    }

void CameraClass::setPosition(D3DXVECTOR3* pos)
    {
    _pos = *pos;
    }

void CameraClass::getRight(D3DXVECTOR3* right)
    {
    *right = _right;
    }

void CameraClass::getUp(D3DXVECTOR3* up)
    {
    *up = _up;
    }

void CameraClass::getLook(D3DXVECTOR3* look)
    {
    *look = _look;
    }

//行走,沿着摄像机观察方向的移动
void CameraClass::walk(float units)
    {
    // 仅在x,z平面移动
    if( _cameraType == LANDOBJECT )
        _pos += D3DXVECTOR3(_look.x, 0.0f, _look.z) * units;

    if( _cameraType == AIRCRAFT )
        _pos += _look * units;
    }

//扫视,是指保持观察方向不变,沿向量right方向从一边平移到另一边
void CameraClass::strafe(float units)
    {
    // 仅在x,z平面移动
    if( _cameraType == LANDOBJECT )
        _pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) * units;

    if( _cameraType == AIRCRAFT )
        _pos += _right * units;
    }
//飞行模式,升降,指沿着向量up方向的移动
void CameraClass::fly(float units)
    {
    // 仅在y轴移动
    if( _cameraType == LANDOBJECT )
        _pos.y += units;

    if( _cameraType == AIRCRAFT )
        _pos += _up * units;
    }

//pitch, yaw, roll的概念 http://www.cnblogs.com/mikewolf2002/p/5151606.html

void CameraClass::pitch(float angle)
    {
    D3DXMATRIX T;
    D3DXMatrixRotationAxis(&T, &_right, angle);

   // 绕着right向量,旋转up和look
    D3DXVec3TransformCoord(&_up,&_up, &T);
    D3DXVec3TransformCoord(&_look,&_look, &T);
    }

void CameraClass::yaw(float angle)
    {
    D3DXMATRIX T;

    //对LANDOBJECT,总是绕着(0,1,0)旋转。
    if( _cameraType == LANDOBJECT )
        D3DXMatrixRotationY(&T, angle);

    //对于aircraft,绕着up向量旋转
    if( _cameraType == AIRCRAFT )
        D3DXMatrixRotationAxis(&T, &_up, angle);

   // 绕着up或者y轴,旋转right和look
    D3DXVec3TransformCoord(&_right,&_right, &T);
    D3DXVec3TransformCoord(&_look,&_look, &T);
    }

void CameraClass::roll(float angle)
    {
    //只对aircraft模式才左roll旋转
    if( _cameraType == AIRCRAFT )
        {
        D3DXMATRIX T;
        D3DXMatrixRotationAxis(&T, &_look, angle);

        // 绕着look向量,旋转up和right
        D3DXVec3TransformCoord(&_right,&_right, &T);
        D3DXVec3TransformCoord(&_up,&_up, &T);
        }
    }

void CameraClass::getViewMatrix(D3DXMATRIX* V)
    {
    // 保持view局部坐标系,各轴的彼此正交
    D3DXVec3Normalize(&_look, &_look);
    // look X right
    D3DXVec3Cross(&_up, &_look, &_right);
    D3DXVec3Normalize(&_up, &_up);

    D3DXVec3Cross(&_right, &_up, &_look);
    D3DXVec3Normalize(&_right, &_right);

   // 生成view矩阵:
    float x = -D3DXVec3Dot(&_right, &_pos);
    float y = -D3DXVec3Dot(&_up, &_pos);
    float z = -D3DXVec3Dot(&_look, &_pos);

    (*V)(0,0) = _right.x; (*V)(0, 1) = _up.x; (*V)(0, 2) = _look.x; (*V)(0, 3) = 0.0f;
    (*V)(1,0) = _right.y; (*V)(1, 1) = _up.y; (*V)(1, 2) = _look.y; (*V)(1, 3) = 0.0f;
    (*V)(2,0) = _right.z; (*V)(2, 1) = _up.z; (*V)(2, 2) = _look.z; (*V)(2, 3) = 0.0f;
    (*V)(3,0) = x;        (*V)(3, 1) = y;     (*V)(3, 2) = z;       (*V)(3, 3) = 1.0f;
    }

void CameraClass::setCameraType(CameraType cameraType)
    {
    _cameraType = cameraType;
    }

修改GraphicsClass.h, 设置成员变量m_Camera为public,以便我们在GraphicsClass类中对齐进行操作。

class GraphicsClass
    { 
… 
        bool Frame();
        CameraClass* m_Camera; //设为public,便于在SystemClass中控制
    private:

    };

SystemClass.cpp代码的Frame函数更新如下:

bool SystemClass::Frame()
    {
    bool result;

    //检测用户是否按下ESC键,如果按下,退出程序.
    if(m_Input->IsKeyDown(VK_ESCAPE))
        {
        return false;
        }

    //如果A,S,D,W,Q,E,Z,X,C键按下,移动摄像机
    if(GetAsyncKeyState('W') & 0x8000)    //前后
        m_Graphics->m_Camera->walk(-0.1);
    if(GetAsyncKeyState('S') & 0x8000)   
        m_Graphics->m_Camera->walk(0.1);
    if(GetAsyncKeyState('A') & 0x8000)    //左右
        m_Graphics->m_Camera->strafe(-0.1);
    if(GetAsyncKeyState('D') & 0x8000)   
        m_Graphics->m_Camera->strafe(0.1);
    if(GetAsyncKeyState('Q') & 0x8000)    //上下
        m_Graphics->m_Camera->fly(-0.1);
    if(GetAsyncKeyState('E') & 0x8000)   
        m_Graphics->m_Camera->fly(0.1);
    if(GetAsyncKeyState('Z') & 0x8000)   
        m_Graphics->m_Camera->pitch(PI/180);
    if(GetAsyncKeyState('X') & 0x8000)   
        m_Graphics->m_Camera->yaw(PI/180);
    if(GetAsyncKeyState('C') & 0x8000)   
        m_Graphics->m_Camera->roll(PI/180);

   
  //动画,旋转摄像机
    m_Graphics->m_Camera->roll(PI/180);

    // 执行帧渲染函数.
    result = m_Graphics->Frame();
    if(!result)
        {
        return false;
        }
    return true;
    }

注意:我们在SystemClass.cpp中增加了摄像机旋转的代码,所以这个立方体就在哪儿不停转啊转,永不休止……

    m_Graphics->m_Camera->roll(PI/180);

程序执行结果如下图,图中的立方体一直处于旋转状态(其实我们是在旋转摄像机):

完整的代码请参考:

工程文件myTutorialD3D11_7

代码下载:

http://files.cnblogs.com/mikewolf2002/myTutorialD3D11.zip

Directx11教程(8) 一个新的camera类的更多相关文章

  1. Directx11教程(4) 一个最基本D3D应用程序(2)

    原文:Directx11教程(4) 一个最基本D3D应用程序(2) 接着上篇教程的代码,本篇加入基本的D3D代码,实现一个完整的D3D11程序框架. 我们增加一个新类D3DClass, 用来处理3D渲 ...

  2. Directx11教程(3) 一个最基本D3D应用程序(1)

    原文:Directx11教程(3) 一个最基本D3D应用程序(1)       在前一篇教程程序代码的基础上,这次我们将增加2个类: InputClass,键盘处理的代码将放在这个类里面,Graphi ...

  3. Hibernate的多表查询,分装到一个新的实体类中的一个方法

    不知道是否还有其他方法实现,请高人指点. 如果涉及到多张表多字段查询,并且想利用查询出来的字段在界面层构建一个新的实体类,可以使用这种方法: 如果查询出来的多字段中,有多个字段的名字都相同(如想查询出 ...

  4. Directx11教程(20) 一个简单的水面

    原文:Directx11教程(20) 一个简单的水面 nnd,以前发的这篇教程怎么没有了?是我自己误删除了,还是被系统删除了? 找不到存稿了,没有心情再写一遍了.      简单说一下,本篇教程就是实 ...

  5. CREATE OPERATOR CLASS - 定义一个新的操作符类

    SYNOPSIS CREATE OPERATOR CLASS name [ DEFAULT ] FOR TYPE data_type USING index_method AS { OPERATOR ...

  6. 策划编写一个新的Helper类

    https://code.csdn.net/jy02305022/blqw-data 有朋友看见的话给点意见呗

  7. ASP.NET + MVC5 入门完整教程八 -—-- 一个完整的应用程序(上)

    https://blog.csdn.net/qq_21419015/article/details/80509513 SportsStore 1.开始创建Visual Studio 解决方案和项目这里 ...

  8. C#图解教程 第六章 深入理解类

    深入理解类 类成员成员修饰符的顺序实例类成员静态字段从类的外部访问静态成员 静态字段示例静态成员的生存期 静态函数成员其他静态类成员类型成员常量常量与静态量属性 属性声明和访问器属性示例使用属性属性和 ...

  9. JDK1.8新特性——Optional类

    JDK1.8新特性——Optional类 摘要:本文主要学习了JDK1.8新增加的Optional类. 部分内容来自以下博客: https://www.cnblogs.com/1ning/p/9140 ...

随机推荐

  1. 主成分分析(PCA)原理详解_转载

    一.PCA简介 1. 相关背景 在许多领域的研究与应用中,往往需要对反映事物的多个变量进行大量的观测,收集大量数据以便进行分析寻找规律.多变量大样本无疑会为研究和应用提供了丰富的信息,但也在一定程度上 ...

  2. AdaBoost笔记之代码

    最近要做二分类问题,先Mark一下知识点和代码,参考:Opencv2.4.9源码分析——Boosting   以下内容全部转自此文 一 原理 二 opencv源码 1.先看构建Boosting的参数: ...

  3. 掌握ES6/ES2015核心内容

    ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准.因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015. 也就是说,ES6就是ES2015. ...

  4. Python+Django+Ansible Playbook自动化运维项目实战

    Python+Django+AnsiblePlaybook自动化运维项目实战 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单 ...

  5. Django项目: 5.新闻主页

    一.功能需求分析 1.功能 轮播图 推荐文章列表 文章标签导航 文章列表 分页 二.模型设计 根据功能分析,我们需要如下表 1.表和字段分析 文章分类表 文章表 文章评论表 推荐文章表 轮播图表 2. ...

  6. leetcode 699. Falling Squares 线段树的实现

    线段树实现.很多细节值得品味 都在注释里面了 class SegTree: def __init__(self,N,query_fn,update_fn): self.tree=[0]*(2*N+2) ...

  7. HTML5中类jQuery选择器querySelector和querySelectorAll的使用

    支持的浏览IE8+,Firefox3.5+,Safari3.1+ Chrome和Opera 10+ 1.querySelector()方法接收一个选择符,返回第一个匹配的第一个元素,如果没有返回nul ...

  8. struts2-result-servletAPI-获得参数-参数封装

    1 结果跳转方式  转发 重定向 转发到Action 重定向到Action 2 访问servletAPI方式 2.1 原理 2.2 获得API 通过ActionContext ★★★★ 通过Servl ...

  9. there is no permission with id `12`

    Laravel 本地环境添加菜单之后, 线上报错, 线上线上用同一个数据库, 原因是缓存问题, 解决方法: php artisan cache:clear 如果缓存有重要数据的, 那就要清除对应的缓存 ...

  10. JS数组的相关方法

    数组创建 JavaScript中创建数组有两种方式,第一种是使用 Array 构造函数: ? 1 2 3 var arr1 = new Array(); //创建一个空数组 var arr2 = ne ...