# How to add golden line to my maps?

I have used displacement to create a map (based on a heighmap) Now I would like to add golden topographic line. I succeed to have :

• either a white map
• either a full golden map But I would like both. Is that doable ?

Please find attached an image of my situation

Edit

Thank you very much Carlo. I have been able to reproduce your work. only a small issue remains : The lignes does not have always the same thickness. On valley, the lignes sometimes are as large as the floor. I added an image in my main post for explanation

## Math nodes on object's coordinates

There are several approach you can take, the easiest is to build a shader that is capable of recognizing the isolines (lines that have something in common, the same Z in this case).

Start by adding a texture coordinate node. We'll use the object coordinate (the position of it's origin in this case) as a starting point.

Whit a separate XYZ node we isolate the Z channel:

Then we use the math "modulo" node to subdivide the Z channel in several segments, and the math "compare" node to extract an interval from each segment (you can use a color ramp if you want a smooth edge).

We basically obtained the lines, now we just need to use the results as mask for the mix shader node between our two shaders.

• Really cool :). I really appreciate the explanation, math nodes are not my strong suit. Feb 15, 2021 at 20:41
• Thank you very much Carlo. I have been able to reproduce your work. only a small issue remains : The lignes does not have always the same thickness. On valley, the lignes sometimes are as large as the floor. I added an image in my main post for explanation. Feb 15, 2021 at 23:06
• A quick hack would be to avoid having a line in those areas, you just need another math node before the modulo and add some small values to shift the lines up. Otherwise @Markus von Broady suggested a terrific alternative. Feb 16, 2021 at 18:04

You can use an (epsilon) range around the desired contour-heights as the thickness of your contour-line, here using a Ping-Pong node to get a triangle wave in Z, and a Compare, with an epsilon around wave-zeros.

Then you can modify the epsilon, making it smaller on shallower slopes, by multiplying it with the arccos of the Z-component of the surface's normal at the shading-points. Here in Object-Space, assuming the object is Z-up:

This corrects from a simple slice-by-Z, on the left, to contours with even thickness, on the right..

(above) a vertical orthogonal view.. (below) orthographic, from an angle.

(The black-and-white mask can be used to mix between any shaders, colors)

• Kick the next person that says Trigonometry is useless in the knee !! (don't) The simplicity of it is awesome !! Feb 22, 2021 at 20:26
• Trigonometry is useless in the knee!! (sorry @gorgious, had to, long day, might delete later :) Feb 22, 2021 at 22:03
• @JachymMichal Haha yeah that is some high level syntax right there !! :) And thanks for the bounty spotlight, I would totally have missed the other answers ! Feb 22, 2021 at 22:21
• @JachymMichal it seems like you let arise a new species of "terrific" ;) Thanks Robin, that's really nice 🙂 Feb 22, 2021 at 22:48
• You're a class act @Robin :). No worries, it wouldn't be fair to award only one of these answers anyway. Feb 23, 2021 at 10:32

The "small issue" you're having is actually not that small. The way a shader works, is it calculates a pixel color independently from other pixels. Carlo calculates mathematically if a pixel is within given area (e.g. on a given height), but he doesn't have access to neighboring information. Ideally, you would prepare textures in a separate program, using algorithms that analyze the raster image in order to create paths between areas of the same colors, and then you would color these paths. It's not a trivial task either, and Blender nodes are much more limited, because you only have a single pass, where each pixel is calculated on its own. Well... Since you're using a height map texture, and you can transform the mapping to shift it (kind of like moving a piece of paper when it's being scanned), you actually can sample other pixels. Still, if you create a whole program, finding edges, in nodes, not only the node setup will be HUGE, but it will be run for each pixel instead of once. I'm saying all this to preemptively justify the complexity of following node setup (and the result isn't jaw-dropping):

Result: (black/yellow for better contrast)

TLDR - how it works: instead of drawing (yellow) two borders directly, I prepare to draw one area inside two borders. Then I'm sampling 8 neighboring pixels if they're also in the area. If less than 8 pixels are - I'm on a border and only then I actually draw (that is, I actually change color from black to yellow).

Explanation of nodes from left to right:

• Offset and divide by - how far is the neighboring pixel from the current pixel. The 2nd node is just a value used to divide the former. This way I gain precision on manually changing the input value.
• 4 nodes representing the offsets, making the diagonal offsets shorter, but perhaps I shouldn't.
• 8 nodes combining XYZ into vectors, creating vector offsets in 8 cardinal directions.
• standard UV mapping into the image texture, and then 8 image textures moved a little used the 8 XYZ combine nodes.
• custom node group, checking if a given pixel is in the specified area.

So far an example path for a pixel:

The green node:

• There are MIN and MAX inputs to precise the area. You could replace it with a single value or modulo, as well as you can replace Greater than + Less than with Compare like with Carlo's answer.
• Again, I divide those values just so it's easier to interactively modify them.

Let's go out of the green node and continue:

• Add math nodes to count all 1 outputs from green nodes, meaning that those pixels are inside the area.
• If less than 8 such pixels got counted - it's a border pixel.
• If the current pixel is also in the area, then the multiplication on this node will be 1 * 1 = 1 and the next RGB node will use yellow color.

Now, let's disconnect those two nodes:

And let's go to the green node and adjust MIN and MAX to get an interesting area:

Reconnect recently disconnected nodes and adjust the Offset node for border thickness. Large thickness will produce artifacts, which you can combat by adding more samples (so more XYZ - Mapping - Texture - green - Add node paths)

I took the height map from here: 50 displacement map variations of gaea's hero mountain Link to the folder with the texture: Var_05

• Great! Thanks for tackiling a non trivial problem. Feb 16, 2021 at 17:43
• as a newbie, is this akin to a Kernel Convolution? Feb 18, 2021 at 18:02
• @ThorSummoner I might have incorrectly used the cardinal directions term, I simply meant cardinal directions (4) and intermediary directions between them, so N, NE, E, SE, S, SW, W, NW. Yes, on 2D space. The correct terminology would be 4 cardinal and 4 ordinal directions. Feb 18, 2021 at 21:19
• I think its really interesting to use circle sample via a unit-vector length diagonal sampler (x,y) + (+/-0.707, +/-0.707) instead of a square sample (x,y) + (+/-1.0, +/-1.0) which I've seen more often, I think too much in an axis-aligned world. Feb 18, 2021 at 21:25
• Thanks, @JachymMichal ! Feb 23, 2021 at 18:34