Inconsistent results from the vector math node (cross product)?

I am trying to take the cross product of the vector (0,1,0) with a cube’s normals, but I’m getting odd inconsistencies.

The cube on the left is a cube I appended from a file I’ve been working on (should be a normal cube I added). The middle cube is the default cube. The right cube is made of 6 separate planes. The top image shows the normals. The middle image shows the vector (0,1,0). The bottom image shows the cross product.

I’m not sure why the results are so inconsistent when the inputs are exactly the same. I made sure the normals were the same by checking each vector component. They even use the same material! I also tried changing the vector spaces using the vector transform node, but still couldn’t get anything consistent. They also use the same material.

Here is the .blend file:
Inconsistent Results.blend (518.0 KB)

Edit: I have been trying to test a parallax material and the results of the right cube are what I need.


The others give incorrect perspective results.

I notice that if I make the X and Z components of the Combine XYZ into small non-zero values (like 0.001 say) then things look consistent. Seems like math is happening and you’re hitting imprecise floating point results maybe?

You’re might be right, but it seems those results are incorrect!

I made some nodes to take the cross product, and they confirm that the results of the left cube are correct. Using non-zero values in the X and Z components of the Combine XYZ node does produce consistent results, but they are not the same results produced by the nodes I made.

Vector Math:

Nodes:

G̶o̶i̶n̶g̶ ̶b̶a̶c̶k̶ ̶t̶o̶ ̶t̶h̶e̶ ̶l̶e̶f̶t̶ ̶c̶u̶b̶e̶-̶-̶t̶h̶e̶ ̶o̶n̶e̶ ̶p̶r̶o̶d̶u̶c̶i̶n̶g̶ ̶c̶o̶r̶r̶e̶c̶t̶ ̶r̶e̶s̶u̶l̶t̶s̶ ̶w̶h̶e̶n̶ ̶t̶h̶e̶ ̶X̶ ̶a̶n̶d̶ ̶Z̶ ̶c̶o̶m̶p̶o̶n̶e̶n̶t̶s̶ ̶a̶r̶e̶ ̶0̶-̶-̶I̶t̶ ̶s̶e̶e̶m̶s̶ ̶I̶ ̶c̶a̶n̶n̶o̶t̶ ̶r̶e̶p̶l̶i̶c̶a̶t̶e̶ ̶t̶h̶e̶ ̶r̶e̶s̶u̶l̶t̶s̶ ̶i̶t̶ ̶g̶i̶v̶e̶s̶.̶ ̶A̶d̶d̶i̶n̶g̶ ̶n̶e̶w̶ ̶c̶u̶b̶e̶s̶ ̶p̶r̶o̶d̶u̶c̶e̶s̶ ̶t̶h̶e̶ ̶m̶i̶d̶d̶l̶e̶ ̶r̶e̶s̶u̶l̶t̶s̶,̶ ̶b̶u̶t̶ ̶t̶h̶e̶ ̶l̶e̶f̶t̶ ̶c̶u̶b̶e̶ ̶s̶h̶o̶u̶l̶d̶ ̶h̶a̶v̶e̶ ̶j̶u̶s̶t̶ ̶b̶e̶e̶n̶ ̶a̶ ̶s̶i̶m̶p̶l̶e̶ ̶c̶u̶b̶e̶,̶ ̶t̶o̶o̶!

Edit: Actually, now it seems that adding new cubes does produce the same results, and it might just be the default cube with the problem. It’s odd that the defualt cube is not the same as all the others.

Here’s the updated .blend file:
Inconsistent Results.blend (556.2 KB)

Strange default_cube:confused:
But I need to test this fully to see where the error might come from (in the case of the cube made of planes).

EDIT: after setting the rotation of all planes from the 3rd cube, by hand… the results are now ok. So this is a floating point problem, which in case of the cross product can produce very different results.

>>> a= D.objects['Cube'].data.vertices
>>> for i in a:
...     i.co
...     
Vector((1.0, 0.9999999403953552, -1.0))
Vector((1.0, -1.0, -1.0))
Vector((-1.0000001192092896, -0.9999998211860657, -1.0))
Vector((-0.9999996423721313, 1.0000003576278687, -1.0))
Vector((1.0000004768371582, 0.999999463558197, 1.0))
Vector((0.9999993443489075, -1.0000005960464478, 1.0))
Vector((-1.0000003576278687, -0.9999996423721313, 1.0))
Vector((-0.9999999403953552, 1.0, 1.0))

>>> a= D.objects['Cube.001'].data.vertices
>>> for i in a:
...     i.co
...     
Vector((-1.0, -1.0, -1.0))
Vector((-1.0, -1.0, 1.0))
Vector((-1.0, 1.0, -1.0))
Vector((-1.0, 1.0, 1.0))
Vector((1.0, -1.0, -1.0))
Vector((1.0, -1.0, 1.0))
Vector((1.0, 1.0, -1.0))
Vector((1.0, 1.0, 1.0))

I think the problem here is that the vector (0,1,0) exactly matches the surface normal. As you’ve discovered, adding a slight offset resolves it. The cross product returns a vector that is perpendicular to both input vectors. In the case where both input vectors are identical there is no unique solution and the actual result is indeterminate. The solution is to ensure the two inputs never ecactly match.

The real problem is that the cross product can drastically change direction for very small changes in the orientation of one of the vectors. A cross product of two equal vectors gives a vector with magnitude 0, and this is also expected as sin(V^V) = 0.0.

In the case posted above we have: a Cube, and two meshes that aren’t really cubes!
If we want the math to be consistent with what we expect, we should use data that is consistent…

1 Like

Wait, as the angle between the vectors approaches 0 or pi, shouldn’t the magnitude tend toward 0? It seems the vector math node doesn’t seem to produce results like that.

In post 3, the first image shows the cross of (0.01,1,0.01) with the normal. The bright red left faces of the cubes in the image indicate a high magnitude. The normal vector on the left face should be (0,-1,0), so (0.01,1,0.01) x (0,-1,0) = (0.01,0,-0.01). The magnitude is being calculated too high for some reason.

Is there something about Blender’s Vector Math node that I’m missing? The results don’t make sense to me.

Ok, I can confirm this! probably a bug…
The cross product node is outputing a normalized vector!! :open_mouth:
I’ll check the source to see what happens.

edit:
here’s the culprit code:

//svm_math_util.h
[...]
 else if (type == NODE_VECTOR_MATH_CROSS_PRODUCT) {
    *Vector = safe_normalize_len(cross(Vector1, Vector2), Fac);
  }

//util_math_float3.h
ccl_device_inline float3 safe_normalize_len(const float3 a, float *t)
{
  *t = len(a);
  return (*t != 0.0f) ? a / (*t) : a;
}

So a solution would just multiply the Vector by the fac, to have the correct result.

1 Like

Oh, thanks for solving that! I wonder why it outputs a normalized vector when it seems it might be better to have the user specify that. At least the workaround is simple.

I really find it funny to think that the default cube really is a special cube!

it was a surprise for me also (I normally use osl for most vector ops)… But it was nice to dig this out. I discovered not only that cross product comes normalized(I can remove all ‘normalize nodes’ out of my libraries), but also that the ‘Value’ output of the normalize operation outputs the length of the Vector1(and no more sqrt(dot(v,v)) to get the vector magnitude!). :smiley:

EDIT: I took a look to the actual manual, and i’m surprised with the accuracy in it!

It could be a bit more «artist friendly», as it says |v| when it could say (also) the length of v,…
But … !! :star_struck:

Congrats to the people involved in the manual!!
Really a great improvement!!!

3 Likes