使画布填充包括形状中的笔划

时间:2017-11-01 05:46:34

标签: javascript html5 canvas fill stroke

好的,所以我在纯javascript中制作3D渲染引擎,当然是挑战 - 测试我的线性代数技能。我没有使用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上的4k +面孔下降到10fps。 (常规计算机上60fps)

无论如何,输出这个:

*Dragon Rendered 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();

的运行速度是原来的两倍,但是渲染到屏幕上的结果是:(不同型号)

*bunny*

在三角形的边缘到处都有丑陋的线条(当我重新添加笔划时会被删除)

然而,fps翻倍且性能提升很好......

所以我相信这些线是因为画布填充不包括它所描绘的区域(大纲,如你所说)。

我试图用数学来修复它,虽然它有效,但有些边缘情况并不是

所以我的问题如下有没有办法让上下文填充包括没有描边的描边区域,因为它非常昂贵?

1 个答案:

答案 0 :(得分:3)

同时使用笔触和填充将强制光栅化两次,这解释了大概的双倍时间。

你在三角形之间出现毛刺的原因是因为舍入误差和抗锯齿。对此没有直接的解决方案;当然,笔划将覆盖毛刺,但如果没有笔划,则要求您至少偏移并扩展每个其他三角形。

然而,您可以使用一个小技巧来掩盖间隙,即在顶部偏移上重绘整个图像(作为位图)只需一个像素(您可能会使用0.5像素,但需要消除锯齿) )。这增加了时间,但远远小于光栅化或重新计算路径。

假设左边的结果是你所拥有的(在这里模拟)有明显的差距。如右图所示将其重新绘制在顶部将覆盖间隙,而不会有太多失真。

illustration

只需使用:

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>