Link

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

๋จผ์ € 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,
			&copyRegion);

		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๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ์ž‘์„ฑ๋œ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๋ฐ”์ธ๋”ฉํ•œ ๋’ค, ๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ๋งˆ์  ํƒ€ ์ฒดํฌ ํŒจํ„ด์ด ์ ์šฉ๋œ ์›์ˆญ์ด ๋จธ๋ฆฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

chapter2

Next: Engine Architecture