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:
- WebGL 2 (Default in modern Three.js): WebGL 2
supports NPOT textures natively. You can set
texture.generateMipmaps = trueon any NPOT texture, and the GPU will generate mipmaps and apply wrapping modes (likeRepeatWrapping) without requiring the texture to be resized. - WebGL 1: WebGL 1 has strict limitations. It cannot generate mipmaps for NPOT textures, nor can it use repeat wrapping modes. If you attempt to use mipmaps on an NPOT texture in WebGL 1, Three.js will automatically resize the texture to the nearest power-of-two (POT) dimension behind the scenes. This resizing process can cause performance overhead during loading and may slightly distort your image.
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.
Option 1: Disabling Mipmap Generation (Recommended for WebGL 1 Compatibility)
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:
- If
generateMipmapsistrue: Ensure yourminFilteris set to a mipmap filter (e.g.,LinearMipmapLinearFilter,LinearMipmapNearestFilter). - If
generateMipmapsisfalse: Ensure yourminFilteris set toLinearFilterorNearestFilter. - 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.