tamaño - insertar imagenes en javascript
¿Manipulación de imágenes y mapeo de texturas usando HTML5 Canvas? (1)
En un motor 3D en el que estoy trabajando, he logrado dibujar un cubo en 3D. El único método para rellenar los lados es usar un color sólido o degradado en lo que a mí respecta. Para hacer las cosas más emocionantes, me encantaría implementar el mapeo de texturas usando un simple mapa de bits.
El punto es que casi no puedo encontrar ningún artículo o ejemplo de código sobre el tema de la manipulación de imágenes en JavaScript. Además, el soporte de imágenes en el lienzo HTML5 parece estar restringido al recorte.
¿Cómo podría ir sobre estirar un mapa de bits para que un mapa de bits rectangular pueda llenar una cara de cubo no regular? En 2D, debido a la perspectiva, una cara de cubo cuadrado proyectada no tiene forma cuadrada, por lo que tendré que estirarla para que encaje en cualquier cuadrilátero.
Esperemos que esta imagen aclare mi punto. La cara izquierda ahora está llena con un degradado blanco / negro. ¿Cómo podría rellenarlo con un mapa de bits, una vez que se ha mapeado la textura?
¿Alguien tiene algún consejo sobre el mapeo de texturas en perspectiva (o la manipulación de imágenes) utilizando JavaScript y HTML5 Canvas?
Edit: ¡ Lo tengo funcionando, gracias a 6502!
Sin embargo, requiere bastante CPU, por lo que me encantaría escuchar cualquier idea de optimización.
Resultado utilizando la técnica de 6502 - Imagen de textura utilizada.
Creo que nunca obtendrás un resultado preciso ... Pasé un tiempo investigando cómo hacer gráficos 3D utilizando el contexto 2d de Canvas y encontré que era viable hacer sombreado gouraud de mapeo de textura al calcular los gradientes y matrices 2d apropiados:
- Los polígonos sólidos son, por supuesto, fáciles.
- El relleno de Gouraud solo es posible en un componente (es decir, no puede tener un triángulo donde cada vértice es un RGB arbitrario lleno de interpolación bilineal, pero puede realizar ese relleno utilizando, por ejemplo, tres tonos arbitrarios de un solo color)
- El mapeo de textura lineal se puede hacer usando recorte y dibujo de imagen
Yo implementaría el mapeo de texturas con perspectiva correcta utilizando la subdivisión de malla (como en PS1).
Sin embargo, encontré muchos problemas ... por ejemplo, el dibujo de imágenes con una transformación de matriz (necesaria para el mapeo de texturas) es bastante inexacto en Chrome y en IMO es imposible obtener un resultado con precisión de píxeles; en general, no hay forma de desactivar el suavizado cuando se dibuja en un lienzo y esto significa que obtendrá líneas transparentes visibles al subdividir en triángulos. También encontré que la representación de múltiples pasos funciona realmente mal en Chrome (probablemente debido a la forma en que se implementa la representación acelerada por hw).
En general, este tipo de representación es sin duda una carga para los navegadores web y, aparentemente, estos casos de uso (matrices extrañas, por ejemplo) no se prueban muy bien. Incluso pude hacer que Firefox se estrellara tan mal que derribó todo el sistema de X en mi Ubuntu.
Puede ver los resultados de mis esfuerzos here o como un video here ... IMO seguramente impresiona que esto se pueda hacer en un navegador sin usar extensiones 3D, pero no creo que los problemas actuales se solucionen en el futuro.
De todos modos, la idea básica utilizada para dibujar una imagen de modo que las 4 esquinas terminen en una posición de píxeles específica es dibujar dos triángulos, cada uno de los cuales usará interpolación bilineal.
En el siguiente código, asumo que tiene una texture
objeto de imagen y 4 esquinas, cada una de las cuales es un objeto con campos x,y,u,v
donde x,y
son coordenadas de píxeles en el lienzo de destino y u,v
son coordenadas de píxeles en texture
:
function textureMap(ctx, texture, pts) {
var tris = [[0, 1, 2], [2, 3, 0]]; // Split in two triangles
for (var t=0; t<2; t++) {
var pp = tris[t];
var x0 = pts[pp[0]].x, x1 = pts[pp[1]].x, x2 = pts[pp[2]].x;
var y0 = pts[pp[0]].y, y1 = pts[pp[1]].y, y2 = pts[pp[2]].y;
var u0 = pts[pp[0]].u, u1 = pts[pp[1]].u, u2 = pts[pp[2]].u;
var v0 = pts[pp[0]].v, v1 = pts[pp[1]].v, v2 = pts[pp[2]].v;
// Set clipping area so that only pixels inside the triangle will
// be affected by the image drawing operation
ctx.save(); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2); ctx.closePath(); ctx.clip();
// Compute matrix transform
var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2;
var delta_a = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2;
var delta_b = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2;
var delta_c = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2
- v0*u1*x2 - u0*x1*v2;
var delta_d = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2;
var delta_e = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2;
var delta_f = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2
- v0*u1*y2 - u0*y1*v2;
// Draw the transformed image
ctx.transform(delta_a/delta, delta_d/delta,
delta_b/delta, delta_e/delta,
delta_c/delta, delta_f/delta);
ctx.drawImage(texture, 0, 0);
ctx.restore();
}
}
Esas fórmulas extrañas y feas para todas esas variables "delta" se usan para resolver dos sistemas lineales de tres ecuaciones en tres incógnitas usando Cramer''s método Cramer''s y el esquema de Sarrus para los determinantes 3x3.
Más específicamente, estamos buscando los valores de a
, b
, ... f
para que se cumplan las siguientes ecuaciones
a*u0 + b*v0 + c = x0
a*u1 + b*v1 + c = x1
a*u2 + b*v2 + c = x2
d*u0 + e*v0 + f = y0
d*u1 + e*v1 + f = y1
d*u2 + e*v2 + f = y2
delta
es el determinante de la matriz.
u0 v0 1
u1 v1 1
u2 v2 1
y, por ejemplo, delta_a
es el determinante de la misma matriz cuando reemplaza la primera columna con x0
, x1
, x2
. Con estos puedes calcular a = delta_a / delta
.