Сделать заливку холста включенной в фигуры обводкой

Хорошо, поэтому я делаю движок 3D-рендеринга на чистом javascript, конечно, в качестве задачи - проверить свои навыки линейной алгебры. Я не использую webgl, поэтому, пожалуйста, не говорите "используйте webgl".

В любом случае, программное обеспечение примет треугольники, камеру и локальные преобразования и отобразит данные на экране (я даже сделал это интерактивным).

Однако есть только 6 строк кода рендеринга, а именно:

// some shading and math calculations then this:
context.fillStyle = color;
context.strokeStyle = color;
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x0, y0);
context.closePath();
context.fill();
context.stroke();

И хотя это работает, на моем Chromebook скорость падает до 10 кадров в секунду с 4k+ лицами. (60 кадров в секунду на обычном компьютере)

В любом случае, это выводит это:

*Визуализация дракона в 3D*

Но чтобы сделать это быстрее, а также из-за того, что изменения состояния холста происходят медленно, я удалил обводку, сделав код рендеринга:

// some shading and math calculations then this:
context.fillStyle = color;
//context.strokeStyle = color;
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x0, y0);
context.closePath();
context.fill();
//context.stroke();

который работает в два раза быстрее, но в результате на экране отображается следующее: (другая модель)

*кролик*

который имеет уродливые линии повсюду по краям треугольников (которые удаляются, когда я снова добавляю штрих)

Тем не менее, fps удваивается, а прирост производительности велик...

Поэтому я считаю, что линии вызваны тем, что заливка холста не включает область, где она должна быть обведена (как вы можете сказать, контур).

Я пытался исправить это с помощью математики, и хотя это работает, есть некоторые крайние случаи, когда это не так.

Итак, мой вопрос заключается в следующем: Есть ли способ сделать так, чтобы контекстная заливка включала область обводки без обводки, потому что это очень дорого?


person notrota    schedule 01.11.2017    source источник
comment
как вы реализовали zIndex в холсте?   -  person juztcode    schedule 28.01.2021


Ответы (1)


Использование как обводки, так и заливки вызовет растеризацию дважды, что объясняет приблизительное двойное время.

Причина, по которой вы получаете сбои между треугольниками, заключается в ошибках округления и сглаживания. Для этого нет прямого решения; обводка, конечно, покроет сбои, но чтобы сделать это без обводки, вам потребуется сместить и расширить по крайней мере каждый второй треугольник.

Тем не менее, вы можете использовать небольшой трюк, чтобы скрыть разрыв, а именно перерисовать все изображение (как растровое изображение) со смещением сверху всего на один пиксель (вы можете обойтись 0,5 пикселя, но тогда потребуется сглаживание). Это увеличивает время, но гораздо меньше, чем растеризация или пересчет путей.

Скажите, что результат слева — это то, что у вас есть (смоделировано здесь) с явным зазором. Перерисовав его сверху, как показано справа, вы закроете зазор без особых искажений.

иллюстрация

Просто используйте:

ctx.drawImage(sourceCanvas, 1, 1);

Совет: при вызове только fill() вам не нужен closePath(), поскольку он называется неявным, экономя одну операцию. Микроскопический выигрыш, возможно, но все же (при более сложной геометрии он даже может иметь влияние :) ).

Примечание: рисование самому себе приведет к внутреннему размещению временной растровой копии. Однако вам нужно будет выполнить только одну дополнительную операцию drawImage(). Можно использовать рендеринг вне холста, но дважды рисовать на основном отображаемом холсте. Так или иначе...

var ctx = c.getContext("2d");

ctx.fillStyle = "#777";
tri(10,10, 72,17, 40.2, 100);

// simulates gap
ctx.fillStyle = "#222";
tri(72.5,17.5, 40.7,100.5, 90,25);

// fill entire image back again, drawn twice here for demo
ctx.drawImage(c, 100, 0);
ctx.drawImage(c, 0, 0, 100, 150, 101, 1, 100, 150);

ctx.fillText("Raster", 5, 8);
ctx.fillText("Offset self", 105, 8);

function tri(x0,y0,x1,y1,x2,y2) {
  ctx.beginPath();
  ctx.moveTo(x0, y0);
  ctx.lineTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.fill();
}
<canvas id=c></canvas>

person Community    schedule 01.11.2017