I am looking to create an algorithm which adds a bevel effect to a bitmap using it's alpha as a bump map.

How would I go doing something like this? I have tried specular lighting but I only get the highlight and not the shade.

Here is the effect I am talking about(made using Photoshop): enter image description here

All of these were done using size: 30px(The depth of the bevel from the contour of the bitmap), angle 130, altitude 50.

From left to right, top to bottom:

  1. Chisel Hard bevel
  2. Chisel Soft Bevel
  3. Smooth Bevel
  4. Chisel Hard with soften: 16px - a blurred bevel?

I am trying to create each of these effects, how would I go about creating the basic bevel? and what do I need to get to each of these from that bevel


2 Answers 2


This can be accomplished with a convolution of the distance transform.

Use a distance transform on the edge of the mask. Then threshold this distance transform to remove values beyond some distance. I think the secret to getting the shading is to convolve the distance transform result with a kernel that looks something like this:

[ -1.0  -1.0  -1.0
  -1.0   0.0   0.0
  -1.0   0.0   1.0 ]

This should get you started in the right direction:

#include "opencv/cv.h"
#include "opencv/highgui.h"

using namespace cv;
using namespace std;

int main() {
    Mat mask, dist, bevel;
    mask = Mat::zeros(200, 400, CV_8U);
    rectangle(mask, Point(30,30), Point(180,180), Scalar(255), -1);
    circle(mask, Point(30,30), 50, Scalar(0), -1);
    circle(mask, Point(180,180), 50, Scalar(0), -1);
    circle(mask, Point(300,100), 75, Scalar(255), -1);

enter image description here

    //find edges and invert image for distance transform
    Canny(mask, dist, 50, 150);
    dist = 255-dist;
    distanceTransform(dist, dist, CV_DIST_L2, CV_DIST_MASK_5);
    threshold(dist, dist, 20, 20, CV_THRESH_TRUNC);
    blur(dist, dist, Size(3,3));
    dist.convertTo(bevel, CV_8U);
    equalizeHist(bevel, bevel);

enter image description here

    //convolve with secret sauce
    float d[] = {-1,-2,-3,
                 -2, 0, 0,
                 -3, 0, 1 };
    Mat kernel(3, 3, CV_32F, d);
    kernel = kernel - mean(kernel)[0];
    filter2D(dist, dist, CV_32F, kernel);

    //normalize filtering result to [-1, 1]
    double maxVal;
    minMaxLoc(dist, NULL, &maxVal);
    dist = 128 * dist / maxVal;

    //convert and display result
    dist.convertTo(bevel, CV_8U, 1, 128);
    bevel = bevel.mul(mask)/255;
    imshow("3", bevel);

enter image description here

  • $\begingroup$ Those look awesome! The one with soften looks like a blur, but how would I go making the "Smooth Bevel" from them? $\endgroup$
    – Shedokan
    Commented Oct 29, 2011 at 10:36
  • $\begingroup$ I believe Smooth Bevel blurs the distance mask before convolution, and Soften blurs the result after convolution. $\endgroup$
    – Matt M.
    Commented Oct 30, 2011 at 0:51
  • $\begingroup$ @MattM. may i ask what is the actual name of “secret sauce” matrix do you use in this process? And do u have any source for further reading regarding that? $\endgroup$
    – Albert H M
    Commented May 16 at 3:53

Photoshop's Bevel and Emboss works predictably:

1) Compute a distance transform in a temporary 8-bit single channel image

  • Chisel uses the Euclidean Distance Transform with a Chamfer metric (3x3, 5x5, or 7x7 depending on size). You can use an exact euclidean distance transform if you'd like, I prefer the one from Meijster since it can be made anti-aliased ("A General Algorithm for Computing Distance Transforms in Linear Time", MEIJSTER).

  • Smooth Bevel uses a Chamfer 5-7-11 distance transform followed by two applications of a box blur, to produce the bump map.

2) Apply bump mapping to the intermediate distance transform image. Blinn's original technique is suitable.

3) For softening you can perform a convolution on the surface normals or you can filter them using a kernel.

4) Using the bump map the surface normals are combined with the global light source to compute the lighting intensity as a value from -1 to 1 where negative values are shadows, positive values are highlights, and the absolute value is the magnitude of the light source.

5) Two 8-bit single channel temporary images are calculated, one from the highlight intensities and the other from the shadows. From there it is is trivial matter to use each masks to tint the layer using a colour, blend mode, and opacity - one mask for the highlights and the other for the shadows.

Visual Basic source code for implementing some of this can be found here:


Please visit my open source LayerEffects project to learn more:


I hope this helps someone.

  • $\begingroup$ Thank you, I am very much interested in the Gaussian Distance Transform you mentioned, are you aware of any code available? I'm not able to read formulas well. :( $\endgroup$
    – Shedokan
    Commented Oct 13, 2012 at 21:00
  • $\begingroup$ I have not found anything about the GDT, other than the information I have already posted. $\endgroup$ Commented Oct 13, 2012 at 23:54
  • $\begingroup$ What do you mean by "2) Apply bump mapping to distance transform image"? Distance transform gives distances (1 number for each pixels), while Bump mapping requires normals (2 numbers for each pixel) ... Are you sure you know what you are talking about? $\endgroup$ Commented Feb 13, 2015 at 18:03
  • $\begingroup$ @IvanKuckir I should have been more clear - a surface normal can be computed by treating the distance transform as a height map and computing dx/dy from neighboring vertical and horizontal values. These two numbers provide the normal required for the bump mapping. $\endgroup$ Commented Feb 16, 2015 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.