Dummy Studios
La vitrina digital de un estudio de impresión 3D: un catálogo que se recorre como una galería de museo, en WebGL y a 60fps.
- Next.js 16
- React Three Fiber
- Drei
- Draco
- meshopt
- GLSL
- Poly Haven PBR
La identidad de Dummy Studios llevada a la web: un solo Canvas WebGL con navegación espacial y modo museo donde cada pieza del catálogo se exhibe como una obra. Mármol triplanar procedural sobre escaneos sin UVs, carga por tiers con corrección por FPS en runtime, y post-processing cinematográfico — la marca se siente premium en cualquier dispositivo.
Impresiones / semana
promedio
Pico mensual
impresiones en meses fuertes
GLB optimizados
Objetivo
con degradación
3D bonito que no se cae en un teléfono
Las landings 3D suelen elegir: o se ven increíbles en desktop y mueren en mobile, o degradan tanto que pierden el alma. El objetivo era no elegir.
Performance como sistema, no como parche
Definí tiers estáticos por capacidad y los corregí en caliente leyendo FPS reales. Los assets pasan por un pipeline propio (Draco + meshopt) y el mármol se genera por triplanar para no depender de UVs en los escaneos. El post-proceso es de un solo composer.
Catálogo cinematográfico, adaptable y liviano
56 GLB comprimidos un 33%, carga secuencial con prefetch predictivo y un modo museo que se siente premium en cualquier hardware. La vitrina acompaña a un estudio que hoy promedia unas 20 impresiones por semana y trepa a cientos de impresiones en los meses de mayor demanda.
Lo que se ve
Modo museo
Estatuas de mármol con spotlight deslizándose
Navegación espacial
Transición hero → museo en un solo Canvas
Mármol triplanar
PBR procedural sobre escaneos sin UVs
Pipeline de assets
91MB → 61MB con Draco + meshopt
Una pieza load-bearing
Código real del proyecto — la parte que sostiene la idea.
function makeMarbleMaterial() {
const mat = new MeshStandardMaterial({ color: "#efe9df", roughness: 0.6 });
mat.onBeforeCompile = (shader) => {
shader.uniforms.uAlbedo = { value: MARBLE_ALBEDO };
shader.uniforms.uMarbleScale = { value: MARBLE_SCALE };
// Posición y normal en espacio de MUNDO hacia el fragment.
shader.vertexShader =
"varying vec3 vTriPos;\nvarying vec3 vTriNrm;\n" + shader.vertexShader
.replace("#include <begin_vertex>",
"#include <begin_vertex>\n vTriPos = (modelMatrix * vec4(position,1.0)).xyz;\n" +
" vTriNrm = normalize(mat3(modelMatrix) * normal);");
// Pesos triplanar desde la normal (sesgados por pow 4).
shader.fragmentShader =
"uniform sampler2D uAlbedo;\nvarying vec3 vTriPos;\nvarying vec3 vTriNrm;\n" +
"vec3 mtBlend(vec3 n){ vec3 b=pow(abs(n),vec3(4.0)); return b/max(b.x+b.y+b.z,1e-4); }\n" +
shader.fragmentShader.replace("#include <color_fragment>",
"#include <color_fragment>\n { vec3 bl = mtBlend(vTriNrm); float s = uMarbleScale;\n" +
" float m = texture2D(uAlbedo, vTriPos.zy*s).g*bl.x\n" +
" + texture2D(uAlbedo, vTriPos.xz*s).g*bl.y\n" +
" + texture2D(uAlbedo, vTriPos.xy*s).g*bl.z;\n" +
" diffuseColor.rgb *= (0.74 + 0.46 * m); }");
};
mat.customProgramCacheKey = () => "museum-marble-triplanar";
return mat;
}Mármol triplanar sobre escaneos sin UVs. Los escaneos STL no traen coordenadas UV. Se inyecta mármol muestreando en posición de mundo sobre tres ejes y mezclando por la normal.
En concreto
Un solo Canvas con navegación espacial accesible y un 'modo museo' de estatuas con spotlight.
Mármol PBR triplanar procedural aplicado sobre escaneos sin coordenadas UV.
Carga por tiers en dos fases con corrección adaptativa por FPS en runtime, verificada con tests de red.
Pipeline propio de optimización de assets: 56 GLB de 91MB a 61MB (Draco + meshopt).
Estudio en crecimiento: la vitrina alimenta una operación que ronda las 20 impresiones por semana, con picos de cientos de impresiones en los meses fuertes.