앞 포스트와 이어집니다..
7. Ray Tracing 셰이더 코드 작성
[RTReflection.rgen]
이 셰이더는 레이 트레이싱 작업을 시작하는 셰이더입니다.
화면상의 각 픽셀에 대해 레이를 발사하고, 그 레이가 충돌하는 지점을 찾기 위한 초기 작업을 담당합니다.
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
layout(set = 0, binding = 0) uniform CameraBuffer {
mat4 view;
mat4 proj;
vec3 camPos;
} camera;
layout(set = 1, binding = 0, rgba16f) uniform image2D outputImage;
layout(set = 1, binding = 1) uniform accelerationStructureEXT topLevelAS;
layout(location = 0) rayPayloadEXT vec4 payload;
void main() {
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
const vec2 resolution = vec2(gl_LaunchSizeEXT.xy);
vec2 ndc = (pixelCenter / resolution) * 2.0 - 1.0;
vec4 rayOrigin = inverse(camera.view)[3];
vec4 rayTarget = inverse(camera.proj * camera.view) * vec4(ndc, 1.0, 1.0);
rayTarget /= rayTarget.w;
vec3 direction = normalize(rayTarget.xyz - rayOrigin.xyz);
payload = vec4(0.0);
traceRayEXT(topLevelAS,
gl_RayFlagsOpaqueEXT,
0xFF, // cull mask
0, 0, 0, // sbtRecordOffset, sbtRecordStride, missIndex
rayOrigin.xyz,
0.001, // min t
direction,
10000.0, // max t
0 // payload location
);
imageStore(outputImage, ivec2(gl_LaunchIDEXT.xy), payload);
}
특이한 점은 payload라는 개념인 거 같습니다.
payload는 레이의 추적 결과를 전달하고 저장하는 데 사용됩니다.
기본적으로 초기 값은 (0.0)으로 설정되며,
traceRayEXT 함수에서 레이 트레이싱을 실행한 후, 이 결과는 payload에 업데이트됩니다.
traceRayEXT 함수는 raygen, miss, hit 셰이더를 통해 레이 추적을 수행합니다.
이 함수는 TLAS에서 레이와 충돌하는 지점을 찾습니다.
최종적으로 payload 값은 imageStore를 통해 outputImage에 저장됩니다.
[RTReflection.rchit]
레이가 삼각형과 충돌했을 때 실행되는 셰이더입니다.
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec4 payload;
void main() {
payload = vec4(1.0, 0.0, 0.0, 1.0); // debug
}
지금은 디버깅용으로 빨간색을 payload에 담아 보냅니다.
[RTReflection.rmiss]
레이에 아무것도 히트하지 않았을 때 실행되는 셰이더 입니다.
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec4 payload;
void main() {
payload = vec4(0.0, 0.0, 0.0, 1.0); // debug
}
지금은 디버깅용으로 검정색을 payload에 담아 보냅니다.
8. 커맨드버퍼에 Ray Tracing 기록
Ray Tracing 파이프라인을 커맨드버퍼에 담는 과정은 간단합니다.
먼저 만든 파이프라인을 바인딩하고
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipeline->getPipeline());
디스크립터셋도 바인딩합니다.
VkDescriptorSet sets[] = {
m_globlaDescSets[currentFrame]->getDescriptorSet(), // set=0 (camera)
m_rtDescSets[currentFrame]->getDescriptorSet() // set=1 (outputImage + TLAS)
};
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR,
m_rtPipeline->getPipelineLayout(), 0, 2, sets, 0, nullptr);
그리고 vkCmdTraceRayKHR 함수를 실행합니다.
VkStridedDeviceAddressRegionKHR emptyRegion{};
g_vkCmdTraceRaysKHR(
cmd,
&m_rtPipeline->getRaygenRegion(),
&m_rtPipeline->getMissRegion(),
&m_rtPipeline->getHitRegion(),
&emptyRegion,
m_extent.width,
m_extent.height,
1);
raygenRegion, missRegion, hitRegion은 Shader Binding Table(SBT)에서 정의한 각 셰이더의 범위입니다.
emptyRegion은 교차 셰이더(intersection shader)를 사용할 때 필요한 파라미터입니다.
현재는 교차 셰이더를 사용하지 않기 때문에 빈 구조체를 전달해야 합니다.
없으면 에러납니다!
10. 확장 설정
Vulkan 확장 설정도 해야합니다.
Ray Tracing은 기본적으로 1.2 버전부터 사용 가능합니다.
vulkan 인스턴스 생성할 때 버전을 바꿔줍니다.
appInfo.apiVersion = VK_API_VERSION_1_2;
하지만 1.2 버전의 기본 기능으로 제공되는 것은 아닙니다.
따라서 추가확장을 명시해야합니다.
ray tracing 을 쓰기 위한 확장들을 간단하게 설명하겠습니다.
VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME
가속 구조를 생성하고 관리할 수 있게 해주는 확장입니다.
이 확장을 통해 BLAS와 TLAS를 생성하고 레이 트레이싱에 필요한 구조체들을 처리할 수 있습니다.
VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME
레이 트레이싱 파이프라인과 셰이더, SBT를 바인딩하여 레이 트레이싱 작업을 최적화할 수 있습니다.
이 확장 덕분에 traceRayEXT를 사용하여 레이 추적을 실행할 수 있습니다.
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME
GPU의 메모리 주소를 셰이더에서 직접 참조할 수 있게 해주며,
SBT에서 메모리 주소를 직접 사용하여 레이 트레이싱 셰이더를 최적화합니다.
VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME
GPU와 CPU 간의 작업 동기화를 효율적으로 관리하여
레이 트레이싱과 같은 고부하 작업에서 성능을 최적화할 수 있습니다.
VK_KHR_SPIRV_1_4_EXTENSION_NAME
최신 SPIR-V 버전인 1.4를 사용하여 레이 트레이싱 셰이더 성능을 최적화할 수 있습니다.
이 확장 덕분에 최신 SPIR-V 기능을 활용할 수 있습니다.
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME
부동소수점 연산을 셰이더 내에서 제어할 수 있어, 레이 트레이싱에서 정확한 계산을 필요로 할 때 유용합니다.
이로써 Vulkan에서 Ray Tracing을 사용할 수 있는 환경이 갖춰졌습니다.
정리하자면
0. Vulkan에서 레이트레이싱에 필요한 확장을 준비합니다.
1. 레이트레이싱에 필요한 가속구조(BLAS -> TLAS)를 준비합니다.
2. 결과를 저장할 이미지를 준비합니다.
3. 이 데이터를 셰이더에서 볼 수 있게 바인딩 합니다.
4. Pipeline과 Shader Binding Table을 준비합니다.
5. 레이를 쏘는 커맨드를 적재하면 됩니다.
하나하나 기능이 도입될 때의 흐름과
거기에 필요한 구조를 잘 알고 있어야
나중에 문제가 생기거나, 최적화 여지를 찾을 때 중요합니다.
현재 구현된 기능은 아주 기본적인 부분이지만
이를 바탕으로 Reflection과 Global Illumination(GI) 기능을 추가할 예정입니다.
'Computer Graphics > Project' 카테고리의 다른 글
RT reflection 첫 도입 (0) | 2025.04.14 |
---|---|
20250414 (0) | 2025.04.14 |
Vulkan에 Ray Tracing 도입 하기 (1) (0) | 2025.04.11 |
Vulkan에 Ray Tracing 도입 하기 (0) (0) | 2025.04.11 |
20250411 (0) | 2025.04.11 |