DirectX11学习笔记三:绘图基础

 DirectX的一大特色之一便是Shader渲染,包括顶点Shader渲染以及像素Shader渲染。设置Shader机制是为了使程
序员能够控制GPU绘制顶点或像素的方式,简单的说,我们设计的Shader代码是运行在GPU硬件中的,这样提高了渲
染效率。上篇幅简要简绍了下DirectX管线渲染的基本步骤,本篇幅从绘制基本的正方体开始详细描述绘制过程。

 

1,Vertex Shader

    struct VertexIn  
    {  
        float3  pos     : POSITION;  
        float4  color   : COLOR;  
    };  

    Vertex Shader是对模型的每一个顶点进行操作加工的代码,所以输入输出自然都是一个顶点结构数据。

    上图的float3和float4是HLSL内置类型。

   级着色器语言(High Level Shader Language,简称HLSL),由微软拥有及开发的一种语言,HLSL 独立的工作在 Windows 平台上,只能供微软的Direct3D使用。 HLSL是微软抗衡GLSL的产品,同时不能与OpenGL标准兼容。他跟Nvidia的Cg非常相似。 HLSL的主要作用为将一些复杂的图像处理,快速而又有效率地在显示卡上完成,与组合式或低阶Shader Language相比,能降低在编写复杂特殊效果时所发生编程错误的机会。 HLSL已经整合到了 DirectX 9中。

   HLSL我会在后面专门写一篇笔记学习

    POSITION和COLOR分别对应顶点的坐标以及颜色向量,这两个声明是在C++语言中设置输入布局InputLayout内容设定的

   

    struct VertexOut  
    {  
       float4 posH : SV_POSITION;  
       float4 color : COLOR;  
    };  

    SV_POSITION是默认固定的系统值,不得改变,其他可以为 任意设定值,因为顶点输出需要内存对齐,所以输出的顶点是经过矩阵计算后得出的float4格式的值,一般为(x,y,z,w)w表示其缩放值,一般为1.

   

    Shader计算顶点的函数模型如下:

VertexOut VS(VertexIn in)
{
  VertexOut out;
  out.pos=mul(float4(in.pos,1),modelMatrix);
  out.pos=mul(out.pos,worldMatrix);
  out.pos=mul(out.pos,viewMatrix);
  
   out.color=in.color;
   return out;
}

2.Pixel Shader

    根据管线渲染步骤,顶点Shader计算是先于像素Shader计算的,在光栅化阶段,一个三角形在其所覆盖的每个像素处使用插值来计算相应顶点的各个属性,然后把插值后的顶点传递给像素着色器。在简单的没有Geometry Shader和Tessellator时,顶点着色器的输出就是像素着色器的输入

    float4 PS(VertexOut pin):SV_TARGET  
    {  
        return pin.color;  
    }  

    SV_TARGET是系统值,表示该函数返回的是用于下一个阶段OutPut Merger的颜色值,这里只简单的做了颜色值得传递。


3.常量缓冲区

  在HLSL中,设置常量缓冲区声明为cbuffer,着色器Shader代码里,我们经常把刷新频率相似的常量放置在相同的常量缓存中。如世界矩阵以及其他转换矩阵针对当个物体设计,放在同一个常量缓存里,而场景灯光的位置,朝向,颜色等这下针对整个场合都要每帧渲染的的数据,以相同格式放在一个cbuffer


4.Effect框架利用

Effect框架是微软提供给我们的一个开源的代码库,用来把shader代码和相应的渲染状态合理的组织到一起,来实现一个针对性的特效。比如渲染水面、云彩、阴影等不同特效时,需要用到不同的渲染状态,这样每个特效都可以写成单独的Effect。


Effect组成为:

technique11:一个特效的一种渲染方法即为technique11,一个特效可以有多种方法。

pass:一个technique11必须包含至少一个pass,指的是物体的实际渲染过程,一个technique11可以包含多个pass以实现混合效果


下面为一个特效的简单effect

    cbuffer PerFrame  
    {  
        float4x4    g_worldViewProj;  
    };  
      
    struct VertexIn  
    {  
        float3  pos     : POSITION;  
        float4  color   : COLOR;  
    };  
      
    struct VertexOut  
    {  
        float4  posH    : SV_POSITION;  
        float4  color   : COLOR;  
    };  
      
    VertexOut VS(VertexIn vin)  
    {  
        VertexOut vout;  
        vout.posH = mul(float4(vin.pos,1.f),g_worldViewProj);  
        vout.color = vin.color;  
          
        return vout;  
    }  
      
    float4 PS(VertexOut pin):SV_TARGET  
    {  
        return pin.color;  
    }  
      
    technique11 BasicDraw  
    {  
        Pass p0  
        {  
            SetVertexShader(CompileShader(vs_5_0,VS()));  
            SetPixelShader(CompileShader(ps_5_0,PS()));  
        }  
    }  

如图所示,一个Effect包含了特效所需要的所有常量缓存,Vertex渲染方法,Pixel渲染方法, 而后包含一个technique11方法,里面包含一个名为p0的Pass,Pass里指定了用于Vertex和Pixel渲染的方法。

既然我们使用了Effect来封装整个渲染的过程,那在C++里面为其编辑就要用到effect专有的接口了

有ID3DX11Effect,technique11为ID3D11EffectTechnique。

改变shader里的数据需要通过相应的接口

ID3DX11EffectMatixVariable 对应 Float4x4接口

ID3DX11EffectVetorVariable 对应 Float4接口

ID3DX11EffectVariable 对已无类型接口


这些接口通过如下方式获得:

  1. g_fxWorldViewProj = g_effect->GetVariableByName("g_worldViewProj")->AsMatrix();  

这里的“g_worldViewProj”即shader中的全局变量名字。之后就可以在C++程序中通过更改g_fxWorldViewProj接口来更新shader中相应的全局变量了:

  1. g_fxWorldViewProj->SetMatrix(reinterpret_cast<float*>(&worldViewProj)); 


4输入布局(input layout)

    设置输入布局主要是为了指定GPU按特定格式读取顶点缓存中的Void*数据,在未定义输入布局之前,顶点缓存中的所有数据都为Void*类型,即为GPU对其是陌生的。

    在顶点结构中,针对每个成员,我们要定义一个相应的D3D11_INPUT_ELEMENT_DESC。这个结构定义了该成员相应的有关信息,定义如下:

    typedef struct D3D11_INPUT_ELEMENT_DESC {  
      LPCSTR                     SemanticName;  
      UINT                       SemanticIndex;  
      DXGI_FORMAT                Format;  
      UINT                       InputSlot;  
      UINT                       AlignedByteOffset;  
      D3D11_INPUT_CLASSIFICATION InputSlotClass;  
      UINT                       InstanceDataStepRate;  
    } D3D11_INPUT_ELEMENT_DESC;  
具体信息见 Direct11参数列表中的条目1;


接下来我们就对每一个顶点的参数成员定义一个D3DD_INPUT_ELEMENT_DESC,并组成数组

    D3D11_INPUT_ELEMENT_DESC inputDesc[2] =   
    {  
        { "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 }  
    };  
接下来创建这个input layout

    HRESULT ID3D11Device::CreateInputLayout(  
      [in]   const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,  
      [in]   UINT NumElements,  
      [in]   const void *pShaderBytecodeWithInputSignature,  
      [in]   SIZE_T BytecodeLength,  
      [out]  ID3D11InputLayout **ppInputLayout  
    );  

4, 顶点缓存和索引缓存

   对于顶点,我们需要为其设定一个缓存,即buffer当然,指定顶点索引的数组也需要为其设定一个索引缓存。

建立缓存前,我们需要设计一个缓存描述符来描述缓存格式,所有的缓存都可以用D3D11_BUFFER_DESC

    typedef struct D3D11_BUFFER_DESC {  
      UINT        ByteWidth;  
      D3D11_USAGE Usage;  
      UINT        BindFlags;  
      UINT        CPUAccessFlags;  
      UINT        MiscFlags;  
      UINT        StructureByteStride;  
    } D3D11_BUFFER_DESC;  

具体的属性介绍参见DirectX11参数列表条目2

接下来,我们用到D3D11_SUBRESOURCE_DATA来之定缓存的数据源

    typedef struct D3D11_SUBRESOURCE_DATA {  
      const void *pSysMem;  
      UINT       SysMemPitch;  
      UINT       SysMemSlicePitch;  
    } D3D11_SUBRESOURCE_DATA;  

有关 D3D11_SUBRESOURCE_ DATA参数介绍见Direct11参数列表条目3


这样,物体顶点数据的准备工作就全部完成了,接下来就是相关渲染的准备


5.渲染阶段

一般为下几个步骤:

1,清屏

2,指定inputlayout,顶点缓存,索引缓存,图元拓扑类型

3,宣传technique11,逐步进行pass渲染

    g_deviceContext->IASetInputLayout(g_inputLayout);  
    //指定顶点缓存  
    UINT stride = sizeof(Vertex);  
    UINT offset = 0;  
    g_deviceContext->IASetVertexBuffers(0,1,&g_VB,&stride,&offset);  
    //指定索引缓存  
    g_deviceContext->IASetIndexBuffer(g_IB,DXGI_FORMAT_R32_UINT,0);  
    //指定图元拓扑类型  
    g_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);  

最后运用Effect接口获取目标technique11中的pass,设定该pass方式,然后正式渲染。

    ID3DX11EffectTechnique *tech = g_effect->GetTechniqueByName("BasicDraw");  
    D3DX11_TECHNIQUE_DESC techDesc;  
    tech->GetDesc(&techDesc);  
    for(UINT i=0; iGetPassByIndex(i)->Apply(0,g_deviceContext);  
        g_deviceContext->DrawIndexed(36,0,0);  
    }  








你可能感兴趣的:(DirectX11学习笔记三:绘图基础)