`
xuela_net
  • 浏览: 495148 次
文章分类
社区版块
存档分类
最新评论

Windows 8 Directx 开发学习笔记(二)建立模型及初始化设备

 
阅读更多

上一篇中介绍的DirectxApp类给整个应用搭建了一个框架,而这篇文章涉及的CubeRenderer类则是负责填充框架,呈现实际内容:一个旋转的彩色立方体。CubeRenderer类中的方法通过名称就很容易理解,但是它们之间的联系和功能实现比较复杂,而且CubeRenderer的成员较多,涉及到DirectX的基本原理和概念,看上去无从下手。没办法,打算按照代码的顺序去看,遇到哪里不明白就去查找对应的原理。看下CubeRenderer.h头文件就转到CubeRenderer.cpp,想了解下整个绘制过程。主要研究下这个文件。首先自然是包含头文件和使用命名空间,然后是构造函数。

#include "pch.h"
#include "CubeRenderer.h"
 
using namespace DirectX;
using namespace Microsoft::WRL;
using namespaceWindows::Foundation;
using namespaceWindows::UI::Core;
 
CubeRenderer::CubeRenderer():
   m_loadingComplete(false),
   m_indexCount(0)
{
}

接着就是一个100多行的CreateDeviceResources方法,它实现创建设备资源的功能。CubeRenderer类继承自Direct3Dbase类,CreateDeviceResources方法首先调用基类的CreateDeviceResources方法,然后读入生成的顶点着色器和像素着色器文件。注意ReadDataAsync方法中的Async表明这是一个异步方法。

Direct3DBase::CreateDeviceResources();
 
   auto loadVSTask =DX::ReadDataAsync("SimpleVertexShader.cso");
   auto loadPSTask =DX::ReadDataAsync("SimplePixelShader.cso");

这里涉及到DirectX编程很重要的三个概念:顶点着色器、像素着色器和异步编程。

顶点着色器SimpleVertexShader可以看成是一个以顶点作为输入输出数据的方法,只不过这个方法会由显示硬件去执行。每个将要绘制的顶点都会通过顶点着色器,可以概念性地认为在显示硬件上执行了如下代码:

for(UINT i = 0; i <numVertices; ++i)
    outputVertex[i] = SimpleVertexShader(inputVertex[i]);

之所以单独提出这个着色器是因为顶点着色器方法可在GPU上运行,执行速度非常快。

像素着色器SimplePixelShader则用来渲染像素片段,它的输入是与顶点着色器的输出对应的。

异步编程简单的说就是异步方法会立即返回,但返回的不是结果,其代码会在后台运行直至结束,因此不影响其他线程的操作。不过异步操作的返回时间不确定,为保证内容的同步,需要一些控制,then方法就是其中一种。then方法包括的部分会在调用它的任务成功完成后执行,看代码时暂时把then去掉,方便理解。关于以上概念的详细介绍见附注。

auto createVSTask =loadVSTask.then([this](Platform::Array<byte>^fileData) {
      DX::ThrowIfFailed(
          m_d3dDevice->CreateVertexShader(
             fileData->Data,
             fileData->Length,
             nullptr,
             &m_vertexShader
             )
          );
 
      constD3D11_INPUT_ELEMENT_DESC vertexDesc[] =
      {
          { "POSITION", 0,DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
          { "COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,D3D11_INPUT_PER_VERTEX_DATA, 0 },
      };
 
      DX::ThrowIfFailed(
          m_d3dDevice->CreateInputLayout(
             vertexDesc,
             ARRAYSIZE(vertexDesc),
             fileData->Data,
             fileData->Length,
             &m_inputLayout
             )
          );
   });

以上的代码就是在载入顶点着色器成功后,依照其说明数据创建顶点着色器,之后创建输入布局InputLayout来描述顶点着色器的分量结构,让DirectX知道如何使用其中的每个分量。接着是创建像素着色器和一个来保存模型、视图和投影矩阵的常量缓冲区,代码如下:

auto createPSTask =loadPSTask.then([this](Platform::Array<byte>^fileData) {
      DX::ThrowIfFailed(
          m_d3dDevice->CreatePixelShader(
             fileData->Data,
             fileData->Length,
             nullptr,
             &m_pixelShader
             )
         );
 
      CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer),D3D11_BIND_CONSTANT_BUFFER);
      DX::ThrowIfFailed(
          m_d3dDevice->CreateBuffer(
             &constantBufferDesc,
             nullptr,
             &m_constantBuffer
             )
          );
   });

上面准备工作完成后,开始创建一个正方体的模型。首先定义模型的顶点。正方体有八个顶点,所以顶点数组有八个元素,包含各顶点的位置和颜色信息。

 auto createCubeTask= (createPSTask && createVSTask).then([this] () {
      VertexPositionColor cubeVertices[] =
      {
          {XMFLOAT3(-0.5f, -0.5f, -0.5f),XMFLOAT3(0.0f, 0.0f, 0.0f)},
          {XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)},
          {XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)},
          {XMFLOAT3(-0.5f,  0.5f, 0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)},
          {XMFLOAT3( 0.5f, -0.5f, -0.5f),XMFLOAT3(1.0f, 0.0f, 0.0f)},
          {XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)},
          {XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)},
          {XMFLOAT3( 0.5f,  0.5f, 0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)},
      };
 
      D3D11_SUBRESOURCE_DATA vertexBufferData ={0};
      vertexBufferData.pSysMem = cubeVertices; //指明数据存储位置
      vertexBufferData.SysMemPitch = 0;
      vertexBufferData.SysMemSlicePitch = 0;
      CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices),D3D11_BIND_VERTEX_BUFFER); //缓冲区格式描述
      DX::ThrowIfFailed(
          m_d3dDevice->CreateBuffer(
             &vertexBufferDesc,
             &vertexBufferData,
             &m_vertexBuffer
             )
          );

绘制一个正方体,光有顶点还不够,DirectX不知道是要绘制一个正方体还是两个交叉的平面,所以如何连接各顶点绘制六个面需要进行说明,这个说明就由索引缓冲区完成。因为DirectX只绘制三角形,所以一个正方形面需要两个三角形拼接。

   unsigned short cubeIndices[] =
      {
          0,2,1, // -x
          1,2,3,
 
          4,5,6, // +x
          5,7,6,
 
          0,1,5, // -y
          0,5,4,
 
          2,6,7, // +y
          2,7,3,
 
          0,4,6, // -z
          0,6,2,
 
          1,3,7, // +z
          1,7,5,
      };
 
      m_indexCount = ARRAYSIZE(cubeIndices);
 
      D3D11_SUBRESOURCE_DATA indexBufferData ={0};
      indexBufferData.pSysMem = cubeIndices;
      indexBufferData.SysMemPitch = 0;
      indexBufferData.SysMemSlicePitch = 0;
      CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices),D3D11_BIND_INDEX_BUFFER);
      DX::ThrowIfFailed(
          m_d3dDevice->CreateBuffer(
             &indexBufferDesc,
             &indexBufferData,
             &m_indexBuffer
             )
          );
   });
 
   createCubeTask.then([this] () {
      m_loadingComplete = true;
   });
}

完成这些工作后,资源创建完成,进入下一阶段的处理。

附注:

关于顶点着色器和像素着色器可以参考博文

C++Directx11开发笔记四:着色器之顶点着色器和像素着色器


更详细的介绍可以看《Directx 10 3D游戏编程入门》的第5章

游戏编程入门系列最新的是介绍DirectX 11,可惜是英文版,刚接触有些概念理解不好。这本书虽然介绍的是DirectX10,但是它和DirectX 11的许多内容是相通的,可以参考。我并没有找到这本书的印刷版,好像没有正式出版,只能在此对译者汤毅表示感谢,如果有一天这本书正式出版,一定买一本支持。


异步编程推荐

使用 C++ 异步编程(Metro 风格应用)


2012.11.26更新:

顶点着色器文件SimpleVertexShader.cso和像素着色器文件SimplePixelShader.cso是由SimpleVertexShader.hlsl和SimplePixelShader.hlsl编译而成,在GPU上运行。在Direct3D 11中,GPU必须包含一个正确的顶点和像素着色器。这里不做任何更改,保持默认。感谢liuwumiyuhuiping提出的问题。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics