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

Guy*_*uff 6 html javascript 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();
Run Code Online (Sandbox Code Playgroud)

虽然这有效,但在我的 Chromebook 上,当 4k+ 面孔出现时,速度会下降到 10fps。(普通计算机上为 60fps)

无论如何,输出是这样的:

*龙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();
Run Code Online (Sandbox Code Playgroud)

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

*兔子*

三角形边缘处都有难看的线条(当我重新添加笔画时,这些线条会被删除)

然而,帧率翻倍,性能提升也很大......

所以我相信这些线条是因为画布填充不包括它所描画的区域(轮廓正如你可能会说的)而引起的。

我尝试用数学来修复它,尽管它有效,但在一些边缘情况下却不起作用

所以我的问题如下有没有一种方法可以使上下文填充包含笔划区域而无需笔划,因为它非常昂贵?

小智 8

同时使用描边和填充将强制光栅化两次,这解释了大约两倍的时间。

三角形之间出现故障的原因是舍入误差和抗锯齿。对此没有直接的解决方案;当然,描边将覆盖小故障,但是要在没有描边的情况下完成此操作,将需要您至少偏移并扩展每个其他三角形。

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

假设左侧的结果是您所得到的(此处模拟的),并且有明显的间隙。如右图所示在顶部重新绘制它可以覆盖间隙,而不会造成太多扭曲。

插图

只需使用:

ctx.drawImage(sourceCanvas, 1, 1);
Run Code Online (Sandbox Code Playgroud)

提示:仅调用时fill()不需要,closePath()因为它被称为隐式,节省了一个操作。也许是微观增益,但仍然如此(对于更复杂的几何形状,它甚至可能会产生影响:))。

注意:绘制自身将导致临时位图副本的内部分配。但是,您只需要执行一项额外drawImage()操作。该选项是使用画布外渲染,但在主显示画布上绘制两次。无论哪种方式...

ctx.drawImage(sourceCanvas, 1, 1);
Run Code Online (Sandbox Code Playgroud)
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();
}
Run Code Online (Sandbox Code Playgroud)