creo que nunca obtener un resultado preciso ... pasé algún tiempo investigando cómo hacer gráficos en 3D usando el contexto 2d lienzo y lo encontré viable para hacer el mapeo de texturas sombreado Gouraud calculando gradientes y matrices 2D apropiadas:
- polígonos sólidos son, por supuesto, fácil
- llenado Gouraud es posible sólo en un componente (es decir, no se puede tener un triángulo donde cada vértice es un RGB arbitraria lleno de interpolación bilineal, pero y ou puede hacer que el llenado usando, por ejemplo tres tonos arbitrarios de un solo color)
- mapeado de texturas lineal se puede hacer usando el recorte y la imagen de dibujo
I sería implementar perspectiva correcta asignación de texturas utilizando subdivisión de malla (como en PS1).
Sin embargo, encontré muchos problemas ... por ejemplo, el dibujo de la imagen con una transformación matricial (necesaria para el mapeo de texturas) es bastante inexacto en Chrome e IMO; es imposible obtener un resultado preciso; en general, no hay forma de desactivar el antialiasing cuando se dibuja en un lienzo, lo que significa que obtendrá líneas transparentes visibles al subdividir en triángulos. También encontré el renderizado multipaso funcionando realmente mal en Chrome (probablemente debido a la forma en que se implementa el renderizado hw-accellerated).
En general, este tipo de renderizado es sin duda un estrés 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 fallara tanto que quitó todo el sistema X de mi Ubuntu.
Puede ver los resultados de mis esfuerzos here o como un video here ...IMO seguramente está impresionando que esto se puede hacer en un navegador sin utilizar 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 para que las 4 esquinas terminen en una posición de píxeles específicos es dibujar dos triángulos, cada uno de los cuales utilizará la interpolación bilineal.
En el siguiente código que se supone que tiene un objeto de imagen texture
y 4 esquinas de cada uno de los 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 feas fórmulas extrañas para todas esas variables "delta" se usan para resolver dos sistemas lineales de tres ecuaciones en tres incógnitas usando el método Cramer's y el esquema Sarrus para determinantes 3x3.
Más específicamente Buscamos a los valores de a
, b
, ... f
por lo que las siguientes ecuaciones se satisfacen
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
.
Si está fabricando un verdadero motor 3D, esta podría no ser la mejor manera de realizar texturas. Las coordenadas de textura/colores/normales, etc. deben almacenarse por vértice para permitir más flexibilidad. –
Es un proyecto hobby simple, no se convertirá en mucho más que este cubo en realidad. – pimvdb
@pimvdb ¿hay alguna posibilidad de compartir un código? –