skip to Main Content

I’m looking to create a bevel effect on a (non-rectangular) shape (in a canvas element). Searched almost the entire internets: no luck so far. Looking for suggestions. I do not want to implement a bevel effect myself until I exhaust all existing possibilities. Target browser is Chrome.

Here is an image without a bevel applied, and an image with the effect I am looking for. This was done in Photoshop:

original bevel

Edit:
I wrangled together something with markE’s suggestion.
The two problems with the shadow offset approach are:

1) There is a border that must be drawn, which I did not want.

2) The border’s thickness determines the strength of the shadow.


I did not want the border to be visible so I needed a way to have as small a border as possible, and then to cut out the border. As of now that involves a number of different steps:

1) First, I create a shape with a transparent fill color, a border, and a slight shadow (with (0,0) offsets) and a blur of 1. I then draw the shape onto itself to increase the opacity of the shadow.

2) Then, I create a shape with a transparent fill color, a border, and shadows as described by markE. I set the lineWidth to a very small number — e.g. .5. I apply the shape in (1) onto this shape (via globalCompositeOperation = 'destination-out'), then draw the shape over itself 3 times in order to increase the opacity of the shadow.

3) Then I draw the normal shape, without a border. I apply (2) onto the normal shape and again cut what would have been the border with the shape from (1) using globalCompositeOperation = 'destination-out'.

Here is the result:

result

2

Answers


  1. You can create a bezel effect with an inset-shadow.

    enter image description here

    The process is straightforward:

    1. Define a non-rectangular path,
    2. Create a clipping path from the non-rectangular path,
    3. Apply a shadow-stroke (which is the bevel effect);

    Here’s example code and a Demo: 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>
    
    Login or Signup to reply.
  2. SO the question is first – how does the bevel effect work?

    You might say that bevel effect is also 3d-ing your flat 2d image, and it really does (well, in the way the object’s been lit).

    Basically, shading calculations are done like they were done on some actual 3d object, but since you have only 2d image, you would also need something known as – normal map.

    Normal map is 2d image that, instead of colors in the image, has encoded surface normal information in the RGB channels. So every pixel in the image has RGB component and R channel is for surface normal in the x-direction, G is for normal in y-direction and B is for z-axis component of the normal.

    Read more about normal mapping here.

    Another option is having the bump map. That’s an image that instead of color of the pixels or information about normals, holds the information about “height” of the pixels.

    Read more about bump mapping here.

    Whether you have bump map or normal map, results are quite similar. Bump maps have only one channel in the image (greyscale images), so they are easier to make, and tend to be more “realistic looking”, but it depends what effect are you after.

    Here’s an example of the bump map for the bevel effect on the square image.

    bevel - bump map

    The problem is that images in general have non-uniform shapes (like your example), so how to create bump maps for those?

    The solution is to create something known as “Eucledian Distance Matrix“.

    EDM’s are widely used in Photoshop and they hold the information how much certain pixel is away from your image (nearest colored pixel on your layer). EDMs are used for stroking objects and for – bevel effect. (There are plenty of resources for how to generate EDM)

    So example EDM for 4×4 picture that has only pixel colored in 2nd row and 2nd column would look like this.

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

    Using EDM, you can get information how much certain pixel is “inside” your image and then generate the bump map based on that information.

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

    This code is just an example, pseudo-code, but you should get the idea.

    So we have now made out bump map, and it’s time to calculate the lighting (do shading).

    There are plenty of shading models that can be used – list. And here’s more visual difference between them – link.

    I would start with Lambert model. There are plenty of resource that you can find about it.

    Generally, you need surface normal (or bump map from which you can then calculate the surface normal (use differentials)) and light vector. From that information and diffuse color (well, in this case, pixel color in the image) you can calculate how lit that pixel is and then output shaded pixel.

    I haven’t posted a lot of working code, since stroke effect and bevel effects are quite complex, but you should get the idea. This is how they ‘really’ work in photo editing software, and as you can see, it’s not that easy.

    If you have any question feel free to ask.

    Random link about bevel effect – here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search