Heuristic42
Blog
Opengl
Meta
Rendering
4
created
Jul 7 at 11:21
Hello Ray, a Hello World Vulkan Ray Tracing Tutorial
\*WORK IN PROGRESS\* Want to do some quick 3D graphics progr…
–
pknowles
created
Jul 4 at 6:54
Why std::move and move semantics?
A few years ago, I was fortunate enough to have Jon Kalb explai…
–
pknowles
edited
Jul 4 at 5:15
3D Rendering (Computer Graphics)
Rendering in 3D computer graphics is computing a 2D image from …
–
pknowles
edited
Jul 4 at 4:16
Heuristic42, A Graphics Programming Website
#[Blog](/blog/) An unstructured assortment of personal graph…
–
pknowles
edited
Jun 24 at 15:10
RAII++ - the powerful implication of always initializing
If you search, most definitions of RAII refer to using "**scope…
–
pknowles
created
Jun 24 at 15:01
RAII++ - the powerful implication of always initializing
If you search, most definitions of RAII refer to using "**scope…
–
pknowles
comment
Jun 15 at 11:42
Matrices
[deleted]
–
anonymous
comment
Jun 1 at 11:01
Matrices
[deleted]
–
anonymous
comment
May 18 at 22:30
Matrices
[deleted]
–
anonymous
created
Apr 10 at 12:38
test2
;</script>
–
Nia
comment
Feb 8 at 6:26
Matrices
[deleted]
–
anonymous
edited
Feb 3 at 7:54
Embedding GDB pretty printers, just like natvis
Pretty printers are awesome, but the setup can be a real pain. …
–
pknowles
created
Feb 1 at 12:27
Embedding GDB pretty printers, just like natvis
Pretty printers are awesome, but the setup can be a real pain. …
–
pknowles
comment
Jan 26 at 8:20
Matrices
[deleted]
–
anonymous
comment
Jan 15 at 7:46
Matrices
[deleted]
–
anonymous
comment
Jan 14 at 8:05
Making a real EMF Reader
All good/my bad. A half implemented feature that I really shoul…
–
pknowles
comment
Jan 14 at 8:03
Making a real EMF Reader
I don't have a circuit diagram sorry. The LEDs are all on separ…
–
pknowles
comment
Jan 10 at 0:07
Making a real EMF Reader
а есть подробные схемы что к чему подключать и куда припаивать…
–
anonymous
comment
Jan 5 at 18:00
Matrices
[deleted]
–
anonymous
comment
Dec 15 '24
Matrices
[deleted]
–
anonymous
comment
Nov 27 '24
DerBard: Custom Split Mechanical Keyboard Prototype
hello
–
anonymous
comment
Nov 20 '24
Matrices
[deleted]
–
anonymous
created
Oct 21 '24
Iterators: pointers vs cursors
You're already doing both of these by hand. This post emphaisze…
–
pknowles
…
View All
Log in
Hello Ray, a Hello World Vulkan Ray Tracing Tutorial
leave this field blank to prove your humanity
Article title
*
Article revisions must have a non-empty title
Article body
*
\*WORK IN PROGRESS\* Want to do some quick 3D graphics programming? Want the siplicity of OpenGL but with newer Vulkan features --- like ray tracing? That's a tough ask, but lets see what can be done. I've made a small library, [`vulkan_objects`](https://github.com/pknowles/vulkan_objects), that attempts to provide vulkan more directly and big usability shortcuts for mimimal effort. It's unique in using the vulkan `vk.xml` specification directly, which shortcuts a lot of code to support multiple versions and mix precompiled binaries. Lets compare and then try an example to see how quickly we can bootstrap a project to call `vkCmdDraw` or `vkCmdTraceRaysKHR` (without hiding the code in non-generic "I wrote this part for you" functions/classes). Vulkan famously needs 1000 lines of code to draw a single triangle, and that's probably still taking some shortcuts. Imagine ray tracing, when acceleration structures and shader binding tables are needed. Moreover the libraries and tech stack needed to just get started are complicated. For example, vulkan needs a "loader" to populate API function pointers, there may be multiple vulkan implementations to choose from (physical GPUs or software) and it needs a separate shader compiler. Vulkan by itself doesn't do any validation/error checking. You have to set up an intermediate "layer" for that. Your dependencies are already starting to look like this: [Vulkan SDK](https://vulkan.lunarg.com/) or [volk](https://github.com/zeux/volk), [glslang](https://github.com/KhronosGroup/glslang)/[shaderc](https://github.com/google/shaderc)/[slang](https://github.com/shader-slang/slang) and a custom validation layer install; maybe [vk-bootstrap](https://github.com/charles-lunarg/vk-bootstrap) for device selection. Not to mention dealing with the verbosity of the C API. See [VulkanTutorial](https://github.com/Overv/VulkanTutorial), [SaschaWillems/Vulkan](https://github.com/SaschaWillems/Vulkan), [nvpro-samples](https://github.com/nvpro-samples/), [vk_raytracing_tutorial_KHR](https://nvpro-samples.github.io/vk_raytracing_tutorial_KHR/). These aren't bad but maybe there's opportunity to lower the barrier for entry even further. # A Quicker Start With [`vulkan_objects`](https://github.com/pknowles/vulkan_objects), assuming linux with gcc and cmake, although it's really similar for Windows, and tested with an NVIDA GPU. mkdir hello_ray cd hello_ray git init git submodule add git@github.com:pknowles/vulkan_objects.git Add a `CMakeLists.txt` file: cmake_minimum_required(VERSION 3.3) project(hello_ray_project) set(VULKAN_OBJECTS_FETCH_VVL ON) set(VULKAN_OBJECTS_FETCH_VMA ON) set(VULKAN_OBJECTS_FETCH_SLANG ON) add_subdirectory(vulkan_objects) FetchContent_Declare( glfw GIT_REPOSITORY https://github.com/glfw/glfw.git GIT_TAG 3.4 GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(glfw) add_executable(hello_ray main.cpp) target_link_libraries(hello_ray PRIVATE vulkan_objects glfw) To build and test after populating `main.cpp` below: cmake -S . -B build cmake --build build --parallel 4 ./build/hello_ray Create `main.cpp`. For the moment, copying `debugMessageCallback`, `WindowInstanceCreateInfo` and `RayTracingDeviceCreateInfo` from [`vulkan_objects/test/src/test.cpp`](https://github.com/pknowles/vulkan_objects/blob/main/test/src/test.cpp), removing `debugbreak.h`, the `debug_break()` call, `gtest/gtest.h`. This tutorial is going to be walking through the code of its `TEST(Integration, HelloTriangleRayTracing)` case as a main function. ## Loading Vulkan // Find and load libvulkan.so.1 or vulkan-1.dll // Internally, this calls dlopen() or LoadLibraryW() and loads vkGetInstanceProcAddr() vko::VulkanLibrary library; // Load vkCreateInstance and other top level Vulkan API functions vko::GlobalCommands globalCommands(library.loader()); ## VkInstance, VkDevice, VkQueue // Optional: use GLFW for system compositor window creation // The VkInstance depends on glfwGetPlatform() to choose a compositor at // runtime (e.g. Wayland or X11) and enable the appropriate extensions. vko::glfw::PlatformSupport platformSupport( vko::toVector(globalCommands.vkEnumerateInstanceExtensionProperties, nullptr)); vko::glfw::ScopedInit scopedGlfwInit; // Create the VkInstance // This calls globalCommands.vkCreateInstance() with the VkInstanceCreateInfo of WindowInstanceCreateInfo // It also loads instance Vulkan API functions such as vkCreateDevice using globalCommands.vkGetInstanceProcAddr vko::Instance instance(globalCommands, WindowInstanceCreateInfo(platformSupport)); // Get a list of Vulkan capable implementations and devices. These could be // software implementations or real GPUs from multiple vendors. For // simplicity we'll pick the first in the list, but a real application could // choose with std::ranges::find_if(...) std::vector<VkPhysicalDevice> physicalDevices = vko::toVector(instance.vkEnumeratePhysicalDevices, instance); VkPhysicalDevice physicalDevice = physicalDevices[0]; // Pick a single VkQueue family // Normally, an app would choose from the a list, but again we'll pick the first // See std::vector<VkQueueFamilyProperties> queueProperties = vko::toVector(instance.vkGetPhysicalDeviceQueueFamilyProperties, physicalDevice); uint32_t queueFamilyIndex = 0; // Create a VkDevice // This calls instance.vkCreateDevice with the VkDeviceCreateInfo of RayTracingDeviceCreateInfo // It also loads device Vulkan API functions such as vkGetDeviceQueue using instance.vkGetDeviceProcAddr vko::Device device(instance, physicalDevice, RayTracingDeviceCreateInfo(queueFamilyIndex)); // Get a non-owning handle to the first of the device's queues in the chosen family VkQueue queue = vko::get(device.vkGetDeviceQueue, device, queueFamilyIndex, 0); That's the core of vulkan context initialization done. It's less verbose than many vulkan tutorials out there and also fairly close to the raw API, just with a little layering. We did just use two big boilerplate classes here: - `WindowInstanceCreateInfo` exists just to hold a `VkInstanceCreateInfo` object, which defines `VkInstance` requirements. - `RayTracingDeviceCreateInfo` similarly holds a `VkDeviceCreateInfo` object, which defines `VkDevice` requirements. They are very application-specific and intentionally not part of `vulkan_objects`. Wrapping the CreateInfo objects doesn't buy much IMO. They will evolve over time as you work with vulkan and pick up new features and extensions. Copy/paste them for now or write your own and expect to update them. They may even like their own file, hidden away in a `vko::Instance makeInstance()` or similar call. One interesting thing is the way `vko::Instance` and `vko::Device` are designed. They are both function pointer tables and implicitly cast to `VkInstance` and `VkDevice` respectively. This makes code neater as they're packaged together to be passed as a single argument. It also makes the objects reusable and pluggable. // Implicit cast VkInstance vkinst = instance; // Also a function pointer table PFN_vkCreateDevice vkCreateDevice = instance.vkCreateDevice; // Example Vulkan API call // This isn't a member function, but the actual loaded vkQueueWaitIdle pointer device.vkQueueWaitIdle(queue); ## Window, Surface and Swapchain // The window is the interaction with the system compositor // The surface is vulkan's interaction with that window vko::glfw::Window window = vko::glfw::makeWindow(800, 600, "Vulkan Window"); vko::SurfaceKHR surface = vko::glfw::makeSurface(instance, platformSupport, window.get()); // Query sizes VkSurfaceCapabilitiesKHR surfaceCapabilities = vko::get(instance.vkGetPhysicalDeviceSurfaceCapabilitiesKHR, physicalDevice, surface); surfaceCapabilities.current/min/maxImageExtent // The swapchain are images in that we render to and display on the surface auto surfaceFormats = vko::toVector(instance.vkGetPhysicalDeviceSurfaceFormatsKHR, physicalDevice, surface); VkSurfaceFormatKHR surfaceFormat = ...; auto surfacePresentModes = vko::toVector(instance.vkGetPhysicalDeviceSurfacePresentModesKHR, physicalDevice, surface); VkPresentModeKHR surfacePresentMode = ...; std::optional<vko::simple::Swapchain> swapchain = vko::simple::Swapchain{ device, surface, surfaceFormat, VkExtent2D{uint32_t(width), uint32_t(height)}, queueFamilyIndex, surfacePresentMode, VK_NULL_HANDLE}; `vko::simple::Swapchain` is a container with a `vko::SwapchainKHR`, its image and view handles and utilities for synchronization. It aims to be general, but it's small and easily replaceable. struct Swapchain { SwapchainKHR swapchain; Semaphore acquiredImageSemaphore; std::vector<VkImage> images; // non-owning std::vector<ImageView> imageViews; std::vector<Semaphore> renderFinishedSemaphores; std::vector<bool> presented; // first-use flag, implying VK_IMAGE_LAYOUT_UNDEFINED }; Now, we actually need to handle some expected vulkan errors. The call to `vkAcquireNextImageKHR` and `vkQueuePresentKHR` may return `VK_ERROR_OUT_OF_DATE_KHR` which is the definitive resize event from the surface. Don't rely just on glfw's callback. The specific error can be caught with: try { swapchain->acquire(...); // Render... swapchain->present(...); } catch (const vko::ResultException<VK_ERROR_OUT_OF_DATE_KHR>& e) { // Re-create the swapchain (std::optional for reset) swapchain.reset(); swapchain = vko::Swapchain(...) } ## Buffer, Image, Staging // TODO ## Shaders // TODO ## Ray Tracing // TODO
Toggle Preview
Edit message
*
A description of the changes made
Discard Draft
Save Draft
leave this field blank to prove your humanity
Flag
the thing you clicked
for moderator attention.
Reason choice:
Spam, promoting, advertising without disclosure
Rude, inappropriate, generally offensive
Too arrogant or demeaning to others
Other
Reason:
The reason for raising the flag
Error