์ง๋ ๊ธ์์ ํ ์ค์ณ์ ๋ํ ์ค๋ช ์ ์ด ๊ธ์์ ์กฐ๊ธ ๋ ์์ธํ ๋ค๋ฃจ๊ธฐ ์ํด ์๋ตํ์์ต๋๋ค.
ํ ์ค์ณ๋ฅผ ๋ถ๋ฌ์ฌ ๋ 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