본문 바로가기

Graphics

[Vulkan] Sampler 이해하기

shader가 image를 읽을 때는

2가지 방법이 있다.

The first is to perform a raw load, which directly reads formatted or unformatted data from a specific location in the image, 
and the second is to sample the image using a sampler.



여기서 두번째 방법에 대해 공부해보자.

 

image를 샘플링(sampling)한다는 게 뭘까?

바로 다음 설명을 보면 어느정도 예상이 가능하다.

 

 

Sampling can include operations such as
performing basic transformations on the image coordinates 
or filtering texels to smooth the image data returned to the shader.

 

 

 

image 좌표계에서 기본적인 변환을 수행한다거나,

shader가 image를 사용하기전에 적절한 filtering (eg. smoothing by Gaussian filter)을

수행하는 걸로 이해가 된다. 

 

 

자, sampler에 대한 직관적인 이해가 되었다면

이제 어떻게 사용되고 있는지 확인해보자.

 

 

우선 shader는 자원에 접근하기 위한 handler로써

descriptor를 사용하고,

각각의 descriptor는 descriptor set에 묶여있다.

// Sets
std::vector<VkWriteDescriptorSet> writeDescriptorSets;
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);

// Image descriptors for the offscreen color attachments
VkDescriptorImageInfo texDescriptorPosition =
	vks::initializers::descriptorImageInfo(
		colorSampler,
		offScreenFrameBuf.position.view,
		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

VkDescriptorImageInfo texDescriptorNormal =
	vks::initializers::descriptorImageInfo(
		colorSampler,
		offScreenFrameBuf.normal.view,
		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

VkDescriptorImageInfo texDescriptorAlbedo =
	vks::initializers::descriptorImageInfo(
		colorSampler,
		offScreenFrameBuf.albedo.view,
		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

// Deferred composition
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.composition));
writeDescriptorSets = {
	// Binding 1 : Position texture target
	vks::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &texDescriptorPosition),
	// Binding 2 : Normals texture target
	vks::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &texDescriptorNormal),
	// Binding 3 : Albedo texture target
	vks::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3, &texDescriptorAlbedo),
	// Binding 4 : Fragment shader uniform buffer
	vks::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4, &uniformBuffers.composition.descriptor),
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);

 

descriptor set에 각 descriptor가 어떤 건지 명시해주는 역할을 하는 구조체인

VkWriteDescriptorSet은 다음과 같이 생겼다.

typedef struct VkWriteDescriptorSet {
    VkStructureType                  sType;
    const void*                      pNext;
    VkDescriptorSet                  dstSet;
    uint32_t                         dstBinding;
    uint32_t                         dstArrayElement;
    uint32_t                         descriptorCount;
    VkDescriptorType                 descriptorType;
    const VkDescriptorImageInfo*     pImageInfo;
    const VkDescriptorBufferInfo*    pBufferInfo;
    const VkBufferView*              pTexelBufferView;
} VkWriteDescriptorSet;

 

여기서 VkDescriptorImageInfo, VkDescriptorBufferInfo 은 어떻게 생겼는지 다시 따라가보자.

typedef struct VkDescriptorImageInfo {
    VkSampler        sampler;
    VkImageView      imageView;
    VkImageLayout    imageLayout;
} VkDescriptorImageInfo;

typedef struct VkDescriptorBufferInfo {
    VkBuffer        buffer;
    VkDeviceSize    offset;
    VkDeviceSize    range;
} VkDescriptorBufferInfo;

 

VkDescriptorImageInfo의 멤버로 드디어 

VkSampler 타입의 sampler가 나왔다 !!

 

위 코드 블럭에서는 3개의 descriptor가 모두 같은 sampler인 "colorSampler"를 사용한다.

이 친구는 어떻게 만들어졌는지 또다시 코드를 추적해오자.

 

		// Create sampler to sample from the color attachments
		VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo();
		sampler.magFilter = VK_FILTER_NEAREST;
		sampler.minFilter = VK_FILTER_NEAREST;
		sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
		sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
		sampler.addressModeV = sampler.addressModeU;
		sampler.addressModeW = sampler.addressModeU;
		sampler.mipLodBias = 0.0f;
		sampler.maxAnisotropy = 1.0f;
		sampler.minLod = 0.0f;
		sampler.maxLod = 1.0f;
		sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
		VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &colorSampler));

 

앞서 sampler가 어떤 일을 하는지 살펴보았던 대로,

filtering과 관련된 정보들을 확인할 수 있다.

 

 

 

 


Reference
Vulkan Programming Guide 2016
Vulkan 1.4.304 - A specification (with all ratified extensions)
https://github.com/SaschaWillems/Vulkan - project 'deferred'