使画布填充形状时也包含描边

6

好的,所以我正在纯 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();

运行速度加倍,但呈现到屏幕上的结果是这样的:(不同的模型) *兔子* 在三角形边缘处有丑陋的线条(将描边重新添加后会消失),但fps加倍,性能提升很大......
所以我认为这些线条是由于画布填充不包括它将描边绘制的区域(如你所说的轮廓)。
我尝试用数学方法修复它,虽然它能工作,但在某些极端情况下它不起作用。
因此我的问题如下: 是否有一种方法可以使上下文填充包括描边区域而不需要进行昂贵的描边操作?

你是如何在Canvas中实现zIndex的呢? - juztcode
1个回答

8

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

三角形之间出现故障的原因是由于舍入误差和抗锯齿。这并没有一个直截了当的解决方案;描边将覆盖故障,但要在没有描边的情况下完成,则需要对至少每个其他三角形进行偏移和扩展。

然而,您可以使用小技巧来覆盖间隙,即在上面重新绘制整个图像(作为位图),只需偏移一个像素(您可能可以摆脱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>


谢谢你的提示!“当只调用fill()时,不需要closePath()”。 - gbenroscience

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接