Bevel shader

(varkenvarken) #1

A first implementation of a bevel shader (to simulate rounded edges on sharp meshes). Works pretty well for convex meshes.



(RickyBlender) #2

did anyone made a test with this OSL?
I tried it on a non smoothed UVsphere as shown in thread but did not see much effect on edges !


(varkenvarken) #3

What were your settings? The effect is rather subtle. To illustrate I used the following settings for the smooth image below: Delta = 0.43, Perturb 0.9. (and for the unsmoothed image Perturb = 0 of course)



Difference (Made in Gimp: import both images as layers, set top layer mode to ‘difference’ , merge down, desaturate and up the contrast and lightness to get an exagerated map of the differences)

(Secrop) #4

Very interesting, I was thinking about this same thing today…


(Sevensheaven) #5

It looks good, but how does a scripting noob like me add this to Blender (2.69) ?


(Secrop) #6

@sevensheaven get a recent build from, change the engine to cycles, enable OSL suport in render menu, and add a script node in the node editor.
The current official version of blender will crash with the getmessage() function.

(varkenvarken) #7

Small addition:

You can then copy an .osl script to the text editor and set the script node to ‘Internal’ (the default) and select the name of the text buffer from the srop down. You can also save the script to an external file and set the script node to ‘External’, an then use the file selector to point to the script. If necessary you can (re)compile the script by clicking the refresh button (to small arrows in circle), but normally when you select a text buffer or file the script gets compiled right away. Any errors will appear in the console.

(Fatesailor) #8

Having such a shader is a very good idea… it can be very useful in many cases. I tried to use it following all the settings but I did not receive a satisfactory result, as it is evident from the image below. Did you try to use it on objects standing in a normal environment Varkenvarken? Trying it in an light colored background would be better because in such a case you will have the opportunity to detect the shader’s effect on the shilouette edges of the object (as you see in the image below there appears a dark contour around the object).

(varkenvarken) #9

@Fatesailor: thanks for the example. I will have to set up a scene with real bevels to compare the lighting.

(Sevensheaven) #10

Thanks VarkenVarken. As I use the official Blender release for deadline-driven production work, I’ll have to wait for an easier to install, add-on implementation of the bevel shader before I can test it, sorry.

Good luck. I’ve added your blog to my feeds and will keep following this thread.

(DingTo) #11

This is as easy as it will get. Just add a “Script” node to the node editor (Shading Nodes, Cycles), and inside the node you have a file selector in which you can load the .osl shader from your computer.

Edit: Ah getmessage() crashes in 2.69, didn’t read it carefully enough. :slight_smile:

(varkenvarken) #12

@Sevensheaven: understandable :slight_smile: OSL getmessage() will no doubt work in 2.70-release, easier to install will be a different issue: once the shader is finished it can, just like any other shader, be distributed as part of a material in a .blend and this material can then be linked/appended. You will still need to enable OSL in your scene though.

Edit: Jeez, Thomas beat me by one minnute with his reply :slight_smile:

(varkenvarken) #13

In the script presented earlier I did not normalize the resulting normal. Doing so gives much better results (but still leaves the dark contours mentioned by Fatesailor)

I think I know BTW what causes those dark contours: near the edge of real beveled object not only is the normal diferent but a ray may actually miss the object, showing what’s behind. Without real displacement this is hard to implement although I am working on an idea. Of course for microbevels this might not be a problem (microbevel = low value of Perturb in the shader):

(notice nice highlight on icosphere and hardly noticable dark contours, but of course the edge does not look as rounded as real beveled geometry)

(Sevensheaven) #14

Thanks for the replies, Thomas and VarkenVarken.

The latest test results look very promising, keep up the good work. Since I left 3ds Max for Blender I’ve missed the Round Corners shader option in Mental Ray materials. Before that I used the f-Edge plug-in for 3ds Max, which offered some cool extra edge-based effects:


(RickyBlender) #15

ok tried it this morning and I simply forgot to set OSL in render panel!

did test on 2.69 and it crashes!

on latest buildbot it does work fine
but as reported you can see on cube it does darker edges !
see pic here

does this replace the object smooth function and/or the verts smooth in edit mode may be or more like a complement ?
sphere looks very smoothed
does this take into consideration the vert group or sharp edges ?


(Fatesailor) #16

Just wanted to give an example of scenes where such a shading may be very useful… the mesh below is constituted from a sum of boolean-ed cubes (the boolean operations have been made in Wings 3D), its not having a ‘proper’ topology for applying to it actual geometry bevelling makes things difficult. That’s to say on one hand we do have the difficulty of applying actual bevelling to such a geometry, while on the other hand we do have the difficulty to model such a complex shape without the use of booleans. So, in such a condition, having in our disposition a good bevel shader would be an essential help:

(Fatesailor) #17

Below I put the Blender file too, so that whoever wants may experiment with applying the shader on the object:

(varkenvarken) #18

Making some progress, no dark contours anymore:

I’ll do a full write-up on the new shader and node setup used but just a quick explanation for now: If we’re close enough to the edge we render the cube transparent. That will of course expose the inside of a faces at the back of a mesh so we render backfaces with the same material transparent too. We estimate the distance to an edge checking how many of the normals from the bundle of slightly disturbed normals hits the same face that we are shading, somewthing that is determined by checking whether the normal at the disturbed hit position is the same as at the shading position. We only render edge transparent at all if it is a convex edge, i.e. if we hit a face within some set distance when we extend the inidence vector. Anyway, if this sounds confusing (it does even to me :slight_smile: wait till I find time to draw a proper diagram.

Meanwhile, Fatesailor’s cube thingy looks promissing too:

(I know, for a proper comparison camera angle an lighting should be the same, I’ll do that later)

@RickyBlender: ‘No’ to all your questions :slight_smile: Smooth shading interpolates face normals over the whole face (*), vertex smoothing in edit mode actually alters the geometry and neither vertex groups nor edge weights/seams etc. are available to OSL (just vertex colors). Simply put, form OSL’s perspective there is no geometry, just the point being shaded (and possible other points with shaders, accessible through the trace() function).

(*) I’ve seen articles where people create a bevel shader by blending the face normal (Ng in OSL) with the smoothed normal (N) depending on how close they are to an edge. You still have to determine somehow your distance to the edge.

(Piotr Adamowicz) #19

Hi varkenvarken. I didn’t have a lot of success with your shader so I made my own:

It’s actually a modified version of a script I saw some time ago on this forum, which itself was a modified version of something Brecht posted somewhere, but at this point I can’t tell who was the author.
Anyway, here it is:

void rng_seed(output int rng, int seed)
  int chash = seed;
  if (chash == 0) chash = 1;
  rng = chash * 30391861;

float rng_uniform(output int rng)
  float res = rng / float(2137483647) * 0.5 + 0.5;
  rng *= 30391861;
  return res;

void to_unit_disk(float x, float y, output float x_out, output float y_out)
  float r, phi;
  float a = 2.0 * x - 1.0;
  float b = 2.0 * y - 1.0;
  if(a > -b) 
  { if(a > b) 
    { r = a;
      phi = M_PI_4 *(b/a);
    { r = b;
      phi = M_PI_4 *(2.0 - a/b);
  } }
  { if(a < b) 
    { r = -a;
      phi = M_PI_4 *(4.0 + b/a);
    { r = -b;
      if(b != 0.0) phi = M_PI_4 *(6.0 - a/b);
      else phi = 0.0;
  } }
  x_out = r * cos(phi);
  y_out = r * sin(phi);

void make_orthonormals(vector N, output vector a, output vector b)
  if(N[0] != N[1] || N[0] != N[2]) a = cross(vector(1, 1, 1), N);
  else a = cross(vector(-1, 1, 1), N);
  a = normalize(a);
  b = cross(N, a);

vector sample_cos_hemisphere(vector N, float randu, float randv)
  vector T, B;
  make_orthonormals(N, T, B);
  to_unit_disk(randu, randv, randu, randv);
  float costheta = sqrt(max(1.0 - randu * randu - randv * randv, 0.0));

  return randu * T + randv * B + costheta * N;

shader node_occlusion2(
  color Effect = color(0),
  color No_Effect = color(1),
  int Mode = 0, /* 0: Concave (AO) 1:Convex (Wear) 2:Both */
  int InvertEffect = 0,
  float Distance = 0.2,
  int Samples = 1,
  output color Color = 0,
  output float Fac = 0,
  output normal outNormal = N
  int i, rng;
  float f, randu, randv, ray_t, hits = 0;
  vector ray_P, ray_R;
  normal hit_normal = N;
  float hit_dist;

  f = fmod(cellnoise(P*123456.0), 1.0);
  rng_seed(rng, int(f * 21374647));
  for(i = 0; i < Samples; i++) 
  { randu = rng_uniform(rng);
    randv = rng_uniform(rng);
    ray_P = P;
    ray_R = sample_cos_hemisphere(-N, randu, randv);
    ray_t = Distance;
    if (!Mode)
    { if(trace(ray_P, -ray_R, "maxdist", ray_t)) {
            hits += 1.0;
            int HitNormal = getmessage ("trace", "N", hit_normal);
            outNormal = outNormal + (hit_normal);
    else if (Mode == 1)
    { if(trace(ray_P, ray_R, "maxdist", ray_t)) {
           hits += 1.0;
           int HitNormal = getmessage ("trace", "N", hit_normal);
           outNormal = outNormal - (hit_normal);
    else { 
        if(trace(ray_P, -ray_R, "maxdist", ray_t)) {
            hits += 1.0;
            int HitNormal = getmessage ("trace", "N", hit_normal);
            outNormal = outNormal + (hit_normal);
        if(trace(ray_P, ray_R, "maxdist", ray_t)) {
           hits += 1.0;
           int HitNormal = getmessage ("trace", "N", hit_normal);
           outNormal = outNormal - (hit_normal);
  Fac = 1.0 - (hits/Samples);
  if(InvertEffect) Color = mix(No_Effect, Effect, Fac);
  else Color = mix(Effect, No_Effect, Fac);
  outNormal = normalize(outNormal);

(varkenvarken) #20


seems that that script uses the same approach to determine the blend between normals but with a different sampling distribution. I can’t tell from your image if it suffers from the same dark contour issue as my earlier script, so maybe we shoudl do a comparison under the same lighting conditions. I will certainly have to study it some more :slight_smile:

Meanwhile I made a proper comparison of @Fatesailor’s cube object with and without my new shader:

(yes, the bevel is way too strong, but it’s the contours I am interested in now :slight_smile: