Vulkanの入門をRustでやっていきます。目次はこちら。今回までの実装をしたGitHubはこちら。
Vulkanのグラフィックパイプライン
VulkanのグラフィックパイプラインはOpenGL等と似たような感じで、図のようになっています。
(画像はhttps://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Introductionより引用)
シェーダの作成と読み込み
Vulkanoの場合、vulkano-shadersを利用してコンパイル時にGLSLをSPIR-Vに変換することができるので、今回はこれを使います。
まず、GLSLでvertex shaderとfragment shaderを作成します。このとき、
#extension GL_ARB_separate_shader_objects : enable
をつけてください。次にmain.rs
で
mod vertex_shader {
vulkano_shaders::shader! {
ty: "vertex",
path: "path/to/vertex_shader"
}
}
mod fragment_shader {
vulkano_shaders::shader! {
ty: "fragment",
path: "path/to/fragment_shader"
}
}
vertex_shader::Shader::load(device.clone()).expect("Failed to load the vertex shader");
fragment_shader::Shader::load(device.clone()).expect("Failed to load the fragment shader");
とします。これで最低限のシェーダが読み込めました。
render passの作成
参考元と順番が異なりますが、Vulkanoではパイプラインを作るのにrender passが必要になるので先にこれを作ります。またVulkanoの場合は通常、single_pass_renderpass!
マクロで各アタッチメントやパスを書いていくので、C/C++とは大分変わります。
let render_pass = single_pass_renderpass!(device.clone(),
attachments: { /* 各アタッチメント */ },
pass: { /* パス */ }
).unwrap()
各アタッチメントの設定
各アタッチメントは上のattachments:
に書いていきます。今回はcolorAttachment
を設定します。
attachments: {
color: {
load: Clear,
store: Store,
format: swapchain.format(),
samples: 1,
}
},
ここで、load
及びstore
はVulkanのloadOp
及びstoreOp
に相当するもので、samples
は今回はマルチサンプリングをしていないので1
となっています。
パスの設定
上で設定したアタッチメントを用いてパスを設定します。
pass: {
color: [color],
depth_stencil: {}
}
fixed functionの設定
fixed functionとはViewportやラスタライザ等の総称のようです。Vulkanの場合、fixed functionに分類されるものはパイプライン毎に固定で基本は変更できないようです。ただし、Viewport等は設定時に動的に変換することも出来ます。
Vulkanoの場合はvulkano::pipeline::GraphicsPipeline
にいわゆるビルダーパターンで作っていきます。
vertex input及びvertex shaderの設定
今回の参考元ではvertex shader自身がデータを持っているので外部から頂点データを入力する必要がありません。Vulkanoの場合、こういうときはvulkano::pipeline::vertex::BufferlessDefinition
を使います。なお、頂点データを入力する際にはimpl_vertex!
を使います。
.vertex_input(BufferlessDefinition)
.vertex_shader(vertex_shader.main_entry_point(), ())
viewportの設定
上記のように動的なviewportにすることも可能ですがここでは固定サイズにし、scissorは全体を写すようにします。
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [
swapchain.dimensions()[0] as _,
swapchain.dimensions()[1] as _
],
depth_range: 0.0..1.0,
};
builder.viewports(vec![viewport]);
Subpass
の設定
上で作成したrender passをSubpass
に変換して使います。
.render_pass(Subpass::from(render_pass, 0).unwrap())
まとめと感想
今回も特に実行結果は変化せずシェーダの読み込みやパイプラインの作成で終わりましたが、次回はいよいよ描画できるはずです。