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

Windogs

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

公告

View Post

DirectX11笔记2:时间,数学库,渲染管线

一个游戏程序当然需要各种计时,书中代码给予了GameTimer类:

#ifndef GAMETIMER_H
#define GAMETIMER_H

class GameTimer
{
public:
    GameTimer();

    float TotalTime()const;  // in seconds        返回程序运行(当然不包括暂停)的总时间
    float DeltaTime()const; // in seconds        返回mDeltaTime,当前帧与上一帧的时间差

    void Reset(); // Call before message loop.
    void Start(); // Call when unpaused.
    void Stop();  // Call when paused.
    void Tick();  // Call every frame.

private:
    double mSecondsPerCount;    //存储cpu 1秒有多少count,用于将获得的count计算为秒
    double mDeltaTime;

    __int64 mBaseTime;                //记录Reset函数调用时的计时count
    __int64 mPausedTime;            //暂停的总时间
    __int64 mStopTime;                //最近一次暂停时(Stop被调用)的计时Count
    __int64 mPrevTime;                //记录上一帧的计时count,以此来计算2帧的count差,当start时会被设为start的count来保证游戏的时间连贯性,reset时会为0
    __int64 mCurrTime;                //Trick调用时,会用于记录这一帧的计时count

    bool mStopped;                    //是否暂停
};

#endif // GAMETIMER_H

以及时间类在消息循环中的基本运用

int D3DApp::Run()
{
    MSG msg = {0};
  
    mTimer.Reset();
 
    while(msg.message != WM_QUIT)
    {
        // 如果接收到Window消息,则处理这些消息
        if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        // 否则,则运行动画/游戏
        else
        {  
            mTimer.Tick();
 
            if( !mAppPaused )
            {
                CalculateFrameStats();
                UpdateScene(mTimer.DeltaTime());   
                DrawScene();
            }
            else
            {
                Sleep(100);
            }
        }
    }
 
    return (int)msg.wParam;
}

 

 

XNA数学库:

在我的环境下(2013+win10)无法像原先一样直接#include <xnamath.h>调用XNA中的数学库,

需要使用

#include <DirectXMath.h>
#include <DirectXPackedVector.h>

(lib已经在上一节包含进来了)

具体内容参见:https://msdn.microsoft.com/en-us/library/windows/desktop/ee418730.aspx

XNA 介绍参见:https://msdn.microsoft.com/en-us/library/windows/desktop/ee415675.aspx

然后就可以使用书中的代码了,

这里可能会有一个小问题,反正我遇到了:

书中的矩阵输出重载代码:

ostream& operator<<(ostream& os, CXMMATRIX m)
{
    for(int i = 0; i < 4; ++i)
    {
        for(int j = 0; j < 4; ++j)
            os << m(i, j) << "\t";
        os << endl;
    }
    return os;
}

可能是新版的矩阵格式改变了,参照msdn资料

——————————————————————————————————————————————————————————————-————————————————

struct XMMATRIX {
  XMVECTOR r[4];
};

Members

r

Array of four vectors, representing the rows of the matrix.

Remarks

In the DirectXMath.h header file, the system uses an alias to the XMMATRIX object, specifically CXMMATRIX. The header uses the alias to comply with the optimal in-line calling conventions of different compilers. For most projects using DirectXMath it is sufficient to simply treat this as an exact alias to XMMATRIX.

Effectively:

typedef const XMMATRIX CXMMATRIX;

———————————————————————————————————————————————————————————————————————————————

我将输出矩阵的重载改成了:

ostream& operator<<(ostream& os, DirectX::FXMVECTOR v)
{
    DirectX::XMFLOAT4 dest;
    DirectX::XMStoreFloat4(&dest, v);

    os << "(" << dest.x << ", " << dest.y << ", " << dest.z << ", " << dest.w << ")";
    return os;
}

ostream& operator<<(ostream& os, DirectX::CXMMATRIX m)
{
    for (int i = 0; i < 4; ++i)
    {
        os << m.r[i]<< "\t";
        os << endl;
    }
    return os;
}

测试代码:

DirectX::XMMATRIX v
(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 4.0f, 0.0f,
1.0f, 2.0f, 3.0f, 1.0f);


std::cout << "1" << std::endl << v << std::endl;

 

 

渲染管线:

这个暂时比较复杂,这一节先说明顶点着色,并给予一个可以绘制最简单的三角形的工程。

 以下所有内容请配合工程代码食用

丶顶点着色阶段:

1.需要自己定义一个顶点格式的结构体,表达我们的这个顶点需要哪些元素:

struct VERTEX

{

  FLOAT X, Y, Z;      //需要一个三维的X,Y,Z 的坐标,当然可以使用XMFLOAT3  代替

  XMFLOAT4 Color;     //需要一个用于描述颜色的4个float (在我的环境下无法使用D3DX的color类型)

};

 sizeof(VERTEX)为0x1c

2.然后需要描述该顶点结构体的分量结构,我的理解就是用来”描述“结构体中成员的作用,以下为书中示例:

struct Vertex2
{
    XMFLOAT3 Pos;       // 0-byte offset
    XMFLOAT3 Normal;   // 12-byte offset
    XMFLOAT2 Tex0;      // 24-byte offset
    XMFLOAT2 Tex1;      // 32-byte offset
};
    typedef struct D3D11_INPUT_ELEMENT_DESC {
    LPCSTR SemanticName;    //一个与元素相关的字符串。它可以是任何有效的语义名,用于将顶点结构体中的元素映射为顶点着色器参数
    UINT SemanticIndex;        //附加在语义上的索引值,比如一个顶点结构体需要多组纹理坐标,我们不需要一个新的语义名,而是在语义名的后面加上一个索引值(参见第3,4项)。
    DXGI_FORMAT Format;        //一个用于指定元素格式的DXGI_FORMAT枚举类型成员,有固定的类型,更多参见msdn。
    UINT InputSlot;            //指定当前元素来自于哪个输入槽(0~15),现在暂时就一个输入槽,暂时为0
    UINT AlignedByteOffset  //(1个float4字节)这个元素相对于结构体头的偏移,看上面的结构体,第一个pos12字节,所以第二个normal相对于结构体头的偏移为12
    D3D11_INPUT_CLASSIFICATION InputSlotClass;    //现在暂时指定为D3D11_INPUT_PER_VERTEX_DATA
    UINT InstanceDataStepRate;    //现在暂时指定为0
    } D3D11_INPUT_ELEMENT_DESC;
    例如:
D3D11_INPUT_ELEMENT_DESC desc2[]
= { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT,0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0} {"TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0}

以我所定义的顶点为例,需要的定点描述为:

struct VERTEX

{

  FLOAT X, Y, Z;

  XMFLOAT4 Color;

}



D3D11_INPUT_ELEMENT_DESC ied[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },  //对应结构体中的坐标float X,Y,Z { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },  //对应上述结构体中的颜色,颜色需要4个float表示 };

 书中对于这个输入参数与顶点结构的类型进行了进一步的讨论,由于这里还没接触effect,先不说明。

 

3.调用CreateInputLayout获取一个表示输入布局的ID3D11InputLayout接口的指针(?)

    HRESULT ID3D11Device::CreateInputLayout
    (
    const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,    //一个用于描述顶点结构体的D3D11_INPUT_ELEMENT_DESC数组(上面的结构体)
    UINT NumElements,                                    //数组的元素数量(从1开始)
    const void *pShaderBytecodeWithInputSignature,        //指向顶点着色器参数的字节码的指针
    SIZE_T BytecodeLength,                                //顶点着色器参数的字节码长度,单位为字节。
    ID3D11InputLayout **ppInputLayout                    //返回创建后的ID3D11InputLayout指针
    );

我的示例:

ID3D11InputLayout *pLayout;            // the pointer to the input layout,全局变量

D3D11_INPUT_ELEMENT_DESC ied[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

dev->CreateInputLayout(ied, 2, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &pLayout);

 

对于vertexShaderBuffer与dev参数以及CreateInputLayout的作用会在之后再进行讨论(?)

 

丶顶点缓冲

说明:我们在上面定义了”一个“顶点“使用”的结构体与用于“描述”顶点成员的数据结构,但是这里并没有实际的点,我们没有一个有效的数据。顶点缓冲的作用就是把实际的坐标,颜色等数据存储到GPU上去。

填写一个D3D11_BUFFER_DESC结构体,描述我们所要创建的缓冲区

    typedef struct D3D11_BUFFER_DESC{
        UINT ByteWidth;        //我们将要创建的顶点缓冲区的大小,单位为字节
        D3D11_USAGE Usage;    //一个用于指定缓冲区用途的D3D11_USAGE枚举类型成员。有4个固定的可选值,更多参见msdn
        UINT BindFlags;        //对于顶点缓冲区,该参数应设为D3D11_BIND_VERTEX_BUFFER
        UINT CPUAccessFlags;  //指定CPU对资源的访问权限。设置为0则表示CPU无需读写缓冲。如果CPU需要向资源写入数据,则应指定D3D11_CPU_ACCESS_WRITE
        UINT MiscFlags;        //我们不需要为顶点缓冲区指定任何杂项(miscellaneous)标志值,所以该参数设为0
        UINT StructureByteStride;    //存储在结构化缓冲中的一个元素的大小,以字节为单位。这个属性只用于结构化缓冲,其他缓冲可以设置为0,因此这里先为0 即可。
    } D3D11_BUFFER_DESC;

这里,我们先画一个三角形,需要3个坐标点:

VERTEX OurVertices[] =
{
{ 0.0f, 0.5f, 0.0f, XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
{ 0.0f, -0.5f, 0.0f, XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{ -0.45f, -0.0f, 0.0f, XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }
};

创建:

D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));

bd.Usage = D3D11_USAGE_DYNAMIC;     // write access access by CPU and GPU
bd.ByteWidth = sizeof(VERTEX)* 3;       // 3个结构体的大小
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; //对于定点缓冲区,这个参数固定
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

// D3D11_USAGE_DYNAMIC:表示应用程序(CPU)会频繁更新资源中的数据内容(例如,每帧更新一次)。GPU可以从这种资源中读取数据,使用映射API(ID3D11DeviceContext::Map)时,CPU可以向这种资源中写入数据。

//由于已经ZeroMemory过了,所以以下的可以省略,还是写一下吧。

bd.StructureByteStride = 0;
bd.MiscFlags = 0;

然后可以使用CreateBuffer创建这个Buffer

    HRESULT CreateBuffer(
    [in]            const D3D11_BUFFER_DESC      *pDesc,        //上面的D3D11_BUFFER_DESC
    [in, optional]  const D3D11_SUBRESOURCE_DATA *pInitialData,    //表示初始化时需要导入的数据,这里我们只分配空间,就填NULL,当然也可以直接导入,下面会说明.
    [out, optional]       ID3D11Buffer           **ppBuffer        //返回创建的Buffer地址的指针
    );

ID3D11Buffer *pVBuffer; // the pointer to the vertex buffer 这个是全局变量

dev->CreateBuffer(&bd, NULL, &pVBuffer);

然后可以使用Map方法导入了:

这里不做说明了,msdn查一下吧。

D3D11_MAPPED_SUBRESOURCE ms;
devcon->Map(pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
memcpy(ms.pData, OurVertices, sizeof(OurVertices)); 

 

当然,也可以在CreateBuffer的时候直接填写,先看一下这个结构:

    typedef struct D3D11_SUBRESOURCE_DATA { 
    const void *pSysMem;    //pSysMem:包含初始化数据的系统内存数组的指针。当缓冲区可以存储n个顶点时,对应的初始化数组也应至少包含n个顶点
    UINT SysMemPitch;        //顶点缓冲区不使用该参数。
    UINT SysMemSlicePitch;    //顶点缓冲区不使用该参数。

 

很简单,不是吗?至少,比上一个简单多了。。。

D3D11_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = OurVertices;

dev->CreateBuffer(&bd, &vinitData, &pVBuffer); 

 

最后一步,把创建好了的顶点的缓冲区安装到设备的输入槽里,这样才能在渲染的时候显示。

所谓的输入槽就是用于顶点输入(废话),书中有写,D3D有16个输入槽(0——15),这样多个顶点就可以同时输入,当然,这里先只用一个。

这里注意,输入槽并不是用于绘制顶点,绘制他的是管线。

首先是绑定输入槽

方法:

    void IASetVertexBuffers(
    [in]                 UINT                StartSlot,    //顶点缓冲区所要绑定的起始输入槽。
    [in]                 UINT                NumBuffers,//顶点缓冲区所要绑定的输入槽的数量
    [in, optional]       ID3D11Buffer *const *ppVertexBuffers,//指向顶点缓冲区数组的第一个元素的指针
    [in, optional] const UINT                *pStrides,    //指向步长数组的第一个元素的指针,这个步长是指顶点缓冲区中的单元素的字节长度
    [in, optional] const UINT                *pOffsets    //指向偏移数组的第一个元素的指针
    );

 

使用Draw方法绘制:

    void Draw(
  [in] UINT VertexCount,    //需要绘制的顶点数量
  [in] UINT StartVertexLocation//顶点起始绘制的偏移位置,这里从第一个开始,偏移为0
    );

 

以下的代码当然是在绘制的函数中进行的。

UINT stride = sizeof(VERTEX);
UINT offset = 0;
devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);

devcon->Draw(3, 0);

 下一节继续

工程代码:

file

 

posted on 2015-11-09 19:47  Windogs  阅读(497)  评论(0)    收藏  举报

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