์ฐ๋ฆฌ๋ ์ด๋ฏธ ์ปดํจํธ ๊ธฐ๋ฐ ๋ ๋๋ง์์ ์ด๋ฏธ์ง ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค๋ฃฌ ์ ์ด ์์ต๋๋ค. ํ์ง๋ง ์์ง ๊ทธ๋ํฝ ์ ฐ์ด๋์์ ์ด๋ฏธ์ง๋ฅผ ๋ ๋๋ง ๋ฐ ํ๋ฉด ์ถ๋ ฅ ์ฉ๋๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ค๋ฃจ์ง ์์์ต๋๋ค. ์ด์ ์์ง์ ๊ธฐ๋ณธ ํ ์ค์ณ๋ค์ ์์ฑํ๊ณ , ์ดํ์๋ ํ์ผ์์ ํ ์ค์ณ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ณผ์ ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๋จผ์ VulkanEngine์ ์ด๋ฏธ์ง๋ฅผ ๊ด๋ฆฌํ๊ณ ํ๊ดดํ๋ ํจ์๋ฅผ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
ํค๋์ ๋ค์ ํจ์๋ฅผ ์ถ๊ฐํฉ๋๋ค.
class VulkanEngine {
AllocatedImage create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped = false);
AllocatedImage create_image(void* data, VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped = false);
void destroy_image(const AllocatedImage& img);
}
์ด์ vk_engine.cpp์ ๋ค์ ํจ์๋ฅผ ์์ฑํฉ์๋ค.
AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped)
{
AllocatedImage newImage;
newImage.imageFormat = format;
newImage.imageExtent = size;
VkImageCreateInfo img_info = vkinit::image_create_info(format, usage, size);
if (mipmapped) {
img_info.mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(size.width, size.height)))) + 1;
}
// always allocate images on dedicated GPU memory
VmaAllocationCreateInfo allocinfo = {};
allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
allocinfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
// allocate and create the image
VK_CHECK(vmaCreateImage(_allocator, &img_info, &allocinfo, &newImage.image, &newImage.allocation, nullptr));
// if the format is a depth format, we will need to have it use the correct
// aspect flag
VkImageAspectFlags aspectFlag = VK_IMAGE_ASPECT_COLOR_BIT;
if (format == VK_FORMAT_D32_SFLOAT) {
aspectFlag = VK_IMAGE_ASPECT_DEPTH_BIT;
}
// build a image-view for the image
VkImageViewCreateInfo view_info = vkinit::imageview_create_info(format, newImage.image, aspectFlag);
view_info.subresourceRange.levelCount = img_info.mipLevels;
VK_CHECK(vkCreateImageView(_device, &view_info, nullptr, &newImage.imageView));
return newImage;
}
์ด๋ drawImage๋ฅผ ์์ฑํ์ ๋์ ๋์ผํ ์์
์ด๋ฉฐ, ํด๋น ์์
์ ๋ณ๋์ ํจ์๋ก ๋ณต์ฌํ ๊ฒ์
๋๋ค. ๋จผ์ ์ด๋ฏธ์ง์ ํฌ๊ธฐ์ ํฌ๋งท ์ ๋ณด๋ฅผ AllocatedImage ๊ตฌ์กฐ์ฒด์ ์ ์ฅํ๊ณ , ๊ทธ ํ VkImageCreateInfo๋ฅผ ์์ฑํฉ๋๋ค. ์ดํ VMA๋ฅผ ํตํด ์ด๋ฏธ์ง๋ฅผ ํ ๋นํ๊ณ ๋ง์ง๋ง์ผ๋ก ์ด๋ฏธ์ง ๋ทฐ๋ฅผ ์์ฑํฉ๋๋ค. ์ด์ ๊ณผ ๋ค๋ฅธ ์ ์ aspect ํ๋๊ทธ๋ฅผ ์ค์ ํ๋ค๋ ๊ฒ์
๋๋ค. ์ด๋ฏธ์ง๊ฐ D32 float์ ๊ฐ์ ๊น์ด ํฌ๋งท์ด ์๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก VK_IMAGE_ASPECT_COLOR_BIT
๋ฅผ ์ค์ ํฉ๋๋ค.
์ด๋ฏธ์ง์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ก๋ํ๊ธฐ ์ํด ์ง๋ ์ฑํฐ์์ ๋ฒํผ์ ๋ฐ์ดํฐ๋ฅผ ์ผ๋ ๋ฐฉ์๊ณผ ์ ์ฌํ ์ ์ฐจ๋ฅผ ๋ฐ๋ฆ ๋๋ค. ์์ ์คํ ์ด์ง ๋ฒํผ๋ฅผ ์์ฑํ๊ณ ํด๋น ๋ฒํผ์ ํฝ์ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ ๋ค, VkCmdCopyBufferToImage๋ฅผ ํธ์ถํ๋ immediate submit์ ์ํํฉ๋๋ค. ์ด ๊ธฐ๋ฅ์ ์ฒ๋ฆฌํ๋ ํจ์๋ฅผ ์์ฑํฉ์๋ค. ๊ธฐ์กด create_image ํจ์์ ์ค๋ฒ๋ก๋ฉ ๋ฒ์ ์ผ๋ก ๋ง๋ค๊ณ , ํฝ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ๊ธฐ ์ํด void* ์ธ์๋ฅผ ์ถ๊ฐํ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์๋ ๋๋ถ๋ถ์ ์ด๋ฏธ์ง ํ์ผ์ด ์ฌ์ฉํ๋ RGBA 8๋นํธ ํฌ๋งท์ ๊ธฐ์ค์ผ๋ก ํ ์ค์ณ๋ฅผ ํ๋์ฝ๋ฉํ ๊ฒ์ ๋๋ค.
AllocatedImage VulkanEngine::create_image(void* data, VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped)
{
size_t data_size = size.depth * size.width * size.height * 4;
AllocatedBuffer uploadbuffer = create_buffer(data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
memcpy(uploadbuffer.info.pMappedData, data, data_size);
AllocatedImage new_image = create_image(size, format, usage | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, mipmapped);
immediate_submit([&](VkCommandBuffer cmd) {
vkutil::transition_image(cmd, new_image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy copyRegion = {};
copyRegion.bufferOffset = 0;
copyRegion.bufferRowLength = 0;
copyRegion.bufferImageHeight = 0;
copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyRegion.imageSubresource.mipLevel = 0;
copyRegion.imageSubresource.baseArrayLayer = 0;
copyRegion.imageSubresource.layerCount = 1;
copyRegion.imageExtent = size;
// copy the buffer into the image
vkCmdCopyBufferToImage(cmd, uploadbuffer.buffer, new_image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
©Region);
vkutil::transition_image(cmd, new_image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
});
destroy_buffer(uploadbuffer);
return new_image;
}
์ฐ์ ํฝ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์์ ๋งํผ์ ๊ณต๊ฐ์ ๊ฐ๋ CPU_TO_GPU ๋ฉ๋ชจ๋ฆฌ ํ์ ์ ์คํ ์ด์ง ๋ฒํผ๋ฅผ ํ ๋นํ๊ณ , memcpy๋ฅผ ์ํํด ํด๋น ๋ฒํผ์ ํฝ์ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํฉ๋๋ค.
๊ทธ ๋ค์ ๊ธฐ์กด์ create_image ํจ์๋ฅผ ํธ์ถํ๋ VK_IMAGE_USAGE_TRANSFER_DST_BIT
์ VK_IMAGE_TRANSFER_SRC_BIT
๋ฅผ ์ถ๊ฐํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ๊ฑฐ๋ ์ด๋ฏธ์ง์ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ ์ ์๋๋ก ํฉ๋๋ค.
์ด๋ฏธ์ง์ ์คํ ์ด์ง ๋ฒํผ๊ฐ ์ค๋น๋์์ผ๋ฉด, immediate submit์ ์ํํด ์คํ ์ด์ง ๋ฒํผ์ ํฝ์ ๋ฐ์ดํฐ๋ฅผ ์ด๋ฏธ์ง๋ก ๋ณต์ฌํฉ๋๋ค.
์ค์์ฒด์ธ ์ด๋ฏธ์ง์์ ์์
ํ๋ ๊ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ๋จผ์ ์ด๋ฏธ์ง ๋ ์ด์์์ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
๋ก ์ ํํฉ๋๋ค. ๊ทธํ ๋ณต์ฌ ๋ช
๋ น ํ๋ผ๋ฏธํฐ๋ฅผ ๋ด์ copyRegion ๊ตฌ์กฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค. ์ด ๊ตฌ์กฐ์ฒด์๋ ์ด๋ฏธ์ง ํฌ๊ธฐ์ ๋ณต์ฌ ๋์ ์ด๋ฏธ์ง ๋ ์ด์ด, ๊ทธ๋ฆฌ๊ณ ๋ฐ๋งต ๋ ๋ฒจ ์ ๋ณด๊ฐ ํ์ํฉ๋๋ค. ์ด๋ฏธ์ง ๋ ์ด์ด๋ ์ฌ๋ฌ ๋ ์ด์ด๋ฅผ ๊ฐ๋ ํ
์ค์ณ์ ์ฌ์ฉ๋๋ฉฐ, ๋ํ์ ์ธ ์์๋ ํ๋ธ๋งต ํ
์ค์ณ์
๋๋ค. ํ๋ธ๋งต์ ์ฌ์ฏ ๊ฐ์ ๋ฉด์ ๊ฐ์ง๋ฏ๋ก ๋ ์ด์ด ์๊ฐ 6์ด ๋ฉ๋๋ค. ์ดํ ๋ฐ์ฌ ํ๋ธ๋งต์ ๊ตฌ์ฑํ ๋ ๋ค๋ฃจ๊ฒ ๋ ์์ ์
๋๋ค.
๋ฐ๋งต์ ๊ฒฝ์ฐ, ์ต์์ ๋ ๋ฒจ์ธ 0๋ฒ ๋ ๋ฒจ์ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ๋ฉฐ, ํ์ฌ ์ด๋ฏธ์ง๋ ์ถ๊ฐ์ ์ธ ๋ฐ๋งต ๋ ๋ฒจ์ ๊ฐ๊ณ ์์ง ์์ต๋๋ค. ์ง๊ธ์ create_image ํจ์์ mipmapped bool ๊ฐ๋ง ์ ๋ฌํ๊ณ ์์ผ๋ฉฐ, ๋ณ๋์ ์ฒ๋ฆฌ๋ ํ์ง ์๊ณ ์์ต๋๋ค. ๋ฐ๋งต ์์ฑ์ ์ดํ ๋จ๊ณ์์ ๋ค๋ฃฐ ์์ ์ ๋๋ค.
๋ง์ง๋ง์ผ๋ก ์ด๋ฏธ์ง ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํ๊ธฐ ์ํด destroy_image() ํจ์๋ฅผ ๊ตฌํํ๊ฒ ์ต๋๋ค.
void VulkanEngine::destroy_image(const AllocatedImage& img)
{
vkDestroyImageView(_device, img.imageView, nullptr);
vmaDestroyImage(_allocator, img.image, img.allocation);
}
๋จผ์ ์ด๋ฏธ์ง ๋ทฐ๋ฅผ ํ๊ดดํ๊ณ VMA๋ฅผ ์ฌ์ฉํด ์ด๋ฏธ์ง๋ฅผ ํ๊ดดํฉ๋๋ค. ์ด ๊ณผ์ ์ ํตํด ์ด๋ฏธ์ง์ ๊ทธ์ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ํ๊ดด๋ฉ๋๋ค.
์ด๋ฌํ ํจ์๋ค์ ์ฌ์ฉํด ๊ธฐ๋ณธ ํ ์ค์ณ๋ค์ ์ค์ ํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ ํฐ์, ๊ฒ์์, ํ์, ์ฒดํฌ๋ณด๋ ํ ์ค์ณ๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ํ ์ค์ณ ๋ก๋ฉ์ ์คํจํ์ ๋ ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ณธ ํ ์ค์ณ๋ค์ ๋ฏธ๋ฆฌ ์ค๋นํด๋ ์ ์์ต๋๋ค.
์ด๋ฌํ ํ ์คํธ ์ด๋ฏธ์ง๋ค์ VulkanEngine ํด๋์ค์ ์ถ๊ฐํฉ๋๋ค. ์ด ํ ์ค์ณ๋ค๊ณผ ๊ธฐํ ์ด๋ฏธ์ง์์ ์ฌ์ฉํ ์ ์๋๋ก ์ํ๋ฌ๋ค๋ ๋ช๊ฐ ํจ๊ป ์์ฑํ ์์ ์ ๋๋ค.
AllocatedImage _whiteImage;
AllocatedImage _blackImage;
AllocatedImage _greyImage;
AllocatedImage _errorCheckerboardImage;
VkSampler _defaultSamplerLinear;
VkSampler _defaultSamplerNearest;
์ฌ๊ฐํ ๋ฉ์๋ฅผ ์์ฑํ๋ ์ฝ๋ ์ดํ์, init_default_data()
ํจ์์ ๋ด๋ถ์์ ์ด ๊ธฐ๋ณธ ํ
์ค์ณ๋ค์ ์์ฑํด ๋ด
์๋ค.
//3 default textures, white, grey, black. 1 pixel each
uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1));
_whiteImage = create_image((void*)&white, VkExtent3D{ 1, 1, 1 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT);
uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1));
_greyImage = create_image((void*)&grey, VkExtent3D{ 1, 1, 1 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT);
uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0));
_blackImage = create_image((void*)&black, VkExtent3D{ 1, 1, 1 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT);
//checkerboard image
uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1));
std::array<uint32_t, 16 *16 > pixels; //for 16x16 checkerboard texture
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
pixels[y*16 + x] = ((x % 2) ^ (y % 2)) ? magenta : black;
}
}
_errorCheckerboardImage = create_image(pixels.data(), VkExtent3D{16, 16, 1}, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT);
VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
sampl.magFilter = VK_FILTER_NEAREST;
sampl.minFilter = VK_FILTER_NEAREST;
vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerNearest);
sampl.magFilter = VK_FILTER_LINEAR;
sampl.minFilter = VK_FILTER_LINEAR;
vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerLinear);
_mainDeletionQueue.push_function([&](){
vkDestroySampler(_device,_defaultSamplerNearest,nullptr);
vkDestroySampler(_device,_defaultSamplerLinear,nullptr);
destroy_image(_whiteImage);
destroy_image(_greyImage);
destroy_image(_blackImage);
destroy_image(_errorCheckerboardImage);
});
๊ธฐ๋ณธ ์์ ์ด๋ฏธ์ง 3๊ฐ๋ ๊ฐ๊ฐ ํ๋์ ์์์ ๊ฐ์ง ๋จ์ผ ํฝ์ ์ด๋ฏธ์ง๋ก ์์ฑํฉ๋๋ค. ์ฒดํฌ๋ณด๋ ํ ์ค์ณ๋ 16 * 16 ํฌ๊ธฐ์ ํฝ์ ๋ฐฐ์ด์ ์์ฑํ๊ณ , ๊ฐ๋จํ ์ํ ์ฐ์ฐ์ ํตํด ๊ฒ์ ์๊ณผ ๋ง์ ํ ์์ด ๋ฒ๊ฐ์ ๋ํ๋๋ ํจํด์ผ๋ก ๋ง๋ญ๋๋ค.
์ํ๋ฌ์๋ min/mag filters๋ฅผ ์ ์ธํ ๋ชจ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋์์ต๋๋ค. ํํฐ๋ Linear ํน์ Nearest๋ก ์ค์ ํ ์ ์์ผ๋ฉฐ, Nearest๋ ํฝ์ ํํ๊ฐ ๊ทธ๋๋ก ๋ณด์ด๋ ๋๋ ทํ ๋๋์ ์ฃผ๊ณ , Linear๋ ํฝ์ ์ฌ์ด๋ฅผ ๋ธ๋ฌ ์ฒ๋ฆฌํ์ฌ ๋ถ๋๋ฝ๊ฒ ๋ณด์ด๊ฒ ํฉ๋๋ค.
์ ฐ์ด๋์ ์ด๋ฏธ์ง ๋ฐ์ธ๋ฉํ๊ธฐ
์ปดํจํธ ๊ธฐ๋ฐ ๋ ๋๋ง์ ์ํํ ๋, ์ํ๋ง ๋ก์ง ์์ด ํ
์ค์ณ์ ์ฝ๊ธฐ ์ฐ๊ธฐ ํ๊ธฐ ์ํด VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
๋ฅผ ์ฌ์ฉํด ์ด๋ฏธ์ง๋ฅผ ๋ฐ์ธ๋ฉํ์์ต๋๋ค. ์ด๋ ๋ค์ฐจ์ ๊ตฌ์กฐ์ ๋ค๋ฅธ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์์ ๊ฐ๋ ๋ฒํผ๋ฅผ ๋ฐ์ธ๋ฉํ๋ ๊ฒ๊ณผ ์ ์ฌํฉ๋๋ค. ํ์ง๋ง ๊ทธ๋ํฝ์ค ๋ ๋๋ง์ ํ ๋์๋ ํ
์ค์ณ์ ์ ๊ทผํ ๋ GPU์ ๊ณ ์ ํ๋์จ์ด๋ฅผ ์ฌ์ฉํด์ผ ํ๋ฉฐ, ์ด๋ฅผ ์ํด ์ํ๋ฌ๊ฐ ํ์ํฉ๋๋ค. ์ํ๋ฌ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์๋ ์ด๋ฏธ์ง์ ์ํ๋ฌ๋ฅผ ํ๋๋ก ๋ฌถ๋ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ, 2๊ฐ์ ๋์คํฌ๋ฆฝํฐ๋ฅผ ํตํด VK_DESCRIPTOR_TYPE_SAMPLER
์ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. GPU ์ ์กฐ์ฌ์ ๋ฐ๋ฅด๋ฉด ๊ฐ๋ณ ๋์คํฌ๋ฆฝํฐ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ด ๋ฐ์ดํฐ ์ค๋ณต์ด ์ ์ด ๋ ๋น ๋ฅผ ์ ์๋ค๊ณ ํฉ๋๋ค. ํ์ง๋ง ์ด ๋ฐฉ์์ ๋ค๋ฃจ๊ธฐ ๋ณต์กํ๋ฏ๋ก, ์ง๊ธ์ ์ฌ์ฉํ์ง ์์ ์์ ์
๋๋ค. ๋์ , ์
ฐ์ด๋๋ฅผ ๊ฐ๋จํ๊ฒ ๋ง๋ค๊ธฐ ์ํด combined ๋์คํฌ๋ฆฝํฐ๋ฅผ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
์ด์ ์ ๊ทธ๋ ธ๋ ์ง์ฌ๊ฐํ ๋ํ์ ํด๋น ๋ํ ์์ ์ด๋ฏธ์ง๋ฅผ ํ์ํ๋ ๋ฐฉ์์ผ๋ก ์์ ํ ์์ ์
๋๋ค.์ด๋ฅผ ์ํด ์ด๋ฏธ์ง๋ฅผ ํ์ํ๋ ์๋ก์ด ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋๊ฐ ํ์ํ๋ฉฐ, ์ด๋ฆ์ text_image.frag
๋ก ํ๊ฒ ์ต๋๋ค.
//glsl version 4.5
#version 450
//shader input
layout (location = 0) in vec3 inColor;
layout (location = 1) in vec2 inUV;
//output write
layout (location = 0) out vec4 outFragColor;
//texture to access
layout(set =0, binding = 0) uniform sampler2D displayTexture;
void main()
{
outFragColor = texture(displayTexture,inUV);
}
ํ๋๊ทธ๋จผํธ ์ ฐ์ด๋์ color์ UV๋ผ๋ 2๊ฐ์ง ์ ๋ ฅ์ด ์์ต๋๋ค. ์ ฐ์ด๋๊ฐ color๋ฅผ ์ฌ์ฉํ์ง๋ ์์ง๋ง ์ด์ ๊ณผ ๋์ผํ ์ ์ ์ ฐ์ด๋๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๊ทธ๋๋ก ์ ์งํฉ๋๋ค.
ํ
์ค์ณ๋ฅผ ์ํ๋งํ๊ธฐ ์ํด texture(textureSampler, coordinates)
๋ฅผ ์ฌ์ฉํฉ๋๋ค. ํน์ ํฝ์
์ ์ง์ ์ ๊ทผํ ์ ์๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ๋ ์์ต๋๋ค. ํ
์ค์ณ ๊ฐ์ฒด๋ uniform sampler2D
๋ก ์ ์ธ๋ฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ ค๋ฉด ํ์ดํ๋ผ์ธ ๋ ์ด์์์ ์์ ํด์ผ ํ๋ฏ๋ก, ๊ทธ๊ฒ๋ ํจ๊ป ์ ๋ฐ์ดํธํ๊ฒ ์ต๋๋ค.
VulkanEngine์ ๋ ์ด์์์ ์ถ๊ฐํฉ์๋ค. ์ดํ์๋ ๊ณ์ ์ฌ์ฉํ ์์ ์ ๋๋ค.
class VulkanEngine {
VkDescriptorSetLayout _singleImageDescriptorLayout;
}
init_descriptors()์์ ๋จ์ ๋ถ๋ถ์ ์์ฑํฉ์๋ค.
{
DescriptorLayoutBuilder builder;
builder.add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
_singleImageDescriptorLayout = builder.build(_device, VK_SHADER_STAGE_FRAGMENT_BIT);
}
ํ๋์ ์ด๋ฏธ์ง-์ํ๋ฌ ๋์คํฌ๋ฆฝํฐ๋ง ๊ฐ๋ ๋์คํฌ๋ฆฝํฐ ์
์ ์ค์ ํฉ๋๋ค. ์ด์ init_mesh_pipeline()
ํจ์๋ฅผ ์์ ํ์ฌ ์ด๋ฅผ ๋ฐ์ํ ์ ์์ต๋๋ค. ํจ์์ ์์ ๋ถ๋ถ์ ์์ ํ๋ฉด์, ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋๋ฅผ ๊ต์ฒดํ๊ณ , ๋์คํฌ๋ฆฝํฐ ์
๋ ์ด์์์ ํ์ดํ๋ผ์ธ ๋ ์ด์์ ์์ฑ์ ์ฐ๊ฒฐํฉ๋๋ค.
void VulkanEngine::init_mesh_pipeline()
{
VkShaderModule triangleFragShader;
if (!vkutil::load_shader_module("../../shaders/tex_image.frag.spv", _device, &triangleFragShader)) {
fmt::print("Error when building the fragment shader \n");
}
else {
fmt::print("Triangle fragment shader succesfully loaded \n");
}
VkShaderModule triangleVertexShader;
if (!vkutil::load_shader_module("../../shaders/colored_triangle_mesh.vert.spv", _device, &triangleVertexShader)) {
fmt::print("Error when building the vertex shader \n");
}
else {
fmt::print("Triangle vertex shader succesfully loaded \n");
}
VkPushConstantRange bufferRange{};
bufferRange.offset = 0;
bufferRange.size = sizeof(GPUDrawPushConstants);
bufferRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info();
pipeline_layout_info.pPushConstantRanges = &bufferRange;
pipeline_layout_info.pushConstantRangeCount = 1;
pipeline_layout_info.pSetLayouts = &_singleImageDescriptorLayout;
pipeline_layout_info.setLayoutCount = 1;
VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_meshPipelineLayout));
}
์ด์ draw ํจ์์์ ํ์ดํ๋ผ์ธ์ ๋ฐ์ธ๋ฉํ ๋ ํ์ํ ๋์คํฌ๋ฆฝํฐ ์ ์ ๋์ ์ผ๋ก ์์ฑํ์ฌ ์ํ๋ ํ ์ค์ณ๋ฅผ ํ๋ฉด์ ์ถ๋ ฅํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ด๋ฅผ draw_geometry()
ํจ์์ ์์ฑํ์ฌ ์์ญ์ด ๋ฉ์๋ฅผ ํ
์ค์ณ๊ฐ ์ ์ฉ๋ ๋ฉ์๋ก ๋ฐ๊ฟ๋ด
์๋ค.
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipeline);
//bind a texture
VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate(_device, _singleImageDescriptorLayout);
{
DescriptorWriter writer;
writer.write_image(0, _errorCheckerboardImage.imageView, _defaultSamplerNearest, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.update_set(_device, imageSet);
}
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipelineLayout, 0, 1, &imageSet, 0, nullptr);
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;
GPUDrawPushConstants push_constants;
push_constants.worldMatrix = projection * view;
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);
์ ฐ์ด๋์์ ์ฌ์ฉํ๋ _singleImageDescriptorLayout์ ์ด์ฉํด ํ๋ ์๋ณ ๋์คํฌ๋ฆฝํฐ ์ ํ ๋น๊ธฐ์์ ์๋ก์ด ๋์คํฌ๋ฆฝํฐ ์ ์ ํ ๋นํฉ๋๋ค.
๊ทธ๋ฐ ๋ค์ DescriptorWrite๋ฅผ ์ฌ์ฉํด ๋ฐ์ธ๋ฉ 0๋ฒ์ ์ด๋ฏธ์ง ๋์คํฌ๋ฆฝํฐ๋ฅผ ์์ฑํฉ๋๋ค. ์ด ๋์คํฌ๋ฆฝํฐ์๋ _errorCheckerboardImage๋ฅผ ์ค์ ํ๊ณ , ํฝ์ ๊ฐ ๋ธ๋ ๋ฉ์ด ์ผ์ด๋์ง ์๋๋ก nearest-sampler๋ฅผ ์ ๋ฌํฉ๋๋ค. ์ดํ ์์ฑ๋ ๋ด์ฉ์ ๊ธฐ๋ฐ์ผ๋ก ๋์คํฌ๋ฆฝํฐ ์ ์ ์ ๋ฐ์ดํธํ๊ณ ๋ฐ์ธ๋ฉํ ๋ค, ๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์งํํฉ๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ๋ง์ ํ ์ฒดํฌ ํจํด์ด ์ ์ฉ๋ ์์ญ์ด ๋จธ๋ฆฌ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
Next: Engine Architecture