Three.js Endless Grid Floor with UV Offset
This article explains how to create an endless, repeating grid floor in Three.js by manipulating texture UV offsets. You will learn how to set up a repeating texture on a ground plane and programmatically update its offset relative to the camera’s movement, creating the illusion of an infinite surface without rendering massive geometry.
To achieve an endless floor effect, you pair a standard
PlaneGeometry with a texture configured for infinite
repetition. By centering the plane under the camera and shifting the
texture’s UV offsets in the opposite direction of the camera’s movement,
the grid appears to stretch infinitely in all directions.
Step 1: Create and Configure the Grid Texture
First, you need a grid texture. You can load an image using
TextureLoader or generate a grid procedurally. The crucial
step is configuring the texture to repeat infinitely using
THREE.RepeatWrapping.
import * as THREE from 'three';
// Load or generate your texture
const textureLoader = new THREE.TextureLoader();
const gridTexture = textureLoader.load('path/to/grid-pattern.png');
// Enable infinite wrapping in both X and Y directions
gridTexture.wrapS = THREE.RepeatWrapping;
gridTexture.wrapT = THREE.RepeatWrapping;
// Control the density of the grid lines
const gridScale = 10;
gridTexture.repeat.set(gridScale, gridScale);Step 2: Set Up the Floor Mesh
Create a finite plane mesh and assign a material using your wrapped texture. Position the plane horizontally by rotating it on the X-axis.
// A moderately sized plane is sufficient since it moves with the camera
const floorGeometry = new THREE.PlaneGeometry(100, 100);
const floorMaterial = new THREE.MeshBasicMaterial({
map: gridTexture,
side: THREE.DoubleSide
});
const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.rotation.x = -Math.PI / 2; // Lay flat on the XZ plane
scene.add(floorMesh);Step 3: Animate UV Offsets in the Render Loop
To create the illusion of an infinite floor, you must perform two actions inside your animation loop: 1. Move the floor mesh to match the camera’s X and Z coordinates so the player never walks off the edge of the geometry. 2. Offset the texture’s UV coordinates based on the camera’s position to keep the grid aligned with the world coordinate system.
function animate() {
requestAnimationFrame(animate);
// 1. Keep the floor centered under the camera
floorMesh.position.x = camera.position.x;
floorMesh.position.z = camera.position.z;
// 2. Offset the texture coordinates based on camera movement
// Divide by the physical size represented by one texture repeat to maintain alignment
gridTexture.offset.x = camera.position.x / gridScale;
gridTexture.offset.y = -camera.position.z / gridScale;
renderer.render(scene, camera);
}
animate();Using this method, the camera can travel infinitely in any direction. The underlying mesh moves with the camera while the texture offsets update dynamically, resulting in a seamless, lightweight, and endless grid floor.