Input Layout
我们通过 发送 一块数据 到显卡 来进行处理 几何体的。为了告知 Direct3D 我们定义了什么属性, 如 顺序, 大小, 等, 在此使用一种 叫做 Input Layout 的 结构, 以让API 知道 如何 具体处理 我们将要绘制的几何体。(跟我在前面说过的那样)在DirectX11中, 使用 描述 *_Desc 来进行组织 这些 “告知API 去做什么, 怎么做, 对哪些数据进行处理的” 结构体 chunk data:
typedef struct D3D11_INPUT_ELEMENT_DESC{ LPCSTR SemanticName; //描述 本 结构体数据的 目的;“POSITION” 标明 本结构体用来 描述 vertex‘ position;“COLOR” 标明 本结构体用来描述 vertex’ color;“NORMAL” 标明 本结构体用来描述 法向量;等等。 UINT SemanticIndex; //就是索引。对于某些vertex 可能 有多个 color 描述, 则 SemanticName 都为 “COLOR”, 那么 SemanticName = “COLOR”, SemanticIndex =0 的结构体 表示第一份 颜色描述; SemanticName = “COLOR”, SemanticIndex =1 的结构体 表示第二份 颜色描述;更可能的是, 同一个vertex 会有多份的 纹理映射坐标, 这就需要 编号区别了。 DXGI_FORMAT Format; // 本结构描述的 格式 , 例如 DXGI_FORMAT_R32G32B32_FLOAT UINT InputSlot; // 也是索引, 不过是用来确定 vertex buffer的, 在Direct3D 中,可以同时绑定 多个 vertex buffer, 这是就需要 用 inputSlot 来指明 我们要描述的 数据所在 哪个 vertex buffer中。 UINT AlignedByteOffset; // 在 指定 vertex buffer 中 的 开头偏移位置。 D3D11_INPUT_CLASSIFICATION InputSlotClass; // 用以区分如何 处理 被描述的 数据, 究竟是按照 “per vertex” 还是 “per object”。 UINT InstanceDataStepRate; // 指明同一场景中, 同时能够存在多上 实体。}D3D11_INPUT_ELEMENT_DESC;
其中的 CreateInputLayout() 如
HRESULT CreateInputLayout( const D3D11_INPUT_ELEMENT_DESC *pInputElements, //创建的 InputLayout 通过 一组 描述_DESC创建 UINT NumElements, // 上面数组的大小, 即多少个 _DESC描述 const void* pShaderBytecodeWithInputSignature, //对应的 已编译 vertex shader code, 他的 签名 要和 本input layout 一致 SIZE_T BytecodeLength,//上述 vertex shader bytecode的大小 ID3D11InputLayout **ppInputLayout //最终 返回结果存放。);
自从 Direct3D 10 开始, shader 是一项作为基本流程要求 , 进行 Direct3D 的渲染工作。vertex shader 是 已经经过编译的 代码, 直接由 GPU 执行。 设备 device 对 每一个 vertex 执行 vertex shader 。 此外 Direct3D 支持 其他各种类型的 shader , 将会在 第七章中进行 详细讨论。即使是 渲染几何体 也需要使用 shaders, 下面有必要先 简单 引入 shader 以便完成 InputLayout 的工作:
首先, 加载并编译 vertex-shader 的文本文件(本例中的是 sampleShader.fx ) 成 二进制代码, 编译后的 二进制数据 存放在 两个 buffer里面:
ID3DBlog *vsBuffer = NULL;
ID3DBlog *errorBuffer = NULL;
// vsBuffer 是 vertex-shader-Buffer 的缩写 // ID3DBlog 定义 内存块, 用以存放大数据(的吧, 我联想到 数据库的 blog数据类型); 用处是作为 数据缓存, 可用于 存储vertex, adjacency, 以及 在处理网格优化 时的 meterial材质信息等. 还可作为 使用API 编译 vertex, geometry, pixel-shader 时的返回值(此例如是)
不过 errorBuffer 用以存储 错误、警告等信息, 实际有效地 二进制代码将会进入 vsBuffer( vertex-shader-Buffer);
第二步骤, 直接 使用 CreateVertexShader() 函数, 将编译好的 二进制数据(存放于 vsBuffer内)作为输入, 创建 vertexShapder。
如下所示:
// 声明定义各种 条件, 存储位置
// 第一步: 编译 shader文件result = D3DX11CompileFromFile( "sampleShader.fx", 0, 0, "VS_Main", "vs_4_0", shaderFlags, 0, 0, &vsBuffer, &errorBuffer, 0);// 结果处理, 查看是否失败
//第二步, 创建最终的 shaderresult = d3dDevice_->CreateVertexShader( vsBuffer->GetBufferPointer(), vsBuffer->GetBufferSize(), 0, &solidColorVS);// 检查是否成功if( FAILED( result)){ if( vsBuffer) vsBuffer->Release(); return false;}
其中, D3DX11CompileFromFile( "sampleShader.fx", // LPCTSTR pSrcFile : 待编译 的 HLSL shader源文件 0, // const D3D10_SHADER_MACRO *pDefines : shader code 使用的全局宏,使用方式和 c/c++ 的一样; // 可在 c/c++ 内定义这种宏, 如定义宏 "AGE", 并其值为 18表示岁数: /* const D3D_SHADER_MACRO defineMacros[] = { "AGE", "18", }; */ //但在 本函数中做什么用呢? 暂未知(估计是, shader-code内部使用, 或者是 HLSL编译时用到的 "外部符号表"?呵呵。) 0, // LPD3D10INCLUDE pInclude : HLSL 内部使用的 ?? "VS_Main", // LPCSTR pFunctionName : HLSL shader 源文件内 定义的入口 函数; // 因为 一个 shader 文件中可能包好了多种类型的 shader ( vertex? pixel? geometry? ... ?), // 各种类型有对应的各种不同的入口, 而"VS_Main()" 是本例使用的入口, 估计, D3DX11CompileFromFile() 函数在编译时, // 只需要处理 "VS_Main()" 函数相关的代码块即可 // 此处所谓的 编译, 应该就是指 HLSL编译器进行 编译吧 "vs_4_0", // LPCSTR pProfile : HLSL 进行编译时, 采用哪种版本的 shader, vs_4_0 表示 shader4.0, vs_5_0 表示 shader5.0 // 这有啥关键之处? 我们用 c++ 编译 c的函数时, 需要引用 extern"C" 进行声明 下面的函数按照 C方式进行 修饰, // 否则编译器会提示无法找到 obj模块中的某个函数; // shader4.0 和 shader5.0 版本的不同, 会导致 D3DX11CompileFromFile() 产生的二进制代码不同, // 放到当前固定版本的显卡时,受其支持shader版本的限制, 会导致无法运行着色。 shaderFlags, // UINT Flags1 : HLSL 编译条件/即功能选项/方式 0, // UINT Flags2 : HLSL 编译产生结果时, 将使用如何的特效 0, // ID3DX11ThreadPump *pPump : 线程相关(池?) &vsBuffer, // ID3D10Blog **ppShader : 最终编译出来的二进制数据存放未知 &errorBuffer, // ID3D10Blog **ppErrorMsgs : 编译时的错误、警告信息等 0 // HRESULT *pHResult : 结果);