Link

μ»΄ν“¨νŠΈ 셰이더λ₯Ό μ‹€ν–‰ν•΄ ν‘œμ‹œν•  μˆ˜λ‹¨μ΄ 있으며, 엔진에 디버그 UIλ₯Ό μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯도 μžˆμŠ΅λ‹ˆλ‹€. 이제 UIλ₯Ό 톡해 μ…°μ΄λ”λ‘œ 데이터λ₯Ό μ „μ†‘ν•˜κ³  μƒν˜Έμž‘μš©ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

셰이더에 데이터λ₯Ό μ „μ†‘ν•˜λŠ” 데 ν‘Έμ‹œμƒμˆ˜λ₯Ό μ‚¬μš©ν•  κ²ƒμž…λ‹ˆλ‹€. ν‘Έμ‹œμƒμˆ˜λŠ” 적은 μ–‘μ˜ 데이터λ₯Ό GPU둜 보낼 수 μžˆλŠ” Vulkan의 고유 κΈ°λŠ₯μž…λ‹ˆλ‹€. 이 데이터λ₯Ό 적게 μœ μ§€ν•˜λŠ” 것이 μ€‘μš”ν•œλ°, λŒ€λΆ€λΆ„μ˜ λ“œλΌμ΄λ²„κ°€ 데이터가 νŠΉμ • λ°”μ΄νŠΈ 수 μ΄ν•˜μΌ λ•Œ κ°€μž₯ λΉ λ₯Έ 경둜λ₯Ό μ°Ύμ•„μ£ΌκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€(μžμ„Έν•œ ν¬κΈ°λŠ” GPU μ œμ‘°μ‚¬ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”). μ£Ό μš©λ‘€λŠ” κ°μ²΄λ§ˆλ‹€μ˜ 인덱슀 ν˜Ήμ€ 행렬을 μ „λ‹¬ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. λ§Œμ•½ 보내야 ν•˜λŠ” 데이터가 κ½€ λ§Žλ‹€λ©΄ λ‹€μŒ μ±•ν„°μ—μ„œ μ‚¬μš©ν•  λ‹€λ₯Έ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

ν‘Έμ‹œ μƒμˆ˜λŠ” νŒŒμ΄ν”„λΌμΈμ„ 생성할 λ•Œ κ΅¬μ„±ν•©λ‹ˆλ‹€. μ½”λ“œλ₯Ό λ‹¨μˆœν•˜κ²Œ μœ μ§€ν•˜κ³  λ§Žμ€ μˆ˜μ •μ„ ν”Όν•˜κΈ° μœ„ν•΄ ν‘Έμ‹œμƒμˆ˜μ— 기본적으둜 4개의 vec4λ₯Ό μ‚¬μš©ν•˜κ² μŠ΅λ‹ˆλ‹€. 16개의 floatλŠ” μ…°μ΄λ”μ—μ„œ μΆ©λΆ„ν•©λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈμ˜ 셰이더 폴더에 μ—¬λŸ¬ μ»΄ν“¨νŠΈ 셰이더가 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό λ°”κΏ” μ‚¬μš©ν•  수 있으며, νŠœν† λ¦¬μ–Όμ—μ„œλŠ” λ‹¨μˆœν•œ μƒ‰μƒμ—λ§Œ μ§‘μ€‘ν•˜κ² μ§€λ§Œ μ—¬λŸ¬ 데λͺ¨ 셰이더λ₯Ό μ μš©ν•΄ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

λͺ¨λ“  μ»΄ν“¨νŠΈ 셰이더가 같은 λ ˆμ΄μ•„μ›ƒμ„ κ³΅μœ ν•˜κΈ° λ•Œλ¬Έμ— UI에 λ“œλ‘­λ‹€μš΄μ„ μΆ”κ°€ν•˜λŠ” κ²ƒμœΌλ‘œ μ‚¬μš©ν•  νŒŒμ΄ν”„λΌμΈμ„ 선택할 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ λŸ°νƒ€μž„μ— λ‹€μ–‘ν•œ μ»΄ν“¨νŠΈ 셰이더λ₯Ό λ²ˆκ°ˆμ•„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν‘Έμ‹œμƒμˆ˜λ₯Ό μ μš©ν•  μ˜ˆμ œμ— μ‚¬μš©ν•  μ…°μ΄λ”λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. μ΄λŠ” 2개의 색상을 Yμ’Œν‘œμ— 따라 ν˜Όν•©ν•˜μ—¬ 수직 κ·ΈλΌλ””μ–ΈνŠΈλ₯Ό λ§Œλ“­λ‹ˆλ‹€.

셰이더 ν΄λ”μ˜ gradient_color.compμ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

#version 460

layout (local_size_x = 16, local_size_y = 16) in;

layout(rgba16f,set = 0, binding = 0) uniform image2D image;

//push constants block
layout( push_constant ) uniform constants
{
 vec4 data1;
 vec4 data2;
 vec4 data3;
 vec4 data4;
} PushConstants;

void main() 
{
    ivec2 texelCoord = ivec2(gl_GlobalInvocationID.xy);

	ivec2 size = imageSize(image);

    vec4 topColor = PushConstants.data1;
    vec4 bottomColor = PushConstants.data2;

    if(texelCoord.x < size.x && texelCoord.y < size.y)
    {
        float blend = float(texelCoord.y)/(size.y); 
    
        imageStore(image, texelCoord, mix(topColor,bottomColor, blend));
    }
}

λŒ€λΆ€λΆ„ 이전 κΈ€μ—μ„œ μž‘μ„±ν–ˆλ˜ κ·ΈλΌλ””μ–ΈνŠΈ 셰이더와 λ™μΌν•©λ‹ˆλ‹€. 4개의 vec4λ₯Ό λ‹΄λŠ” ν‘Έμ‹œμƒμˆ˜ 블둝을 μΆ”κ°€ν–ˆμœΌλ©° μ΄λ‘œλΆ€ν„° 상단과 ν•˜λ‹¨μ˜ 색상을 λΆˆλŸ¬μ˜΅λ‹ˆλ‹€. data3와 data4λŠ” μ‹€μ œλ‘œ μ‚¬μš©λ˜μ§€λŠ” μ•Šμ§€λ§Œ μ…°μ΄λ”μ˜ ν‘Έμ‹œμƒμˆ˜ λ²”μœ„λ³΄λ‹€ λ„“λ‹€λŠ” 이유둜 검증 λ ˆμ΄μ–΄κ°€ κ²½κ³ λ₯Ό λ°œμƒν•˜λŠ” 것을 λ°©μ§€ν•˜κΈ° μœ„ν•΄ μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

ν‘Έμ‹œμƒμˆ˜ λ²”μœ„λ₯Ό κ΅¬μ„±ν•˜κΈ° μœ„ν•΄ νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒμ„ μˆ˜μ •ν•  ν•„μš”κ°€ μžˆμŠ΅λ‹ˆλ‹€. λ¨Όμ € μ΄λŸ¬ν•œ ν‘Έμ‹œ μƒμˆ˜λ₯Ό μ§μ ‘μ μœΌλ‘œ λ‚˜νƒ€λ‚΄λŠ” ꡬ쑰체λ₯Ό vk_engine.h에 μΆ”κ°€ν•©λ‹ˆλ‹€.

struct ComputePushConstants {
	glm::vec4 data1;
	glm::vec4 data2;
	glm::vec4 data3;
	glm::vec4 data4;
};

ν‘Έμ‹œμƒμˆ˜ λ²”μœ„λ₯Ό μ„€μ •ν•˜κΈ° μœ„ν•΄ init_pipelines의 μ‹œμž‘ λΆ€λΆ„μ—μ„œ νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒμ˜ μƒμ„±ν•˜λŠ” μ½”λ“œλ₯Ό μˆ˜μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ•„λž˜λŠ” μˆ˜μ •λœ λ²„μ „μž…λ‹ˆλ‹€.

VkPipelineLayoutCreateInfo computeLayout{};
computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
computeLayout.pNext = nullptr;
computeLayout.pSetLayouts = &_drawImageDescriptorLayout;
computeLayout.setLayoutCount = 1;

VkPushConstantRange pushConstant{};
pushConstant.offset = 0;
pushConstant.size = sizeof(ComputePushConstants) ;
pushConstant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;

computeLayout.pPushConstantRanges = &pushConstant;
computeLayout.pushConstantRangeCount = 1;

VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout));

VkPushConstantRangeλ₯Ό VkPipelineLayoutCreateInfo ꡬ쑰체에 μΆ”κ°€ν•©λ‹ˆλ‹€. ν‘Έμ‹œμƒμˆ˜ λ²”μœ„λŠ” μ˜€ν”„μ…‹μ„ λ‹΄μœΌλ©°(μ—¬κΈ°μ„œλŠ” 0으둜 μ„€μ •ν•©λ‹ˆλ‹€.), 크기와 μ‹€ν–‰ 단계 ν”Œλž˜κ·Έλ₯Ό μ§€μ •ν•©λ‹ˆλ‹€. ν¬κΈ°μ—λŠ” 우리 ꡬ쑰체의 cpp 버전을 μ‚¬μš©ν•  것이고, μ‹€ν–‰ 단계 ν”Œλž˜κ·ΈλŠ” μš°λ¦¬κ°€ μ‹€ν–‰ν•˜λŠ” μœ μΌν•œ 단계인 μ»΄ν“¨νŠΈ(compute)일 κ²ƒμž…λ‹ˆλ‹€.

이후에 셰이더λ₯Ό λ°”κΎΈκΈ°λ§Œ ν•˜λ©΄ λ©λ‹ˆλ‹€.

VkShaderModule computeDrawShader;
if (!vkutil::load_shader_module("../../shaders/gradient_color.comp.spv", _device, &computeDrawShader))
{
	std::cout << "Error when building the colored mesh shader" << std::endl;
}

μ΄κ²ƒμœΌλ‘œ 셰이더에 ν‘Έμ‹œμƒμˆ˜λ₯Ό μ „λ‹¬ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ λͺ¨λ“  μž‘μ—…μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. 이제 이λ₯Ό λ Œλ”λ§ λ£¨ν”„μ—μ„œ μ‚¬μš©ν•΄λ΄…μ‹œλ‹€.

	// bind the gradient drawing compute pipeline
	vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipeline);

	// bind the descriptor set containing the draw image for the compute pipeline
	vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipelineLayout, 0, 1, &_drawImageDescriptors, 0, nullptr);

	ComputePushConstants pc;
	pc.data1 = glm::vec4(1, 0, 0, 1);
	pc.data2 = glm::vec4(0, 0, 1, 1);

	vkCmdPushConstants(cmd, _gradientPipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(ComputePushConstants), &pc);
	// execute the compute pipeline dispatch. We are using 16x16 workgroup size so we need to divide by it
	vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), std::ceil(_drawExtent.height / 16.0), 1);

ν‘Έμ‹œ μƒμˆ˜λ₯Ό μ—…λ°μ΄νŠΈ ν•˜κΈ° μœ„ν•΄ VkCmdPushConstantsλ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€. 이 ν•¨μˆ˜λŠ” νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒ, μž‘μ„±ν•  λ°μ΄ν„°μ˜ μ˜€ν”„μ…‹(μ—¬κΈ°μ„œλŠ” 0μž…λ‹ˆλ‹€.) 그리고 λ°μ΄ν„°μ˜ 크기와 볡사할 포인터λ₯Ό μš”κ΅¬ν•©λ‹ˆλ‹€. λ˜ν•œ μ„œλ‘œ λ‹€λ₯Έ 셰이더 단계에 λŒ€ν•΄ ν‘Έμ‹œμƒμˆ˜λ₯Ό μ—…λ°μ΄νŠΈν•  수 있기 λ•Œλ¬Έμ— 셰이더 단계 ν”Œλž˜κ·Έλ„ ν•„μš”ν•©λ‹ˆλ‹€.

μ΄κ²ƒμœΌλ‘œ λͺ¨λ“  섀정이 λλ‚¬μŠ΅λ‹ˆλ‹€. 이 μ‹œμ μ—μ„œ ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰μ‹œν‚¨λ‹€λ©΄ λΉ¨κ°„ 색과 νŒŒλž€ μƒ‰μœΌλ‘œ μ΄μ–΄μ§€λŠ” κ·ΈλΌλ””μ–ΈνŠΈλ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

ImGui λ§€κ°œλ³€μˆ˜

μ§€κΈˆμ€ ν•˜λ“œμ½”λ”©λœ 색상을 μ „λ‹¬ν•˜κ³  μžˆμ§€λ§Œ, ImGui에 μž‘μ€ 창을 μΆ”κ°€ν•˜μ—¬ 색상을 λ°”κΏ”λ³Ό 수 도 μžˆμŠ΅λ‹ˆλ‹€.

κ·Έ κ°’μœΌλ‘œ μ‚¬μš©ν•  ComputePushConstant ꡬ쑰체 ν•˜λ‚˜μ™€ ν•¨κ»˜ 그릴 μ»΄ν“¨νŠΈ νŒŒμ΄ν”„λΌμΈμ˜ 배열을 λ‹΄μ•„μ•Ό ν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ λ‹€μ–‘ν•œ μ»΄ν“¨νŠΈ 셰이더λ₯Ό μ „ν™˜ν•  수 있게 λ©λ‹ˆλ‹€.

vk_engine.h에 ν•΄λ‹Ή ꡬ쑰체λ₯Ό μΆ”κ°€ν•©μ‹œλ‹€.

struct ComputeEffect {
    const char* name;

	VkPipeline pipeline;
	VkPipelineLayout layout;

	ComputePushConstants data;
};

이제 VulkanEngine ν΄λž˜μŠ€μ— μ…°μ΄λ”μ˜ 배열을 μΆ”κ°€ν•˜κ³ , λ Œλ”λ§ μ‹œ μ‚¬μš©ν•  인덱슀λ₯Ό λ‹΄λŠ” μ •μˆ˜λ„ ν•¨κ»˜ μΆ”κ°€ν•©λ‹ˆλ‹€.

std::vector<ComputeEffect> backgroundEffects;
int currentBackgroundEffect{0};

init_pipelines의 μ½”λ“œλ₯Ό μˆ˜μ •ν•΄ 2개의 μ΄νŽ™νŠΈλ₯Ό 생성해 λ΄…μ‹œλ‹€. ν•˜λ‚˜λŠ” κ·ΈλΌλ””μ–ΈνŠΈ, λ‹€λ₯Έ ν•˜λ‚˜λŠ” λ°€ν•˜λŠ˜ μ…°μ΄λ”μž…λ‹ˆλ‹€.

ν•˜λŠ˜ μ…°μ΄λ”λŠ” μ—¬κΈ°μ„œ μ„€λͺ…ν•˜κΈ°μ—λŠ” λ‹€μ†Œ λ³΅μž‘ν•˜λ―€λ‘œ, sky.comp의 μ½”λ“œλ₯Ό 확인해 λ³΄λŠ” 것도 μ’‹μŠ΅λ‹ˆλ‹€. shadertoyμ—μ„œ 가져와 μ—¬κΈ°μ„œ μ»΄ν“¨νŠΈ μ…°μ΄λ”λ‘œ μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ μ•½κ°„ λ°”κΎΈμ—ˆμŠ΅λ‹ˆλ‹€. ν‘Έμ‹œμƒμˆ˜μ˜ data1은 ν•˜λŠ˜ μƒ‰μƒμ˜ x/y/zλ₯Ό 담을 것이며, wλŠ” λ³„μ˜ 숫자λ₯Ό μ œμ–΄ν•  λ•Œ μ‚¬μš©ν•  κ²ƒμž…λ‹ˆλ‹€.

2개의 셰이더가 μžˆμœΌλ―€λ‘œ 2개의 VkShaderModule이 ν•„μš”ν•©λ‹ˆλ‹€.

VkShaderModule gradientShader;
if (!vkutil::load_shader_module("../../shaders/gradient_color.comp.spv", _device, &gradientShader)) {
	fmt::print("Error when building the compute shader \n");
}

VkShaderModule skyShader;
if (!vkutil::load_shader_module("../../shaders/sky.comp.spv", _device, &skyShader)) {
	fmt::print("Error when building the compute shader \n");
}

VkPipelineShaderStageCreateInfo stageinfo{};
stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stageinfo.pNext = nullptr;
stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
stageinfo.module = gradientShader;
stageinfo.pName = "main";

VkComputePipelineCreateInfo computePipelineCreateInfo{};
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
computePipelineCreateInfo.pNext = nullptr;
computePipelineCreateInfo.layout = _gradientPipelineLayout;
computePipelineCreateInfo.stage = stageinfo;

ComputeEffect gradient;
gradient.layout = _gradientPipelineLayout;
gradient.name = "gradient";
gradient.data = {};

//default colors
gradient.data.data1 = glm::vec4(1, 0, 0, 1);
gradient.data.data2 = glm::vec4(0, 0, 1, 1);

VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &gradient.pipeline));

//change the shader module only to create the sky shader
computePipelineCreateInfo.stage.module = skyShader;

ComputeEffect sky;
sky.layout = _gradientPipelineLayout;
sky.name = "sky";
sky.data = {};
//default sky parameters
sky.data.data1 = glm::vec4(0.1, 0.2, 0.4 ,0.97);

VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline));

//add the 2 background effects into the array
backgroundEffects.push_back(gradient);
backgroundEffects.push_back(sky);

//destroy structures properly
vkDestroyShaderModule(_device, gradientShader, nullptr);
vkDestroyShaderModule(_device, skyShader, nullptr);
_mainDeletionQueue.push_function([=]() {
	vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr);
	vkDestroyPipeline(_device, sky.pipeline, nullptr);
	vkDestroyPipeline(_device, gradient.pipeline, nullptr);
});

pipelines ν•¨μˆ˜λ₯Ό μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€. 기쑴의 νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒμ€ κ·ΈλŒ€λ‘œ μœ μ§€ν•˜λ©΄μ„œ, 이제 두 개의 μ„œλ‘œ λ‹€λ₯Έ νŒŒμ΄ν”„λΌμΈμ„ μƒμ„±ν•˜μ—¬ ComputeEffect 벑터에 μ €μž₯ν•©λ‹ˆλ‹€. λ˜ν•œ 각 μ΄νŽ™νŠΈμ— κΈ°λ³Έκ°’ 데이터λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.

이제 이 κΈ°λŠ₯을 μœ„ν•œ ImGui 디버그 창을 μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή μ½”λ“œλŠ” run()ν•¨μˆ˜ 내뢀에 λ“€μ–΄κ°€λ©° 기쑴의 데λͺ¨ μ΄νŽ™νŠΈ ν˜ΈμΆœμ„ μƒˆλ‘œμš΄ UI 둜직으둜 λŒ€μ²΄ν•  κ²ƒμž…λ‹ˆλ‹€.

		ImGui::NewFrame();
		
		if (ImGui::Begin("background")) {
			
			ComputeEffect& selected = backgroundEffects[currentBackgroundEffect];
		
			ImGui::Text("Selected effect: ", selected.name);
		
			ImGui::SliderInt("Effect Index", &currentBackgroundEffect,0, backgroundEffects.size() - 1);
		
			ImGui::InputFloat4("data1",(float*)& selected.data.data1);
			ImGui::InputFloat4("data2",(float*)& selected.data.data2);
			ImGui::InputFloat4("data3",(float*)& selected.data.data3);
			ImGui::InputFloat4("data4",(float*)& selected.data.data4);
		}
		ImGui::End();

		ImGui::Render();

λ¨Όμ € μ„ νƒλœ μ»΄ν“¨νŠΈ μ΄νŽ™νŠΈλ₯Ό λ°°μ—΄ 인덱싱을 톡해 κ°€μ Έμ˜΅λ‹ˆλ‹€. κ·Έ ν›„ ImGui::Textλ₯Ό μ‚¬μš©ν•΄ μ΄νŽ™νŠΈ 이름을 좜λ ₯ν•˜λ©°, νŽΈμ§‘ν•  μ •μˆ˜ μŠ¬λΌμ΄λ”μ™€ float4 μž…λ ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ ν•  일은 λ Œλ”λ§ 루프λ₯Ό μˆ˜μ •ν•΄ 셰이더λ₯Ό μ„ νƒν•˜μ—¬ 그에 λŒ€μ‘ν•˜λŠ” 데이터λ₯Ό μ‚¬μš©ν•˜λ„λ‘ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

	ComputeEffect& effect = backgroundEffects[currentBackgroundEffect];

	// bind the background compute pipeline
	vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, effect.pipeline);

	// bind the descriptor set containing the draw image for the compute pipeline
	vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipelineLayout, 0, 1, &_drawImageDescriptors, 0, nullptr);

	vkCmdPushConstants(cmd, _gradientPipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(ComputePushConstants), &effect.data);
	// execute the compute pipeline dispatch. We are using 16x16 workgroup size so we need to divide by it
	vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), std::ceil(_drawExtent.height / 16.0), 1);

크게 바뀐 것은 μ—†μŠ΅λ‹ˆλ‹€. μ»΄ν“¨νŠΈ μ΄νŽ™νŠΈ 배열에 μ—°κ²°ν•˜κ³  ν•΄λ‹Ή ν•­λͺ©μœΌλ‘œλΆ€ν„° ν‘Έμ‹œμƒμˆ˜λ₯Ό μ—…λ‘œλ“œν•΄μ£ΌκΈ°λ§Œ ν•˜λ©΄ λ©λ‹ˆλ‹€.

μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰ν•˜μ—¬ 디버그 창을 톡해 셰이더λ₯Ό μ„ νƒν•˜κ³  νŒŒλΌλ―Έν„°λ₯Ό μˆ˜μ •ν•΄λ³΄μ„Έμš”.

Next: Chapter 3: The graphics pipeline