Trucos de rendering para voxels
En el post anterior cubrí el pipeline de generación. Aquí me centro en el lado del renderer: qué optimizaciones de verdad mueven la aguja y cuáles son ricer pero innecesarias para terrenos pequeños.
Hidden-block culling#
Antes de instanciar nada, descarto cualquier bloque cuyos 6 vecinos sean sólidos opacos — nunca será visible desde fuera.
function shouldEmit(world, x, y, z) {
if (!world[x][z][y]) return false;
const occluded =
SOLID_OPAQUE(world[x+1]?.[z]?.[y]) &&
SOLID_OPAQUE(world[x-1]?.[z]?.[y]) &&
SOLID_OPAQUE(world[x]?.[z]?.[y+1]) &&
SOLID_OPAQUE(world[x]?.[z]?.[y-1]) &&
SOLID_OPAQUE(world[x]?.[z+1]?.[y]) &&
SOLID_OPAQUE(world[x]?.[z-1]?.[y]);
return !occluded;
}Para un grid 48×48×12, esto reduce ~30 000 bloques a ~5 000 instancias visibles. Memoria y drawcalls bajan ~6×.
Color jitter por instancia#
Un terreno de cubos planos del mismo color se ve a juguete. La misma InstancedMesh puede tener color por instancia:
for (let i = 0; i < count; i++) {
const [x, y, z] = positions[i];
const k = 1 + (hash3(x, y, z) * 2 - 1) * jitter;
mesh.setColorAt(i, base.clone().multiplyScalar(k));
}
mesh.instanceColor!.needsUpdate = true;Con jitter = 0.08 se ve natural sin parecer ruido. Con 0 parece de juguete; con 0.2+ ya canta.
Greedy meshing: cuándo SÍ y cuándo NO#
Greedy meshing colapsa caras adyacentes del mismo material en un solo quad. Reduce drawcalls dramáticamente para mundos enormes (Minecraft-scale).
Para mi grid 48×48 con InstancedMesh:
- Sin greedy: ~15 drawcalls (1 por tipo de bloque visible)
- Con greedy: ~1 drawcall por tipo, pero geometría custom en CPU cada regeneración
El coste de generar la mesh greedy en cliente cada vez que cambias el seed mata el ahorro. Solo merece la pena si:
- El mundo es estático (generado una vez, nunca cambia)
- El mundo es grande (>128³)
- Estás GPU-bound, no CPU-bound
Para terrenos pequeños client-side regenerables, InstancedMesh gana.
Próximos experimentos#
- Animación de agua con vertex shader (oscilación leve)
- Día/noche rotando la luz direccional
- LOD: bloques lejanos como mesh estático low-poly