Link

์ดํ›„์— ์”ฌ ๋กœ๋”ฉ์„ ์ œ๋Œ€๋กœ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š”, ์‚ฌ๊ฐํ˜•๋ณด๋‹ค ๋” ๋‚˜์€ ๋ฉ”์‹œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด GLTF ํŒŒ์ผ๋กœ๋ถ€ํ„ฐ ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ ์ •๋ณด๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ์ •๋ณด๋ฅผ ๋ฌด์‹œํ•˜๋Š” ๋‹จ์ˆœํ•œ ํŽธ๋ฒ•์„ ํ†ตํ•ด ์ด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.

starting-point ๋ ˆํฌ์ง€ํ† ๋ฆฌ์—๋Š” basic.glb๋ผ ๋ถˆ๋ฆฌ๋Š” GLTF ํŒŒ์ผ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŒŒ์ผ์—๋Š” ์›์ ์„ ์ค‘์‹ฌ์œผ๋กœ ๋ฐฐ์น˜๋œ ํ๋ธŒ์™€ ๊ตฌ, ๊ทธ๋ฆฌ๊ณ  ์›์ˆญ์ด ๋จธ๋ฆฌ ๋ฉ”์‹œ๊ฐ€ ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ฒ˜๋Ÿผ ๋‹จ์ˆœํ•œ ํŒŒ์ผ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์‹ค์ œ GLTF ๋กœ๋”ฉ ์„ค์ • ์—†์ด๋„ ์‰ฝ๊ฒŒ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GLTF ํŒŒ์ผ์€ ๋ฉ”์‹œ์˜ ๋ชฉ๋ก์„ ํฌํ•จํ•˜๋ฉฐ, ๊ฐ ๋ฉ”์‹œ๋Š” ์—ฌ๋Ÿฌ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ•˜๋‚˜์˜ ๋ฉ”์‹œ๊ฐ€ ์—ฌ๋Ÿฌ ๋จธํ…Œ๋ฆฌ์–ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์กฐ๋กœ, ์ด๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ๋“œ๋กœ์šฐ์ฝœ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ GLTF ํŒŒ์ผ์—๋Š” ์”ฌ-ํŠธ๋ฆฌ ํ˜•ํƒœ์˜ ์”ฌ ๋…ธ๋“œ๋„ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ผ๋ถ€ ๋…ธ๋“œ๋Š” ๋ฉ”์‹œ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” ๋ฉ”์‹œ ๋ฐ์ดํ„ฐ๋งŒ ๋กœ๋”ฉํ•˜๊ฒ ์ง€๋งŒ, ์ถ”ํ›„ ์ „์ฒด ์”ฌํŠธ๋ฆฌ์™€ ๋จธํ…Œ๋ฆฌ์–ผ๋„ ๋ถˆ๋Ÿฌ์˜ฌ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ๊ณผ ๊ด€๋ จ๋œ ์ฝ”๋“œ๋Š” vk_loader.cpp/h์— ์ž‘์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ๋ฉ”์‹œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ ํ•„์š”ํ•œ ๋‘ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•ฉ์‹œ๋‹ค.

#include <vk_types.h>
#include <unordered_map>
#include <filesystem>

struct GeoSurface {
    uint32_t startIndex;
    uint32_t count;
};

struct MeshAsset {
    std::string name;

    std::vector<GeoSurface> surfaces;
    GPUMeshBuffers meshBuffers;
};

//forward declaration
class VulkanEngine;

์ฃผ์–ด์ง„ MeshAsset์€ ํŒŒ์ผ ์ด๋ฆ„๊ณผ ๋ฉ”์‹œ ๋ฒ„ํผ๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•ด๋‹น ๋ฉ”์‹œ์˜ ํ•˜์œ„๋ฉ”์‹œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” GeoSurface ๊ฐ์ฒด์˜ ๋ฐฐ์—ด๋„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์„œ๋ธŒ๋ฉ”์‹œ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ์—๋Š” ๊ฐ๊ฐ ๋ณ„๋„์˜ ๋“œ๋กœ์šฐ์ฝœ์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ์„œ๋ธŒ๋ฉ”์‹œ์˜ ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์˜ ๋ฒ„ํผ์— ์ด์–ด๋ถ™์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋“œ๋กœ์šฐ ์ฝœ์—์„œ๋Š” StartIndex์™€ count๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ vk_loader.cpp์— ์ถ”๊ฐ€ํ•ฉ์‹œ๋‹ค. ์ดํ›„ ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

#include "stb_image.h"
#include <iostream>
#include <vk_loader.h>

#include "vk_engine.h"
#include "vk_initializers.h"
#include "vk_types.h"
#include <glm/gtx/quaternion.hpp>

#include <fastgltf/glm_element_traits.hpp>
#include <fastgltf/parser.hpp>
#include <fastgltf/tools.hpp>

ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

std::optional<std::vector<std::shared_ptr<MeshAsset>>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath);

std::optional์ด ์ฒ˜์Œ์œผ๋กœ ๋“ฑ์žฅํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. std::optional์€ ํƒ€์ž…(์—ฌ๊ธฐ์„œ๋Š” ๋ฉ”์‹œ ์—์…‹์˜ vector)์„ ๋ž˜ํ•‘ํ•˜๋Š” ํ‘œ์ค€ ํด๋ž˜์Šค๋กœ, ๊ฐ’์ด ์—†๊ฑฐ๋‚˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ด์œ ๋กœ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์ด ์‹คํŒจํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, null์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ํŽธ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์ƒˆ๋กœ์šด STL ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋Š” fastGltf ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์ œ ํŒŒ์ผ์„ ์—ด์–ด๋ด…์‹œ๋‹ค.

    std::cout << "Loading GLTF: " << filePath << std::endl;

    fastgltf::GltfDataBuffer data;
    data.loadFromFile(filePath);

    constexpr auto gltfOptions = fastgltf::Options::LoadGLBBuffers
        | fastgltf::Options::LoadExternalBuffers;

    fastgltf::Asset gltf;
    fastgltf::Parser parser {};

    auto load = parser.loadBinaryGLTF(&data, filePath.parent_path(), gltfOptions);
    if (load) {
        gltf = std::move(load.get());
    } else {
        fmt::print("Failed to load glTF: {} \n", fastgltf::to_underlying(load.error()));
        return {};
    }

์ง€๊ธˆ์€ ๋ฐ”์ด๋„ˆ๋ฆฌ GLTF๋งŒ์„ ์ง€์›ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋จผ์ € loadFromFile๋กœ ํŒŒ์ผ์„ ์—ด๊ณ  loadBinaryGLTF๋ฅผ ํ˜ธ์ถœํ•ด ์—ด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ๋Š”(์•„์ง ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„) ์ƒ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ๋ถ€๋ชจ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ๊ฐ ๋ฉ”์‹œ๋ฅผ ์ˆœํšŒํ•˜์—ฌ ์ •์ ๊ณผ ์ธ๋ฑ์Šค๋ฅผ ์ž„์‹œ std::vector๋กœ ๋ณต์‚ฌํ•˜๊ณ , ์ด๋ฅผ ์—”์ง„์˜ ๋ฉ”์‹œ๋กœ ์—…๋กœ๋“œ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์—ฌ๋Ÿฌ MeshAsset ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐฐ์—ด๋กœ ๊ตฌ์„ฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

    std::vector<std::shared_ptr<MeshAsset>> meshes;

    // use the same vectors for all meshes so that the memory doesnt reallocate as
    // often
    std::vector<uint32_t> indices;
    std::vector<Vertex> vertices;
    for (fastgltf::Mesh& mesh : gltf.meshes) {
        MeshAsset newmesh;

        newmesh.name = mesh.name;

        // clear the mesh arrays each mesh, we dont want to merge them by error
        indices.clear();
        vertices.clear();

        for (auto&& p : mesh.primitives) {
            GeoSurface newSurface;
            newSurface.startIndex = (uint32_t)indices.size();
            newSurface.count = (uint32_t)gltf.accessors[p.indicesAccessor.value()].count;

            size_t initial_vtx = vertices.size();

            // load indexes
            {
                fastgltf::Accessor& indexaccessor = gltf.accessors[p.indicesAccessor.value()];
                indices.reserve(indices.size() + indexaccessor.count);

                fastgltf::iterateAccessor<std::uint32_t>(gltf, indexaccessor,
                    [&](std::uint32_t idx) {
                        indices.push_back(idx + initial_vtx);
                    });
            }

            // load vertex positions
            {
                fastgltf::Accessor& posAccessor = gltf.accessors[p.findAttribute("POSITION")->second];
                vertices.resize(vertices.size() + posAccessor.count);

                fastgltf::iterateAccessorWithIndex<glm::vec3>(gltf, posAccessor,
                    [&](glm::vec3 v, size_t index) {
                        Vertex newvtx;
                        newvtx.position = v;
                        newvtx.normal = { 1, 0, 0 };
                        newvtx.color = glm::vec4 { 1.f };
                        newvtx.uv_x = 0;
                        newvtx.uv_y = 0;
                        vertices[initial_vtx + index] = newvtx;
                    });
            }

            // load vertex normals
            auto normals = p.findAttribute("NORMAL");
            if (normals != p.attributes.end()) {

                fastgltf::iterateAccessorWithIndex<glm::vec3>(gltf, gltf.accessors[(*normals).second],
                    [&](glm::vec3 v, size_t index) {
                        vertices[initial_vtx + index].normal = v;
                    });
            }

            // load UVs
            auto uv = p.findAttribute("TEXCOORD_0");
            if (uv != p.attributes.end()) {

                fastgltf::iterateAccessorWithIndex<glm::vec2>(gltf, gltf.accessors[(*uv).second],
                    [&](glm::vec2 v, size_t index) {
                        vertices[initial_vtx + index].uv_x = v.x;
                        vertices[initial_vtx + index].uv_y = v.y;
                    });
            }

            // load vertex colors
            auto colors = p.findAttribute("COLOR_0");
            if (colors != p.attributes.end()) {

                fastgltf::iterateAccessorWithIndex<glm::vec4>(gltf, gltf.accessors[(*colors).second],
                    [&](glm::vec4 v, size_t index) {
                        vertices[initial_vtx + index].color = v;
                    });
            }
            newmesh.surfaces.push_back(newSurface);
        }

        // display the vertex normals
        constexpr bool OverrideColors = true;
        if (OverrideColors) {
            for (Vertex& vtx : vertices) {
                vtx.color = glm::vec4(vtx.normal, 1.f);
            }
        }
        newmesh.meshBuffers = engine->uploadMesh(indices, vertices);

        meshes.emplace_back(std::make_shared<MeshAsset>(std::move(newmesh)));
    }

    return meshes;

๋ฉ”์‹œ์˜ ๊ฐ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ iterateAccessorํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์›ํ•˜๋Š” ์ •์  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ์ •์  ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•  ๋•Œ๋Š” ์ธ๋ฑ์Šค ๋ฒ„ํผ๋„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ๋Š” uploadMesh๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ตœ์ข… ๋ฒ„ํผ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๋ฉ”์‹œ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ปดํŒŒ์ผ ์‹œ ํ”Œ๋ž˜๊ทธ์ธ OverrideColors๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ๋‹ค๋ฉด, ์ •์  ์ƒ‰์ƒ์„ ๋””๋ฒ„๊น…์— ์œ ์šฉํ•œ ๋ฒ•์„  ๋ฒกํ„ฐ๋กœ ๋ฎ์–ด์”๋‹ˆ๋‹ค.

์œ„์น˜ ๋ฐฐ์—ด์€ ํ•ญ์ƒ ์กด์žฌํ•˜๋ฏ€๋กœ, Vertex ๊ตฌ์กฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“  ์†์„ฑ์€ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ ํ›„ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ์ด๋ฅผ ๊ทธ๋ ค๋ด…์‹œ๋‹ค.

	std::vector<std::shared_ptr<MeshAsset>> testMeshes;

๋จผ์ € ์ด๋ฅผ VulkanEngine ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. init_default_data()์—์„œ ๋ถˆ๋Ÿฌ์™€ vk_engine์˜ include ๋ชฉ๋ก์— #include <vk_loader.h>๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

testMeshes = loadGltfMeshes(this,"..\\..\\assets\\basicmesh.glb").value();

์ œ๊ณต๋œ ํŒŒ์ผ์—๋Š” ์ธ๋ฑ์Šค 0๋ฒˆ์— ํ๋ธŒ, ์ธ๋ฑ์Šค 1๋ฒˆ์— ๊ตฌ, ๊ทธ๋ฆฌ๊ณ  ์ธ๋ฑ์Šค 2๋ฒˆ์—๋Š” ์›์ˆญ์ด ๋จธ๋ฆฌ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ๋ฉ”์‹œ ๊ฒƒ์„ ๊ทธ๋ฆด ๊ฒƒ์ด๋ฉฐ, ์ด์ „์˜ ๊ทธ๋ฆฐ ์‚ฌ๊ฐํ˜• ๋ฐ”๋กœ ๋‹ค์Œ์— ๊ทธ๋ฆฌ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

	push_constants.vertexBuffer = testMeshes[2]->meshBuffers.vertexBufferAddress;

	vkCmdPushConstants(cmd, _meshPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), &push_constants);
	vkCmdBindIndexBuffer(cmd, testMeshes[2]->meshBuffers.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);

	vkCmdDrawIndexed(cmd, testMeshes[2]->surfaces[0].count, 1, testMeshes[2]->surfaces[0].startIndex, 0, 0);

์ด์ œ ์›์ˆญ์ด ๋จธ๋ฆฌ๊ฐ€ ์ƒ‰์ƒ๊ณผ ํ•จ๊ป˜ ๋ Œ๋”๋ง ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. OverrideColors๋ฅผ ์ง€๊ธˆ์€ true๋กœ ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ•์„  ๋ฒกํ„ฐ๊ฐ’์ด ์ƒ‰์ƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์…ฐ์ด๋”์—์„œ๋Š” ์กฐ๋ช…์ด ์ ์šฉ๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด ์˜ต์…˜์„ ๋„๋ฉด ์›์ˆญ์ด ๋จธ๋ฆฌ๋Š” ์ˆœ์ˆ˜ํ•œ ํฐ์ƒ‰์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ์›์ˆญ์ด ๋จธ๋ฆฌ๊ฐ€ ๋ณด์ด์ง€๋งŒ, ๊ฑฐ๊พธ๋กœ ๋’ค์ง‘ํ˜€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€ํ™˜ ํ–‰๋ ฌ์„ ์กฐ์ •ํ•ด๋ด…์‹œ๋‹ค.

GLTF์—์„œ ์ถ•์€ OpenGL์—์„œ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. OpenGL์—์„œ๋Š” Y์ถ•์ด ์œ„ ๋ฐฉํ–ฅ์„ ๊ฐ€๋ฆฌํ‚ค์ง€๋งŒ Vulkan์—์„œ๋Š” Y์ถ•์ด ์•„๋ž˜ ๋ฐฉํ–ฅ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ณผ์ ์œผ๋กœ ํ™”๋ฉด์ด ๋’ค์ง‘ํ˜€ ๋ณด์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” ๋ทฐํฌํŠธ๋ฅผ ๋’ค์ง‘์–ด ์ „์ฒด ๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋ฅผ ๋’ค์ง‘๋Š” ๋ฐฉ์‹์ด๋ฉฐ, ์ด๋Š” DirectX์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” ํˆฌ์˜ ํ–‰๋ ฌ์— Y์ถ• ๋ฐ˜์ „์„ ํฌํ•จํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ํ›„์ž๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ์ฝ”๋“œ์—์„œ ๋” ์ ์ ˆํ•œ ๋ณ€ํ™˜ ํ–‰๋ ฌ์„ ์ ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ draw_geometry()์˜ ํ‘ธ์‹œ์ƒ์ˆ˜ ํ˜ธ์ถœ ์ง์ „์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

	glm::mat4 view = glm::translate(glm::vec3{ 0,0,-5 });
	// camera projection
	glm::mat4 projection = glm::perspective(glm::radians(70.f), (float)_drawExtent.width / (float)_drawExtent.height, 10000.f, 0.1f);

	// invert the Y direction on projection matrix so that we are more similar
	// to opengl and gltf axis
	projection[1][1] *= -1;

	push_constants.worldMatrix = projection * view;

#include <glm/gtx/transform.hpp>๋ฅผ vk_engine์˜ ์ƒ๋‹จ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด cpp์—์„œ ๋ณ€ํ™˜ ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋จผ์ € ์นด๋ฉ”๋ผ ์‹œ์ ์˜ ๋ทฐ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” ๋’ค๋กœ ์›€์ง์ด๋Š” ๋ณ€ํ™˜ ํ–‰๋ ฌ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

ํˆฌ์˜ ํ–‰๋ ฌ์—๋Š” ์•ฝ๊ฐ„์˜ ๊ผผ์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. near ๊ฐ’์—๋Š” 10000์„, far ๊ฐ’์—๋Š” 0.1์„ ์ „๋‹ฌํ•จ์„ ์ฃผ๋ชฉํ•˜์„ธ์š”. ๊นŠ์ด๋ฅผ ๋’ค์ง‘์–ด 1์ด near plane, ๊นŠ์ด 0์ด far plane์ด ๋˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ๊นŠ์ด ํ…Œ์ŠคํŠธ์˜ ํ’ˆ์งˆ์„ ๋†’์ด๋Š” ๊ธฐ๋ฒ• ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์—”์ง„์„ ์‹คํ–‰ํ•œ๋‹ค๋ฉด ์›์ˆญ์ด ๋ชจ๋ธ์ด ์•ฝ๊ฐ„ ๊นจ์ง„ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊นŠ์ด ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋จธ๋ฆฌ ๋’ค์ชฝ์˜ ์‚ผ๊ฐํ˜•์ด ์•ž์ชฝ์˜ ์‚ผ๊ฐํ˜•์„ ๋ฎ์–ด์จ ์ž˜๋ชป๋œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊นŠ์ด ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•ด๋ด…์‹œ๋‹ค.

์ด์ œ ๋ Œ๋”๋ง ์‹œ drawimage์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€๋ฅผ VulkanEngineํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•ด๋ด…์‹œ๋‹ค.

AllocatedImage _drawImage;
AllocatedImage _depthImage;

์ด์ œ drawImage๋ฅผ ๋”ฐ๋ผ init_swapchainํ•จ์ˆ˜์—์„œ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

	_depthImage.imageFormat = VK_FORMAT_D32_SFLOAT;
	_depthImage.imageExtent = drawImageExtent;
	VkImageUsageFlags depthImageUsages{};
	depthImageUsages |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;

	VkImageCreateInfo dimg_info = vkinit::image_create_info(_depthImage.imageFormat, depthImageUsages, drawImageExtent);

	//allocate and create the image
	vmaCreateImage(_allocator, &dimg_info, &rimg_allocinfo, &_depthImage.image, &_depthImage.allocation, nullptr);

	//build a image-view for the draw image to use for rendering
	VkImageViewCreateInfo dview_info = vkinit::imageview_create_info(_depthImage.imageFormat, _depthImage.image, VK_IMAGE_ASPECT_DEPTH_BIT);

	VK_CHECK(vkCreateImageView(_device, &dview_info, nullptr, &_depthImage.imageView));

๊นŠ์ด ์ด๋ฏธ์ง€๋Š” drawImage์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ดˆ๊ธฐํ™”๋˜์ง€๋งŒ, Usageํ”Œ๋ž˜๊ทธ์— VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๊นŠ์ด ํฌ๋งท์œผ๋กœ VK_FORMAT_D32_SFLOAT๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๊นŠ์ด ์ด๋ฏธ์ง€๋ฅผ ์‚ญ์ œ ํ์— ๋„ฃ๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

_mainDeletionQueue.push_function([=]() {
	vkDestroyImageView(_device, _drawImage.imageView, nullptr);
	vmaDestroyImage(_allocator, _drawImage.image, _drawImage.allocation);

	vkDestroyImageView(_device, _depthImage.imageView, nullptr);
	vmaDestroyImage(_allocator, _depthImage.image, _depthImage.allocation);
});

๋ Œ๋”๋ง ๋ฃจํ”„์—์„œ ๊นŠ์ด ์ด๋ฏธ์ง€๋ฅผ drawImage์—์„œ์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ VK_IMAGE_LAYOUT_UNDEFINED์—์„œ VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” draw_geometry()ํ˜ธ์ถœ ์ง์ „์— ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
vkutil::transition_image(cmd, _depthImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);

์ด์ œ renderPassBeginInfo๋ฅผ ์ด ๊นŠ์ด ์–ดํƒœ์น˜๋จผํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•˜๊ณ , ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. draw_geometry() ์ƒ๋‹จ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค.

	VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(_drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_GENERAL);
	VkRenderingAttachmentInfo depthAttachment = vkinit::depth_attachment_info(_depthImage.imageView, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);

	VkRenderingInfo renderInfo = vkinit::rendering_info(_windowExtent, &colorAttachment, &depthAttachment);

rendering_info๋ฅผ ์œ„ํ•ด vkinit ๊ตฌ์กฐ์ฒด์— ๊นŠ์ด ์–ดํƒœ์น˜๋จผํŠธ๋ฅผ ์œ„ํ•œ ๊ณต๊ฐ„์€ ๋งˆ๋ จํ•ด๋‘์—ˆ์ง€๋งŒ, ๊นŠ์ด ๊ฐ’์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•œ clear ๊ฐ’๋„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ depth_attachment_info๋ฅผ ์œ„ํ•ด vkinit ๊ตฌํ˜„๋ถ€๋ฅผ ์‚ดํŽด๋ด…์‹œ๋‹ค.

VkRenderingAttachmentInfo vkinit::depth_attachment_info(
    VkImageView view, VkImageLayout layout /*= VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL*/)
{
    VkRenderingAttachmentInfo depthAttachment {};
    depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
    depthAttachment.pNext = nullptr;

    depthAttachment.imageView = view;
    depthAttachment.imageLayout = layout;
    depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    depthAttachment.clearValue.depthStencil.depth = 0.f;

    return depthAttachment;
}

์ƒ‰์ƒ ์–ดํƒœ์น˜๋จผํŠธ์—์„œ ์ˆ˜ํ–‰ํ–ˆ๋˜ ์ž‘์—…๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ, loadOP๋ฅผ clear๋กœ, ๊ทธ๋ฆฌ๊ณ  ๊นŠ์ด ์ดˆ๊ธฐํ™” ๊ฐ’์„ 0.f๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊นŠ์ด 0์„ far ๊ฐ’์œผ๋กœ, 1์„ near ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚จ์€ ์ผ์€ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๊นŠ์ดํ…Œ์ŠคํŠธ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ž‘์„ฑํ•œ pipelineBuilder์—์„œ ๊นŠ์ด ์˜ต์…˜์„ ๋งŒ๋“ค์–ด ๋‘์—ˆ์ง€๋งŒ ํ™œ์„ฑํ™”ํ•˜์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์ฑ„์šฐ๋Š” ํ•จ์ˆ˜๋ฅผ PipelineBuilder์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

void PipelineBuilder::enable_depthtest(bool depthWriteEnable, VkCompareOp op)
{
    _depthStencil.depthTestEnable = VK_TRUE;
    _depthStencil.depthWriteEnable = depthWriteEnable;
    _depthStencil.depthCompareOp = op;
    _depthStencil.depthBoundsTestEnable = VK_FALSE;
    _depthStencil.stencilTestEnable = VK_FALSE;
    _depthStencil.front = {};
    _depthStencil.back = {};
    _depthStencil.minDepthBounds = 0.f;
    _depthStencil.maxDepthBounds = 1.f;
}

์Šคํ…์‹ค ๋ถ€๋ถ„์€ ์•„์ง ๋น„์›Œ๋’€์ง€๋งŒ, ๊นŠ์ด ํ…Œ์ŠคํŠธ๋ฅผ ํ™œ์„ฑํ™” ํ•œ ํ›„ depthOp๋ฅผ ๊ตฌ์กฐ์ฒด์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ์ด ์„ค์ •์„ ์‹ค์ œ ํŒŒ์ดํ”„๋ผ์ธ ์ƒ์„ฑ์— ๋ฐ˜์˜ํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. init_mesh_pipeline์„ ๋ฐ”๊ฟ”๋ด…์‹œ๋‹ค.

	//pipelineBuilder.disable_depthtest();
	pipelineBuilder.enable_depthtest(true, VK_COMPARE_OP_GREATER_OR_EQUAL);

	//connect the image format we will draw into, from draw image
	pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat);
	pipelineBuilder.set_depth_format(_depthImage.imageFormat);

ํŒŒ์ดํ”„๋ผ์ธ ๋นŒ๋”์—์„œ enable_depthtest ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , depthWrite๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉฐ, ์—ฐ์‚ฐ์ž๋กœ GREATER_OR_EQUAL์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์•ž์„œ ์„ค๋ช…ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๊นŠ์ด 0์ด far์ด๊ณ  1์ด near์ด๋ฏ€๋กœ ํ˜„์žฌ ํ”ฝ์…€์ด ๊นŠ์ด ์ด๋ฏธ์ง€์˜ ๊ฐ’๋ณด๋‹ค ๋” ํฐ ๊ฒฝ์šฐ์—๋งŒ ๋ Œ๋”๋ง๋˜๋„๋ก ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

init_triangle_pipeline ํ•จ์ˆ˜์˜ set_depth_format ํ˜ธ์ถœ ๋ถ€๋ถ„๋„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค. ๋น„๋ก ๊นŠ์ด ํ…Œ์ŠคํŠธ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์—ˆ๋”๋ผ๋„ ๋ Œ๋” ํŒจ์Šค๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๊ณ  ๊ฒ€์ฆ๋ ˆ์ด์–ด๊ฐ€ ๊ฒฝ๊ณ ๋ฅผ ์ถœ๋ ฅํ•˜์ง€ ์•Š๋„๋ก ์ •ํ™•ํ•œ ํฌ๋งท์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ์—”์ง„์„ ์‹คํ–‰ํ•˜๋ฉด ์›์ˆญ์ด ๋ชจ๋ธ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ Œ๋”๋ง ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‚ผ๊ฐํ˜•๊ณผ ์‚ฌ๊ฐํ˜•์—๋Š” ๊นŠ์ด ํ…Œ์ŠคํŠธ ์„ค์ •์ด ๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๊นŠ์ด ์–ดํƒœ์น˜๋จผํŠธ์—์„œ ์ฝ๊ฑฐ๋‚˜ ์“ฐ์ง€ ์•Š์œผ๋ฉฐ, ๊ทธ ๊ฒฐ๊ณผ ๋„ํ˜•๋“ค์ด ์›์ˆญ์ด ๋ชจ๋ธ ๋’ค์— ์žˆ์Œ์—๋„ ์•ž์— ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋œ ๋ฉ”์‹œ๋“ค์„ ์ •๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด, cleanup ํ•จ์ˆ˜์—์„œ ๋ฉ”์ธ ์‚ญ์ œ ํ๋ฅผ ์ •๋ฆฌํ•˜๊ธฐ ์ „์— ๋ฉ”์‹œ ๋ฐฐ์—ด์— ํฌํ•จ๋œ ๋ฒ„ํผ๋“ค์„ ๋ช…์‹œ์ ์œผ๋กœ ํŒŒ๊ดดํ•ด ์ค๋‹ˆ๋‹ค.

for (auto& mesh : testMeshes) {
	destroy_buffer(mesh->meshBuffers.indexBuffer);
	destroy_buffer(mesh->meshBuffers.vertexBuffer);
}

_mainDeletionQueue.flush();

๋‹ค์Œ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ธฐ ์ „์—, ์‚ฌ๊ฐํ˜• ๋ฉ”์‹œ์™€ ๋ฐฐ๊ฒฝ์˜ ์‚ผ๊ฐํ˜• ์ฝ”๋“œ๋Š” ์ง€์šฐ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋”์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. init_triangle_pipeline ํ•จ์ˆ˜์™€ ๊ด€๋ จ๋œ ๊ฐ์ฒด๋“ค, ๊ทธ๋ฆฌ๊ณ  init_default_data์— ์žˆ๋Š” ์‚ฌ๊ฐํ˜•๊ณผ ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. draw_geometry์—์„œ๋Š” vkCmdSetViewport์™€ vkCmdSetScissor ํ˜ธ์ถœ์„ vkCmdBindPipeline(cmd,VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipeline);ํ˜ธ์ถœ ์ดํ›„๋กœ ์˜ฎ๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ผ๊ฐํ˜• ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์‚ญ์ œ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ํ•ด๋‹น ์ƒํƒœ ์„ค์ •์€ ํŒŒ์ดํ”„๋ผ์ธ ๋ฐ”์ธ๋”ฉ ์ดํ›„์— ์ˆ˜ํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ํˆฌ๋ช…ํ•œ ๊ฐ์ฒด์™€ ๋ธ”๋ Œ๋”ฉ์„ ์„ค์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: Blending