~/projects/vulkanrays/
VulkanRays
A compact C++17 Vulkan renderer exploring device/swapchain lifecycle, command buffers, graphics pipelines, descriptors, ImGui integration, and interactive 3D fundamentals.

I wanted the explicit version
VulkanRays is a small renderer I built to understand what higher-level graphics frameworks normally decide for me.
The project creates an SDL2 window and Vulkan surface, selects a physical and logical device, builds a swapchain and depth resources, records command buffers, submits synchronized frames, and tears everything down deterministically. There is no engine or helper framework hiding the lifetime of those objects.
That was the point. I wanted to see where queues, memory types, image layouts, pipeline state, descriptors, fences, and semaphores enter a frame. The result is intentionally compact: a procedural grid and pyramids, a first-person camera, and an ImGui overlay rather than a content-heavy renderer.
Immutable state changed how I organized the renderer
Vulkan makes many rendering decisions explicit before a draw call is recorded.
I used separate graphics pipelines for triangle meshes and grid lines. They share a render pass but keep primitive topology and related state obvious. Render objects own vertex and index buffers plus per-object uniform buffers, and descriptor sets connect each model-view-projection update to the shader before drawing.
Command recording follows the frame in order: begin the render pass, bind a pipeline and object resources, issue geometry draws, hand the pass to the ImGui Vulkan backend, then submit with per-frame synchronization. That structure made the cost of every convenience visible and gave each class a clear Vulkan lifetime.
Resize was a resource-lifetime problem
A window resize invalidates more than the screen dimensions.
When presentation returns an out-of-date result, the renderer waits at the right boundary, destroys swapchain-dependent resources, chooses the new extent, and rebuilds image views, depth state, framebuffers, and related command resources. Objects with longer lifetimes remain intact.
Getting that sequence right was more instructive than adding another shape. It forced me to reason about ownership and dependency: which resources are tied to the device, which to the swapchain, and which can be recreated without leaking or using an object the GPU still owns.
The project stays deliberately small
VulkanRays is not a ray tracer, game engine, or production renderer.
It has no bindless descriptor model, frustum culling, multi-threaded command recording, advanced lighting, or optimizing compiler runtime. Per-object uniform buffers are simple and explicit at this scale; a larger scene would need a different data strategy. The name predates the final scope and should not imply features the repository does not contain.
What it does provide is a complete enough frame loop to make modern explicit graphics less abstract: platform surface, device and queues, swapchain, depth, two pipelines, buffers, descriptors, command recording, synchronization, camera input, UI, resize, and cleanup.
What I carried forward
The project made resource lifetime and data movement feel less magical.
It gave me a concrete way to connect Computer Engineering coursework with software: CPU-side object state becomes uniform data, commands describe work for a queue, synchronization controls when resources may be reused, and a swapchain turns rendered images into presentation.
I keep VulkanRays in the portfolio as one focused lower-level project, not as my primary identity. Its value is that I wired the lifecycle myself, documented the tradeoffs, and can point to the exact boundary where I would change the architecture for a larger workload.
- Two explicit pipelines keep topology state clear for a tiny scene.
- Per-object UBOs favor readability over scale.
- Swapchain recreation is organized around actual resource dependencies.
- The implemented scope is a renderer foundation, not a ray tracer or compiler project.