# Understanding how "vertex of corner" works in geometry node

I've this test case which starts from a simple mesh made of two connected faces, captures the face indices then instanciate and realize a grid on each initial mesh face centers.

After that I'm testing how to sample information on the initial mesh from each point of the realized instances.

This information is stored so that I can see the result in the spreadsheet.

The initial mesh has the following face and vertex indices (vertex 3 is at 0,0 and vertex 0 is at 1,2):

Now the sampled values which come from the node group in the setting:

If I sample the face corners locations, all is good as we can see here:

But if I sample in the point domain using "vertex of corner" node, I've a result that I was not expecting:

What am i missing?

The final goal is to be able to get the vertex normals from the face corners as they are different from the face corners normals.

I presume I can also capture all that before instancing, but I wanted to avoid storing too much data.

This part of the documentation may be the key point, about "sample index":

If the Geometry used for the input is the same as the geometry from the field context, this node is equivalent to the Evaluate at Index Node. Using that node is usually preferable since avoiding the geometry socket makes the whole setup easier to use in other situations and share.

But the doc doesn't tell explicitly what is happening when the contexts are different.

• The way I see it, the source geometry has 6 vertices. The destination geometry has 8. The 'Index' input of Sample Index is the destination index. 2 vertex-indices in the destination are unaccounted for. (And in the other case, even though the count is the same, you're lucky the corner indices match.) But.. this stuff scrambles my head, so I could be wrong. May 8, 2023 at 12:17
• @RobinBetts hi. I don't know if you gave a look to blender.stackexchange.com/questions/291926/…. In fact this is the same principle, except for "vertex of corner". That's the point.... May 8, 2023 at 12:20
• (Phew!) @Crantisz has said it :) May 8, 2023 at 13:14

## 2 Answers

Then you are using Fields (dashed lines) you should know, that data flows in opposite direction. Let me show you. I will mark two different meshes in your setup (splitted and not) with 2 colors:

Splitted geometry (that you get after realize instances) is yellow, original is blue. As long as you see solid lines, data will flow directly from left to right.

Now the data Geometry nodes starts to collect data by using Fields, nodes will be asked in backwards direction:

Going inside group nodes:

Sample Index is quite different node. It can transfer data from one geometry to another. Here is the data flow that shows the flow of original geometry (blue):

The node that I marked by blue, has 6 points, 8 corners. But all data flows in yellow lines initiated by the splitted mesh, with 8 vertexes and 8 corners.

This is that you are probably missed.

btw, you can replace this set of nodes by split edges node:

• Hi and thanks. I don't get what you call "splitted" here? I mean I see the yellow marks, but why are you calling it "splitted" ? May 8, 2023 at 11:52
• You have original mesh: 6 vertices, 2 connected faces, 8 corners, and "splitted": 8 vertices, 2 disconnected faces, 8 corners, May 8, 2023 at 11:54
• @lemon Usually you do the same thing by adding a Split Edge node. So I called that splitted mesh=) May 8, 2023 at 11:59
• I most likely need to think it another way. But why is it working as (I) expected without "vertex of corner"? May 8, 2023 at 12:01
• FYI my goal was not to split the mesh, not at all. It occurs here that the instances have the same shape, but this is not the goal. And if you test that with smaller grid instance, we still obtain original mesh locations May 8, 2023 at 12:03

Starting with geometry:

Vertices: [(1,2), (0,2), (1,0), (0,0), (1,1), (0,1)]
Faces: [(0,1,5,4), (4,5,3,2)]


The indices of vertices defining faces also tell us about the corners. We can confirm the order is the same (the above order I took from Python API), by using a viewer:

For example The first 4 corners belong to the first face, and then corners # 4,5,6,7 belong to the 2nd face, so the corner #7 is the last corner of the 2nd face, as we can see above the last corner is associated with vertex index #2, and the vertex index #2 should have coordinate (1,0), and the position matches:

You pass that geo to Capture Attribute and capture index:

It's pretty straight-forward, you now have an array for faces: [0, 1]

Vertices: [(1,2), (0,2), (1,0), (0,0), (1,1), (0,1)]
Faces: [(0,1,5,4), (4,5,3,2)]
Faces_anon_attr: [0, 1]


Now you convert those faces to points using Mesh to Points, so the mesh data is now:

Points: [(0.5, 1.5), (0.5, 0.5)]  - taken from centers of faces
anonymous_attribute: [0, 1]  - simple copy


You also create a Grid:

Vertices: [(-0.5,-0.5), (-0.5,+0.5), (+0.5,-0.5), (+0.5,+0.5)]
Faces: [(0, 2, 3, 1)]


Now you implicitly convert the grid to an instance, let's call it my_quad, and spawn that Instance on the Points:

Your geometry now:

Instances: ['my_quad', 'my_quad']
Instances_pos: [(0.5, 1.5), (0.5, 0.5)]  - simple copy from points
Instances_anonymous_attr: [0, 1] - simple copy from points


And now you Realize Instances, so the geometry becomes:

Vertices: [(-0.5,-0.5), (-0.5,+0.5), (+0.5,-0.5), (+0.5,+0.5)] - from my_quad
+    (0.5, 1.5)   (0.5, 1.5)   (0.5, 1.5)   (0.5, 1.5)  - from instance pos

Verts ctd:[(-0.5,-0.5), (-0.5,+0.5), (+0.5,-0.5), (+0.5,+0.5)] - from my_quad
(0.5, 0.5)   (0.5, 0.5)   (0.5, 0.5)   (0.5, 0.5)

=

Vertices: [(0,1), (0,2), (1,1), (1,2)] + [(0,0), (0,1), (1,0), (1,1)]
Faces: [(0, 2, 3, 1)] + [(4, 6, 7, 5)] - taken from my_quad but offset by domain_size * instance index

Vertices_anon_attr: [0, 0, 0, 0] + [1, 1, 1, 1] - for each vertex from my_quad duplicate data from instance.
Edges_anon_attr: same as above
Corners_anon_attr: same as above
Faces_anon_attr: [0] + [1] - same logic, but my_quad had only 1 face.


Your first Store Named Attribute is easy:

So you create Face index attribute in Point domain. You're connecting a field from face domain, but I already calculated all possible interpolations above, so regardless when Blender decides to calculate it, it will be the same stuff as listed above as Vertices_anon_attr but this time with a name:

Vertices: [(0,1), (0,2), (1,1), (1,2), (0,0), (0,1), (1,0), (1,1)]
Faces: [(0, 2, 3, 1), (4, 6, 7, 5)]

Vertices_anon_attr: [0, 0, 0, 0, 1, 1, 1, 1]
Vertices["Face index"]: [0, 0, 0, 0, 1, 1, 1, 1]
Edges_anon_attr: same as above
Corners_anon_attr: same as above
Faces_anon_attr: [0] + [1] - same logic, but my_quad had only 1 face.


Finally, your 2nd Store Named Attribute (and the logic repeats 3 more times later):

Hopefully my collage is readable:

1. Iterate over each point.
2. Context is vert#0 from the geometry described most recently in this post.
3. Let's follow the arrow in reverse, going to the Group Output, then to Sample Index.
4. Sample Index evaluates "Index" field. The context DOESN'T yet change!
5. Again we follow backwards to the "Vertex of Corner"
6. "Vertex of Corner" needs to evaluate "Corner Index". So again, need to go backwards to "Corners of Face".
7. "Corners of Face" needs to evaluate "Face Index" field, so again go backwards to Group Input…
8. Group Input got this field from "Capture Attribute" node, so that's the anonymous attribute we stored in the beginning. But remember: the context DIDN'T yet change! We're still evaluate the new goemetry, we're still on vert#0!
9. So let me copy-paste the table from which we read:
Vertices_anon_attr: [0, 0, 0, 0, 1, 1, 1, 1]

1. We're on first vertex, so we read the first value, 0. Now we can go forward on the field, until we reach "Corners of Face" that requested it.
2. "Corners of Face" now reads "Sort Index" which actually is a constant, for the entire tree evaluation it's just 0.
3. So "Corners of Face" sorts corners of face#0 by default weights, which I'm 95% sure is index field: corner#0, corner#1, corner#2, corner#3
4. Now it takes the first (index 0) element of this list - corner#0 and finally outputs its index 0 to "Vertex of Corner"
5. "Vertex of Corner" checks which vertex is associated with corner#0, let me copy-paste the relevant table from above:
Faces: [(0, 2, 3, 1), (4, 6, 7, 5)]

1. So "Vertex of Corner" outputs 0.
2. "Sample Index" finally got the index. So now it finally CHANGES context to point (vertex in this case) #0 in the geometry in the output socket of the "Capture Attribute" node. It wants to sample the position. Let me copy-paste the relevant array:
Vertices: [(1,2), (0,2), (1,0), (0,0), (1,1), (0,1)]

1. The node is sampling the first element from this array, so position (1, 2). The context returns to vert#0 of the geometry the "Store Named Attribute" operates on currently, and outputs it to the Group Output.
2. Group Output sends it further to the "Store Named Attribute"
3. The position (1,2) is saved for the first element for N0 array.
4. "Store Named Attribute" increments context to the vert#1 of its left socket geometry and the process continues until all vertices are processed. Then the process is also repeated for next SNA nodes.

Let's analyze the first vertex of N1 node, to confirm the order of sorted face corners:

2'. vert#1 9. Vertices_anon_attr: [0, 0, 0, 0, 1, 1, 1, 1] 10. First Vert again, the value in the anon_attr is 0 again. 11'. In this custom group, the "Corner rank" constant is 1. 12. corner#0, corner#1, corner#2, corner#3 13'. Take the second (index 1) element of this list. 14. Faces: [(0, 2, 3, 1), (4, 6, 7, 5)] 15'. "Vertex of Corner" outputs 2. 16'. Context chnages to vert#2 in the CA out geo. Vertices: [(1,2), (0,2), (1,0), (0,0), (1,1), (0,1)] 17'. Third element (index 2) is position (1, 0).

The only thing I can add here, is that since "Corner rank" inputs are constants, For each face you will always sample the same corner, therefore 4 first values of each named attribute will always be the same, as well as 4 last values, because you're sampling the same corner, just either from the first or the second face.

More reading:

• Thank you for the time you took for these explainations, I really appreciate. I've to read again before I can feedback on your answer (as it is late here, that will be tomorrow). May 8, 2023 at 17:24
• I think the key point is 4. in the last part of your explanation. Though, the documentation is not clear (my last edit in the quesiton). But yes, the context is the key and that works (I think) without "vertex of corner" because the other inputs are not context dependent (constants). But so, what is the solution? Capturing all before "mesh to points"? (PS: in this context "store named attributes" were only here for debug purpose) May 8, 2023 at 17:48
• @lemon the "evaluate at index" node also changes context, but it doesn't switch to another geometry. The context it changes is it can switch domain or index from currently evaluated context. E.g. For every vertex you can read a position of a previous vertex by connecting index-1 to the index socket. For "Sample index" node, you can do the same, but also connect a different geometry. May 8, 2023 at 20:08