我正与HTML5
合作canvas
。我已经画了一个2D圆圈。现在我想用一个颜色遮住圆圈。但阴影看起来像一个3D圆圈。这可能用帆布吗?。谢谢你。
答案 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
中找到,并具有控制图层的各种属性。第一层应该使用lights
和comp = source-in
,否则最终会显示背景。所有其他层灯都可以满足您的需求。
标志lum = 1
告诉着色器灯光是镜面的,必须包含spec
,因为我没有审查它的存在。
灯光的颜色位于数组col中,代表红色,绿色和蓝色。这些值可以大于256且小于0,因为自然界中的光具有巨大的动态范围,并且某些效果需要您将入射光线提升到RGB像素的255极限之上。
我添加了一个&#34;乘法&#34;到分层结果。这是烟雾和镜子方法中的魔力。
如果您喜欢使用值和图层播放代码。移动鼠标以更改光源位置。
这不是真正的照明,它是假的,但只要看起来不错就关心谁。洛尔
<强>更新强>
发现了一个错误,所以修复它,当我在这里时,更改代码,以便在单击鼠标左键时随机化灯光。这样您就可以看到将specPower > 1
与渐变结合使用时可以实现的照明范围。
ctx.globalCompositeOperation
&#13;
答案 1 :(得分:4)
正如@ danday74所说,你可以使用渐变来为你的圆圈增加深度。
您还可以使用阴影为圆圈添加深度。
这是一个概念验证,展示了一个3D甜甜圈:
我留给你设计你想要的圈子
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考虑使用图像蒙版,一个定义透明度的黑/白掩模(这里不是正确的解决方案)