DirectX11笔记3:基本绘图,渲染,绘制一个旋转方形

上一节的笔记本身写的十分糟糕,那个程序也写的十分糟糕。。。。。。。。。若是真的有人看的话,说声抱歉。函数

这一节主要是记录一个旋转的正方形的制做过程,先说好:如下全部内容请配合上传了的代码食用。。。。。。。。。。若是真的有人看的话。布局

 

首先,先大概介绍一下绘制一个图形的基本流程:visual-studio

一.建立基本的D3D对象:学习

1.使用D3D11CreateDeviceAndSwapChain建立D3D设备对象与交换链。this

2.使用CreateRenderTargetView建立后一个绘制缓冲区。spa

3.若是须要,建立模板与景深缓冲区。3d

4.使用RSSetViewports设置视窗。code

 

二.编译,连接到effect:orm

1.组建,加载顶点着色器。对象

2.初始化着色器的输入布局。

3.组建,加载像素着色器。

 

三.建立绘制图形:

1.建立,定义顶点坐标,建立相应的缓冲区,用已定义的顶点初始化,再将建立好的缓冲区绑到设备输入槽上去。

2.建立顶点索引,建立相应的缓冲区,用以建立的索引初始化。

3.设置好投影空间的位置,中心坐标,z轴坐标(下面再进行详细介绍)。

4.HLSL相关。

 

关于如何建立D3D的一些基本对象就再也不叙述了。直接从第二个开始:

首先咱们须要一个.fx的文件,关于文件如何组成的以后再说明,咱们只要知道如今这个文件给予了程序2个接口:顶点着色器与像素着色器。

关于顶点,咱们的图像在程序中都是由一个个三角形组成的,参见书中的各类图形,假设这里咱们须要绘制一个方形,那么咱们至少须要2个三角形,4个顶点去描述这个方形。

顶点着色器经过咱们输入的顶点与它相应的属性去描述各个三角形经过各类坐标变换,最后组成了咱们的图像。

像素着色器,图形的颜色是由一个个像素点组成的,像素着色器告诉GPU哪些像素须要着色,须要什么颜色。

(以上是我如今的理解,可能会有许多错误)

代码以前,先对书中内容进行一些讨论:因为D3DX系列的函数在我如今的环境下没法使用,因此书中的代码是没法正常经过编译的,主要是书中使用了D3DX11CompileFromFile去编译fx文件,这对个人学习产生了极大的困扰。

关于这个问题的解决方案,看一下stackoverflow的这个回答。

http://stackoverflow.com/questions/30579016/d3d11-d3dx11createeffectfrommemory-returns-e-noiterface

以及

http://stackoverflow.com/questions/12549472/using-directx-effect11-with-visual-studio-2012

总之,虽然我下载了D3DX的开源代码,而且编译成功了,可是仍是没有成功搭建出使用D3DX11CompileFromFile以及之类的函数的环境,因此选择使用其余的函数。

个人代码

HRESULT DrawFunction::CompileTheShader(void)
{
    HRESULT hr = S_OK;//ret
    ID3DBlob* pVSBlob = nullptr;
    ID3DBlob* pErrorBlob = nullptr;

    //Compiler vertex shader
    //组建加载顶点着色器
    hr = D3DCompileFromFile(L"Squance.fx", nullptr, nullptr, "VS", "vs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, &pVSBlob, &pErrorBlob);
    if (FAILED(hr))
    {
#ifdef _DEBUG
        printf("Can not find FX File .\nPlease run this executable from the directory that contains the FX file\n Error HRESULT:%ld\n\n", hr);
        return hr;
#else
        MessageBox( nullptr,
            L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
        return hr;
#endif
    }
    //Create VERTEX shader
    hr = dev->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &pVertexShader);
    if (FAILED(hr))
    {
        pVSBlob->Release();
        return hr;
    }
    // Define the input layout
    //定义输入布局
    D3D11_INPUT_ELEMENT_DESC layout[] =
    {
        { "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 },
    };
    UINT numElements = ARRAYSIZE(layout);

    // Create the input layout
    hr = dev->CreateInputLayout(layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &pVertexLayout);
    if (FAILED(hr))
        return hr;

    // Set the input layout
    devContext->IASetInputLayout(pVertexLayout);
    // Compile the pixel shader
    //组建加载像素着色器
    ID3DBlob* pPSBlob = nullptr;
    hr = D3DCompileFromFile(L"Squance.fx", nullptr, nullptr, "PS", "ps_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, &pPSBlob, &pErrorBlob);
    if (FAILED(hr))
    {
#ifdef _DEBUG
        printf("Can not find FX File .\nPlease run this executable from the directory that contains the FX file\n Error HRESULT:%ld\n\n", hr);
        return hr;
#else
        MessageBox(nullptr,
            L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK);
        return hr;
#endif
    }
    // Create the pixel shader
    hr = dev->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &pPixelShader);
    if (FAILED(hr))
        return hr;
    //release resource

    pVSBlob->Release();
    pPSBlob->Release();

    return hr;
}

 

关于输入布局:

对于顶点着色器,咱们须要知道输入的顶点包含了哪些结构与元素,这里我就只包含了一个顶点的坐标与颜色(顶点的颜色。。。。好奇怪。。。)

 

 

建立绘制的图像:

上面提到了三角形与图像的关系,仍是用方形为例,一个方形须要4个顶点,因此就定义出来

这里我定义了2个方形,由于在绘图的时候会出现背面消影的问题,若是我须要一个旋转的,二面的方形,就须要把它的正反面都定义出来。

struct SimpleVertex    
    {
        DirectX::XMFLOAT3 Pos;
        DirectX::XMFLOAT4 Color;
    };

    SimpleVertex vertices[] =
    {    //正面
        { DirectX::XMFLOAT3(+1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },//
        { DirectX::XMFLOAT3(+1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },//
        { DirectX::XMFLOAT3(-1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 0.5f, 1.0f, 1.0f) },//
        { DirectX::XMFLOAT3(-1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },//绿
        //反面
        { DirectX::XMFLOAT3(+1.0f, +1.0f, -0.1f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
        { DirectX::XMFLOAT3(+1.0f, -1.0f, -0.1f), DirectX::XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },
        { DirectX::XMFLOAT3(-1.0f, -1.0f, -0.1f), DirectX::XMFLOAT4(0.0f, 0.5f, 1.0f, 1.0f) },
        { DirectX::XMFLOAT3(-1.0f, +1.0f, -0.1f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) }
    };

 

 

定义顶点索引:

顶点索引的原理在书中已经说明了,目的就是为了避免对顶点进行复刻,一个顶点只须要定义一次,减小内存与处理器的负担。

    WORD indices[] =
    {
        0, 2, 1,
        0, 3, 2,

        4, 5, 6,
        4, 6, 7
    };

 

这是一个以上面的8个顶点为例的索引,能够看见建立了4个三角形。

而后须要注意一点:背面消隐,具体看书中5.10.2的部分,简而言之,一个三角形只有一面是“有颜色”去显示的,而三角形的正反面是由你定义三角形时的顶点顺序有关的

 

 

投影的视角,坐标:

与其叫投影,我更喜欢将他比喻成相机或眼睛。

想象一下,咱们绘制好的图形须要一个摄像机去把它放到咱们的屏幕上,须要如下几个参数:

1.摄像机的位置,相对于世界空间,摄像机的摆放在哪一个坐标。

2.摄像机的朝向,也就是镜头的方向,镜头向哪里拍摄。

3.垂直方向,在上面2项决定好的状况下,还须要一个固定镜头垂直的方向,来防止镜头随意的旋转,来告诉镜头Z轴。

DirectX::XMVECTOR Eye = DirectX::XMVectorSet(0.0f, 0.0f, 4.0f, 0.0f);;    //眼睛坐标
    DirectX::XMVECTOR Point = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //看向的位置
    DirectX::XMVECTOR Eye_Up = DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);//定义眼睛"向上"的方向
    View_View = DirectX::XMMatrixLookAtLH(Eye, Point, Eye_Up);

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

 

上面的代码中,我将镜头设置在(0,0,4)的位置,看向原点(0,0,0),镜头的垂直方向是(0,1,0)就是Y轴。

 

 

HLSL语言:

使用时,须要设置FX文件的属性为不参与生成,不然会报一个没有main入口的错误。

着色器的编写须要HLSL这种语言,好在他与C++极其类似。据说之前的着色器语言是用汇编写的。。。这真是太糟糕了。

对于语法什么的也没啥好讲的,主要是1个功能:

常量缓冲

C++代码中有:

//结构定义
    struct ConstantBuffer        //用于描述视角坐标
    {
        DirectX::XMMATRIX mWorld;
        DirectX::XMMATRIX mView;
        DirectX::XMMATRIX mProjection;
    };

//建立
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(ConstantBuffer);
    bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    bd.CPUAccessFlags = 0;
    hr = dev->CreateBuffer(&bd, nullptr, &pConstantBuffer);
    if (FAILED(hr))
    {
#ifdef _DEBUG
        printf("Can not create the constant buffer\n Error HRESULT:%ld\n\n", hr);
#endif
        return hr;
    }

 

fx文件中有:

cbuffer ConstantBuffer : register(b0)
{
    matrix World;
    matrix View;
    matrix Projection;
}

VS_OUTPUT VS(float4 Pos : POSITION, float4 Color : COLOR)
{
    VS_OUTPUT output = (VS_OUTPUT)0;
    output.Pos = mul(Pos, World);
    output.Pos = mul(output.Pos, View);
    output.Pos = mul(output.Pos, Projection);
    output.Color = Color;
    return output;
}

 

能够看做着色器与程序共享了一块内存,运行时,着色器不能改变常量缓冲,咱们经过C++代码改变常量缓冲的内容,加以该变着色器的显示。

 

最后,也是最重要的,数学变换原理:

1.局部坐标,世界坐标相互转换:

上面的的内容说了,咱们在世界坐标的某点有一个摄像机,然而,对于程序而言,绘图就是绘制一个个像素点,坐标分析的步骤须要咱们去完成:

在世界坐标内有一个坐标点,那么在咱们一相机为原点的坐标系内它的坐标是什么呢?

部分代码:

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

    // Initialize the view matrix
    DirectX::XMVECTOR Eye = DirectX::XMVectorSet(0.0f, 0.0f, 4.0f, 0.0f);;    //眼睛坐标
    DirectX::XMVECTOR Point = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //看向的位置
    DirectX::XMVECTOR Eye_Up = DirectX::XMVectorSet(0.0f, 1.0f, 100.0f, 0.0f);//定义眼睛"向上"的方向
    View_View = DirectX::XMMatrixLookAtLH(Eye, Point, Eye_Up);        //XMMatrixLookAtLH函数返回的是世界->视图变换矩阵

 

代码中定义了一个单位矩阵做为原世界坐标,在(0,0,4)这点定义了相机,而后看一个API:XMMatrixLookAtLH,它的做用是产生一个变换矩阵,用于将世界坐标转换为局部坐标,也就是一相机为原点的坐标。描述一下它的实现原理:

将固定好的相机方向单位化,计算出从局部坐标转换成世界坐标的矩阵,而后求这个矩阵的逆矩阵,就是从世界坐标转换为局部坐标的转换矩阵。

咱们能够直接在书中3.4.5节找到直接求逆的公式,完整的用代码表示就是:

XMMATRIX XM_CALLCONV XMMatrixLookAtLH
(
    FXMVECTOR Eye
    FXMVECTOR At
    FXMVECTOR Up
)

zaxis = normal(At - Eye)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)
   
对应矩阵:
xaxis.x yaxis.x zaxis.x
0 xaxis.y yaxis.y zaxis.y 0 xaxis.z yaxis.z zaxis.z 0 -dot(xaxis, eye) -dot(yaxis, eye) -dot(zaxis, eye) 1

 

这里normal指求单位向量,cross指向量叉乘,dot指向量点乘

 

2.投影空间

如今,咱们有了一个本身的相机坐标,与相对于相机坐标的经过世界——》局部矩阵转换后的点。

那么,咱们须要一个投影方式,将他push到咱们的屏幕上。

投影空间能够看做一个4棱锥的投射。在书中,咱们须要如下4个变量表示这个锥形:

1.垂直视角域

2.纵横比

3.近剪裁面

4.远剪裁面

示例:咱们如今以本身的眼睛为例,眼睛看向的方向为z轴,头的向上的方向为y轴,肩膀的方向为x轴。

很明显,眼睛向上的视角是有限的,你固然不可能看到额头上有什么,这个视角,就是垂直视角域。那么,水平视角域怎么处理呢?它使用了一个纵横比,就是你水平能够看见的长度的极限除以你垂直能够看见长度 的极限。至于远,近裁剪面,定义了你的视野最远与最近能够看见的东西。

代码:

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

 

这里,我定义了垂直视角为90度,这里指的是总视角0到180度的,横纵比为800/600,最近显示0.01,最远显示100。

好,看一下我定义的边长为2的正方形:

        { DirectX::XMFLOAT3(+1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },//
        { DirectX::XMFLOAT3(+1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },//
        { DirectX::XMFLOAT3(-1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 0.5f, 1.0f, 1.0f) },//
        { DirectX::XMFLOAT3(-1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },//绿

 

以及摄像机的位置:

    DirectX::XMVECTOR Eye = DirectX::XMVectorSet(0.0f, 0.0f, 4.0f, 0.0f);;    //眼睛坐标
    DirectX::XMVECTOR Point = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //看向的位置
    DirectX::XMVECTOR Eye_Up = DirectX::XMVectorSet(0.0f, 1.0f, 100.0f, 0.0f);//定义眼睛"向上"的方向

 

摄像机在(0,0,4)看向(0,0,0)

那么能够计算出这个正方形的高度占显示总高度的大小,为4分之1,就是说,咱们的若是程序窗口的高度为600像素(假设D3D占满整个屏幕),那么显示的时候这个正方形边长为150像素。

 

 

 

 

 

 

 

其它:

我仍是没搞懂HLSL中mul的详细做用,以及投影详细的数学推导,何时看懂了再写。

***工程代码***

相关文章
相关标签/搜索