In|Framez® Papers

Faking micro-bevel with mentalRay

By Homam Bahnassi


This article is divided into the following sections:


Introduction

It is very rare to find perfect hard edges in the real world even on the hardest surfaces around. Actually it is something not desirable (imagine a building with a corner as sharp as a blade… yikes!).
However, most 3D images are made of discrete polygons that have no sense of humor. They’re just that! Polygons made of very sharp and clear edges. This usually contributes to a mysterious unnatural feeling about the final rendered image (ever wondered why a perfectly-rendered architectural 3D shot still looks CG?).
This problem is exaggerated more and more with materials that have a specular term added to their definition, making rendered objects look way too CG (if you know what I mean).

Micro-beveling in action

Manually fixing this issue is a very time-consuming process in most cases (find the edges, add more polygons there, adjust the normals…etc).

The cost of hand-made beveling

A more efficient method for achieving this is to fake it using shaders (mentalRay, or better yet, in real-time). So, we are now looking at a ‘render-time’ solution to the problem.

This tutorial will be divided to two parts. The first one (this one) will cover doing this effect using mentalRay. The other part will achieve the same effect but in real-time (by using the new version of
DSK|Shaderbass). So let’s waste no more time…


Skill Requirements




A Normal Story

Before we get our hands dirty with shader work, we’ll first take a brief look at the main concept behind this effect.
It is fairly simple; it depends on modifying the lighting interaction between the object’s surface and light sources for each sample.
As we all know, the surface normal is the component that controls this kind of interaction.
We can experience this very easily in XSI with the
normal editing add-on.

Tweaking normals

So, first turn on ‘Normal Viewing’ from the camera display options and take your time to play around with your object’s normals either using the normal editing add-on or by modifying the hardedges and smoothing angles.
Note how as you change the surface normal direction, the affected surface’s interaction to light changes also.
Now we will depend on this fact to customize the surface normals around the object’s hard edges to give them a beveled look.
We will implement this in XSI without writing any line of code actually (most of us aren’t so much of programming techies).
Did you use the ‘vector state’ shader node before? This magical mentalRay shader node will be of great help to us. This shader node returns a wide variety of states about the currently rendered sample in mentalRay.
So in XSI, if we grab an instance of this node and open its property editor, we’ll see how we can specify which of the available vector states to return, so we use them in our assembled render network.

Obtaining a vector state node

Amongst the available states, we are most interested in two of them. These are the “Geometry Normal Vector” and the “Normal VectorNormal Vector” states.
Each one of these two states returns the surface normal at each sample mentalRay is rendering. But they differ in that the “Geometry Normal Vector” returns the unsmoothed surface normal (called face normal too) without taking into account the actual surface normals while the “Normal Vector” simply returns the actual normal for each sample.

Face normals and smoothed normals

Now what we need to do with these states is simple.
We’ll use them to render the unsmoothed normals (Geometry Normal Vector) all over the object and mask the smoothed normals (Normal Vector) only around the edges, and that’s our next step in this tutorial.


Bump input, FX Tree and other stuff

Yes, the bump input port in the material node is the right place for controlling object normals.
Since the idea of bump maps actually depends on perturbing the object’s normals to fake bumpiness, we can use the same concept to make the vector state control the surface normals.

So let’s grab a sphere, open its render tree, and connect a vector state node to its bump input.
Now if we switch the state parameter in the vector state node to “Geometry Normal Vector” and draw a render region, we’ll see how the sphere is now rendered without smoothing the faces.
Of course, it doesn’t make any sense to use state parameters other than the “Geometry Normal Vector” and the “Normal Vector” with the bump input so don’t bother testing other options because you won’t get something meaningful.

Geometry normal vector rendering

Next, we need to find an easy and controllable way to mask the interpolated normals around the object’s edges. Actually, there are many ways to do it. One way that is both simple and controllable is to generate a masking texture map that is controlled via the FX Tree.
The generation of the masking texture map is fairly simple. Just create a unique UV set for the object, and then stamp the texture projection over a black background to a file.
Now this generated texture file can be modified with the FX Tree before it’s used as a masking texture for the normals.

Grab a cube for example, add unique UVs to it either via the “Unique UVs” texture projection or the “Cubic” texture projection.
Next, open the texture editor for the cube and make sure that all the object faces are positioned correctly.
Now import a black picture with a medium resolution (e.g. 1024x1024) and stamp the UVs over it.

Geometry normal vector rendering

Now all the ingredients are ready and what remains is to assemble them in the render tree.


Building the render tree in XSI

We start by creating a new “phong” material for the stamped cube. Next, we call its render tree to build the micro-bevel shader network.
First, get two vector state nodes and set one of them to the “Normal Vector” and the other to “Geometry Normal Vector”.
Rename these nodes to something meaningful so you can recall which one is which later.
Next, grab a “mix_2colors” and import the stamped clip to the render tree.
Now connect the “Geometry Normal Vector” state to the “mix_2colors” base color input, the “Normal Vector” state to color1 input and the stamped image to the weight input. Finally, connect the “mix2_colors” to the bump input in the material node. While connecting nodes you’ll notice some conversion nodes being automatically added for converting vector types to color or the other way. Anyways, you should get a tree very similar to the one in the picture below.

The render network

Now before rendering and testing our work we need first to smooth the object’s normals or we’ll end up with both the normal and geometry normal being identical (unsmoothed).
So open the geometry approximation for the cube and uncheck the automatic discontinuity option under the “Polygon Mesh” tab.
Now if we draw a render region, we’ll see almost no effect at all. This is because the masking lines are so thin that they’re not enough to render the effect clearly.
To control the thickness of the masking lines we’ll depend on the integrated fx tree we have in XSI.
So change one of the viewports to fx tree or open a floating one and open the fx viewer to see the modification results directly on the stamped picture.
Now get the stamped image from the clip menu in the fx tree and grab two fx operators, the “box blur” from the “filter” group and the “Glowing Edges” from the “Painterly 2” group.
Now connect them between the “FromClip” and “ToClip” nodes as shown in the picture below.

The render network

Now to control the masking edges thickness and smoothness all what we need to do is to play the “Edge Width Parameter” in the “Glowing Edges” node and the width and height parameters in the “box blur” node.
Try adjusting these parameters to something close to the picture above, then refresh the render region to see how it looks.

Alrighty… So that’s how we managed to get a nice approximation of micro-beveling in mentalRay without even writing a single line of code. Later, I will show you how to implement the same effect but in real-time using the new version of
DSK|ShaderBass.

Final results




One Last Note

This tutorial used only an approximation to the real effect. Since this is actually just a variance over bump mapping, then the geometry itself is not affected by this effect. That means that rounded edges will shade rounded, but will take the same space and silhouette as an un-rounded edge. As long as the beveling thickness remains small, this won’t be a problem. After all, we’re talking about ‘micro-beveling’, not a ‘round-edges’ shader!

If you’re after perfectly accurate results, then there is no way but to write a custom mentalRay shader along with an XSI plug-in. This can solve the silhouette issue, as well as give more accurate shading over the edges since the smoothed normals can now be calculated locally to the edges instead of the current crude “whole-object” smoothed normals calculation.

That’s it for today… By now!