バルカン。開発者ガイド。プログラム不可能なパイプラインステージ

私はイジェフスクでCGTribeの翻訳者として働いており、ここではVulkanチュートリアル(元の-vulkan-tutorial.com)のロシア語への翻訳を公開してい ます。



今日は、固定関数と呼ばれるグラフィックスパイプラインの基本に関するセクションの新しい章の翻訳を紹介したいと思います。



コンテンツ
1.



2.



3.



4.







  1. (pipeline)



5.



  1. Staging


6. Uniform-



  1. layout
  2. sets


7.



  1. Image view image sampler
  2. image sampler


8.



9.



10. -



11. Multisampling



FAQ









プログラム不可能なパイプラインステージ





初期のグラフィックスAPIは、グラフィックスパイプラインのほとんどの段階でデフォルトの状態を使用していました。Vulkanでは、ビューポートサイズから始まり、混色機能で終わるすべての状態を明示的に記述する必要があります。この章では、プログラム不可能なパイプラインステージを設定します。



頂点入力



VkPipelineVertexInputStateCreateInfo構造体 は、頂点シェーダーに渡される頂点データの形式を記述します。説明には2つのタイプがあります。



  • 属性の説明:頂点シェーダーに渡されるデータ型、データバッファーへのバインド、およびその中のオフセット
  • バインド:データアイテム間の距離と、データと出力ジオメトリのバインド方法(インスタンスごとまたは頂点のバインド)(ジオメトリのインスタンス化を参照


頂点シェーダーで頂点データをハードコーディングしたので、ロードするデータがないことを示します。これを行うには、構造を入力してみましょう VkPipelineVertexInputStateCreateInfo



頂点バッファに関する章の後半で、この質問に戻ります。



VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.pVertexBindingDescriptions = nullptr; // Optional
vertexInputInfo.vertexAttributeDescriptionCount = 0;
vertexInputInfo.pVertexAttributeDescriptions = nullptr; // Optional
      
      





メンバー pVertexBindingDescriptions



pVertexAttributeDescriptions



は、頂点属性をロードするための上記のデータを記述する構造体の配列を指します。この構造を関数のcreateGraphicsPipeline



直後に 追加し shaderStages



ます。



入力アセンブラ



VkPipelineInputAssemblyStateCreateInfo構造体 は、頂点から形成されるジオメトリと、ラインストリップや三角ストリップなどのジオメトリでジオメトリの再起動が許可されるかどうかという2つのことを記述します。ジオメトリはフィールドに表示され topology



、次の値をとることができます。



  • VK_PRIMITIVE_TOPOLOGY_POINT_LIST



    :ジオメトリは個別のポイントとして描画され、各頂点は個別のポイントです
  • VK_PRIMITIVE_TOPOLOGY_LINE_LIST



    :ジオメトリは線分のセットとして描画され、頂点の各ペアは個別の線を形成します
  • VK_PRIMITIVE_TOPOLOGY_LINE_STRIP



    :ジオメトリは連続したポリラインとして描画され、後続の各頂点はポリラインに1つのセグメントを追加します
  • VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST



    :ジオメトリは三角形のセットとして描画され、3つの頂点ごとに独立した三角形が形成されます
  • VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP



    : ,


通常、頂点は、頂点バッファに配置した順序で順番にロードされます。ただし、インデックスバッファを使用すると、ロード順序を変更できます。これにより、頂点の再利用などの最適化が可能になります。あなたがいる場合primitiveRestartEnable



の値を指定する フィールドで VK_TRUE



、あなたはトポロジーと線や三角形を中断することができる VK_PRIMITIVE_TOPOLOGY_LINE_STRIP



VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP



し、特別なインデックスを使用して新しいプリミティブを描き始めます 0xFFFF



0xFFFFFFFF







チュートリアルでは、個々の三角形を描画するため、次の構造を使用します。



VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
      
      





ビューポートとはさみ



ビューポートは、出力がレンダリングされるフレームバッファの領域を記述します。ほとんど常にからの座標(0, 0)



には 、ビューポート用に設定されています (width, height)







VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float) swapChainExtent.width;
viewport.height = (float) swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
      
      





スワップチェーンと画像のサイズは、値WIDTH



HEIGHT



ウィンドウ異なる場合があることに注意してください 後で、スワップチェーンからの画像がフレームバッファとして使用されるため、正確なサイズを使用する必要があります。



minDepth



そして maxDepth



フレームバッファのための深さ値の範囲を決定します。これらの値はの範囲内[0,0f, 1,0f]



である 必要があり、 minDepth



さらに多くの場合があります maxDepth



デフォルト値を使用します- 0.0f



そして、 1.0f



通常とは異なることを何もしない場合。



ビューポートがフレームバッファで画像をどのように引き伸ばすかを決定する場合、はさみがどのピクセルを保存するかを決定します。シザー長方形の外側のすべてのピクセルは、ラスタライズ中に破棄されます。クリッピング長方形は、画像を変換するのではなく、トリミングするために使用されます。違いは下の写真に示されています。左側のクリッピング長方形は、そのサイズがビューポートサイズよりも大きい限り、そのような画像を取得するための多くの可能なオプションの1つにすぎないことに注意してください。







このチュートリアルでは、画像をフレームバッファー全体にレンダリングするため、はさみの長方形がビューポートと完全に重なるように指定します。



VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapChainExtent;
      
      





次に、VkPipelineViewportStateCreateInfo構造体を使用して、ビューポートとはさみに関する情報を組み合わせる必要があり ます。一部のビデオカードでは、複数のビューポートとクリッピング長方形を同時に使用できるため、それらに関する情報が配列として送信されます。一度に複数のビューポートを使用するには、対応するGPUオプションを有効にする必要があります。



VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
      
      





ラスタライザ



ラスタライザは、ジオメトリを頂点シェーダーから複数のフラグメントに変換します。深度テスト面カリング、はさみテストここで実行さ れ、ポリゴンをフラグメントで塗りつぶす方法が構成されます。ポリゴン全体を塗りつぶすか、ポリゴンのエッジのみを塗りつぶします(ワイヤーフレームレンダリング)。これらはすべて、VkPipelineRasterizationStateCreateInfo構造で構成されます



VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
      
      





フィールドがdepthClampEnable



設定されている 場合 VK_TRUE



、そのフラグメントは近距離および遠距離の平面の外側にあり、切り取られず、それらをプッシュします。これは、たとえば、シャドウマップを作成するときに役立ちます。このパラメーターを使用するには、対応するGPUオプションを有効にする必要があります。



rasterizer.rasterizerDiscardEnable = VK_FALSE;
      
      





rasterizerDiscardEnable



設定されている VK_TRUE



場合、ラスタライズステージは無効になり、出力はフレームバッファに渡されません。



rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
      
      





polygonMode



チャンクの生成方法を決定します。次のモードを使用できます。



  • VK_POLYGON_MODE_FILL



    :ポリゴンは完全にフラグメントで満たされています
  • VK_POLYGON_MODE_LINE



    :ポリゴンのエッジは線に変換されます
  • VK_POLYGON_MODE_POINT



    :ポリゴンの頂点はドットとして描画されます


を除いてこれらのモードを使用 VK_POLYGON_MODE_FILL



するには、対応するGPUオプションを有効にする必要があります。




rasterizer.lineWidth = 1.0f;
      
      





フィールド lineWidth



は、セグメントの厚さを設定します。サポートされるチャンクの最大幅はハードウェアによって異なり、チャンク1,0f



厚い 場合はGPUオプションを有効にする必要があります wideLines







rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
      
      





このパラメータcullMode



は、顔のカリングタイプを定義します クリッピングを完全にオフにすることも、前面および/または非前面のクリッピングをオンにすることもできます。変数 frontFace



は、前面を定義するために頂点がトラバースされる順序(時計回りまたは反時計回り)を決定します。



rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f; // Optional
rasterizer.depthBiasClamp = 0.0f; // Optional
rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
      
      





ラスタライザーは、フラグメントの勾配に応じて、定数値を追加するか、深度をオフセットすることで、深度値を変更できます。これは通常、シャドウマップを作成するときに使用されます。これは必要ないので、にdepthBiasEnable



インストール します VK_FALSE







マルチサンプリング



VkPipelineMultisampleStateCreateInfo構造 は、マルチサンプリングを構成します。これは、アンチエイリアシングメソッドの1つです これは主にエッジで機能し、同じピクセルにラスタライズされた異なるポリゴンの色を組み合わせます。これにより、最も目に見えるアーティファクトを取り除くことができます。マルチサンプリングの主な利点は、ほとんどの場合、フラグメントシェーダーがピクセルごとに一度だけ実行されることです。これは、たとえば、より高い解像度でレンダリングしてからダウンサイジングするよりもはるかに優れています。マルチサンプリングを使用するには、対応するGPUオプションを有効にする必要があります。



VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling.minSampleShading = 1.0f; // Optional
multisampling.pSampleMask = nullptr; // Optional
multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
multisampling.alphaToOneEnable = VK_FALSE; // Optional
      
      





それを含めるまで、次の記事のいずれかでそれに戻ります。



深度テストとステンシルテスト



デプスバッファやステンシルバッファを使用する場合は、VkPipelineDepthStencilStateCreateInfoを使用してそれらを構成する必要があります これはまだ必要ないのでnullptr



、この構造体へのポインターの代わりに渡すだけ です。デプスバッファの章でこれに戻ります。



混色



フラグメントシェーダーによって返される色は、フレームバッファーに既にある色とマージする必要があります。このプロセスは混色と呼ばれ、2つの方法があります。



  • 古い値と新しい値を混合して、出力色を取得します
  • ビット演算を使用して古い値と新しい値を連結します


混色の構成には、2種類の構造が使用されます。VkPipelineColorBlendAttachmentState構造に は、接続されている各フレームバッファーの設定が含まれ、VkPipelineColorBlendStateCreateInfo構造に は、グローバルな混色設定が含まれます。この場合、使用されるフレームバッファは1つだけです。



VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
      
      





この構造 VkPipelineColorBlendAttachmentState



により、最初の方法で混色をカスタマイズできます。次の擬似コードは、実行されたすべての操作の最良のデモンストレーションです。



if (blendEnable) {
    finalColor.rgb = (srcColorBlendFactor * newColor.rgb) <colorBlendOp> (dstColorBlendFactor * oldColor.rgb);
    finalColor.a = (srcAlphaBlendFactor * newColor.a) <alphaBlendOp> (dstAlphaBlendFactor * oldColor.a);
} else {
    finalColor = newColor;
}

finalColor = finalColor & colorWriteMask;
      
      





blendEnable



設定する VK_FALSE



と、フラグメントシェーダーからの色が変更されずに渡されます。設定されている場合 VK_TRUE



、2つのブレンド操作を使用して新しい色が計算されます。最終的な色はcolorWriteMask



、出力画像のどのチャネルに書き込まれるかを決定するために使用してフィルタリング されます。



最も一般的なカラーブレンディングはアルファブレンディングで、透明度に基づいて新しい色が古い色とブレンドされます。 finalColor



次のように計算されます。



finalColor.rgb = newAlpha * newColor + (1 - newAlpha) * oldColor;
finalColor.a = newAlpha.a;
      
      





これは、次のオプションを使用して構成できます。



colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
      
      





可能なすべての操作は 、仕様のVkBlendFactorおよび VkBlendOp列挙にあります。



2番目の構造は、すべてのフレームバッファーの構造の配列を参照し、上記の計算で混合係数として使用できる混合定数を指定できるようにします。



VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f; // Optional
colorBlending.blendConstants[1] = 0.0f; // Optional
colorBlending.blendConstants[2] = 0.0f; // Optional
colorBlending.blendConstants[3] = 0.0f; // Optional
      
      





2番目のミキシング方法(ビット演算)を使用する場合は、に設定 VK_TRUE



logicOpEnable



ます。次に、フィールドでビット演算を指定できます logicOp



各フレームバッファに接続されているかのように第1の方法は自動的に、使用できなくなりますので注意してくださいが blendEnable



発見されました VK_FALSE



colorWriteMask



どのチャネルコンテンツを変更するかを決定するためのビット演算にも使用されることに注意してください 私たちが行ったように、両方のモードをオフにすることができます。この場合、フラグメントの色は変更なしでフレームバッファに書き込まれます。



動的状態



グラフィックスパイプラインの一部の状態は、ビューポートサイズ、チャンク幅、ブレンド定数など、パイプラインを再作成せずに変更できます。これを行うには、VkPipelineDynamicStateCreateInfo構造体に入力し ます。



VkDynamicState dynamicStates[] = {
    VK_DYNAMIC_STATE_VIEWPORT,
    VK_DYNAMIC_STATE_LINE_WIDTH
};

VkPipelineDynamicStateCreateInfo dynamicState{};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = 2;
dynamicState.pDynamicStates = dynamicStates;
      
      





その結果、これらの設定の値はパイプラインの作成段階では考慮されないため、レンダリング時に直接指定する必要があります。次の章でこれに戻ります。nullptr



動的状態を使用したくない場合は、この構造体へのポインターの代わりに使用できます



パイプラインレイアウト



シェーダーでは、uniform



動的に変更できる-variables-グローバル変数を使用して 、シェーダーを再作成せずにシェーダーの動作を変更できます。これらは通常、変換行列を頂点シェーダーに渡すため、またはフラグメントシェーダーでテクスチャサンプラーを作成するために使用されます。



これらのユニフォームは、VkPipelineLayoutオブジェクトを使用してパイプラインを作成するときに指定する必要があります 今のところこれらの変数は使用しませんが、空のパイプラインレイアウトを作成する必要があります。



後で他の関数から参照するので、オブジェクトを保持するクラスのメンバーを作成しましょう。




VkPipelineLayout pipelineLayout;
      
      





次に、関数でオブジェクトを作成しましょう createGraphicsPipeline







VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0; // Optional
pipelineLayoutInfo.pSetLayouts = nullptr; // Optional
pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional

if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
    throw std::runtime_error("failed to create pipeline layout!");
}
      
      





この構造体は、動的変数をシェーダーに渡すもう1つの方法であるプッシュ定数も指定します。後でそれらを知るようになります。プログラムのライフサイクル全体を通してパイプラインを使用するため、最後にパイプラインを破棄する必要があります。



void cleanup() {
    vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
    ...
}
      
      





結論



プログラム不可能な状態について知っておくべきことはこれだけです。それらを最初から設定するのに多くの作業が必要でしたが、グラフィックスパイプラインで発生するほとんどすべてがわかったはずです。



グラフィックスパイプラインを作成するには、最後のオブジェクトであるレンダーパスを作成する必要があります。



All Articles