Skip to main content

Overview

Filament’s Texture class supports 2D textures, 3D textures, cube maps, and automatic mipmap generation. Textures can be created with various internal formats and compression types.

Creating Textures

Basic 2D Texture

#include <filament/Texture.h>

Texture* texture = Texture::Builder()
    .width(512)
    .height(512)
    .levels(1)
    .format(Texture::InternalFormat::RGBA8)
    .build(*engine);

Cube Map Texture

Texture* cubemap = Texture::Builder()
    .width(256)
    .height(256)
    .levels(8)  // mipmap levels
    .sampler(Texture::Sampler::SAMPLER_CUBEMAP)
    .format(Texture::InternalFormat::RGB16F)
    .build(*engine);

3D Texture

Texture* volume = Texture::Builder()
    .width(128)
    .height(128)
    .depth(128)
    .sampler(Texture::Sampler::SAMPLER_3D)
    .format(Texture::InternalFormat::RGBA8)
    .build(*engine);

Texture Formats

Common Internal Formats

FormatDescriptionUse Case
RGBA88-bit RGBAStandard color textures
RGB88-bit RGBColor without alpha
R88-bit single channelMasks, grayscale
RGBA16F16-bit float RGBAHDR color
RGB16F16-bit float RGBHDR without alpha
R16F16-bit float singleHDR grayscale
DEPTH2424-bit depthDepth buffers
SRGB8_A8sRGB 8-bit RGBAsRGB color textures

Compressed Formats

FormatDescriptionPlatform
EAC_R1111-bit single channelMobile (OpenGL ES)
EAC_RG1111-bit two channelMobile (OpenGL ES)
ETC2_RGB8RGB 8-bit compressedMobile (OpenGL ES)
ETC2_SRGB8_A8sRGB+Alpha compressedMobile (OpenGL ES)
DXT1_RGBBC1 RGB compressionDesktop
DXT5_RGBABC3 RGBA compressionDesktop
RGBA_ASTC_4x4ASTC 4x4 blockModern GPUs
Check format support:
if (Texture::isTextureFormatSupported(*engine, 
    Texture::InternalFormat::ETC2_RGB8)) {
    // Use compressed format
}

Uploading Texture Data

Single Level Upload

// Prepare pixel data
std::vector<uint8_t> pixels(width * height * 4);
// ... fill pixel data ...

Texture::PixelBufferDescriptor buffer(
    pixels.data(),
    pixels.size(),
    Texture::Format::RGBA,
    Texture::Type::UBYTE
);

texture->setImage(*engine, 0, std::move(buffer));

Multiple Mipmap Levels

for (size_t level = 0; level < mipLevels; level++) {
    size_t w = std::max(1u, width >> level);
    size_t h = std::max(1u, height >> level);
    
    std::vector<uint8_t> mipData(w * h * 4);
    // ... generate mip data ...
    
    Texture::PixelBufferDescriptor buffer(
        mipData.data(), mipData.size(),
        Texture::Format::RGBA, Texture::Type::UBYTE
    );
    
    texture->setImage(*engine, level, std::move(buffer));
}

Cube Map Upload

// Cubemap faces treated as 2D array with 6 layers
// Order: +X, -X, +Y, -Y, +Z, -Z
for (size_t face = 0; face < 6; face++) {
    std::vector<uint8_t> faceData(width * height * 4);
    // ... load face data ...
    
    Texture::PixelBufferDescriptor buffer(
        faceData.data(), faceData.size(),
        Texture::Format::RGBA, Texture::Type::UBYTE
    );
    
    // Upload to layer (face)
    cubemap->setImage(*engine, 0,
        0, 0, face,  // x, y, z offsets
        width, height, 1,  // dimensions
        std::move(buffer));
}

Automatic Mipmap Generation

Filament can generate mipmaps automatically:
Texture* texture = Texture::Builder()
    .width(512)
    .height(512)
    .levels(0xFF)  // auto-calculate max levels
    .format(Texture::InternalFormat::RGBA8)
    .usage(Texture::Usage::SAMPLEABLE | 
           Texture::Usage::COLOR_ATTACHMENT)
    .build(*engine);

// Upload base level
texture->setImage(*engine, 0, std::move(baseBuffer));

// Generate all mip levels
texture->generateMipmaps(*engine);
Requirements for generateMipmaps():
  • Format must be color-renderable
  • Usage must include SAMPLEABLE and COLOR_ATTACHMENT
  • Cannot be used with 3D textures

Texture Providers (glTF)

When loading glTF assets, texture providers decode image data:

STB Provider

Handles PNG and JPEG formats:
auto stbProvider = createStbProvider(engine);
resourceLoader.addTextureProvider("image/png", stbProvider);
resourceLoader.addTextureProvider("image/jpeg", stbProvider);

KTX2 Provider

Handles Basis Universal compressed textures:
auto ktx2Provider = createKtx2Provider(engine);
resourceLoader.addTextureProvider("image/ktx2", ktx2Provider);

WebP Provider

Handles WebP format (if supported):
if (isWebpSupported()) {
    auto webpProvider = createWebpProvider(engine);
    resourceLoader.addTextureProvider("image/webp", webpProvider);
}

Texture Swizzling

Remap texture channels:
Texture* texture = Texture::Builder()
    .width(256)
    .height(256)
    .format(Texture::InternalFormat::RGBA8)
    .swizzle(
        Texture::Swizzle::CHANNEL_0,  // R
        Texture::Swizzle::CHANNEL_0,  // G (use R channel)
        Texture::Swizzle::CHANNEL_0,  // B (use R channel)
        Texture::Swizzle::CHANNEL_1   // A (use G channel)
    )
    .build(*engine);
Check support:
if (Texture::isTextureSwizzleSupported(*engine)) {
    // Use swizzling
}

External Textures

For platform-specific image sources (video, camera):
Texture* externalTexture = Texture::Builder()
    .width(1920)
    .height(1080)
    .sampler(Texture::Sampler::SAMPLER_EXTERNAL)
    .format(Texture::InternalFormat::RGB8)
    .external()
    .build(*engine);

// Set platform-specific image
auto imageHandle = platform->createExternalImage(nativeImage);
externalTexture->setExternalImage(*engine, imageHandle);

Asynchronous Operations

For non-blocking texture creation and upload:
auto callback = [](Texture* texture, void* user) {
    std::cout << "Texture ready!" << std::endl;
};

Texture* texture = Texture::Builder()
    .width(512)
    .height(512)
    .format(Texture::InternalFormat::RGBA8)
    .async(nullptr, callback, nullptr)
    .build(*engine);

// Check if ready
if (texture->isCreationComplete()) {
    // Texture is ready for use
}

Best Practices

  1. Use compressed formats on mobile for better performance
  2. Generate mipmaps for textures that will be minified
  3. Use appropriate precision: RGBA8 for LDR, RGBA16F for HDR
  4. Check format support before using platform-specific formats
  5. Power-of-two dimensions for better compatibility (not required)
  6. Release pixel buffers after upload to free memory

See Also

Build docs developers (and LLMs) love