Link

์ง€๋‚œ ๊ธ€์—์„œ ํ…์Šค์ณ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ด ๊ธ€์—์„œ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด ์ƒ๋žตํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ…์Šค์ณ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ stb_image๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” png, jpeg ๋“ฑ์„ ์ง€์›ํ•˜๋Š” ๋‹จ์ผ ํ—ค๋” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์•„์‰ฝ์ง€๋งŒ KTX๋‚˜ DDS์™€ ๊ฐ™์€ ํฌ๋งท์€ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํฌ๋งท์€ GPU์— ๊ฑฐ์˜ ์ง์ ‘ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๊ณ , GPU๊ฐ€ ์••์ถ•๋œ ์ƒํƒœ๋กœ ๋ฐ”๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ž˜ํ”ฝ ์šฉ๋„๋กœ ๋” ์ ํ•ฉํ•˜๋ฉฐ VRAM๋„ ์ ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.

fastgltf๋Š” ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ์‹์„ ์ œ๊ณตํ•˜๋ฏ€๋กœ, ์ด๋Ÿฌํ•œ ๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

load_image ํ•จ์ˆ˜์— ํ•ด๋‹น ๋กœ์ง์„ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค.

std::optional<AllocatedImage> load_image(VulkanEngine* engine, fastgltf::Asset& asset, fastgltf::Image& image)
{
    AllocatedImage newImage {};

    int width, height, nrChannels;

    std::visit(
        fastgltf::visitor {
            [](auto& arg) {},
            [&](fastgltf::sources::URI& filePath) {
                assert(filePath.fileByteOffset == 0); // We don't support offsets with stbi.
                assert(filePath.uri.isLocalPath()); // We're only capable of loading
                                                    // local files.

                const std::string path(filePath.uri.path().begin(),
                    filePath.uri.path().end()); // Thanks C++.
                unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrChannels, 4);
                if (data) {
                    VkExtent3D imagesize;
                    imagesize.width = width;
                    imagesize.height = height;
                    imagesize.depth = 1;

                    newImage = engine->create_image(data, imagesize, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT,false);

                    stbi_image_free(data);
                }
            },
            [&](fastgltf::sources::Vector& vector) {
                unsigned char* data = stbi_load_from_memory(vector.bytes.data(), static_cast<int>(vector.bytes.size()),
                    &width, &height, &nrChannels, 4);
                if (data) {
                    VkExtent3D imagesize;
                    imagesize.width = width;
                    imagesize.height = height;
                    imagesize.depth = 1;

                    newImage = engine->create_image(data, imagesize, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT,false);

                    stbi_image_free(data);
                }
            },
            [&](fastgltf::sources::BufferView& view) {
                auto& bufferView = asset.bufferViews[view.bufferViewIndex];
                auto& buffer = asset.buffers[bufferView.bufferIndex];

                std::visit(fastgltf::visitor { // We only care about VectorWithMime here, because we
                                               // specify LoadExternalBuffers, meaning all buffers
                                               // are already loaded into a vector.
                               [](auto& arg) {},
                               [&](fastgltf::sources::Vector& vector) {
                                   unsigned char* data = stbi_load_from_memory(vector.bytes.data() + bufferView.byteOffset,
                                       static_cast<int>(bufferView.byteLength),
                                       &width, &height, &nrChannels, 4);
                                   if (data) {
                                       VkExtent3D imagesize;
                                       imagesize.width = width;
                                       imagesize.height = height;
                                       imagesize.depth = 1;

                                       newImage = engine->create_image(data, imagesize, VK_FORMAT_R8G8B8A8_UNORM,
                                           VK_IMAGE_USAGE_SAMPLED_BIT,false);

                                       stbi_image_free(data);
                                   }
                               } },
                    buffer.data);
            },
        },
        image.data);

    // if any of the attempts to load the data failed, we havent written the image
    // so handle is null
    if (newImage.image == VK_NULL_HANDLE) {
        return {};
    } else {
        return newImage;
    }
}

ํ•จ์ˆ˜๋Š” ๊ธธ์ง€๋งŒ, ์‹ค์งˆ์ ์œผ๋กœ ๊ฐ™์€ ์ž‘์—…์„ ์„ธ ๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋Š” ํ…์Šค์ณ๊ฐ€ GLTF/GLB ํŒŒ์ผ ์™ธ๋ถ€์— ์ €์žฅ๋œ ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํ…์Šค์ณ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•ด stb_load๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์„ฑ๊ณตํ•˜๋ฉด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ๋Š” fastgltf๊ฐ€ ํ…์Šค์ณ๋ฅผ std::vector์œผ๋กœ ๋ถˆ๋Ÿฌ์˜จ ๊ฒฝ์šฐ ์ž…๋‹ˆ๋‹ค. ํ…์Šค์ณ๊ฐ€ base64๋กœ ์ธ์ฝ”๋”ฉ๋˜์–ด ์žˆ๊ฑฐ๋‚˜ ์™ธ๋ถ€ ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์ง์ ‘ ์ฝ๋„๋ก ์„ค์ •ํ–ˆ์„ ๋•Œ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ stbi_load_from_memory๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์„ธ ๋ฒˆ์งธ ๊ฒฝ์šฐ๋Š” ์ด๋ฏธ์ง€ ํŒŒ์ผ์ด ๋ฐ”์ด๋„ˆ๋ฆฌ GLB ํŒŒ์ผ์— ์ž„๋ฒ ๋”ฉ ๋˜์–ด ์žˆ์„ ๋•Œ BufferView์—์„œ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ์™€ ๋™์ผํ•˜๊ฒŒ stbi_load_from_memory๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์—”์ง„์„ ์ปดํŒŒ์ผํ•˜๋ฉด STB_image ํ•จ์ˆ˜ ์ •์˜๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. STB๋Š” ๋‹จ์ผ ํ—ค๋” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฏ€๋กœ, ํ•จ์ˆ˜ ์ •์˜๋ฅผ ์ปดํŒŒ์ผ ํ•˜๋ ค๋ฉด ํ•˜๋‚˜์˜ ์†Œ์Šค ํŒŒ์ผ์— ํŠน์ • ๋งคํฌ๋กœ๋ฅผ ์ •์˜ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ vk_images.cpp ํ˜น์€ ๋‹ค๋ฅธ cpp ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•˜๋ฉด ํ•ด๋‹น ํŒŒ์ผ์— ํ•จ์ˆ˜ ์ •์˜๊ฐ€ ํฌํ•จ๋˜์–ด ๋งํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

์ด์ œ GLTF๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ํ•จ์ˆ˜๋กœ ๋˜๋Œ์•„๊ฐ€ ๊ฑฐ๊ธฐ์„œ ์ด๋ฏธ์ง€๋„ ํ•จ๊ป˜ ๋ถˆ๋Ÿฌ์˜ค๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

    // load all textures
	for (fastgltf::Image& image : gltf.images) {
		std::optional<AllocatedImage> img = load_image(engine, gltf, image);

		if (img.has_value()) {
			images.push_back(*img);
			file.images[image.name.c_str()] = *img;
		}
		else {
			// we failed to load, so lets give the slot a default white texture to not
			// completely break loading
			images.push_back(engine->_errorCheckerboardImage);
			std::cout << "gltf failed to load texture " << image.name << std::endl;
		}
	}

์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ์„ฑ๊ณตํ•œ๋‹ค๋ฉด ์ด๋ฏธ์ง€๋ฅผ ๋ชฉ๋ก์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์‹คํŒจํ•œ๋‹ค๋ฉด ์—๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋‹ค์‹œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์˜ค๋ธŒ์ ํŠธ์— ํ…์Šค์ณ๊ฐ€ ์ ์šฉ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ LoadedGLTF์˜ clearAll ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด ์ž์›๋“ค์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์ œ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

void LoadedGLTF::clearAll()
{
    VkDevice dv = creator->_device;

    descriptorPool.destroy_pools(dv);
    creator->destroy_buffer(materialDataBuffer);

    for (auto& [k, v] : meshes) {

		creator->destroy_buffer(v->meshBuffers.indexBuffer);
		creator->destroy_buffer(v->meshBuffers.vertexBuffer);
    }

    for (auto& [k, v] : images) {
        
        if (v.image == creator->_errorCheckerboardImage.image) {
            //dont destroy the default images
            continue;
        }
        creator->destroy_image(v);
    }

	for (auto& sampler : samplers) {
		vkDestroySampler(dv, sampler, nullptr);
    }
}

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

๋งˆ์ง€๋ง‰์œผ๋กœ ์ƒ˜ํ”Œ๋Ÿฌ๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๊ฐ ์ƒ˜ํ”Œ๋Ÿฌ๋ฅผ ํŒŒ๊ดดํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ ์ค‘์ธ ํ”„๋ ˆ์ž„์—์„œ LoadedGLTF๋ฅผ ์ฆ‰์‹œ ์‚ญ์ œํ•  ์ˆ˜๋Š” ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ตฌ์กฐ์ฒด๊ฐ€ ์—ฌ์ „ํžˆ ์‚ฌ์šฉ์ค‘์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„์— LoadedGLTF๋ฅผ ์‚ญ์ œํ•˜๋ ค๋ฉด cleanup์—์„œ ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ VkQueueWait์„ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜, ํ”„๋ ˆ์ž„๋ณ„ ์‚ญ์ œํ์— ์ถ”๊ฐ€ํ•˜์—ฌ ์‚ญ์ œ๋ฅผ ์ง€์—ฐ์‹œ์ผœ์•ผํ•ฉ๋‹ˆ๋‹ค. LoadedGLTF๋Š” shared_ptr๋กœ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ๋žŒ๋‹ค ์บก์ณ ๊ธฐ๋Šฅ์„ ์ ์ ˆํžˆ ํ™œ์šฉํ•ด ์ด ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํˆฌ๋ช…ํ•œ ๊ฐ์ฒด

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

ํˆฌ๋ช…ํ•œ ๊ฐ์ฒด๋“ค์€ ๊นŠ์ด ๋ฒ„ํผ์— ๊ฐ’์„ ๊ธฐ๋กํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ํˆฌ๋ช…ํ•œ ๊ฐ์ฒด๋ฅผ ๋จผ์ € ๊ทธ๋ฆฐ ๋’ค ๋ถˆํˆฌ๋ช… ๊ฐ์ฒด๊ฐ€ ๊ทธ๋ ค์ง„๋‹ค๋ฉด ์‹œ๊ฐ์ ์ธ ๊ฒฐํ•จ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด, ํˆฌ๋ช… ๊ฐ์ฒด๋Š” ํ”„๋ ˆ์ž„์˜ ๋งˆ์ง€๋ง‰์— ๋ Œ๋”๋ง๋˜๋„๋ก ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

struct DrawContext {
    std::vector<RenderObject> OpaqueSurfaces;
    std::vector<RenderObject> TransparentSurfaces;
};

์ด์ œ draw_geometryํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ๋ฐ˜๋ณต๋ฌธ์—์„œ Vulkan ํ˜ธ์ถœ์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋‚ด๋ถ€ ๋ฐ˜๋ณต๋ฌธ์„ draw() ๋žŒ๋‹ค๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , ํ•ด๋‹น ๋žŒ๋‹ค๋ฅผ ๋ฐ˜๋ณต๋ฌธ ๋‚ด์—์„œ ํ˜ธ์ถœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

auto draw = [&](const RenderObject& draw) {
    vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, draw.material->pipeline->pipeline);
    vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, draw.material->pipeline->layout, 0, 1, &globalDescriptor, 0, nullptr);
    vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, draw.material->pipeline->layout, 1, 1, &draw.material->materialSet, 0, nullptr);

    vkCmdBindIndexBuffer(cmd, draw.indexBuffer, 0, VK_INDEX_TYPE_UINT32);

    GPUDrawPushConstants pushConstants;
    pushConstants.vertexBuffer = draw.vertexBufferAddress;
    pushConstants.worldMatrix = draw.transform;
    vkCmdPushConstants(cmd, draw.material->pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), &pushConstants);

    vkCmdDrawIndexed(cmd, draw.indexCount, 1, draw.firstIndex, 0, 0);
};

for (auto& r : mainDrawContext.OpaqueSurfaces) {
    draw(r);
}

for (auto& r : mainDrawContext.TransparentSurfaces) {
    draw(r);
}

์ด์ œ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š” ํˆฌ๋ช… ๊ฐ์ฒด๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. GLTF ๊ตฌ์กฐ์ฒด๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค๋ฉด ์กฐ๋ช… ํ—ค์ผ๋กœ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ณด์ด๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•จ์ˆ˜์˜ ๋์—์„œ ํˆฌ๋ช… ๊ฐ์ฒด ๋ฐฐ์—ด๋„ ๋ฐ˜๋“œ์‹œ ์ดˆ๊ธฐํ™” ํ•ด์ฃผ์„ธ์š”.

// we delete the draw commands now that we processed them
mainDrawContext.OpaqueSurfaces.clear();
mainDrawContext.TransparentSurfaces.clear();

์ด๋กœ์จ ์—”์ง„์˜ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋Šฅ์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฒŒ์ž„์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์„ ํƒ์ ์œผ๋กœ ๋ช‡ ๊ฐ€์ง€ ์„ฑ๋Šฅ ๋ฐ ํ’ˆ์งˆ ํ–ฅ์ƒ์„ ์œ„ํ•œ ๊ฐœ์„  ์ž‘์—…์„ ์ถ”๊ฐ€๋กœ ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: Faster Draw