Pregunta

En un motor 3D en el que estoy trabajando, he logrado dibujar con éxito un cubo en 3D. El único método para llenar los lados es usar un color sólido o un gradiente en lo que a mí respecta. Para hacer las cosas más emocionantes, realmente me encantaría implementar el mapeo de texturas usando un mapa de bits simple.

El punto es que apenas puedo encontrar ningún artículo o muestra de código sobre el tema de la manipulación de imágenes en JavaScript. Además, el soporte de imagen en el lienzo HTML5 parece estar restringido al cultivo.

¿Cómo podría estirar un mapa de bits para que un mapa de bits rectangular pueda llenar una cara de cubo no regular? En 2D, una cara de cubo cuadrada proyectada es, debido a la perspectiva, no a una forma cuadrada, por lo que tendré que estirarlo para que se ajuste en cualquier cuadrilátero.

Ojalá esta imagen aclare mi punto. La cara izquierda ahora está llena con un gradiente blanco/negro. ¿Cómo podría llenarlo con un mapa de bits, después de haber sido mapeado por textura?

Cube

¿Alguien tiene algún consejo sobre el mapeo de textura en perspectiva (o la manipulación de la imagen) usando el lienzo JavaScript y HTML5?

Editar: ¡Lo tengo funcionando, gracias a 6502!

Sin embargo, es más bien intensivo en CPU, por lo que me encantaría escuchar cualquier idea de optimización.

Resultado utilizando la técnica de 6502 - Imagen de textura utilizada

¿Fue útil?

Solución

Creo que nunca obtendrá un resultado preciso ... Pasé algún tiempo investigando cómo hacer gráficos 3D usando el contexto de lienzo 2D y me pareció viable hacer el sombreado de mapeo de textura guraud calculando 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 sea un RGB arbitrario lleno de interpolación bilineal, pero puede hacer ese relleno usando por ejemplo tres tonos arbitrarios de un solo color)
  • La mapeo de textura lineal se puede hacer con recorte y dibujo de imágenes

Implementaría el mapeo de texturas correcto de perspectiva 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 textura) es bastante inexacto en Chrome y en mi opinión, es imposible obtener un resultado preciso de píxel; En general, no hay forma de desactivar la antialiasing al dibujar un lienzo y esto significa que obtendrá líneas transparentes visibles al subdividir en triángulos. También encontré que la representación multipasa funcionaba realmente mal en Chrome (probablemente debido a cómo se implementa la representación acelerado por HW).

En general, este tipo de representación seguramente es 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 se estrellara tanto que derribó toda la X Susssystem en mi Ubuntu.

Puedes ver los resultados de mis esfuerzos aquí o como un video aquí... La OMI seguramente impresiona que esto se puede 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 para que las 4 esquinas terminen en posición de píxeles específicos es dibujar dos triángulos, cada uno de los cuales usará la interpolación bilineal.

En el siguiente código supongo que tiene un objeto de imagen texture y 4 esquinas, cada uno de los cuales es un objeto con campos x,y,u,v dónde x,y son coordenadas de píxeles en el lienzo objetivo 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 feas extrañas para todas esas variables "delta" se utilizan para resolver dos sistemas lineales de tres ecuaciones en tres incógnitas usando Cramero método y Sarro esquema para determinantes 3x3.

Más específicamente estamos buscando los valores de a, b, ... f para que las siguientes ecuaciones estén satisfechas

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top