How to Use generateMipmaps with NPOT Textures in Three.js

Working with non-power-of-two (NPOT) textures in Three.js often requires careful management of mipmaps to ensure optimal rendering performance and visual quality. This article explains how to properly configure the generateMipmaps property for NPOT textures, discusses how WebGL versions affect their behavior, and details the necessary minification filtering adjustments required to avoid rendering errors.

Understanding NPOT Textures in WebGL 1 vs. WebGL 2

How Three.js handles the generateMipmaps property on NPOT textures depends heavily on the WebGL context of your renderer:

How to Utilize generateMipmaps

Depending on your project’s performance requirements and target devices, you have two primary ways to configure generateMipmaps for NPOT textures.

If you want to avoid the performance cost of auto-resizing NPOT textures in WebGL 1, or if you want to save GPU memory in WebGL 2, you should disable mipmap generation.

When you disable generateMipmaps, you must also change the texture’s minification filter (minFilter). By default, Three.js uses LinearMipmapLinearFilter, which expects mipmaps to exist. Without mipmaps, the texture will render as black or throw WebGL warnings.

import * as THREE from 'three';

const textureLoader = new THREE.TextureLoader();
textureLoader.load('path/to/npot-image.jpg', (texture) => {
    // Disable mipmap generation
    texture.generateMipmaps = false;

    // Set minification filter to a non-mipmap option
    texture.minFilter = THREE.LinearFilter; // Or THREE.NearestFilter

    // Apply texture to material
    const material = new THREE.MeshBasicMaterial({ map: texture });
});

Option 2: Enabling Mipmap Generation (WebGL 2 or Auto-Scaling)

If you are targeting WebGL 2, or if you don’t mind the automatic resizing overhead in WebGL 1, you can keep mipmaps enabled to reduce aliasing (shimmering artifacts) when the texture is viewed from a distance.

import * as THREE from 'three';

const textureLoader = new THREE.TextureLoader();
textureLoader.load('path/to/npot-image.jpg', (texture) => {
    // Enable mipmap generation (true by default)
    texture.generateMipmaps = true;

    // Set a mipmap-compatible minification filter
    texture.minFilter = THREE.LinearMipmapLinearFilter; 

    const material = new THREE.MeshBasicMaterial({ map: texture });
});

Summary Checklist

To ensure your NPOT textures render correctly in Three.js, follow these rules:

  1. If generateMipmaps is true: Ensure your minFilter is set to a mipmap filter (e.g., LinearMipmapLinearFilter, LinearMipmapNearestFilter).
  2. If generateMipmaps is false: Ensure your minFilter is set to LinearFilter or NearestFilter.
  3. If targeting WebGL 1: Avoid NPOT textures where possible by resizing your source assets to power-of-two dimensions (e.g., 512x512, 1024x1024) in an image editor before importing them into Three.js.