在Javascript中对非矩形形状创建斜角效果

时间:2014-07-12 20:15:05

标签: javascript canvas

我想在(非矩形)形状上创建斜角效果(在canvas元素中)。搜索了几乎整个互联网:到目前为止没有运气。寻找建议。在我耗尽所有现有可能性之前,我不想自己实施斜面效果。目标浏览器是Chrome。

这是没有应用斜角的图像,以及具有我正在寻找的效果的图像。这是在Photoshop中完成的:

original bevel

编辑: 我和马克的建议纠缠在一起。 阴影偏移方法的两个问题是:

1)必须绘制一个边框,我不想要它。

2)边框的粗细决定了阴影的强度。


我不希望边框可见,所以我需要一种尽可能小的边框,然后切出边框的方法。截至目前,这涉及许多不同的步骤:

1)首先,我创建一个具有透明填充颜色,边框和轻微阴影(带有(0,0)偏移)和模糊为1的形状。然后我将形状绘制到自身上增加阴影的不透明度。

2)然后,我创建一个带有透明填充颜色,边框和阴影的形状,如markE所述。我将lineWidth设置为一个非常小的数字 - 例如0.5。我将(1)中的形状应用到此形状上(通过globalCompositeOperation = 'destination-out'),然后将形状绘制在自身上3次,以增加阴影的不透明度。

3)然后我绘制正常的形状,没有边框。我将(2)应用到正常形状上,并再次使用globalCompositeOperation = 'destination-out'从(1)切割出与形状边界的内容。

结果如下:

result

2 个答案:

答案 0 :(得分:6)

首先问题是 - 斜角效应是如何起作用的?

你可能会说斜面效果也是你的平面2d图像的三维效果,它确实存在(好吧,就像物体点亮的方式一样)。

基本上,着色计算就像在一些实际的3d对象上完成一样,但由于你只有2d图像,你还需要一些称为 - 法线贴图。

法线贴图是2d图像,它代替图像中的颜色,在RGB通道中编码了表面法线信息。因此,图像中的每个像素都有RGB分量,R通道用于x方向的表面法线,G用于y方向的法线,B用于法线的z轴分量。

详细了解法线贴图here

另一种选择是使用凹凸贴图。这是一个图像,而不是像素的颜色或关于法线的信息,保存有关像素“高度”的信息。

详细了解凹凸贴图here

无论是凹凸贴图还是法线贴图,结果都非常相似。凹凸贴图在图像中只有一个通道(灰度图像),因此它们更容易制作,并且往往更“逼真”,但它取决于您的效果。

以下是方形图像上斜角效果的凹凸贴图示例。

bevel - bump map

问题是图像通常具有不均匀的形状(如您的示例),那么如何为这些图像创建凹凸贴图?

解决方案是创建一个称为“Eucledian Distance Matrix”的东西。

EDM广泛用于Photoshop中,它们可以保存信息中某些像素远离图像的数量(图层上最近的彩色像素)。 EDM用于抚摸对象和斜角效果。 (有很多资源可用于生成EDM)

例如,在第2行和第2列中只有像素着色的4x4图片的EDM示例如下所示。

1 1 1 2
1 0 1 2
1 1 1 2
2 2 2 3

使用EDM,您可以获取信息在图像“内部”有多少像素,然后根据该信息生成凹凸贴图。

if (isInside(x,y))
    if ( dist = innerDistanceAt(x,y) < bevelWidth )
        bumpMap[x][y] = dist/bevelWidth;
    else
        bumpMap[x][y] = 1.0;

这段代码只是一个伪代码示例,但您应该明白这一点。

所以我们现在制作凹凸贴图,是时候计算光照(做阴影)。

有很多可以使用的着色模型 - list。这里有更多的视觉差异 - link

我将从Lambert模型开始。你可以找到很多关于它的资源。

通常,您需要曲面法线(或凹凸贴图,然后可以从中计算曲面法线(使用微分))和光矢量。根据该信息和漫反射颜色(在这种情况下,图像中的像素颜色),您可以计算该像素的亮度,然后输出着色像素。

我没有发布很多工作代码,因为笔画效果和斜角效果非常复杂,但你应该明白这个想法。这就是他们在照片编辑软件中真正起作用的方式,正如你所看到的,它并不那么容易。

如果您有任何问题,请随时提出。

关于斜角效果的随机链接 - here

答案 1 :(得分:2)

您可以使用插入阴影创建边框效果。

enter image description here

这个过程很简单:

  1. 定义非矩形路径
  2. 从非矩形路径
  3. 创建剪切路径
  4. 应用阴影描边(斜角效果);
  5. 以下是示例代码和演示:http://jsfiddle.net/m1erickson/4kvLn/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <style>
        body{ background-color: white; }
        #canvas{border:1px solid red;}
    </style>
    <script>
    $(function(){
    
        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
    
        var points=[];
        points.push({x:130,y:130});
        points.push({x:200,y:130});
        points.push({x:250,y:165});
        points.push({x:200,y:200});
        points.push({x:130,y:200});
    
    var img=new Image();
    img.onload=start;
    img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/landscape2.jpg";
    
    function start(){
    
        definePath();
        ctx.save();
        ctx.strokeStyle="#000";
        ctx.clip();
    
        ctx.drawImage(img,10,0);
    
        ctx.shadowColor = '#000';
        for(var i=0;i<3;i++){
            for(var j=0;j<3;j++){
                ctx.shadowBlur=4+i;
                ctx.lineWidth=0.50;
                ctx.stroke();
            }
        }
        ctx.restore();
    }
    
        function definePath(){
            ctx.beginPath();
            ctx.moveTo(points[0].x,points[0].y);
            for(var i=1;i<points.length;i++){
                var pt=points[i];
                ctx.lineTo(pt.x,pt.y);
            }
            ctx.quadraticCurveTo(80,165,130,130);
            ctx.closePath();
        }
    
    }); // end $(function(){});
    </script>
    </head>
    <body>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>