Подтвердить что ты не робот

Обработка изображений и отображение текстур с помощью HTML5 Canvas?

В 3D-движке, над которым я работаю, мне удалось успешно провести куб в 3D. Единственный способ заполнить стороны - использовать сплошной цвет или градиент, насколько мне известно. Чтобы сделать вещи более захватывающими, мне очень хотелось бы реализовать отображение текстур с помощью простого растрового изображения.

Дело в том, что я вряд ли могу найти какие-либо статьи или примеры кода в отношении обработки изображений в JavaScript. Более того, поддержка изображений в холсте HTML5, по-видимому, ограничена обрезкой.

Как я могу растянуть растровое изображение так, чтобы прямоугольное растровое изображение могло заполнить нерегулярную грань куба? В 2D, спроектированная поверхность квадратного куба, из-за перспективы, не квадратной формы, поэтому мне придется растянуть ее, чтобы она поместилась в любой четырехугольник.

Надеюсь, этот образ прояснит мою мысль. Левое лицо теперь заполнено белым/черным градиентом. Как я мог заполнить его растровым изображением после того, как он был нанесен на карту?

Cube

Есть ли у кого-нибудь советы относительно перспективного сопоставления текстур (или манипуляции с изображениями вообще) с использованием JavaScript и холста HTML5?

Изменить: Я получил работу, благодаря 6502!

Это, однако, довольно интенсивный процессор, поэтому я хотел бы услышать любые идеи оптимизации.

Результат с использованием метода 6502 - Используемое изображение текстуры

4b9b3361

Ответ 1

Я думаю, что вы никогда не получите точного результата... Я потратил некоторое время на изучение того, как делать 3D-графику с использованием контекста canvas 2d, и я счел целесообразным делать текстурирование с помощью gouraud shading, вычислив соответствующие 2d градиенты и матрицы:

  • Твердые многоугольники, конечно же, легкие.
  • Заполнение Gouraud возможно только на одном компоненте (т.е. вы не можете иметь треугольник, где каждая вершина является произвольным RGB, заполненным билинейной интерполяцией, но вы можете делать это заполнение, используя, например, три произвольных оттенка одного цвета)
  • Линейное отображение текстур может быть выполнено с помощью обрезки и рисования изображений

Я бы реализовал перспективное отображение текстуры с использованием деления сетки (например, на PS1).

Однако я нашел много проблем... например, рисование изображений с матричным преобразованием (необходимое для текстурного сопоставления) довольно неточно на хроме и IMO невозможно получить результат с точным пикселем; в общем, нет способа отключить сглаживание при рисовании на холсте, и это означает, что вы получите видимые сквозные линии при разбиении на треугольники. Я также нашел, что многопроцессорный рендеринг работает очень плохо на хроме (вероятно, из-за того, как реализована рендеринг hw-accellerated).

В целом такой вид рендеринга, безусловно, является стрессом для веб-браузеров и, по-видимому, эти варианты использования (например, странные матрицы) не очень хорошо тестируются. Я даже смог заставить Firefox сбой настолько плохо, что он уничтожил всю систему X susbsystem на моем Ubuntu.

Вы можете увидеть результаты моих усилий здесь или как видео здесь... IMO, несомненно, впечатляет, что это можно сделать в браузере без использования 3D-расширений, но я не думаю, что текущие проблемы будут исправлены в будущем.

В любом случае основная идея, используемая для рисования изображения, чтобы 4 угла заканчивались в определенном положении пикселей, состоит в том, чтобы нарисовать два треугольника, каждый из которых будет использовать билинейную интерполяцию.

В следующем коде я предполагаю, что у вас есть объект изображения texture и 4 угла, каждый из которых является объектом с полями x,y,u,v, где x,y являются пиксельными координатами на целевом холсте, а u,v - пиксельными координатами на 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();
    }
}

Эти уродливые странные формулы для всех этих "дельта-переменных" используются для решения двух линейных систем трех уравнений с тремя неизвестными с помощью метода Cramer и Sarrus для детерминант 3x3.

В частности, мы ищем значения a, b,... f, чтобы выполнялись следующие уравнения:

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 является определителем матрицы

u0  v0  1
u1  v1  1
u2  v2  1

и, например, delta_a является определителем одной и той же матрицы при замене первого столбца на x0, x1, x2. С их помощью вы можете вычислить a = delta_a / delta.