7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

次世代グラフィックスAPIAdvent Calendar 2016

Day 10

RenderPassで効率的なポストエフェクト

Posted at

RenderPassを使うことでいくつかのポストエフェクトを効率的に実行できるようになります。ここでは0.0f~1.0fでクリッピングを行うだけの単純なトーンマッピングを実装していきます。その過程でシェーダでのSubpassInputの使い方を解説していきます。

前準備

RenderPassについての詳細は前日の記事を参照してください。

仕様策定

今回実装するトーンマッピングは、次に示すようなパスで実装を行っていきます。

SimpleTonemapPass.png

図中のRrおよびOutはリソース(Image)で、次のようなパラメータで生成されているものとします。

  • Rr: Format=VK_FORMAT_R16G16B16A16_SFLOAT
  • Out: Format=VK_FORMAT_R8G8B8A8_UNORM

RenderPassの作成

ではまずAttachmentから定義していきます。

const attachmentRr = VkAttachmentDescription { 0, VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT,
  VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE,
  VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
const attachmentOut = VkAttachmentDescription { 0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT,
  VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE,
  VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };

通常のレンダリングを受けるほうは内容をクリアする必要がありますが、最終的にデータが必要というわけではないのでその指定を行っています。逆に出力側は全ピクセル出力されるので前のピクセルはどうでもいいのと、後のRenderPassで使用することもあるのでメモリに書き戻すようにしています。また、シェーダ入力(テクスチャ)として使用できるようにfinalLayoutを指定しています。

次にSubpassを定義します。

const passNormalRenderOutput = VkAttachmentReference { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
const passTonemapSource = VkAttachmentReference { 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
const passTonemapOutput = VkAttachmentReference { 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
const passNormalRender = VkSubpassDescription { 0, VK_PIPELINE_BIND_POINT_GRAPHICS,
  0, nullptr, 1, &passNormalRenderOutput, nullptr, nullptr, 0, nullptr };
const passTonemap = VkSubpassDescriptor { 0, VK_PIPELINE_BIND_POINT_GRAPHICS,
  1, &passTonemapSource, 1, &passTonemapOutput, nullptr, nullptr, 0, nullptr};

最初の3行でAttachmentへの参照を定義しています。出力はCOLOR_ATTACHMENT_OPTIMAL、入カはSHADER_READ_ONLY_OPTIMALである必要があるためその設定をしています。残りの4行でパスの定義をしています。

最後に依存性の定義です。今回はパス1の実行より前にパス0が完了している必要があるためそれを記述します。

const depNormalToTone = VkSubpassDependency { 0, 1, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
  VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
  VK_ACCESS_SHADER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT };

全てのピクセルに対して完了している必要はないのでDEPENDENCY_BY_REGION_BITを指定しています。

最後にRenderPassを作成します。

const attachments = std::make_unique<VkAttachmentDescription[2]>({ attachmentRr, attachmentOut });
const passes = std::make_unique<VkSubpassDescription[2]>({ passNormalRender, passTonemap });
const rpinfo = VkRenderPassCreateInfo { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0,
  2, attachments.get(), 2, passes.get(), 1, &depNormalToTone };
auto res = vkCreateRenderPass(device, &rpinfo, nullptr, &renderPass);

トーンマップシェーダの作成

今回は単純にクランプのみを行うシェーダを作成します。頂点シェーダは単純に入力頂点を出力するのみのものを使うとして、以下にフラグメントシェーダのコードを示します。

layout(location = 0) out vec4 color;
layout(set = 0, binding = 0, input_attachment_index = 0) uniform subpassInput intex;

void main() { color = clamp(subpassLoad(intex), vec4(0.0f), vec4(1.0f)); }

2行目の定義が入力アタッチメントを使用する際の定義となります。入力アタッチメントは一種のユニフォーム値として受け渡されて、PipelineLayoutにも定義を行う必要があります。入力アタッチメントはテクスチャではないためsubpassInputという独自の型を使って表します。
subpassInputからピクセルデータを読み込むにはsubpassLoadを使用します。subpassLoadにはsubpassInputしか渡すことができませんが、これはラスタライズしているピクセルに対応したピクセルのデータしか取得することができなくなっているためです。このためブラーなどといった処理はsubpassInputを用いた実装はできません。

入力アタッチメントを受け渡す準備

先のセクションで扱ったように、入力アタッチメントは一種のユニフォーム値として受け渡されるためPipelineLayoutに登録する必要があり、ということは対応するDescriptorSetLayoutも作成する必要があります。
入力アタッチメントが対応するVkDescriptorTypeVK_DESCRIPTOR_TYPE_INPUT_ATTACHMENTなのでそれを指定します。

const descbindToneInput = VkDescriptorSetBinding { 0,
  VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1,
  VK_SHADER_STAGE_FRAGMENT_BIT, nullptr };

ここまでくれば、あとは普通にFramebufferを用意してパイプラインを構成して描画を行うだけです。サンプル全体のコードはありませんがこんなこともできますといった感じで。他には、第二パスで全体を覆うポリゴンではなくて特定の領域のみ覆うポリゴンなどで描画を発行すれば疑似的にステンシルみたいなことができると思います(効率は良くない気がしますが)。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?