├── .gitignore ├── shaders ├── miss.rmiss.spv ├── raygen.rgen.spv ├── closesthit.rchit.spv ├── compile.bat ├── miss.rmiss ├── common.glsl ├── closesthit.rchit └── raygen.rgen ├── .clang-format ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── LICENSE ├── assets ├── CornellBox-Original.mtl └── CornellBox-Original.obj └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vscode 3 | -------------------------------------------------------------------------------- /shaders/miss.rmiss.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yknishidate/single-file-vulkan-pathtracing/HEAD/shaders/miss.rmiss.spv -------------------------------------------------------------------------------- /shaders/raygen.rgen.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yknishidate/single-file-vulkan-pathtracing/HEAD/shaders/raygen.rgen.spv -------------------------------------------------------------------------------- /shaders/closesthit.rchit.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yknishidate/single-file-vulkan-pathtracing/HEAD/shaders/closesthit.rchit.spv -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | IndentWidth: 4 3 | TabWidth: 4 4 | ColumnLimit: 150 5 | UseTab: Never 6 | AccessModifierOffset: -4 7 | SortIncludes: false 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/glfw"] 2 | path = external/glfw 3 | url = https://github.com/glfw/glfw.git 4 | [submodule "external/tinyobjloader"] 5 | path = external/tinyobjloader 6 | url = https://github.com/tinyobjloader/tinyobjloader.git 7 | -------------------------------------------------------------------------------- /shaders/compile.bat: -------------------------------------------------------------------------------- 1 | %VULKAN_SDK%/Bin/glslc.exe raygen.rgen -o raygen.rgen.spv --target-env=vulkan1.3 2 | %VULKAN_SDK%/Bin/glslc.exe closesthit.rchit -o closesthit.rchit.spv --target-env=vulkan1.3 3 | %VULKAN_SDK%/Bin/glslc.exe miss.rmiss -o miss.rmiss.spv --target-env=vulkan1.3 -------------------------------------------------------------------------------- /shaders/miss.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_GOOGLE_include_directive : enable 4 | #include "common.glsl" 5 | 6 | layout(location = 0) rayPayloadInEXT HitPayload payload; 7 | 8 | void main() 9 | { 10 | payload.emission = vec3(0.7, 0.6, 0.5); 11 | payload.done = true; 12 | } 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(single-file-vulkan-pathtracing VERSION 0.1.0) 3 | set(CMAKE_CXX_STANDARD 20) 4 | 5 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) 6 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) 7 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 8 | add_subdirectory(external/glfw) 9 | 10 | file(GLOB SHADERS shaders/*) 11 | add_executable(single-file-vulkan-pathtracing main.cpp ${SHADERS}) 12 | 13 | source_group("Shader Files" FILES ${SHADERS}) 14 | 15 | target_link_libraries(${PROJECT_NAME} PUBLIC glfw) 16 | target_include_directories(${PROJECT_NAME} PUBLIC 17 | "$ENV{VULKAN_SDK}/Include" 18 | "${PROJECT_SOURCE_DIR}/external/tinyobjloader" 19 | ) 20 | -------------------------------------------------------------------------------- /shaders/common.glsl: -------------------------------------------------------------------------------- 1 | 2 | struct HitPayload 3 | { 4 | vec3 position; 5 | vec3 normal; 6 | vec3 emission; 7 | vec3 brdf; 8 | bool done; 9 | }; 10 | 11 | const highp float M_PI = 3.14159265358979323846; 12 | 13 | uint pcg(inout uint state) 14 | { 15 | uint prev = state * 747796405u + 2891336453u; 16 | uint word = ((prev >> ((prev >> 28u) + 4u)) ^ prev) * 277803737u; 17 | state = prev; 18 | return (word >> 22u) ^ word; 19 | } 20 | 21 | uvec2 pcg2d(uvec2 v) 22 | { 23 | v = v * 1664525u + 1013904223u; 24 | v.x += v.y * 1664525u; 25 | v.y += v.x * 1664525u; 26 | v = v ^ (v >> 16u); 27 | v.x += v.y * 1664525u; 28 | v.y += v.x * 1664525u; 29 | v = v ^ (v >> 16u); 30 | return v; 31 | } 32 | 33 | float rand(inout uint seed) 34 | { 35 | uint val = pcg(seed); 36 | return (float(val) * (1.0 / float(0xffffffffu))); 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Single-File Vulkan Pathtracing 2 | 3 | ![image](https://user-images.githubusercontent.com/30839669/167279645-c56a70ac-8941-4a2b-ba1c-05a5d03c3d27.png) 4 | 5 | # Environment 6 | 7 | - C++20 8 | - Vulkan SDK 1.3.250.1 or later 9 | - GPU / Driver that support Vulkan Ray Tracing 10 | - NVIDIA Vulkan beta driver Windows 531.83, Linux 525.47.22 or later 11 | 12 | ## Setup 13 | 14 | ### Manual 15 | 16 | See [Vulkan Tutorial / Development environment](https://vulkan-tutorial.com/Development_environment) 17 | 18 | ### CMake 19 | 20 | ``` 21 | git clone --recursive https://github.com/yknishidate/single-file-vulkan-pathtracing 22 | cd single-file-vulkan-pathtracing 23 | cmake . -B build 24 | ``` 25 | 26 | # References 27 | 28 | - [NVIDIA Vulkan Ray Tracing Tutorial](https://nvpro-samples.github.io/vk_raytracing_tutorial_KHR/) 29 | - [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp) 30 | - [Vulkan Tutorial](https://vulkan-tutorial.com/) 31 | - [SaschaWillems/Vulkan](https://github.com/SaschaWillems/Vulkan) 32 | - [vk_raytrace](https://github.com/nvpro-samples/vk_raytrace) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yuki Nishidate 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/CornellBox-Original.mtl: -------------------------------------------------------------------------------- 1 | # The original Cornell Box in OBJ format. 2 | # Note that the real box is not a perfect cube, so 3 | # the faces are imperfect in this data set. 4 | # 5 | # Created by Guedis Cardenas and Morgan McGuire at Williams College, 2011 6 | # Released into the Public Domain. 7 | # 8 | # http://graphics.cs.williams.edu/data 9 | # http://www.graphics.cornell.edu/online/box/data.html 10 | # 11 | 12 | newmtl leftWall 13 | Ns 10.0000 14 | Ni 1.5000 15 | illum 2 16 | Ka 0.63 0.065 0.05 # Red 17 | Kd 0.63 0.065 0.05 18 | Ks 0 0 0 19 | Ke 0 0 0 20 | 21 | 22 | newmtl rightWall 23 | Ns 10.0000 24 | Ni 1.5000 25 | illum 2 26 | Ka 0.14 0.45 0.091 # Green 27 | Kd 0.14 0.45 0.091 28 | Ks 0 0 0 29 | Ke 0 0 0 30 | 31 | 32 | newmtl floor 33 | Ns 10.0000 34 | Ni 1.0000 35 | illum 2 36 | Ka 0.725 0.71 0.68 # White 37 | Kd 0.725 0.71 0.68 38 | Ks 0 0 0 39 | Ke 0 0 0 40 | 41 | 42 | newmtl ceiling 43 | Ns 10.0000 44 | Ni 1.0000 45 | illum 2 46 | Ka 0.725 0.71 0.68 # White 47 | Kd 0.725 0.71 0.68 48 | Ks 0 0 0 49 | Ke 0 0 0 50 | 51 | 52 | newmtl backWall 53 | Ns 10.0000 54 | Ni 1.0000 55 | illum 2 56 | Ka 0.725 0.71 0.68 # White 57 | Kd 0.725 0.71 0.68 58 | Ks 0 0 0 59 | Ke 0 0 0 60 | 61 | 62 | newmtl shortBox 63 | Ns 10.0000 64 | Ni 1.0000 65 | illum 2 66 | Ka 0.725 0.71 0.68 # White 67 | Kd 0.725 0.71 0.68 68 | Ks 0 0 0 69 | Ke 0 0 0 70 | 71 | 72 | newmtl tallBox 73 | Ns 10.0000 74 | Ni 1.0000 75 | illum 2 76 | Ka 0.725 0.71 0.68 # White 77 | Kd 0.725 0.71 0.68 78 | Ks 0 0 0 79 | Ke 0 0 0 80 | 81 | newmtl light 82 | Ns 10.0000 83 | Ni 1.0000 84 | illum 2 85 | Ka 0.78 0.78 0.78 # White 86 | Kd 0.78 0.78 0.78 87 | Ks 0 0 0 88 | Ke 17 12 4 89 | -------------------------------------------------------------------------------- /shaders/closesthit.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_GOOGLE_include_directive : enable 4 | #include "common.glsl" 5 | 6 | layout(binding = 2, set = 0) buffer Vertices{float vertices[];}; 7 | layout(binding = 3, set = 0) buffer Indices{uint indices[];}; 8 | layout(binding = 4, set = 0) buffer Faces{float faces[];}; 9 | 10 | layout(location = 0) rayPayloadInEXT HitPayload payload; 11 | hitAttributeEXT vec2 attribs; 12 | 13 | struct Vertex 14 | { 15 | vec3 position; 16 | }; 17 | 18 | struct Face 19 | { 20 | vec3 diffuse; 21 | vec3 emission; 22 | }; 23 | 24 | Vertex unpackVertex(uint index) 25 | { 26 | uint stride = 3; 27 | uint offset = index * stride; 28 | Vertex v; 29 | v.position = vec3(vertices[offset + 0], vertices[offset + 1], vertices[offset + 2]); 30 | return v; 31 | } 32 | 33 | Face unpackFace(uint index) 34 | { 35 | uint stride = 6; 36 | uint offset = index * stride; 37 | Face f; 38 | f.diffuse = vec3(faces[offset + 0], faces[offset + 1], faces[offset + 2]); 39 | f.emission = vec3(faces[offset + 3], faces[offset + 4], faces[offset + 5]); 40 | return f; 41 | } 42 | 43 | vec3 calcNormal(Vertex v0, Vertex v1, Vertex v2) 44 | { 45 | vec3 e01 = v1.position - v0.position; 46 | vec3 e02 = v2.position - v0.position; 47 | return -normalize(cross(e01, e02)); 48 | } 49 | 50 | void main() 51 | { 52 | const Vertex v0 = unpackVertex(indices[3 * gl_PrimitiveID + 0]); 53 | const Vertex v1 = unpackVertex(indices[3 * gl_PrimitiveID + 1]); 54 | const Vertex v2 = unpackVertex(indices[3 * gl_PrimitiveID + 2]); 55 | 56 | const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); 57 | const vec3 position = v0.position * barycentricCoords.x + v1.position * barycentricCoords.y + v2.position * barycentricCoords.z; 58 | const vec3 normal = calcNormal(v0, v1, v2); 59 | 60 | const Face face = unpackFace(gl_PrimitiveID); 61 | payload.brdf = face.diffuse / M_PI; 62 | payload.emission = face.emission; 63 | payload.position = position; 64 | payload.normal = normal; 65 | } 66 | -------------------------------------------------------------------------------- /shaders/raygen.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_GOOGLE_include_directive : enable 4 | #include "common.glsl" 5 | 6 | layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; 7 | layout(binding = 1, set = 0, rgba8) uniform image2D outputImage; 8 | layout(push_constant) uniform PushConstants { 9 | int frame; 10 | }; 11 | 12 | layout(location = 0) rayPayloadEXT HitPayload payload; 13 | 14 | void createCoordinateSystem(in vec3 N, out vec3 T, out vec3 B) 15 | { 16 | if(abs(N.x) > abs(N.y)) 17 | T = vec3(N.z, 0, -N.x) / sqrt(N.x * N.x + N.z * N.z); 18 | else 19 | T = vec3(0, -N.z, N.y) / sqrt(N.y * N.y + N.z * N.z); 20 | B = cross(N, T); 21 | } 22 | 23 | vec3 sampleHemisphere(float rand1, float rand2) 24 | { 25 | vec3 dir; 26 | dir.x = cos(2 * M_PI * rand2) * sqrt(1 - rand1 * rand1); 27 | dir.y = sin(2 * M_PI * rand2) * sqrt(1 - rand1 * rand1); 28 | dir.z = rand1; 29 | return dir; 30 | } 31 | 32 | vec3 sampleDirection(float rand1, float rand2, vec3 normal) 33 | { 34 | vec3 tangent; 35 | vec3 bitangent; 36 | createCoordinateSystem(normal, tangent, bitangent); 37 | vec3 dir = sampleHemisphere(rand1, rand2); 38 | return dir.x * tangent + dir.y * bitangent + dir.z * normal; 39 | } 40 | 41 | void main() 42 | { 43 | int maxSamples = 32; 44 | vec3 color = vec3(0.0); 45 | for(uint sampleNum = 0; sampleNum < maxSamples; sampleNum++){ 46 | // Calc seed 47 | uvec2 s = pcg2d(ivec2(gl_LaunchIDEXT.xy) * (sampleNum + maxSamples * frame + 1)); 48 | uint seed = s.x + s.y; 49 | 50 | // Calc ray 51 | const vec2 screenPos = vec2(gl_LaunchIDEXT.xy) + vec2(rand(seed), rand(seed)); 52 | const vec2 inUV = screenPos / vec2(gl_LaunchSizeEXT.xy); 53 | vec2 d = inUV * 2.0 - 1.0; 54 | 55 | vec4 origin = vec4(0, -1, 5, 1); 56 | vec4 target = vec4(d.x, d.y - 1, 2, 1) ; 57 | vec4 direction = vec4(normalize(target.xyz - origin.xyz), 0) ; 58 | 59 | vec3 weight = vec3(1.0); 60 | payload.done = false; 61 | 62 | for(uint depth = 0; depth < 8; depth++){ 63 | traceRayEXT( 64 | topLevelAS, 65 | gl_RayFlagsOpaqueEXT, 66 | 0xff, // cullMask 67 | 0, // sbtRecordOffset 68 | 0, // sbtRecordStride 69 | 0, // missIndex 70 | origin.xyz, 71 | 0.001, 72 | direction.xyz, 73 | 10000.0, 74 | 0 // payloadLocation 75 | ); 76 | color += weight * payload.emission; 77 | origin.xyz = payload.position; 78 | direction.xyz = sampleDirection(rand(seed), rand(seed), payload.normal); 79 | float pdf = 1.0 / (2.0 * M_PI); 80 | weight *= payload.brdf * dot(direction.xyz, payload.normal) / pdf; 81 | if(payload.done){ 82 | break; 83 | } 84 | } 85 | } 86 | color /= maxSamples; 87 | 88 | vec4 oldColor = imageLoad(outputImage, ivec2(gl_LaunchIDEXT.xy)); 89 | vec4 newColor = (vec4(color, 1.0) + (oldColor * frame)) / (frame + 1); 90 | imageStore(outputImage, ivec2(gl_LaunchIDEXT.xy), newColor); 91 | } 92 | -------------------------------------------------------------------------------- /assets/CornellBox-Original.obj: -------------------------------------------------------------------------------- 1 | # The original Cornell Box in OBJ format. 2 | # Note that the real box is not a perfect cube, so 3 | # the faces are imperfect in this data set. 4 | # 5 | # Created by Guedis Cardenas and Morgan McGuire at Williams College, 2011 6 | # Released into the Public Domain. 7 | # 8 | # http://graphics.cs.williams.edu/data 9 | # http://www.graphics.cornell.edu/online/box/data.html 10 | # 11 | 12 | mtllib CornellBox-Original.mtl 13 | 14 | ## Object floor 15 | v -1.01 0.00 0.99 16 | v 1.00 0.00 0.99 17 | v 1.00 0.00 -1.04 18 | v -0.99 0.00 -1.04 19 | 20 | g floor 21 | usemtl floor 22 | f -4 -3 -2 -1 23 | 24 | ## Object ceiling 25 | v -1.02 1.99 0.99 26 | v -1.02 1.99 -1.04 27 | v 1.00 1.99 -1.04 28 | v 1.00 1.99 0.99 29 | 30 | g ceiling 31 | usemtl ceiling 32 | f -4 -3 -2 -1 33 | 34 | ## Object backwall 35 | v -0.99 0.00 -1.04 36 | v 1.00 0.00 -1.04 37 | v 1.00 1.99 -1.04 38 | v -1.02 1.99 -1.04 39 | 40 | g backWall 41 | usemtl backWall 42 | f -4 -3 -2 -1 43 | 44 | ## Object rightwall 45 | v 1.00 0.00 -1.04 46 | v 1.00 0.00 0.99 47 | v 1.00 1.99 0.99 48 | v 1.00 1.99 -1.04 49 | 50 | g rightWall 51 | usemtl rightWall 52 | f -4 -3 -2 -1 53 | 54 | ## Object leftWall 55 | v -1.01 0.00 0.99 56 | v -0.99 0.00 -1.04 57 | v -1.02 1.99 -1.04 58 | v -1.02 1.99 0.99 59 | 60 | g leftWall 61 | usemtl leftWall 62 | f -4 -3 -2 -1 63 | 64 | ## Object shortBox 65 | usemtl shortBox 66 | 67 | # Top Face 68 | v 0.53 0.60 0.75 69 | v 0.70 0.60 0.17 70 | v 0.13 0.60 0.00 71 | v -0.05 0.60 0.57 72 | f -4 -3 -2 -1 73 | 74 | # Left Face 75 | v -0.05 0.00 0.57 76 | v -0.05 0.60 0.57 77 | v 0.13 0.60 0.00 78 | v 0.13 0.00 0.00 79 | f -4 -3 -2 -1 80 | 81 | # Front Face 82 | v 0.53 0.00 0.75 83 | v 0.53 0.60 0.75 84 | v -0.05 0.60 0.57 85 | v -0.05 0.00 0.57 86 | f -4 -3 -2 -1 87 | 88 | # Right Face 89 | v 0.70 0.00 0.17 90 | v 0.70 0.60 0.17 91 | v 0.53 0.60 0.75 92 | v 0.53 0.00 0.75 93 | f -4 -3 -2 -1 94 | 95 | # Back Face 96 | v 0.13 0.00 0.00 97 | v 0.13 0.60 0.00 98 | v 0.70 0.60 0.17 99 | v 0.70 0.00 0.17 100 | f -4 -3 -2 -1 101 | 102 | # Bottom Face 103 | v 0.53 0.00 0.75 104 | v 0.70 0.00 0.17 105 | v 0.13 0.00 0.00 106 | v -0.05 0.00 0.57 107 | f -12 -11 -10 -9 108 | 109 | g shortBox 110 | usemtl shortBox 111 | 112 | ## Object tallBox 113 | usemtl tallBox 114 | 115 | # Top Face 116 | v -0.53 1.20 0.09 117 | v 0.04 1.20 -0.09 118 | v -0.14 1.20 -0.67 119 | v -0.71 1.20 -0.49 120 | f -4 -3 -2 -1 121 | 122 | # Left Face 123 | v -0.53 0.00 0.09 124 | v -0.53 1.20 0.09 125 | v -0.71 1.20 -0.49 126 | v -0.71 0.00 -0.49 127 | f -4 -3 -2 -1 128 | 129 | # Back Face 130 | v -0.71 0.00 -0.49 131 | v -0.71 1.20 -0.49 132 | v -0.14 1.20 -0.67 133 | v -0.14 0.00 -0.67 134 | f -4 -3 -2 -1 135 | 136 | # Right Face 137 | v -0.14 0.00 -0.67 138 | v -0.14 1.20 -0.67 139 | v 0.04 1.20 -0.09 140 | v 0.04 0.00 -0.09 141 | f -4 -3 -2 -1 142 | 143 | # Front Face 144 | v 0.04 0.00 -0.09 145 | v 0.04 1.20 -0.09 146 | v -0.53 1.20 0.09 147 | v -0.53 0.00 0.09 148 | f -4 -3 -2 -1 149 | 150 | # Bottom Face 151 | v -0.53 0.00 0.09 152 | v 0.04 0.00 -0.09 153 | v -0.14 0.00 -0.67 154 | v -0.71 0.00 -0.49 155 | f -8 -7 -6 -5 156 | 157 | g tallBox 158 | usemtl tallBox 159 | 160 | ## Object light 161 | v -0.24 1.98 0.16 162 | v -0.24 1.98 -0.22 163 | v 0.23 1.98 -0.22 164 | v 0.23 1.98 0.16 165 | 166 | g light 167 | usemtl light 168 | f -4 -3 -2 -1 -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 8 | #include 9 | #include 10 | 11 | #define TINYOBJLOADER_IMPLEMENTATION 12 | #include 13 | 14 | VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE 15 | 16 | static constexpr int WIDTH = 1024; 17 | static constexpr int HEIGHT = 1024; 18 | 19 | struct Vertex { 20 | float position[3]; 21 | }; 22 | 23 | struct Face { 24 | float diffuse[3]; 25 | float emission[3]; 26 | }; 27 | 28 | void loadFromFile(std::vector& vertices, std::vector& indices, std::vector& faces) { 29 | tinyobj::attrib_t attrib; 30 | std::vector shapes; 31 | std::vector materials; 32 | std::string warn, err; 33 | 34 | if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, "../assets/CornellBox-Original.obj", "../assets")) { 35 | throw std::runtime_error(warn + err); 36 | } 37 | 38 | for (const auto& shape : shapes) { 39 | for (const auto& index : shape.mesh.indices) { 40 | Vertex vertex{}; 41 | vertex.position[0] = attrib.vertices[3 * index.vertex_index + 0]; 42 | vertex.position[1] = -attrib.vertices[3 * index.vertex_index + 1]; 43 | vertex.position[2] = attrib.vertices[3 * index.vertex_index + 2]; 44 | vertices.push_back(vertex); 45 | indices.push_back(static_cast(indices.size())); 46 | } 47 | for (const auto& matIndex : shape.mesh.material_ids) { 48 | Face face; 49 | face.diffuse[0] = materials[matIndex].diffuse[0]; 50 | face.diffuse[1] = materials[matIndex].diffuse[1]; 51 | face.diffuse[2] = materials[matIndex].diffuse[2]; 52 | face.emission[0] = materials[matIndex].emission[0]; 53 | face.emission[1] = materials[matIndex].emission[1]; 54 | face.emission[2] = materials[matIndex].emission[2]; 55 | faces.push_back(face); 56 | } 57 | } 58 | } 59 | 60 | std::vector readFile(const std::string& filename) { 61 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 62 | if (!file.is_open()) { 63 | throw std::runtime_error("failed to open file!"); 64 | } 65 | 66 | size_t fileSize = file.tellg(); 67 | std::vector buffer(fileSize); 68 | file.seekg(0); 69 | file.read(buffer.data(), fileSize); 70 | file.close(); 71 | return buffer; 72 | } 73 | 74 | struct Context { 75 | Context() { 76 | // Create window 77 | glfwInit(); 78 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 79 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 80 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Pathtracing", nullptr, nullptr); 81 | 82 | // Prepase extensions and layers 83 | uint32_t glfwExtensionCount = 0; 84 | const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 85 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 86 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 87 | 88 | std::vector layers{"VK_LAYER_KHRONOS_validation"}; 89 | 90 | auto vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); 91 | VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); 92 | 93 | // Create instance 94 | vk::ApplicationInfo appInfo; 95 | appInfo.setApiVersion(VK_API_VERSION_1_4); 96 | 97 | vk::InstanceCreateInfo instanceInfo; 98 | instanceInfo.setPApplicationInfo(&appInfo); 99 | instanceInfo.setPEnabledLayerNames(layers); 100 | instanceInfo.setPEnabledExtensionNames(extensions); 101 | instance = vk::createInstanceUnique(instanceInfo); 102 | VULKAN_HPP_DEFAULT_DISPATCHER.init(*instance); 103 | 104 | // Pick first gpu 105 | physicalDevice = instance->enumeratePhysicalDevices().front(); 106 | 107 | // Create debug messenger 108 | vk::DebugUtilsMessengerCreateInfoEXT messengerInfo; 109 | messengerInfo.setMessageSeverity(vk::DebugUtilsMessageSeverityFlagBitsEXT::eError); 110 | messengerInfo.setMessageType(vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation); 111 | messengerInfo.setPfnUserCallback(&debugUtilsMessengerCallback); 112 | messenger = instance->createDebugUtilsMessengerEXTUnique(messengerInfo); 113 | 114 | // Create surface 115 | VkSurfaceKHR _surface; 116 | VkResult res = glfwCreateWindowSurface(VkInstance(*instance), window, nullptr, &_surface); 117 | if (res != VK_SUCCESS) { 118 | throw std::runtime_error("failed to create window surface!"); 119 | } 120 | surface = vk::UniqueSurfaceKHR(vk::SurfaceKHR(_surface), {*instance}); 121 | 122 | // Find queue family 123 | std::vector queueFamilies = physicalDevice.getQueueFamilyProperties(); 124 | for (int i = 0; i < queueFamilies.size(); i++) { 125 | auto supportCompute = queueFamilies[i].queueFlags & vk::QueueFlagBits::eCompute; 126 | auto supportPresent = physicalDevice.getSurfaceSupportKHR(i, *surface); 127 | if (supportCompute && supportPresent) { 128 | queueFamilyIndex = i; 129 | } 130 | } 131 | 132 | // Create device 133 | const float queuePriority = 1.0f; 134 | vk::DeviceQueueCreateInfo queueCreateInfo; 135 | queueCreateInfo.setQueueFamilyIndex(queueFamilyIndex); 136 | queueCreateInfo.setQueuePriorities(queuePriority); 137 | 138 | const std::vector deviceExtensions{ 139 | VK_KHR_SWAPCHAIN_EXTENSION_NAME, 140 | VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 141 | VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 142 | VK_KHR_MAINTENANCE3_EXTENSION_NAME, 143 | VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME, 144 | VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, 145 | VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, 146 | VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, 147 | VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, 148 | }; 149 | 150 | if (!checkDeviceExtensionSupport(deviceExtensions)) { 151 | throw std::runtime_error("Some required extensions are not supported"); 152 | } 153 | 154 | vk::DeviceCreateInfo deviceInfo; 155 | deviceInfo.setQueueCreateInfos(queueCreateInfo); 156 | deviceInfo.setPEnabledExtensionNames(deviceExtensions); 157 | 158 | vk::PhysicalDeviceBufferDeviceAddressFeatures bufferDeviceAddressFeatures{true}; 159 | vk::PhysicalDeviceRayTracingPipelineFeaturesKHR rayTracingPipelineFeatures{true}; 160 | vk::PhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures{true}; 161 | vk::StructureChain createInfoChain{ 162 | deviceInfo, 163 | bufferDeviceAddressFeatures, 164 | rayTracingPipelineFeatures, 165 | accelerationStructureFeatures, 166 | }; 167 | 168 | device = physicalDevice.createDeviceUnique(createInfoChain.get()); 169 | VULKAN_HPP_DEFAULT_DISPATCHER.init(*device); 170 | 171 | queue = device->getQueue(queueFamilyIndex, 0); 172 | 173 | // Create command pool 174 | vk::CommandPoolCreateInfo commandPoolInfo; 175 | commandPoolInfo.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer); 176 | commandPoolInfo.setQueueFamilyIndex(queueFamilyIndex); 177 | commandPool = device->createCommandPoolUnique(commandPoolInfo); 178 | 179 | // Create descriptor pool 180 | std::vector poolSizes{ 181 | {vk::DescriptorType::eAccelerationStructureKHR, 1}, 182 | {vk::DescriptorType::eStorageImage, 1}, 183 | {vk::DescriptorType::eStorageBuffer, 3}, 184 | }; 185 | 186 | vk::DescriptorPoolCreateInfo descPoolInfo; 187 | descPoolInfo.setPoolSizes(poolSizes); 188 | descPoolInfo.setMaxSets(1); 189 | descPoolInfo.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet); 190 | descPool = device->createDescriptorPoolUnique(descPoolInfo); 191 | } 192 | 193 | bool checkDeviceExtensionSupport(const std::vector& requiredExtensions) const { 194 | std::vector availableExtensions = physicalDevice.enumerateDeviceExtensionProperties(); 195 | std::vector requiredExtensionNames(requiredExtensions.begin(), requiredExtensions.end()); 196 | 197 | for (const auto& extension : availableExtensions) { 198 | requiredExtensionNames.erase(std::remove(requiredExtensionNames.begin(), requiredExtensionNames.end(), extension.extensionName), 199 | requiredExtensionNames.end()); 200 | } 201 | 202 | if (requiredExtensionNames.empty()) { 203 | std::cout << "All required extensions are supported by the device." << std::endl; 204 | return true; 205 | } else { 206 | std::cout << "The following required extensions are not supported by the device:" << std::endl; 207 | for (const auto& name : requiredExtensionNames) { 208 | std::cout << "\t" << name << std::endl; 209 | } 210 | return false; 211 | } 212 | } 213 | 214 | uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) const { 215 | vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties(); 216 | for (uint32_t i = 0; i != memProperties.memoryTypeCount; ++i) { 217 | if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { 218 | return i; 219 | } 220 | } 221 | throw std::runtime_error("failed to find suitable memory type"); 222 | } 223 | 224 | void oneTimeSubmit(const std::function& func) const { 225 | vk::CommandBufferAllocateInfo commandBufferInfo; 226 | commandBufferInfo.setCommandPool(*commandPool); 227 | commandBufferInfo.setCommandBufferCount(1); 228 | 229 | vk::UniqueCommandBuffer commandBuffer = std::move(device->allocateCommandBuffersUnique(commandBufferInfo).front()); 230 | commandBuffer->begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit}); 231 | func(*commandBuffer); 232 | commandBuffer->end(); 233 | 234 | vk::SubmitInfo submitInfo; 235 | submitInfo.setCommandBuffers(*commandBuffer); 236 | queue.submit(submitInfo); 237 | queue.waitIdle(); 238 | } 239 | 240 | vk::UniqueDescriptorSet allocateDescSet(vk::DescriptorSetLayout descSetLayout) { 241 | vk::DescriptorSetAllocateInfo descSetInfo; 242 | descSetInfo.setDescriptorPool(*descPool); 243 | descSetInfo.setSetLayouts(descSetLayout); 244 | return std::move(device->allocateDescriptorSetsUnique(descSetInfo).front()); 245 | } 246 | 247 | static VKAPI_ATTR vk::Bool32 VKAPI_CALL 248 | debugUtilsMessengerCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 249 | vk::DebugUtilsMessageTypeFlagsEXT messageTypes, 250 | vk::DebugUtilsMessengerCallbackDataEXT const* pCallbackData, 251 | void* pUserData) { 252 | std::cerr << pCallbackData->pMessage << std::endl; 253 | return VK_FALSE; 254 | } 255 | 256 | GLFWwindow* window; 257 | vk::detail::DynamicLoader dl; 258 | vk::UniqueInstance instance; 259 | vk::UniqueDebugUtilsMessengerEXT messenger; 260 | vk::UniqueSurfaceKHR surface; 261 | vk::UniqueDevice device; 262 | vk::PhysicalDevice physicalDevice; 263 | uint32_t queueFamilyIndex; 264 | vk::Queue queue; 265 | vk::UniqueCommandPool commandPool; 266 | vk::UniqueDescriptorPool descPool; 267 | }; 268 | 269 | struct Buffer { 270 | enum class Type { 271 | Scratch, 272 | AccelInput, 273 | AccelStorage, 274 | ShaderBindingTable, 275 | }; 276 | 277 | Buffer() = default; 278 | Buffer(const Context& context, Type type, vk::DeviceSize size, const void* data = nullptr) { 279 | vk::BufferUsageFlags usage; 280 | vk::MemoryPropertyFlags memoryProps; 281 | using Usage = vk::BufferUsageFlagBits; 282 | using Memory = vk::MemoryPropertyFlagBits; 283 | if (type == Type::AccelInput) { 284 | usage = Usage::eAccelerationStructureBuildInputReadOnlyKHR | Usage::eStorageBuffer | Usage::eShaderDeviceAddress; 285 | memoryProps = Memory::eHostVisible | Memory::eHostCoherent; 286 | } else if (type == Type::Scratch) { 287 | usage = Usage::eStorageBuffer | Usage::eShaderDeviceAddress; 288 | memoryProps = Memory::eDeviceLocal; 289 | } else if (type == Type::AccelStorage) { 290 | usage = Usage::eAccelerationStructureStorageKHR | Usage::eShaderDeviceAddress; 291 | memoryProps = Memory::eDeviceLocal; 292 | } else if (type == Type::ShaderBindingTable) { 293 | usage = Usage::eShaderBindingTableKHR | Usage::eShaderDeviceAddress; 294 | memoryProps = Memory::eHostVisible | Memory::eHostCoherent; 295 | } 296 | 297 | buffer = context.device->createBufferUnique({{}, size, usage}); 298 | 299 | // Allocate memory 300 | vk::MemoryRequirements requirements = context.device->getBufferMemoryRequirements(*buffer); 301 | uint32_t memoryTypeIndex = context.findMemoryType(requirements.memoryTypeBits, memoryProps); 302 | 303 | vk::MemoryAllocateFlagsInfo flagsInfo{vk::MemoryAllocateFlagBits::eDeviceAddress}; 304 | 305 | vk::MemoryAllocateInfo memoryInfo; 306 | memoryInfo.setAllocationSize(requirements.size); 307 | memoryInfo.setMemoryTypeIndex(memoryTypeIndex); 308 | memoryInfo.setPNext(&flagsInfo); 309 | memory = context.device->allocateMemoryUnique(memoryInfo); 310 | 311 | context.device->bindBufferMemory(*buffer, *memory, 0); 312 | 313 | // Get device address 314 | vk::BufferDeviceAddressInfoKHR bufferDeviceAI{*buffer}; 315 | deviceAddress = context.device->getBufferAddressKHR(&bufferDeviceAI); 316 | 317 | descBufferInfo.setBuffer(*buffer); 318 | descBufferInfo.setOffset(0); 319 | descBufferInfo.setRange(size); 320 | 321 | if (data) { 322 | void* mapped = context.device->mapMemory(*memory, 0, size); 323 | memcpy(mapped, data, size); 324 | context.device->unmapMemory(*memory); 325 | } 326 | } 327 | 328 | vk::UniqueBuffer buffer; 329 | vk::UniqueDeviceMemory memory; 330 | vk::DescriptorBufferInfo descBufferInfo; 331 | uint64_t deviceAddress = 0; 332 | }; 333 | 334 | struct Image { 335 | Image() = default; 336 | Image(const Context& context, vk::Extent2D extent, vk::Format format, vk::ImageUsageFlags usage) { 337 | // Create image 338 | vk::ImageCreateInfo imageInfo; 339 | imageInfo.setImageType(vk::ImageType::e2D); 340 | imageInfo.setExtent({extent.width, extent.height, 1}); 341 | imageInfo.setMipLevels(1); 342 | imageInfo.setArrayLayers(1); 343 | imageInfo.setFormat(format); 344 | imageInfo.setUsage(usage); 345 | image = context.device->createImageUnique(imageInfo); 346 | 347 | // Allocate memory 348 | vk::MemoryRequirements requirements = context.device->getImageMemoryRequirements(*image); 349 | uint32_t memoryTypeIndex = context.findMemoryType(requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal); 350 | vk::MemoryAllocateInfo memoryInfo; 351 | memoryInfo.setAllocationSize(requirements.size); 352 | memoryInfo.setMemoryTypeIndex(memoryTypeIndex); 353 | memory = context.device->allocateMemoryUnique(memoryInfo); 354 | 355 | // Bind memory and image 356 | context.device->bindImageMemory(*image, *memory, 0); 357 | 358 | // Create image view 359 | vk::ImageViewCreateInfo imageViewInfo; 360 | imageViewInfo.setImage(*image); 361 | imageViewInfo.setViewType(vk::ImageViewType::e2D); 362 | imageViewInfo.setFormat(format); 363 | imageViewInfo.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); 364 | view = context.device->createImageViewUnique(imageViewInfo); 365 | 366 | // Set image info 367 | descImageInfo.setImageView(*view); 368 | descImageInfo.setImageLayout(vk::ImageLayout::eGeneral); 369 | context.oneTimeSubmit([&](vk::CommandBuffer commandBuffer) { // 370 | setImageLayout(commandBuffer, *image, vk::ImageLayout::eUndefined, vk::ImageLayout::eGeneral); 371 | }); 372 | } 373 | 374 | static vk::AccessFlags toAccessFlags(vk::ImageLayout layout) { 375 | switch (layout) { 376 | case vk::ImageLayout::eTransferSrcOptimal: 377 | return vk::AccessFlagBits::eTransferRead; 378 | case vk::ImageLayout::eTransferDstOptimal: 379 | return vk::AccessFlagBits::eTransferWrite; 380 | default: 381 | return {}; 382 | } 383 | } 384 | 385 | static void setImageLayout(vk::CommandBuffer commandBuffer, vk::Image image, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { 386 | vk::ImageMemoryBarrier barrier; 387 | barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); 388 | barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); 389 | barrier.setImage(image); 390 | barrier.setOldLayout(oldLayout); 391 | barrier.setNewLayout(newLayout); 392 | barrier.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); 393 | barrier.setSrcAccessMask(toAccessFlags(oldLayout)); 394 | barrier.setDstAccessMask(toAccessFlags(newLayout)); 395 | commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, // 396 | vk::PipelineStageFlagBits::eAllCommands, // 397 | {}, {}, {}, barrier); 398 | } 399 | 400 | static void copyImage(vk::CommandBuffer commandBuffer, vk::Image srcImage, vk::Image dstImage) { 401 | vk::ImageCopy copyRegion; 402 | copyRegion.setSrcSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}); 403 | copyRegion.setDstSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}); 404 | copyRegion.setExtent({WIDTH, HEIGHT, 1}); 405 | commandBuffer.copyImage(srcImage, vk::ImageLayout::eTransferSrcOptimal, dstImage, vk::ImageLayout::eTransferDstOptimal, copyRegion); 406 | } 407 | 408 | vk::UniqueImage image; 409 | vk::UniqueImageView view; 410 | vk::UniqueDeviceMemory memory; 411 | vk::DescriptorImageInfo descImageInfo; 412 | }; 413 | 414 | struct Accel { 415 | Accel() = default; 416 | Accel(const Context& context, vk::AccelerationStructureGeometryKHR geometry, uint32_t primitiveCount, vk::AccelerationStructureTypeKHR type) { 417 | vk::AccelerationStructureBuildGeometryInfoKHR buildGeometryInfo; 418 | buildGeometryInfo.setType(type); 419 | buildGeometryInfo.setFlags(vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace); 420 | buildGeometryInfo.setGeometries(geometry); 421 | 422 | // Create buffer 423 | vk::AccelerationStructureBuildSizesInfoKHR buildSizesInfo = context.device->getAccelerationStructureBuildSizesKHR( // 424 | vk::AccelerationStructureBuildTypeKHR::eDevice, buildGeometryInfo, primitiveCount); 425 | vk::DeviceSize size = buildSizesInfo.accelerationStructureSize; 426 | buffer = Buffer{context, Buffer::Type::AccelStorage, size}; 427 | 428 | // Create accel 429 | vk::AccelerationStructureCreateInfoKHR accelInfo; 430 | accelInfo.setBuffer(*buffer.buffer); 431 | accelInfo.setSize(size); 432 | accelInfo.setType(type); 433 | accel = context.device->createAccelerationStructureKHRUnique(accelInfo); 434 | 435 | // Build 436 | Buffer scratchBuffer{context, Buffer::Type::Scratch, buildSizesInfo.buildScratchSize}; 437 | buildGeometryInfo.setScratchData(scratchBuffer.deviceAddress); 438 | buildGeometryInfo.setDstAccelerationStructure(*accel); 439 | 440 | context.oneTimeSubmit([&](vk::CommandBuffer commandBuffer) { // 441 | vk::AccelerationStructureBuildRangeInfoKHR buildRangeInfo; 442 | buildRangeInfo.setPrimitiveCount(primitiveCount); 443 | buildRangeInfo.setFirstVertex(0); 444 | buildRangeInfo.setPrimitiveOffset(0); 445 | buildRangeInfo.setTransformOffset(0); 446 | commandBuffer.buildAccelerationStructuresKHR(buildGeometryInfo, &buildRangeInfo); 447 | }); 448 | 449 | descAccelInfo.setAccelerationStructures(*accel); 450 | } 451 | 452 | Buffer buffer; 453 | vk::UniqueAccelerationStructureKHR accel; 454 | vk::WriteDescriptorSetAccelerationStructureKHR descAccelInfo; 455 | }; 456 | 457 | int main() { 458 | Context context; 459 | 460 | vk::SwapchainCreateInfoKHR swapchainInfo; 461 | swapchainInfo.setSurface(*context.surface); 462 | swapchainInfo.setMinImageCount(3); 463 | swapchainInfo.setImageFormat(vk::Format::eB8G8R8A8Unorm); 464 | swapchainInfo.setImageColorSpace(vk::ColorSpaceKHR::eSrgbNonlinear); 465 | swapchainInfo.setImageExtent({WIDTH, HEIGHT}); 466 | swapchainInfo.setImageArrayLayers(1); 467 | swapchainInfo.setImageUsage(vk::ImageUsageFlagBits::eTransferDst); 468 | swapchainInfo.setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity); 469 | swapchainInfo.setPresentMode(vk::PresentModeKHR::eFifo); 470 | swapchainInfo.setClipped(true); 471 | swapchainInfo.setQueueFamilyIndices(context.queueFamilyIndex); 472 | vk::UniqueSwapchainKHR swapchain = context.device->createSwapchainKHRUnique(swapchainInfo); 473 | 474 | std::vector swapchainImages = context.device->getSwapchainImagesKHR(*swapchain); 475 | 476 | vk::CommandBufferAllocateInfo commandBufferInfo; 477 | commandBufferInfo.setCommandPool(*context.commandPool); 478 | commandBufferInfo.setCommandBufferCount(static_cast(swapchainImages.size())); 479 | std::vector commandBuffers = context.device->allocateCommandBuffersUnique(commandBufferInfo); 480 | 481 | Image outputImage{context, 482 | {WIDTH, HEIGHT}, 483 | vk::Format::eB8G8R8A8Unorm, 484 | vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst}; 485 | 486 | // Load mesh 487 | std::vector vertices; 488 | std::vector indices; 489 | std::vector faces; 490 | loadFromFile(vertices, indices, faces); 491 | 492 | Buffer vertexBuffer{context, Buffer::Type::AccelInput, sizeof(Vertex) * vertices.size(), vertices.data()}; 493 | Buffer indexBuffer{context, Buffer::Type::AccelInput, sizeof(uint32_t) * indices.size(), indices.data()}; 494 | Buffer faceBuffer{context, Buffer::Type::AccelInput, sizeof(Face) * faces.size(), faces.data()}; 495 | 496 | // Create bottom level accel struct 497 | vk::AccelerationStructureGeometryTrianglesDataKHR triangleData; 498 | triangleData.setVertexFormat(vk::Format::eR32G32B32Sfloat); 499 | triangleData.setVertexData(vertexBuffer.deviceAddress); 500 | triangleData.setVertexStride(sizeof(Vertex)); 501 | triangleData.setMaxVertex(static_cast(vertices.size())); 502 | triangleData.setIndexType(vk::IndexType::eUint32); 503 | triangleData.setIndexData(indexBuffer.deviceAddress); 504 | 505 | vk::AccelerationStructureGeometryKHR triangleGeometry; 506 | triangleGeometry.setGeometryType(vk::GeometryTypeKHR::eTriangles); 507 | triangleGeometry.setGeometry({triangleData}); 508 | triangleGeometry.setFlags(vk::GeometryFlagBitsKHR::eOpaque); 509 | 510 | const auto primitiveCount = static_cast(indices.size() / 3); 511 | 512 | Accel bottomAccel{context, triangleGeometry, primitiveCount, vk::AccelerationStructureTypeKHR::eBottomLevel}; 513 | 514 | // Create top level accel struct 515 | vk::TransformMatrixKHR transformMatrix = std::array{ 516 | std::array{1.0f, 0.0f, 0.0f, 0.0f}, 517 | std::array{0.0f, 1.0f, 0.0f, 0.0f}, 518 | std::array{0.0f, 0.0f, 1.0f, 0.0f}, 519 | }; 520 | 521 | vk::AccelerationStructureInstanceKHR accelInstance; 522 | accelInstance.setTransform(transformMatrix); 523 | accelInstance.setMask(0xFF); 524 | accelInstance.setAccelerationStructureReference(bottomAccel.buffer.deviceAddress); 525 | accelInstance.setFlags(vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable); 526 | 527 | Buffer instancesBuffer{context, Buffer::Type::AccelInput, sizeof(vk::AccelerationStructureInstanceKHR), &accelInstance}; 528 | 529 | vk::AccelerationStructureGeometryInstancesDataKHR instancesData; 530 | instancesData.setArrayOfPointers(false); 531 | instancesData.setData(instancesBuffer.deviceAddress); 532 | 533 | vk::AccelerationStructureGeometryKHR instanceGeometry; 534 | instanceGeometry.setGeometryType(vk::GeometryTypeKHR::eInstances); 535 | instanceGeometry.setGeometry({instancesData}); 536 | instanceGeometry.setFlags(vk::GeometryFlagBitsKHR::eOpaque); 537 | 538 | Accel topAccel{context, instanceGeometry, 1, vk::AccelerationStructureTypeKHR::eTopLevel}; 539 | 540 | // Load shaders 541 | const std::vector raygenCode = readFile("../shaders/raygen.rgen.spv"); 542 | const std::vector missCode = readFile("../shaders/miss.rmiss.spv"); 543 | const std::vector chitCode = readFile("../shaders/closesthit.rchit.spv"); 544 | 545 | std::vector shaderModules(3); 546 | shaderModules[0] = context.device->createShaderModuleUnique({{}, raygenCode.size(), reinterpret_cast(raygenCode.data())}); 547 | shaderModules[1] = context.device->createShaderModuleUnique({{}, missCode.size(), reinterpret_cast(missCode.data())}); 548 | shaderModules[2] = context.device->createShaderModuleUnique({{}, chitCode.size(), reinterpret_cast(chitCode.data())}); 549 | 550 | std::vector shaderStages(3); 551 | shaderStages[0] = {{}, vk::ShaderStageFlagBits::eRaygenKHR, *shaderModules[0], "main"}; 552 | shaderStages[1] = {{}, vk::ShaderStageFlagBits::eMissKHR, *shaderModules[1], "main"}; 553 | shaderStages[2] = {{}, vk::ShaderStageFlagBits::eClosestHitKHR, *shaderModules[2], "main"}; 554 | 555 | std::vector shaderGroups(3); 556 | shaderGroups[0] = {vk::RayTracingShaderGroupTypeKHR::eGeneral, 0, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR}; 557 | shaderGroups[1] = {vk::RayTracingShaderGroupTypeKHR::eGeneral, 1, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR}; 558 | shaderGroups[2] = {vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup, VK_SHADER_UNUSED_KHR, 2, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR}; 559 | 560 | // create ray tracing pipeline 561 | std::vector bindings{ 562 | {0, vk::DescriptorType::eAccelerationStructureKHR, 1, vk::ShaderStageFlagBits::eRaygenKHR}, // Binding = 0 : TLAS 563 | {1, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eRaygenKHR}, // Binding = 1 : Storage image 564 | {2, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eClosestHitKHR}, // Binding = 2 : Vertices 565 | {3, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eClosestHitKHR}, // Binding = 3 : Indices 566 | {4, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eClosestHitKHR}, // Binding = 4 : Faces 567 | }; 568 | 569 | // Create desc set layout 570 | vk::DescriptorSetLayoutCreateInfo descSetLayoutInfo; 571 | descSetLayoutInfo.setBindings(bindings); 572 | vk::UniqueDescriptorSetLayout descSetLayout = context.device->createDescriptorSetLayoutUnique(descSetLayoutInfo); 573 | 574 | // Create pipeline layout 575 | vk::PushConstantRange pushRange; 576 | pushRange.setOffset(0); 577 | pushRange.setSize(sizeof(int)); 578 | pushRange.setStageFlags(vk::ShaderStageFlagBits::eRaygenKHR); 579 | 580 | vk::PipelineLayoutCreateInfo pipelineLayoutInfo; 581 | pipelineLayoutInfo.setSetLayouts(*descSetLayout); 582 | pipelineLayoutInfo.setPushConstantRanges(pushRange); 583 | vk::UniquePipelineLayout pipelineLayout = context.device->createPipelineLayoutUnique(pipelineLayoutInfo); 584 | 585 | // Create pipeline 586 | vk::RayTracingPipelineCreateInfoKHR rtPipelineInfo; 587 | rtPipelineInfo.setStages(shaderStages); 588 | rtPipelineInfo.setGroups(shaderGroups); 589 | rtPipelineInfo.setMaxPipelineRayRecursionDepth(4); 590 | rtPipelineInfo.setLayout(*pipelineLayout); 591 | 592 | auto result = context.device->createRayTracingPipelineKHRUnique(nullptr, nullptr, rtPipelineInfo); 593 | if (result.result != vk::Result::eSuccess) { 594 | throw std::runtime_error("failed to create ray tracing pipeline."); 595 | } 596 | 597 | vk::UniquePipeline pipeline = std::move(result.value); 598 | 599 | // Get ray tracing properties 600 | auto properties = context.physicalDevice.getProperties2(); 601 | auto rtProperties = properties.get(); 602 | 603 | // Calculate shader binding table (SBT) size 604 | uint32_t handleSize = rtProperties.shaderGroupHandleSize; 605 | uint32_t handleSizeAligned = rtProperties.shaderGroupHandleAlignment; 606 | uint32_t groupCount = static_cast(shaderGroups.size()); 607 | uint32_t sbtSize = groupCount * handleSizeAligned; 608 | 609 | // Get shader group handles 610 | std::vector handleStorage(sbtSize); 611 | if (context.device->getRayTracingShaderGroupHandlesKHR(*pipeline, 0, groupCount, sbtSize, handleStorage.data()) != vk::Result::eSuccess) { 612 | throw std::runtime_error("failed to get ray tracing shader group handles."); 613 | } 614 | 615 | // Create SBT 616 | Buffer raygenSBT{context, Buffer::Type::ShaderBindingTable, handleSize, handleStorage.data() + 0 * handleSizeAligned}; 617 | Buffer missSBT{context, Buffer::Type::ShaderBindingTable, handleSize, handleStorage.data() + 1 * handleSizeAligned}; 618 | Buffer hitSBT{context, Buffer::Type::ShaderBindingTable, handleSize, handleStorage.data() + 2 * handleSizeAligned}; 619 | 620 | uint32_t stride = rtProperties.shaderGroupHandleAlignment; 621 | uint32_t size = rtProperties.shaderGroupHandleAlignment; 622 | 623 | vk::StridedDeviceAddressRegionKHR raygenRegion{raygenSBT.deviceAddress, stride, size}; 624 | vk::StridedDeviceAddressRegionKHR missRegion{missSBT.deviceAddress, stride, size}; 625 | vk::StridedDeviceAddressRegionKHR hitRegion{hitSBT.deviceAddress, stride, size}; 626 | 627 | // Create desc set 628 | vk::UniqueDescriptorSet descSet = context.allocateDescSet(*descSetLayout); 629 | std::vector writes(bindings.size()); 630 | for (int i = 0; i < bindings.size(); i++) { 631 | writes[i].setDstSet(*descSet); 632 | writes[i].setDescriptorType(bindings[i].descriptorType); 633 | writes[i].setDescriptorCount(bindings[i].descriptorCount); 634 | writes[i].setDstBinding(bindings[i].binding); 635 | } 636 | writes[0].setPNext(&topAccel.descAccelInfo); 637 | writes[1].setImageInfo(outputImage.descImageInfo); 638 | writes[2].setBufferInfo(vertexBuffer.descBufferInfo); 639 | writes[3].setBufferInfo(indexBuffer.descBufferInfo); 640 | writes[4].setBufferInfo(faceBuffer.descBufferInfo); 641 | context.device->updateDescriptorSets(writes, nullptr); 642 | 643 | // Main loop 644 | uint32_t imageIndex = 0; 645 | int frame = 0; 646 | vk::UniqueSemaphore imageAcquiredSemaphore = context.device->createSemaphoreUnique(vk::SemaphoreCreateInfo()); 647 | while (!glfwWindowShouldClose(context.window)) { 648 | glfwPollEvents(); 649 | 650 | // Acquire next image 651 | imageIndex = context.device->acquireNextImageKHR(*swapchain, UINT64_MAX, *imageAcquiredSemaphore).value; 652 | 653 | // Record commands 654 | vk::CommandBuffer commandBuffer = *commandBuffers[imageIndex]; 655 | commandBuffer.begin(vk::CommandBufferBeginInfo()); 656 | commandBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, *pipeline); 657 | commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingKHR, *pipelineLayout, 0, *descSet, nullptr); 658 | commandBuffer.pushConstants(*pipelineLayout, vk::ShaderStageFlagBits::eRaygenKHR, 0, sizeof(int), &frame); 659 | commandBuffer.traceRaysKHR(raygenRegion, missRegion, hitRegion, {}, WIDTH, HEIGHT, 1); 660 | 661 | vk::Image srcImage = *outputImage.image; 662 | vk::Image dstImage = swapchainImages[imageIndex]; 663 | Image::setImageLayout(commandBuffer, srcImage, vk::ImageLayout::eGeneral, vk::ImageLayout::eTransferSrcOptimal); 664 | Image::setImageLayout(commandBuffer, dstImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); 665 | Image::copyImage(commandBuffer, srcImage, dstImage); 666 | Image::setImageLayout(commandBuffer, srcImage, vk::ImageLayout::eTransferSrcOptimal, vk::ImageLayout::eGeneral); 667 | Image::setImageLayout(commandBuffer, dstImage, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::ePresentSrcKHR); 668 | 669 | commandBuffer.end(); 670 | 671 | // Submit 672 | context.queue.submit(vk::SubmitInfo().setCommandBuffers(commandBuffer)); 673 | 674 | // Present image 675 | vk::PresentInfoKHR presentInfo; 676 | presentInfo.setSwapchains(*swapchain); 677 | presentInfo.setImageIndices(imageIndex); 678 | presentInfo.setWaitSemaphores(*imageAcquiredSemaphore); 679 | auto result = context.queue.presentKHR(presentInfo); 680 | if (result != vk::Result::eSuccess) { 681 | throw std::runtime_error("failed to present."); 682 | } 683 | context.queue.waitIdle(); 684 | frame++; 685 | } 686 | 687 | context.device->waitIdle(); 688 | glfwDestroyWindow(context.window); 689 | glfwTerminate(); 690 | } 691 | --------------------------------------------------------------------------------