テクスチャを貼ります
今回はテクスチャを貼ってみたいと思います。
ただ画像フォーマットは結構面倒くさい部分であるので、サンプルコードのほうには最低限しか実装していません。
サンプル理解が終わった後は、有名なテクスチャロードのライブラリや画像ロードのライブラリを使って行くことになるかなと思います。
(C# で簡単に使えるいいテクスチャライブラリが見つからなかったので自作してしまいましたが・・・)
頂点バッファ
今まで頂点データとして位置とカラーでしたが、今回は位置とUV値としました。
struct Vertex { public vec3 Position; public vec2 UV0; }
このデータに合うように CreateVertexInputState 関数の中身を修正しましょう。
テクスチャの準備
今回は tga ファイルから解像度と実データを取得するため SimpleTgaReader を作りました。
解像度と実データを元に、 VkImage, VkImageView, VkSampler を作成していきます。
SimpleTgaReader tex = new SimpleTgaReader("resource/texture.tga"); var command =m_commandBuffers[1]; SampleHelpers.CreateTexture(device, physicalDevice, tex.Width, tex.Height, tex.ImageData, out m_image, out m_imageMemory, graphicsQueue, command); // イメージビューの作成. var imageViewCreateInfo = new VkImageViewCreateInfo() { image = m_image, viewType = VkImageViewType.VK_IMAGE_VIEW_TYPE_2D, format = VkFormat.VK_FORMAT_B8G8R8A8_UNORM, components =new VkComponentMapping(), subresourceRange = new VkImageSubresourceRange() { aspectMask = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT, baseArrayLayer = 0, baseMipLevel = 0, levelCount = 1, layerCount =1, } }; VulkanAPI.vkCreateImageView(device, ref imageViewCreateInfo, out m_imageView ); // サンプラーの作成. var samplerCreateInfo = new VkSamplerCreateInfo() { magFilter = VkFilter.VK_FILTER_LINEAR, minFilter = VkFilter.VK_FILTER_LINEAR, mipmapMode = VkSamplerMipmapMode.VK_SAMPLER_MIPMAP_MODE_NEAREST, addressModeU = VkSamplerAddressMode.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, addressModeV = VkSamplerAddressMode.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, }; VulkanAPI.vkCreateSampler(device, ref samplerCreateInfo, out m_imageSampler);
今までの頂点バッファらと似ている部分もありますが、テクスチャならではの部分があります。
それが VkImageView と サンプラーでしょう。
CreateTexture 関数の中でも処理が詰まっています。ここでの引用は避けますが、流れだけは説明しておきます。
テクスチャをデバイスのローカルメモリにおいて使いたいため、またその領域は直接操作することができないため、ステージングバッファ経由でデータを転送することになります。
このときに、 vkCmdCopyBufferToImage を用いて、イメージとしてのデータに変更します。
あとは、以前やったようにメモリの情報を再セットして、テクスチャとして有効な状態にします。
ディスクリプタの準備
ディスクリプタの準備がちょっと変わります。以前は定数バッファを1つでしたが、今回はテクスチャ用の設定も必要になるのでディスクリプタは2つ必要になります。
ディスクリプタプールの準備は以下のようになります。
// 今は定数バッファを1つ、サンプラーを1つを格納できるだけのディスクリプタプールを準備. VkDescriptorPoolSize descriptorPoolSize = new VkDescriptorPoolSize() { descriptorCount = 1, type = VkDescriptorType.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, }; VkDescriptorPoolSize descriptorPoolSizeForSampler = new VkDescriptorPoolSize() { descriptorCount = 1, type = VkDescriptorType.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, }; var descriptorPoolCreateInfo = new VkDescriptorPoolCreateInfo() { poolSizes = new[] { descriptorPoolSize, descriptorPoolSizeForSampler }, maxSets = 1, flags = VkDescriptorPoolCreateFlags.VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, }; VulkanAPI.vkCreateDescriptorPool(device, ref descriptorPoolCreateInfo, out m_descriptorPool);
そしてディスクリプタセットレイアウトのほうは以下のようになります。
テクスチャ(サンプラ)のデータはバインディング1に設定するようにしています。
var descLayoutBindingForUniform = new VkDescriptorSetLayoutBinding(); descLayoutBindingForUniform.binding = 0; descLayoutBindingForUniform.descriptorCount = 1; descLayoutBindingForUniform.descriptorType = VkDescriptorType.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descLayoutBindingForUniform.stageFlags = VkShaderStageFlagBits.VK_SHADER_STAGE_VERTEX_BIT; var descLayoutBindingForSampler = new VkDescriptorSetLayoutBinding(); descLayoutBindingForSampler.binding = 1; descLayoutBindingForSampler.descriptorCount = 1; descLayoutBindingForSampler.descriptorType = VkDescriptorType.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descLayoutBindingForSampler.stageFlags = VkShaderStageFlagBits.VK_SHADER_STAGE_FRAGMENT_BIT; var descriptorSetLayoutCreateInfo = new VkDescriptorSetLayoutCreateInfo(); descriptorSetLayoutCreateInfo.bindings = new[] { descLayoutBindingForUniform, descLayoutBindingForSampler }; VulkanAPI.vkCreateDescriptorSetLayout(device, ref descriptorSetLayoutCreateInfo, out m_descriptorSetLayout);
続いて、既に生成してある定数バッファやサンプラーから使用するディスクリプタを作ります。
var descUniform = new VkDescriptorBufferInfo() { buffer = m_uniformBuffer, range = Marshal.SizeOf(), }; var descSampler = new VkDescriptorImageInfo() { imageView = m_imageView, sampler = m_imageSampler, }; var descForUniform = SampleHelpers.CreateDescriptorFromUniformBuffer(0, descUniform, m_descriptorSet); var descForSampler = SampleHelpers.CreateDescriptorFromImageSampler(1, descSampler, m_descriptorSet); var descriptorWrites = new[] { descForUniform, descForSampler }; VulkanAPI.vkUpdateDescriptorSets(device, descriptorWrites, null);
描画
実は先のディスクリプタの構成まで終わってしまうと、描画の方は前のコードを変更しなくても問題ないです。
サンプルコードのほうでは4角形にするために設定を変更した程度です。