SharpVulkan キューブの描画

SharpVulkan でキューブを描画する

よくあるキューブ(立方体)を SharpVulkan で描画してみたいと思います。
なお今回でチュートリアルとしては最後になります。

今回はテクスチャを使用しないため、基本となるコードはテクスチャを貼る前のもので、3角形を回転させていたものを改良していくのがよいです。

管理マネージャの導入

いくつものリソースが増えてきてコードを追加していくときに大変になってきました。特に終了処理あたりにその手間を感じるのではないでしょうか。

そこで今回は管理クラスを導入しました。
管理と言うほど大げさなことはしていませんので、 SimpleResourceManager と名付けて Common フォルダに入れています。
このクラスの終了処理時に各解放処理を行うようにしています。
リソース作成時にこのクラスに登録しておけば、後は忘れておけるというのは少し楽になるだろうと思っています。

public class SimpleResourceManager
{
  public void Regist( VkBuffer buffer, VkDeviceMemory memory )
  {
      m_buffers.Add(buffer);
      m_memories.Add(memory);
  }

  public void Destroy( VkDevice device )
  {
      foreach(var layout in m_pipelineLayouts )
      {
          VulkanAPI.vkDestroyPipelineLayout(device, layout);
      }
      m_pipelineLayouts = null;

      foreach( var layout in m_descLayouts )
      {
          VulkanAPI.vkDestroyDescriptorSetLayout(device, layout);
      }
      if (m_descriptorPool != null)
          VulkanAPI.vkDestroyDescriptorPool(device, m_descriptorPool);
      m_descriptorPool = null;

      foreach( var buffer in m_buffers)
      {
          VulkanAPI.vkDestroyBuffer(device, buffer);
      }
      m_buffers = null;
      foreach( var mem in m_memories )
      {
          VulkanAPI.vkFreeMemory(device, mem);
      }
  }

  public VkDescriptorPool CreateDescriptorPool( VkDevice device, VkDescriptorPoolSize[] descPoolSize, int maxSets )
  {
      var descriptorPoolCreateInfo = new VkDescriptorPoolCreateInfo()
      {
          poolSizes = descPoolSize,
          maxSets = (uint)maxSets,
          flags = VkDescriptorPoolCreateFlags.VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
      };
      VulkanAPI.vkCreateDescriptorPool(device, ref descriptorPoolCreateInfo, out m_descriptorPool);
      return m_descriptorPool;
  }
  public VkDescriptorSetLayout CreateDescriptorSetLayout( VkDevice device, VkDescriptorSetLayoutBinding[] bindings )
  {
      VkDescriptorSetLayout layout = null;
      var descriptorSetLayoutCreateInfo = new VkDescriptorSetLayoutCreateInfo();
      descriptorSetLayoutCreateInfo.bindings = bindings;
      VulkanAPI.vkCreateDescriptorSetLayout(device, ref descriptorSetLayoutCreateInfo, out layout);
      m_descLayouts.Add(layout);
      return layout;
  }
  public VkPipelineLayout CreatePipelineLayout( VkDevice device, VkDescriptorSetLayout descSetLayout )
  {
      var createInfo = new VkPipelineLayoutCreateInfo();
      createInfo.setLayouts = new[] { descSetLayout };
      VkPipelineLayout layout = null;
      VulkanAPI.vkCreatePipelineLayout(device, ref createInfo, out layout);
      m_pipelineLayouts.Add(layout);
      return layout;
  }
  private VkDescriptorPool m_descriptorPool;
  private List m_buffers = new List();
  private List m_memories = new List();
  private List m_descLayouts = new List();
  private List m_pipelineLayouts = new List();
}

モデルデータについて

頂点データやインデックスデータを保持するクラスから取得して、頂点バッファ・インデックスバッファを作ります。
インデックスバッファは本シリーズで初めての登場です。

とはいっても作る方法もほぼ頂点バッファと同じです。違うのはデータの種類の部分くらいでしょうか。

SampleHelpers.CreateBuffer(
  device, physicalDevice, 
  ibSize, VkBufferUsageFlags.VK_BUFFER_USAGE_INDEX_BUFFER_BIT, 
  flags, 
  out m_indexBuffer, out m_indexBufferMemory);

// 初期データの書き込み.
MappedMemoryStream mapped;
VulkanAPI.vkMapMemory(device, m_indexBufferMemory, 0, VkDeviceSize.VK_WHOLE_SIZE, 0, out mapped);
mapped.Write(cubeIndices);
VulkanAPI.vkUnmapMemory(device, m_indexBufferMemory);

描画処理

今までの描画処理と違う部分があります。
今回はインデックスバッファを使用するため、描画コードをその追加をする必要があります。

VulkanAPI.vkCmdBindIndexBuffer(command, m_indexBuffer, 0, VkIndexType.VK_INDEX_TYPE_UINT16);
VulkanAPI.vkCmdDrawIndexed(command, 36, 1, 0, 0, 0);

インデックスバッファのセットのためのコマンドを設定し、 インデックス付き描画である vkCmdDrawIndexed を使用して描画する、というコードが上記のようになります。

これで早速描画してみるとどうなるでしょうか・・・

sharp_vulkan_cube_1

このように予想した結果と違い、ポリゴンの前後関係がおかしなことになっています。
何が足りなかったのかというと、デプスバッファの準備・設定が足りていませんでした。今までのコードにデプスバッファ関連の設定を入れていきましょう

デプスバッファを有効化する

デプスバッファを有効化するには、 xaml 側で設定してしまいます。
以下のように EnableDepthBuffer を true に設定します。これでこのコントロール用のデプスバッファが有効化されました。


続いて、描画でデプスバッファを使用できるように変更していきます。

今まではデプスステンシルステートを初期値で使っていましたが、これを以下のように有効化設定していきます。

// デプスステンシルステートの構築.
m_depthStencilState = new VkPipelineDepthStencilStateCreateInfo();
m_depthStencilState.depthTestEnable = true;
m_depthStencilState.depthWriteEnable = true;
m_depthStencilState.depthCompareOp = VkCompareOp.VK_COMPARE_OP_LESS_OR_EQUAL;

デプススバッファに深度値を書込み、そして比較も行うための設定となっています。

続いてバッファクリアの部分も修正が必要になります。

var renderPassBeginInfo = new VkRenderPassBeginInfo()
{
  framebuffer = framebuffer,
  renderArea = renderArea,
  renderPass = vkctrl.GetControlRenderPass(),
  pClearValues = new[] {
      new VkClearValue() { color = clearColor },
      new VkClearValue() { depthStencil = clearDepth },
  }
};

今まではクリア値はカラーのみ必要でした。今回はデプスバッファも使っているため、そのクリア値も設定する必要が出てきました。

これらの設定を行った後で描画を行うと、以下のように正しい予想した結果が描画されると思います。
sharp_vulkan_cube_2

まとめ

キューブを描画するというところまでを SharpVulkan を使ってやってみました。
低レイヤーのグラフィックスAPIである Vulkan で Cube を出すという段階までやってきましたが、いかがでしたでしょうか。

タイトルとURLをコピーしました