# Understanding the bump node

I see a lot of tutorials that explain how to use a bump node by demonstrating its use and giving a hand-waving explanation regarding what it actually does. Things like it "perturbs the normal".

OK, but how? I'd like to know, so I can understand how to control it more precisely.

The current problem I'm facing is that I'm trying to make a tile floor, and need to normal to follow the curve around the edge of the tile. The center face of the tile is a straight-up Z normal, but then edges have a curve to them before the tile disappears into the mortar.

But if I use the "Factor" output from the "Brick Texture" as the "Height" input to the bump node, I would expect that the normal is straight up Z where the tile/brick face is, and is simple scaled to zero where the mortar is. Instead, if I view the output of the bump node, I see that the edges of the tile have X and Y components to them. How is the direction of the vector being changed?

And what does the "distance" input do?

Yes, I've read the documentation. The explanations are similarly imprecise and hand-wavy. That's fine for a surface level understanding, but I'm hoping to gain more precise control so I'd like a deeper understanding.

• blender manual
– Emir
Commented Apr 10, 2021 at 4:58
• @Emir: I stated in my post that I've already read that. It still doesn't make sense to me. Commented Apr 10, 2021 at 5:01
• the bump node is a converter that takes b/w data, like procedural texture outputs, and converts them into a normal map Commented Apr 10, 2021 at 5:47
• Commented Apr 10, 2021 at 5:47
• @R-800 .. actually, at least as far as I understand, vertex-normals are calculated from the surrounding face-normals, (perhaps weighted) and then, in a sort of feedback, used to interpolate smoothed normals over the triangles they define.. I think this would make a good question in its own right. Commented Apr 10, 2021 at 14:14

Okay. Let's say we're rendering a bump map fed by an image texture in Eevee. I'll give a general run-down, but I may simplify some details. You'd be best off just reading a bump map implementation in some shader code (OGL or HLSL). That will tell you exactly what's happening, and it's a lot more accessible than you might think.

The bump node samples the image texture three times. First, at the UV of the sample being evaluated; then, a slight offset in the U axis; then, a slight offset in the V axis. The size of this offset depends on your zoom, texture resolution, and FoV; as you get closer, the offset gets smaller. (I think it actually samples at a screenspace offset of +1 pixel, and then calculates based off of that, but understanding it in terms of UV is probably more intuitive and amounts to the same thing.)

Each time we sample the image, we get a number. For each of our numbers, we subtract the bump node's midlevel, multiply by the bump's scale, and then multiply by our object's scale.

Then we calculate how big those UV offsets were in terms of world space-- we calculate the change in world space, divided by the change in UV. In Eevee, that's pretty easy, because our video cards are designed to work on four samples simultaneously, because calculating these deltas is really important for texture filtering, which is really really important.

Now we know, in two orthogonal axes, how much our height changes over a given world space distance, which lets us calculate a slope. For example, if our height changes 0.5 units in the direction of increasing U over 0.5 world space units, then we have a slope of 45 degrees. If it changes 0.25 units over the same world space, we have a slope of 30 degrees. (This is some basic trigonometry.) We take our base normal and rotate it, in the axis of changing V, by the slope we get by looking at the change in height in U, and we rotate it, in the axis of changing U, by the slope we get by looking at the change in height in V. Now we have a normal that's different than the one originally calculated from our mesh.

If you're okay with calculus, the tl;dr of all this is, we calculate the derivative of our height map and use that as our normal.

How we use this normal depends on the shader. Each shader does something different with a normal; a normal is a fundamental part of almost every shader, and their differences have a lot to do with what they do with their normals. So I won't go through all of them. But we can consider a diffuse BSDF with a 0.0 roughness, which is called a Lambertian diffuse. If we shine a light on this surface, what color do we get? It depends on the angle of the light, relative to the angle of the normal. The color we get is equal to the dot product of the surface normal and the reversed light vector (in whatever space, just so long as they're in the same space) times the color of the light, times the color of the surface, times the falloff fraction. The output color (before color management) is the sum of the contributions from each light.

• This is exactly what I was looking for, thank you! Commented Apr 10, 2021 at 13:06

This answer is a very late supplement-to-a-good-supplement-to-a-good-answer, but I'll chuck it in anyway, just in case it gives a different angle.

The Bump node approximates tangent-space normals from a given height map. In other words, given a set of heights (yellow,below) it approximates the normal vectors to the slopes (blue). In tangent space, Z is the normal of the underlying geometry, (perhaps interpolated by smoothing) and X and Y are the U and V of the map. The XYZ of the approximated normals are encoded as RGB.

I don't know Blender's implementation ( Can someone help here? Is it buried in library functions?) But X and Y components of the approximation can be derived by convolving the height-image with X and Y Sobel filters. In convoulution, a matrix of values is passed over the image, and each central sample's value becomes the sum of the contributions from itself, and the matrix-shaped grid of surrounding samples, as weighted by the values in the matrix.

I would expect that the normal is straight up Z where the tile/brick face is, and is simple scaled to zero where the mortar is.

The length of the normal vector to the slope is always set to 1. The output of Bump node does not represent heights, or scalar values. It represents directions. All blue would represent the original normals in Tangent space, in which the underlying normal-direction is defined as (0,0,1). The colors are the new, modified, directions of the slope-normals, in that space.

The 'Strength' setting of the Bump node, by inspection, is a mix between the approximated normals and (0,0,1). So, as it's reduced, it makes all slopes shallower; they deviate less from the original normal of the surface. The 'Distance' setting scales the height provided by the height-map. As a consequence, it will also make the slopes shallower or steeper.. the distinction is subtle.. but the 'Strength' setting, as far as I can see, chucks some information away.

You can get curved tile profiles from a black-and-white setting of the Brick texture, using a parallel one to put in the colors:

With this sort of result:

.. but I found the color-ramp settings quite hard to find, and a bump-map is an approximation.. there are likely to be artefacts. Better to bake normals from a real model? Or make a procedural tree with the express purpose of making tiles with rounded profiles?

To complement @Nathan's very nice answer with practical examples:

The Brick Texture FAC output includes mortar smoothness (as a gradient).
As you probably know, Bump node interprets gradients as a slope.

The Bump Distance value controls the height of the Bump, so you can match real geometry.
It's in meters, so you may need to scale it down to get a useful result.

[Top view] Metallic cones 0,5 meters high matched by a Bump map set to 0,5 meters

• Thank you. Understanding and controlling the distance input was the solution to my practical problem - @Nathan's answer was key to understanding the rest. Thank you both! Commented Apr 10, 2021 at 14:03