Every demo from all 5 days — OpenGL foundations, 3D lighting, Vulkan from scratch, compute shaders, and real-world capstones. Read the short description, open full instructions, try the challenge, or download the project to run on your machine.
The main demo sequence from the OpenGL & Vulkan training course for the US Navy Department. Each demo introduces one new concept, builds on the previous, and includes a break-to-learn challenge for hands-on reinforcement.
Interactive animated diagram of the GPU pipeline: vertex shader → rasterisation → fragment shader. Explains where each glEnable() call fits, what the driver does vs what you write, and why vertex position flows from CPU RAM to screen pixel.
Explain what happens between vkCmdDraw() and a pixel lighting up on screen.
Sets up a GLFW window, creates an OpenGL 3.3 core context, enters the event loop with glfwPollEvents(), clears to navy blue each frame, prints GPU name to terminal. Proves your environment works before writing a single shader.
Change the clear colour each second using glfwGetTime(). Change window title to show FPS.
Creates a VkBuffer (OpenGL: glGenBuffers), uploads 3 vertices with glBufferData(), then reads back the data to confirm the upload. Prints byte size, memory address, and confirms round-trip integrity. Foundation for every GPU draw call.
Upload 6 vertices (two triangles) and verify all 6 come back correctly.
Shows how a VAO records the connection between a VBO and shader attribute locations. Live diagram printed to terminal: stride=20 bytes, location 0=position (offset 0), location 1=colour (offset 12). Break it by changing stride to see garbage output.
Add a third attribute — UV coordinates — without breaking position or colour.
Compiles vertex and fragment shaders at runtime, links a program, draws the first triangle using glDrawArrays(). Shader compilation errors are printed to terminal with full GLSL log. The simplest possible full render pipeline.
Write a fragment shader that outputs sin(time)*0.5+0.5 as red channel — watch the triangle pulse red.
Uses glDrawElements() + an index buffer to draw a rectangle from only 4 vertices (vs 6 duplicate vertices without EBO). Prints memory saved vs brute-force approach. Introduces TRIANGLE_LIST indexing pattern used in every production renderer.
Draw a hexagon from 6 outer vertices + 1 centre vertex using a 12-index EBO.
Sends a float time uniform each frame via glUniform1f(), drives a translation and colour shift in the vertex shader. Shows glGetUniformLocation() cache pattern and why GPU uniforms are cheaper than per-vertex data for shared values.
Add a glUniform2f() mouse position. Make the triangle follow the cursor using glfwGetCursorPos().
Builds a full projection pipeline: glm::translate/rotate/scale → model matrix, glm::lookAt → view, glm::perspective → projection. WASD keys move the camera; mouse drag rotates. Demonstrates view frustum and near/far plane clipping live.
Add a pitch angle limit (±89°) to prevent camera gimbal lock. Add Shift to sprint.
Implements Blinn-Phong: ambient constant + diffuse dot(N,L) + specular pow(dot(N,H), shininess). UP/DOWN keys change shininess live (2→256). Normal matrix corrects for non-uniform model scale. Orbiting point light demonstrates all three components separately.
Add a second light source of a different colour. See how both contribute to the final fragment colour.
Loads a PNG via stb_image into a GL_TEXTURE_2D object, binds to sampler uniform, applies to a lit 3D cube. UV coordinates are interleaved with positions and normals in a single VBO. Demonstrates mip-mapping, GL_REPEAT wrap mode, and texture × Phong multiplication.
Apply a normal map texture — modify the fragment shader to perturb the normal using the normal map's RGB values.
Writes an entire radar PPI display inside a fragment shader using no textures. atan(x,y) computes bearing, fract() creates range rings, mod() drives the rotating sweep, smoothstep() softens edges. No geometry beyond one quad — the GPU draws everything via math.
Add a contact blip: flash a bright dot at a fixed bearing/range using step() and sin(time).
Renders 1,000 coloured contacts with one glDrawArraysInstanced() call. Instance data (position, colour, size) is in a second VBO with glVertexAttribDivisor(1,1). Toggle instancing off and measure the frame time difference — see CPU overhead vs GPU throughput.
Change divisor to 2 and observe that every pair of contacts shares the same position — explain why.
Two-pass render: scene → off-screen FBO texture, then fullscreen quad → apply shader filter. Keys 1-4 switch between passthrough, greyscale, invert, and Sobel edge detection without rebuilding any scene geometry. Classic deferred rendering foundation.
Write a vignette pass: darken pixels by 1-smoothstep(0.5, 1.0, length(uv-0.5)*1.5).
Two-pass stencil: first pass writes object pixels to stencil buffer; second pass draws scaled-up outline only where stencil=0 (outside original). Then adds a transparent alpha-blended radar sweep sector on top. Demonstrates blending order and depth–stencil interaction.
Add a third pass that draws a transparent red warning zone circle using additive blending.
Creates VkInstance with validation enabled, enumerates all GPUs, scores each by VRAM and device type, creates VkSurfaceKHR via GLFW, destroys everything in correct order. Terminal shows GPU names, VRAM, and queue family availability. No rendering — pure setup.
Add VkPhysicalDeviceProperties output: print maxImageDimension2D and maxUniformBufferRange for each GPU.
Creates VkDevice requesting graphics + present queues, creates VkCommandPool and allocates a primary command buffer, records a no-op (empty) command buffer and submits it with a fence. Validates the full command recording and submission cycle before adding any rendering.
Record vkCmdSetEvent() and vkCmdWaitEvents() in the command buffer and verify the fence signals after the wait.
Creates VkSwapchainKHR, wraps images in VkImageViews, builds a VkRenderPass with LOAD_OP_CLEAR, creates framebuffers, runs the acquire → record → submit → present frame loop clearing to an animated navy colour via VkClearValue. First time real pixels appear.
Change the clear colour each frame using a sine wave on the red channel. Confirm the framerate matches the display refresh.
Compiles GLSL to SPIR-V with glslc, loads .spv at runtime, creates VkShaderModules, builds VkGraphicsPipelineCreateInfo baking all state. Draws a white triangle on navy. No vertex buffer — positions hardcoded in vertex shader via gl_VertexIndex. Complete frame loop with semaphore+fence sync.
Change VK_POLYGON_MODE_FILL to VK_POLYGON_MODE_LINE in the rasteriser and recompile — draw wireframe without shader changes.
Builds the full descriptor system: VkDescriptorSetLayout (binding 0 = uniform buffer), VkDescriptorPool, one VkDescriptorSet per swapchain image. Each frame updates MVP matrices via persistent-mapped UBO memory and binds before vkCmdDraw. Orange triangle rotates via shader MVP with Vulkan Y-flip correction.
Change the model matrix to also include a scale driven by abs(sin(time)) — watch the triangle pulse in size while rotating.
Creates a compute-only pipeline (no swapchain, no window). 256×256 synthetic image uploaded to storage buffer, compute shader inverts RGB of all 65,536 pixels in parallel (16×16 workgroups). Pipeline barrier ensures GPU writes complete before CPU reads. Prints timing and throughput in pixels/sec.
Change the kernel to greyscale: r_out = g_out = b_out = (r+g+b)/3. Recompile compute shader and verify output changes.
A self-contained Vulkan learning series starting from absolute zero. Each project is a standalone CMake build with complete main.cpp, GLSL shaders, and CMakeLists.txt. Shared VulkanBase.h provides common helpers — no duplication, no mystery code.
The definitive Vulkan hello-world. Creates VkInstance with validation, enumerates + scores all GPUs by VRAM and device type, creates VkDevice + queues, builds a VkSwapchainKHR. Every object printed to terminal. No shaders. No rendering. Proves your toolchain is working before anything else.
First rendered output in Vulkan. Vertex positions hardcoded in shader.vert via gl_VertexIndex — no vertex buffer. Builds complete VkGraphicsPipelineCreateInfo baking all fixed-function state. Shows acquire → record → vkCmdBeginRenderPass → vkCmdDraw → present loop with semaphore+fence synchronisation.
Uploads a coloured pentagon from CPU memory to a HOST_VISIBLE buffer using vkMapMemory + memcpy. VkVertexInputBindingDescription describes stride; VkVertexInputAttributeDescription describes location, format, and byte offset for position (vec2) and colour (vec3). Topology: TRIANGLE_FAN from centre vertex.
Draws a rectangle from 4 vertices + 6 indices with vkCmdDrawIndexed. Introduces the staging pattern: CPU writes to HOST_VISIBLE buffer → one-shot command buffer copies to DEVICE_LOCAL memory → staging buffer destroyed. Device-local is faster GPU memory — the standard pattern for all static geometry.
Builds the complete descriptor infrastructure: layout → pool → sets → update. One UBO per swapchain image (prevents CPU overwriting data GPU is reading). Rotating quad driven by MVP matrix. Persistent vkMapMemory + memcpy per frame. proj[1][1]*=-1 corrects Vulkan Y-axis flip.
Uploads a procedural 256×256 checkerboard texture via staging buffer → vkCmdCopyBufferToImage. Two image layout transitions: UNDEFINED→TRANSFER_DST→SHADER_READ_ONLY. Descriptor set with two bindings: UBO (binding 0) + COMBINED_IMAGE_SAMPLER (binding 1). Rotating textured quad.
Adds a depth attachment: VkImage with D32_SFLOAT format, render pass with two attachments, two VkClearValues (colour + depth=1.0). VkPipelineDepthStencilStateCreateInfo enables LESS compare op. 36-vertex cube with unique face colours. Back-face culling ON. Validates face ordering without depth corruption.
Draws 5 cubes with one shared vertex buffer but unique transforms via 80-byte push constants (mat4 model + vec4 colour). Push constants are the fastest per-draw update path — no descriptor overhead. Shared VP UBO for camera. Each cube orbits at a different radius and speed.
Procedurally generated UV sphere (32 stacks × 64 slices). SceneUBO carries model/view/proj + lightPos + viewPos. Normal matrix: transpose(inverse(model)). Blinn-Phong in fragment shader: ambient + diffuse dot(N,L) + specular pow(dot(N,H), shininess). Shininess adjustable with UP/DOWN keys.
Full compute + graphics dual pipeline. Compute shader runs Sobel edge detection / box blur / sharpen on a Mandelbrot fractal (generated in code). Output copied to a VkImage. Graphics renders it as a fullscreen triangle via a combined image sampler. Keys 1/2/3/4 switch filters every frame.
Three production-quality graphics programs built for real naval training scenarios. Each capstone integrates multiple Vulkan concepts into a single working system — the kind of code you would write on a real defence contract.
USS Monterey CIC scenario. Rotating green sweep beam with persistent alpha-blended trail. 4 range rings, north/south/east/west crosshair, 8 simulated surface contacts orbiting at different ranges and speeds. Two pipelines in one render pass: LINE_LIST for rings, TRIANGLE_LIST for sweep sector. Dynamic vertex buffer rebuilt each frame. +/- keys change sweep speed.
Royal Australian Navy bridge simulator scenario. 64×64 grid mesh uploaded once — all wave displacement computed in vertex shader via 4 superimposed Gerstner waves (no CPU geometry rebuild per frame). Blinn-Phong sun lighting, Fresnel edge glow, foam on wave crests, depth colour gradient. Fly camera: WASD + mouse drag + +/- wave height.
Naval simulation game scenario — missile exhaust fire. 100,000 particles simulated entirely on GPU: compute shader integrates velocity, applies gravity, respawns dead particles. Critical compute→graphics pipeline barrier ensures GPU writes complete before vertex reads. Additive blending creates fire glow. Circular soft-edged point sprites via gl_PointCoord. SPACE adds new emitter.
All demos use the same toolchain. Set up once, run everything. Windows 10/11 with Visual Studio 2022 recommended.
lunarg.com/vulkan-sdk and run the installer.
It installs glslc.exe (shader compiler),
VK_LAYER_KHRONOS_validation, and headers.%VULKAN_SDK%\Bin to your PATH.glfw.org/download.C:\libs\glfw.$ENV{GLFW_DIR} automatically.github.com/g-truc/glm (releases page, zip).C:\libs\glm.include/ and lib-vc2022/.glm/ header folder.Replace VK05_UniformBuffer with any demo folder name. Steps are identical for every project.
OpenGL demos (D01–D14) also require GLEW. Download the pre-built Windows binary from glew.sourceforge.net, extract it, and add to your CMakeLists manually, or use vcpkg:
build\Release\.setx GLFW_DIR "C:\libs\glfw" in a new terminal, then rebuild.%VULKAN_SDK%\Bin to your system PATH via System Properties → Environment Variables → Path → Edit → New.