IMPLEMENTATION NOTES / C++ / Vulkan
Vulkan Renderer (MoltenVK)
macOS上で64×64頂点の正弦波地形をVulkan(MoltenVK)で描画。インデックスバッファ、行列変換、深度テスト、スワップチェーン再作成を実装し、描画の流れを学ぶ。
IMPLEMENTATION / EQUATIONS & SOURCE
入力から出力まで、コードで追う。
各節は「何を計算するか → 数式 → 実際のコード」の順に読めます。コードは動作する実装からの抜粋で、単独実行用ではありません。省略した周辺処理は参照元で確認できます。
01 / 正弦波で作る64×64頂点の地形
この描画デモの地形はPerlinではなく、xとzの正弦波の積です。格子間隔0.15、振幅0.6、周波数0.35で頂点を生成し、高さから色を決めます。座標・色・インデックスが分かる単純な地形でVulkanの描画経路を確認する構成です。
xworld = 0.15x − 63·0.15/2
zworld = 0.15z − 63·0.15/2
y = 0.6 sin(0.35xworld) sin(0.35zworld) float worldX = x * kGridScale - half;
float worldZ = z * kGridScale - half;
float height = std::sin(worldX * kHeightFrequency) *
std::sin(worldZ * kHeightFrequency) * kHeightScale;02 / 1セルを2三角形へ分割する
1次元配列の頂点IDをz·64+xとして、セルの四隅i0〜i3を取り出します。三角形の頂点順を(i0,i2,i1)と(i1,i2,i3)へ統一し、インデックスバッファを構成します。頂点を三角形ごとに重複保持する必要がありません。
V = n², T = 2(n−1)², I = 6(n−1)² uint32_t i0 = static_cast<uint32_t>(z * kGridSize + x);
uint32_t i1 = static_cast<uint32_t>(z * kGridSize + x + 1);
uint32_t i2 = static_cast<uint32_t>((z + 1) * kGridSize + x);
uint32_t i3 = static_cast<uint32_t>((z + 1) * kGridSize + x + 1);
indices.push_back(i0);
indices.push_back(i2);
indices.push_back(i1);
indices.push_back(i1);
indices.push_back(i2);
indices.push_back(i3);03 / 頂点シェーダーと座標変換
CPU側で作るtransform行列をbinding=0のUniformBufferObjectで渡し、頂点を同次座標へ変換します。location=0から位置、1から色を読み、色をフラグメント側へ渡します。透視投影行列のY符号を反転する処理はmath_utils.cppにあります。
pclip = transform · (x,y,z,1)ᵀ
pNDC = (pclip.x,pclip.y,pclip.z) / pclip.w2行目はシェーダーがgl_Positionへ書いた後の透視除算。シェーダー内で明示的に割っているわけではありません。
#version 450
layout(binding = 0) uniform UniformBufferObject {
mat4 transform;
} ubo;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 0) out vec3 vColor;
void main() {
gl_Position = ubo.transform * vec4(inPosition, 1.0);
vColor = inColor;
}SOURCE / SCOPE
実装の読み方。
各コード抜粋はリンク先の固定コミットから取得しています。数式は該当処理を説明するための表記です。性能や品質の数値は、掲載した計算例を除き新しい実測を行っていません。ビルド手順と依存環境は各リポジトリを参照してください。
リポジトリへ ↗