Capítulo 822 de 859

Chapter 822: Indexed Textures for Picking and Color (Manual)

Core Idea

A technique for picking and highlighting regions (like countries on a globe) using small id/palette textures instead of heavy per-region 3D geometry, borrowing the idea of "paletted graphics" from older 8-bit systems.

Key Concepts

  • The problem with per-region geometry: generating a separate 3D mesh per country produced a ~15.5MB file — too large to ship just for clicking regions.
  • GPU-picking with an id texture: pre-render an offscreen "index texture" where each region's color encodes its array index, then read back the pixel color under the cursor to identify what was clicked (only ~217KB instead of megabytes of geometry).
  • Paletted graphics / indexed color: instead of storing full RGBA per pixel, store a small index per pixel and look up the actual display color in a separate palette texture — the same approach used by systems like the NES or Amiga.
  • Material.onBeforeCompile: used to inject custom GLSL into three.js's default shader (via string replacement of internal chunks like <color_fragment>) so the material can sample the index texture, look up a palette color, and blend it with diffuseColor.
  • DataTexture: used to build the palette itself from raw RGBA byte data, sized to hold one color per region plus a background entry.

Code Examples

material.onBeforeCompile = function (shader) {
  shader.fragmentShader = shader.fragmentShader
    .replace("#include <common>", `
      #include <common>
      uniform sampler2D indexTexture;
      uniform sampler2D paletteTexture;
      uniform float paletteTextureWidth;
    `)
    .replace("#include <color_fragment>", `
      #include <color_fragment>
      {
        vec4 indexColor = texture2D(indexTexture, vUv);
        float index = indexColor.r * 255.0 + indexColor.g * 255.0 * 256.0;
        vec2 paletteUV = vec2((index + 0.5) / paletteTextureWidth, 0.5);
        vec4 paletteColor = texture2D(paletteTexture, paletteUV);
        diffuseColor.rgb = paletteColor.rgb - diffuseColor.rgb;
      }
    `);
};
  • What it demonstrates: patching the default fragment shader to look up a per-region color from a palette texture using an id encoded in another texture.

Key Takeaways

  1. An id-encoded texture plus offscreen GPU-picking can replace expensive per-region 3D geometry for click detection.
  2. Paletted-graphics-style indexed color (an id texture + a small palette texture) lets you recolor or highlight regions without regenerating geometry or the base texture.
  3. Material.onBeforeCompile is the mechanism for injecting custom shader logic into three.js's built-in materials while keeping their standard lighting/shading pipeline.
  4. Size the palette texture generously (e.g. 512 entries) rather than computing an exact count, since the cost of a slightly oversized palette is negligible.

Connects To

  • DataTexture: used to build the palette texture from raw byte data.
  • Aligning HTML Elements to 3D (manual): the earlier lesson this technique continues from.