# Help Wanted Struggling to get the angle of direction from a height map

Discussion in 'Shader Graph' started by imaginaryhuman, Nov 9, 2019 at 7:09 PM.

1. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
I have height map, which you could think of as fairly smooth 'hills'. Given any point on the map, I need to find out what 'direction' the hill is sloping in, around 360 degrees as viewed from above.

I've tried many things. I looked at bump-mapping tutorials and trying to calculate normals and arctangents and so on and I just can't seem to get it to work.

Let's say you are standing on the top peak of a hill, the hill slopes 'away' from that peak in given directions, around 360 degrees. The let's say I want to paint the parts of hill that are "pointing" in a given direction, a certain color from a palette texture. So I'd end up with what perhaps can be described as how an umbrella looks when viewed from above. This is of course based on the angles of the slopes.

So then depending on the height map, it should pull colors from a palette based on the general direction of the slope of the hill. I've tried with sampling x, x+1 and y+1 pixels and trying to get the normal by normalize(cross(xdiff,ydiff)) ... if that's even right. Then tried to convert that to an angle with arctangent2. But I can tell that this just isn't correct.

Any help much appreciated, it should be fairly simple but I am having trouble getting the calculation right.

2. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
Never mind, I figured it out kind of by accident.

1. Subtract the pixel value at x+1,y from x,y = xdiff
2. Subtract the pixel value at x,y+1 from Xy = ydiff
3. Take the arctangent2 of xdiff,ydiff
5. Divide the degrees by 360.0
6. Add 0.5 to move the 0,0 center so that negative angles are in a positive 'visible' range within 0..1
7. Finally add a lighting direction angle to control the angle
8. Pass this into the X UV coordinate of a palette texture.

3. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
I now have this issue: This is derived from a height map and I'm calculating the angle (0..360) of direction of the slopes, which then turns into a color lookup. But the problem I now get is this artefact where "pivot points" start to show up, or even apparent lines, at the "peak" of a high area. These are very noticeable.

Any suggestions how I could overcome this? I am thinking maybe I have to do a blur or something. Either to blur the height map or a post-processing blur. Or sample more pixels when calculating the slops and find an average? Any other ways I can soften these pointy peaks?

4. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
Another bigger example. From blurring the height map. Thing is I combine two height maps in the shader and it produces these peaks still. hmm. I don't think it relates to HEIGHT of the height map at all, but is more of a visual illusion based on how various angles just happen to come together. 5. ### neoshaman

Joined:
Feb 11, 2011
Posts:
4,572
Sound like a dot product with the normal of the heightmap and the direction of the intended slope would be simpler.

6. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
I have no idea what the direction of the slope is. All I can do is sample the texture and try to figure it out. I think what I managed to do above is 'correct', it's just that it has unexpected artifacts that I need to find a different way to deal with, like fading them out or something.

7. ### neoshaman

Joined:
Feb 11, 2011
Posts:
4,572
From what I understand from the code, you convert the height map to a 2d flow map, you don't need to convert to degree, you can just divide by 2pi right away. The artifacts are probably due to the facts your slope are 2d vector (flow map), the light is what I called the intended slopes, is it 2d too? Anyway if you add a 3d component to turn the flow into a normal, you could just dot product with the light direction and avoid nasty arctan and dodge angle calculation all together, 3d vector will give you smoother result with no pole.

Find code that convert normal stored as 2 components then unpacked back to 3d. Generally you add a vertical component then normalized, as the quick and dirty version, quality version in love some power of two and sqrt I think.

8. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
I sort of get what you're saying. Essentially yes the height map turns into a kind of 'flow' map, a map of "directions flowing downhill" as it were.

I don't get the bit about 2pi.

I use arctangent2 to get the angle ie the direction of flow at a given pixel. So how would that be avoided? Surely I need to know the general direction first before I can do anything like turn it into a normal or whatever?

9. ### neoshaman

Joined:
Feb 11, 2011
Posts:
4,572
Because the flow map is a vector, it is the direction. Dot product will return the angular percent between that and a given direction, here light.

You are basically doing the same thing we do in shader call Lambert lighting. Dot product will return 1 for all flow direction aligning with light, 0 for all that are perpendicular and -1 for all going in opposite directions, you will have to remap to 01 but adding 1 then multiply by 0,5. That's the optimal way. You do it the slow and difficult way.

360 is one circle in degree
2pi is one circle in radian.
But we don't need angle now.

10. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
actually i sort of found an easier way to do it.... in the sampling of y+1 (or more) for a slope calculation, if I used some trig to make that sampling rotatable, I can rotate the texture sampling, which has the effect of rotating a one-direction directional light (usually vertical only). So I can then aim the light and change where the 'highlighted edge' and 'darkened edges' are. That more or less does what I want it to do ie which side of the shapes are 'lit' and white side are 'shadowed'. I will try the dot product light method but I'm not very familiar with the exact code.

Last edited: Nov 11, 2019 at 12:03 AM
11. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
Something like this. 12. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
I don't think the dot product method will work any better unless it's based on a 3d normal, because just the 2d direction vector will give much the same result I was getting before. How do I convert the x and y differences (e.g. x+1 and y+1 samples relative to the center pixel), into a 3d normal representing the slope, that can then be used in lighting?

13. ### bgolus

Joined:
Dec 7, 2012
Posts:
7,480
The “cheap” way is to take the x and y diff values, put them in a Vector3 with a z of 1 and normalize that. The “expensive” way is to derive the z from those two diffs using Pythagorean’s theorem. Really the expensive way isn’t any more expensive, and might even be cheaper.

z = sqrt(1 - x*x - y*y);

You can also use the Normal Unpack node if you feed in a Vector4 with x, y, 0, 1

14. ### imaginaryhuman

Joined:
Mar 21, 2010
Posts:
5,653
I finally got this working, thanks for the tips. ... xdiff (pixel at x+1 - pixel at x) and ydiff (pixel at y+1 - pixel at y) are fed in on the left. Output provides a lookup column in a palette texture. Palette texture is a gradient that colorizes the light and models the contrast etc.  bgolus likes this.