如何在画布上遮蔽圆圈

时间:2016-02-20 02:22:10

标签: html5 canvas

我正与HTML5合作canvas。我已经画了一个2D圆圈。现在我想用一个颜色遮住圆圈。但阴影看起来像一个3D圆圈。这可能用帆布吗?。谢谢你。

3 个答案:

答案 0 :(得分:7)

假冒烟雾和镜子

伪造球体上的光。我猜这是一个球体,就像你说圈子一样,你可能意味着一个甜甜圈。这种技术也适用于甜甜圈。

所以要照明。

Phong Shading

最基本的照明模式是Phong(来自记忆)。它使用入射光线和表面法线(从表面以90度出射的线)之间的角度。反射光量是光强度的角度时间的余弦。

领域轻松

由于球体是对称的,因此我们可以使用径向渐变来为球体上的每个像素应用值,对于直接在头顶上的光线的球体,可以轻松地产生完美的阴影球体。

执行该操作的代码。 x,y是球体的中心,r是半径。当你从球体的中心移出时,光和表面法线之间的角度很容易计算。它从零开始,以Math.PI / 2(90deg)结束。所以反射值是该角度的余弦。

    var grd = ctx.createRadialGradient(x,y,0,x,y,r);
    var step = (Math.PI/2)/r;
    for(var i = 0; i < (Math.PI/2); i += step){
       var c = "" + Math.floor(Math.max(0,255 * Math.abs(Math.cos(i)));
       grd.addColorStop(i/(Math.PI/2),"rgba("+c+","+c+","+c+","1)");
    }

该代码会创建一个适合圆圈的渐变。

荷马食物的Mod

要做甜甜圈,你需要修改我。甜甜圈有一个内半径和外半径(r1,r2),因此在for循环内修改i

 var ii = (i/(Math.PI/2)); // normalise i
 ii *= r2; // scale to outer edge
 ii = ((r1+r2)/2)-ii; // get distance from center line
 ii = ii / ((r2-r1)/2); // normalise to half the width;
 ii = ii * Math.PI * (1/2); // scale to get the surface norm on the donut.
 // use ii as the surface normal to calculate refelected light
 var c = "" + Math.floor(Math.max(0,255 * Math.abs(Math.cos(ii)));

Phong Shading糟透了

通过phong阴影吸收大量时间而不会做。这也不允许偏离中心或甚至部分位于球体后面的灯光。

我们需要添加偏心灯光的功能。幸运的是径向梯度可以偏移

  var grd = ctx.createRadialGradient(x,y,0,x,y,r);

前3个数字是渐变的起始圆,可以放在任何位置。问题在于,当我们移动起始位置时,phong着色模型会崩溃。为了解决这个问题,有一点烟雾和镜子可以使眼睛相信大脑想要的东西。

我们根据光线距离中心的距离,调整径向渐变上每个色标的下降,亮度,扩散和角度。

镜面高光

这有点改善但仍然不是最好的。照明的另一个重要组成部分是镜面反射(亮点)。这取决于反射光和眼睛之间的角度。由于我们不想做所有这些(javascript很慢),我们将通过略微修改phong着色来修改它。我们简单地将表面法线乘以一个大于1的值。虽然不完美,但效果很好。

表面属性和环境

下一个灯是彩色的,球体具有取决于频率的反射特性,并且还有环境光。我们不想对所有这些东西进行建模,因此我们需要一种伪造它的方法。

这可以通过合成完成(用于几乎所有3D电影制作)。我们一次建立一层照明。 2D API为我们提供合成操作,因此我们可以创建多个渐变并对它们进行分层。

涉及到更多数学,但我尽量保持简单。

演示

以下演示实现了球体的实时着色(适用于所有径向对称的对象)除了画布和鼠标的一些设置代码之外,演示有两个部分,主循环通过分层{{1}进行合成函数lights创建渐变。

使用的灯光可以在对象createGradient中找到,并具有控制图层的各种属性。第一层应该使用lightscomp = source-in,否则最终会显示背景。所有其他层灯都可以满足您的需求。

标志lum = 1告诉着色器灯光是镜面的,必须包含spec,因为我没有审查它的存在。

灯光的颜色位于数组col中,代表红色,绿色和蓝色。这些值可以大于256且小于0,因为自然界中的光具有巨大的动态范围,并且某些效果需要您将入射光线提升到RGB像素的255极限之上。

我添加了一个&#34;乘法&#34;到分层结果。这是烟雾和镜子方法中的魔力。

如果您喜欢使用值和图层播放代码。移动鼠标以更改光源位置。

这不是真正的照明,它是假的,但只要看起来不错就关心谁。洛尔

<强>更新

发现了一个错误,所以修复它,当我在这里时,更改代码,以便在单击鼠标左键时随机化灯光。这样您就可以看到将specPower > 1与渐变结合使用时可以实现的照明范围。

&#13;
&#13;
ctx.globalCompositeOperation
&#13;
&#13;
&#13;

答案 1 :(得分:4)

正如@ danday74所说,你可以使用渐变来为你的圆圈增加深度。

您还可以使用阴影为圆圈添加深度。

这是一个概念验证,展示了一个3D甜甜圈:

enter image description here

我留给你设计你想要的圈子

raw
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

var PI=Math.PI;

drawShadow(150,150,120,50);


function drawShadow(cx,cy,r,strokewidth){
  ctx.save();
  ctx.strokeStyle='white';
  ctx.lineWidth=5;
  ctx.shadowColor='black';
  ctx.shadowBlur=15;
  //
  ctx.beginPath();
  ctx.arc(cx,cy,r-5,0,PI*2);
  ctx.clip();
  //
  ctx.beginPath();
  ctx.arc(cx,cy,r,0,PI*2);
  ctx.stroke();
  //
  ctx.beginPath();
  ctx.arc(cx,cy,r-strokewidth,0,PI*2);
  ctx.stroke();
  ctx.shadowColor='rgba(0,0,0,0)';
  //
  ctx.beginPath();
  ctx.arc(cx,cy,r-strokewidth,0,PI*2);
  ctx.fillStyle='white'
  ctx.fill();
  //
  ctx.restore();
}
body{ background-color: white; }
canvas{border:1px solid red; margin:0 auto; }

答案 2 :(得分:2)

您可以调查的各种想法......

1使用图像作为圆的纹理

2使用渐变填充圆,可能是径向渐变

3考虑使用图像蒙版,一个定义透明度的黑/白掩模(这里不是正确的解决方案)