Textured Quad¶
This example shows the complete workflow for texture mapping in Vulkan: loading image data from disk, creating GPU textures, configuring samplers for filtering and wrapping, and binding textures to shaders for sampling. It introduces the fundamentals of texture coordinates, texture views, and combined image samplers.
The example uses the KDGpuExample helper API for simplified setup.

Overview¶
What this example demonstrates:
- Loading image files using the STB Image library
- Creating GPU textures from CPU image data
- Texture samplers with filtering and wrapping modes
- Combined image-sampler descriptors for shader access
- Texture coordinate mapping to geometry
Use cases:
- Applying images, photos, or artwork to 3D models
- Implementing material systems with albedo/diffuse maps
- Creating textured UI elements or 2D sprites
- Foundation for advanced texturing (normal maps, PBR materials)
Vulkan Requirements¶
- Vulkan Version: 1.0+
- Extensions: None (core texture sampling functionality)
- Device Features: Standard sampler support (available on all devices)
Key Concepts¶
Textures in Vulkan:
In Vulkan, rendering with textures requires several components:
- Texture Image (VkImage): GPU-side memory holding the pixel data
- Texture View (VkImageView): Describes how to interpret the image data (format, mip levels, array layers)
- Sampler (VkSampler): Defines how to sample/filter the texture (linear, nearest, anisotropic)
- Descriptor Binding: Makes the texture+sampler accessible in shaders
Unlike OpenGL's glBindTexture, Vulkan separates the image data from the sampling configuration, allowing different sampling modes for the same texture.
Texture Coordinates:
Texture coordinates (UVs) specify which part of the texture maps to each vertex. Coordinates range from (0,0) at the bottom-left to (1,1) at the top-right. Vertex shader passes UVs to fragment shader where they're interpolated and used to sample the texture.
Texture Sampling:
Samplers control how texels (texture pixels) are read when texture coordinates fall between pixel centers:
- Nearest: Sharp, pixelated - picks closest texel
- Linear: Smooth - interpolates between neighboring texels
- Wrapping modes: Repeat, clamp, mirror - control behavior outside [0,1] range
For more on Vulkan textures: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImage.html
Implementation¶
Loading Image Data:
This example uses the STB Image library to load JPEG/PNG files:
1 2 | |
Filename: textured_quad/textured_quad.cpp
1 2 3 4 5 6 7 | |
Filename: textured_quad/textured_quad.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
Filename: textured_quad/textured_quad.cpp
STB Image provides a simple interface to decode various image formats. The loadImage() function returns pixel data in RGBA format along with dimensions.
Creating and Uploading the Texture:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | |
Filename: textured_quad/textured_quad.cpp
Key options:
TextureUsageFlagBits::SampledBit: Texture will be sampled in shadersTextureUsageFlagBits::TransferDstBit: Texture can receive uploaded dataMemoryUsage::GpuOnly: Texture stays on GPU (best performance)TextureLayouttransitions:Undefined→ShaderReadOnlyOptimalfor efficient sampling
The upload uses a staging buffer internally (handled by the helper API) to transfer data from CPU to GPU memory.
Creating Texture View and Sampler:
1 2 | |
Filename: textured_quad/textured_quad.cpp
The texture view acts as a "window" into the texture image. The sampler uses default settings which provide:
- Mag/Min Filter: Linear interpolation (smooth)
- Wrap Mode: Repeat (tile the texture if UVs go outside [0,1])
- Anisotropy: Disabled (can be enabled for better quality at oblique angles)
For custom sampler settings, see VkSamplerCreateInfo.
Binding Texture to Shaders:
1 2 3 4 5 6 7 8 9 10 | |
Filename: textured_quad/textured_quad.cpp
The TextureViewSamplerBinding creates a combined image sampler descriptor. This makes both the texture data and sampling configuration available to the fragment shader as a single descriptor.
In the fragment shader:
1 2 3 4 5 6 7 8 9 10 11 | |
Filename: textured_quad/textured_quad.frag
Primitive Topology:
This example renders a quad using triangle strip topology:
1 2 3 | |
Filename: textured_quad/textured_quad.cpp
Triangle strip connects vertices in sequence: 0-1-2 forms a triangle, then 1-2-3 forms the next. This is more efficient than indexed triangles for simple strips. The quad uses 4 vertices with UVs mapping the full texture:
- Vertex 0: position(-1, 1), UV(0, 1) - Bottom-left
- Vertex 1: position(1, 1), UV(1, 1) - Bottom-right
- Vertex 2: position(-1, -1), UV(0, 0) - Top-left
- Vertex 3: position(1, -1), UV(1, 0) - Top-right
Performance Notes¶
- Texture Formats: R8G8B8A8_UNORM is universally supported but 4 bytes per pixel. Consider compressed formats (BC, ASTC) for larger textures
- Mipmapping: This example doesn't use mipmaps. For textures viewed at varying distances, generate mipmaps to improve performance and quality
- Anisotropic Filtering: Improves quality when viewing textures at oblique angles, costs minimal performance on modern GPUs
- GPU-Only Memory: Using
MemoryUsage::GpuOnlygives best performance since GPU can optimize memory layout
See Also¶
- Hello Triangle - Basic rendering without textures
- Depth Texture Sampling - Sampling depth textures for advanced effects
- Vulkan Texture Guide - Comprehensive texture usage guide
- VkSampler - Sampler configuration reference
- STB Image Library - Image loading library documentation
Further Reading¶
- Texture Filtering - Explains filtering modes and texture wrapping
- Mipmapping - Why and how to use mipmaps for better performance
- Compressed Texture Formats - Overview of GPU texture compression
Updated on 2026-03-31 at 00:02:07 +0000