I have a grid of points generated with Geometry Nodes. On these points, I'm instancing an object. The instances are then translated in height according to the color value of a noise texture. grid instances

It works as intended, I'm also converting the world coordinates of each point into local ones (0 -> 1) for the texture space.

Now I want to rotate each instance as well so that it "fits" the height difference of its neighbors. Basically, I want to calculate a "normal" if it would be a face and rotate it so that it faces in the same direction as the normal would.

For this, I'm calculating the heights of the four neighboring instances (x+1, x-1, y+1, y-1) like I#m calculating the height for the main instance (then one to be rotated). You can see it in 2

geo nodes for point height

Now, what I tried to do, was use the cross product of the two vectors of the "x-neighbors" (x+1, y, and x-1, y) and the cross product of the two "y-neighbors", then add both cross vectors up and dividing by two.

This didn't work as intended. I tried around a lot, but I'm really not sure how to best get the desired result.

In 3 you can also very simply see what's happening with the noise and z, as well as what I mean with neighbors and their cross product. The red point is our "main instance" (x, y).


I guess my trigonometry must be really off. If you need any more information, I'm happy to provide anything you need :)

  • $\begingroup$ Maybe I'm not smart enough to understand what you want, but when you say "rotate so that it fits the height difference of its neighbors", how exactly? A face has four neighbors in X and Y directions and another four diagonal. With a Noise Texture as height map it's quite random where the neighbors are, so each of them e.g. could sit higher than the face you want to rotate. But rotating a square face so that one side goes up makes the opposite go down - but the neighbor there could be higher as well. Now what should happen then? I have no idea how this should work without deforming the face. $\endgroup$ Sep 26, 2022 at 22:04
  • $\begingroup$ To know the proper angle, you have to know what the neighbors' lookups on the noise texture are. This is like a bump map: you need to look up neighbors' bump map values, then compute from the difference in those values, divided by the difference in position. General tools to compute this do not exist in GN. (General tools to compute this do not even exist in shader nodes; the bump node does stuff that you can't replicate with building blocks.) So there is no general solution to this problem. That doesn't mean there isn't a specific solution to this problem for a particular mesh. $\endgroup$
    – Nathan
    Sep 26, 2022 at 22:34
  • $\begingroup$ @GordonBrinkmann I think etlaM wants to align to the normal you'd get from a bump map fed the same texture. I don't think there's a general solution to this problem, but if we limit ourselves to a GN grid, there's a solution. $\endgroup$
    – Nathan
    Sep 26, 2022 at 22:35
  • $\begingroup$ @Nathan Ah okay... as far as I understand bump maps they make the surface appear warped / distorted to fit the different angles between the height values - which you cannot get with an instanced rigid object only by rotating. $\endgroup$ Sep 27, 2022 at 0:04
  • $\begingroup$ @Nathan I think you got it: Basically, I would like to get the normal you would get if you connected the four neighboring points to create a face. This face has a normal, which would rotate the instance in the direction I desire. Oh and also: As described, I am able to look up the values of the neighbors. That's not an issue. The issue is using the right combination of cross products and vector math, which I seem to be too stupid to do. $\endgroup$
    – etlaM
    Sep 27, 2022 at 8:32

1 Answer 1


I hope I understand now what you want: In my example screenshot, there is a square plane surrounded by its neighbors in X and Y directions (for simplicity the neighbors are not rotated). If you now calculate vectors from the neighbors X1 to X2 and from Y1 to Y2 and generate a plane aligned with those two vectors, this should be the orientation of the target plane in the center of those neighbors?

oriented target plane

The maths isn't actually too complicated. As you said in your question, you can look up the neighbors of the values, this would be no issue - so in my example I simply get them from the location of the neighbor planes. The important point is, you have the XYZ values of all neighbors.

At first you calculate the vectors between X1 and X2 as well as Y1 and Y2 by simply subtracting them with Vector Math nodes. To get an orientation out of those vectors you can work with, you plug the vectors into the Vector inputs of Align Euler to Vector nodes. The node for the X vector has to be set to X axis, the one for the Y vector to Y axis.

Now that you have a Euler rotation for the X and the Y axis you simply need to add them together in another Vector Math node and plug them (in my case) into a Transform node. This works because I use a single plane, when you have multiple rotations for multiple instances of an object you should be using a Rotate Instances node.

geonodes setup

But the resulting orientation of the plane aligned to the vectors will not keep it in shape with the grid if that is what's expected. Here is a comparison between the top views of the real resulting of aligning a square plane with the vectors on the left (which can be more extreme depending on the neighbors' height differences), then in the middle what one might expect when having a grid of square planes/objects. On the right then is the actual shape of the plane when the rotation is reset to 0°, it needs to be distorted to fit the square shape when aligned with the normal. That's what I meant in the comments when saying you cannot simply achieve the result without warping or distorting the objects. A bump map keeps all square shapes of the pixels when viewed from above - but this doesn't work with a rigid object.

result vs expectation

//EDIT: A simpler setup would be to take the cross product of the X and Y vectors and plug them into the Align Vector to Euler node. The cross product calculates the vector that is perpendicular to both of the input vectors, which makes it the normal of a plane containing those vectors.

If you then set the Align Vector to Euler node to align the Z axis, you will get a slightly different result than before - both planes have the same X and Y orientation, but the second method will also rotate the plane on the Z axis, which in top view results in the plane no longer being aligned with neither X nor Y axis. So it depends on what you want.

With the cross product you can achieve the same result like the first method by multiplying the Align Vector to Euler output with a vector set to [1, 1, 0] and thus eliminating the Z rotation.

cross product

And this is the top view of the cross product method. As you can see, now there is no edge aligned with any axis.

cross product top view

  • 1
    $\begingroup$ this looks exactly like how I wanted it! Thank you so much :) I tried implementing it, but all the instances are rotated in the same way. So I think the way I get the neighbors' positions must be off. Will try to fix that. Just a question: I was pretty sure I needed a cross product somehow. Is that somehow happening in the Euler to Vector node? $\endgroup$
    – etlaM
    Sep 27, 2022 at 17:25
  • $\begingroup$ @etlaM Well, there is a cross product involved in the Align Euler to Vector node: e.g. the vector in X direction for example is basically a line pointing up or down along the X axis. Y stays the same, so the Y axis is the Pivot which you can set (or leave Auto). The Align Euler to Vector node now creates the cross product of the X vector and (0, 1, 0) which is a vector parallel to the Y axis. Then it gives the rotation necessary to align the object's X axis (chosen at the top) to the input vector by rotating it on the Y axis. I guess I'll edit my answer tomorrow (with a cross product). $\endgroup$ Sep 27, 2022 at 18:53
  • $\begingroup$ @etlaM I've edited the answer to give another (easier) method at the end. $\endgroup$ Sep 28, 2022 at 9:02
  • 1
    $\begingroup$ thank you so much! I finally got it to work. Problem was, I accidentally used a normal float math node for subtraction, not a vector one. Took me three days to figure out lol! Thank you so much for your help with this, now everything looks perfect :) $\endgroup$
    – etlaM
    Oct 2, 2022 at 11:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .