Weird normal values

I’m getting some weird normal values when raycasting against some cubes.

In the image the cubes are all the same and when I log the normals I get the expected values (0, 1, 0), etc, except for the cube positions adjacent to 0 on the XZ axis. Those are giving me normal values like so.

(1, 0, -1.743534 e20)

One thing I’ve noticed is that when I log the vertices they show up in two digits.

(-0.50, 0.50, 0.50)

Instead of.

(-0.5, 0.5, 0.5)

Could that be causing the normals to look weird?

This happens even if I use RecalculateNormals() or not.

It must be some kind of floating point problem or something? I found out it works fine if I do not use any matrix translations on them.

My matrix transformation will rotate some of the cubes.

Matrix4x4 fm = Matrix4x4.Rotate(Quaternion.Euler(0, r, 0));

But even if I rotate them by (0,0,0) the normals will still be messed up on those specific cubes along the XZ axis. :thinking:

If I do a matrix transformation like so.

Matrix4x4 fm = Matrix4x4.Rotate(Quaternion.Euler(0, 0, 0));

Why would that mess up the normals? The vertices all look correct besides having an extra zero at the end, (-0.50), instead of (-0.5).

1 Like

You didn’t copy paste that properly, it’s much more likely that the value is stated as -1.743534-E20
which is the same as -1.743534 \times 10^{-20}
which is the same as -1.743534 \times 0.00000000000000000001
so the value is thus -0.00000000000000000001743534
which is effectively the same as plain ol’ 0, just not exactly the same.

The other reason why I’m sure you didn’t copy paste that properly is because that value cannot be actually stored inside a 32-bit floating point, and all vector components in Unity are 32-bit floating point.

No. And why do you think that the normals look weird?
Look it’s time you learn how decimal numbers can be represented. The numbers containing the letter E is what is called a scientific notation, and there is nothing weird about it. Just look it up, learn something new, and that’s it.

Next, make sure to understand how to debug values properly when it matters to you.
C# can use various string output formats for your convenience, don’t rely on the defaults all the time.

For example Debug.Log($"{myVector:F6}") will print out the components to 6 decimal places. Not everything you read from the console is perfectly representative of whatever is in memory. It’s a string conversion intended for debugging purposes. You can very well output some weird representation, like hexadecimal or whatever. Values are all stored in their own binary encodings, you do realize that? All of these floating point numbers are encoded according to IEEE-754 standard, they’re not really decimal numbers.

1 Like

Ok so thx for that but that is completely unrelated.

Why are the normals not 0 like all the other cubes?

Erm… They are?! I don’t think you understand how floating point works, or what it means to apply a matrix. If you did, you would never say this.

Now the particular reason why only these vertices are different is very likely because there is a slight discrepancy with how normals were calculated regardless if you used RecalculateNormals or did it in Blender or whatever. Both Unity and Blender use the same (or similar) algorithm for the normals that weighs their importance via angle/area of the triangle, and in the end this is stored in 32-bit floating points. It could be that there is a slight natural bias in the corners of the mesh.

Regardless, all of that to point out that if your normals aren’t correct that has nothing to do with whatever you’re outputting to the console, E numbers and two digit values.

Besides, it would be more useful to at least give us the code that produced these strings and copy paste the messages verbatim, instead of telling us your impressions of what happened.

Also try to explain how exactly this mesh is made, what are “cubes”; also did you arrange cubes on a grid, and if so, how, and why and how are your corner cubes different from any other?

And if they were Blender imports they likely have a rotation of something like (-89.98,0,0) which who knows what that will do to your normals.

2 Likes

Plus, there is another thing with the cubes: they have split vertices on several corners. And each of these has a different normal (to produce a sharp corner), so there is a chance you’re hitting the one on the side giving you a “wrong” normal. If that was what you meant to say with (0, 1, 0) and later (1, 0, 0)

I’m using a burst job to build the vertices/triangles/etc and it’s way too much code to paste.

I don’t understand what you mean by they are 0.

If I log a normal of a cube in the center and it shows.

(0, 1, 0)

And then I log a normal of one on the edge of the mesh and it shows something like this.

(1, 0, -1.743534 e20)

That is not 0. If I try to check for 0 in an if statement it’s not going to work.

Here is the code where I am checking for 0.

                bool found = false; int3 target = new(source.x, source.y, source.z); int3 TC = chunk.position;
                float x = hit.normal.x; float y = hit.normal.y; float z = hit.normal.z;
                Debug.Log(x + " " + y + " " + z);
                if (!found && x < 0f && y == 0f && z < 0f) { target.x -= 1; target.z -= 1; TC.x -= 10; TC.z -= 10; found = true; }
                if (!found && x < 0f && y == 0f && z > 0f) { target.x -= 1; target.z += 1; TC.x -= 10; TC.z += 10; found = true; }
                if (!found && x > 0f && y == 0f && z > 0f) { target.x += 1; target.z += 1; TC.x += 10; TC.z += 10; found = true; }
                if (!found && x > 0f && y == 0f && z < 0f) { target.x += 1; target.z -= 1; TC.x += 10; TC.z -= 10; found = true; }

If the mesh vertices are identical (flat surface everywhere) then the normals should either be 1 or 0.

We never check floating points like that. It obviously won’t work, as I said it is not exactly 0.
But it is effectively a zero. What part of my original message you didn’t get?

When you apply rotational matrix or whatever all of the vertices will get manipulated if only ever so slightly. 32-bit floating point has imprecisions and if it wasn’t exactly zero, they will likely stay at a non-zero but very miniscule value, an infinitesimal.

In other words, that’s not really (1, 0, infinitesimal), but instead (1 - infinitesimal, 0, infinitesimal). It has to have magnitude of 1. If you did Debug.Log as I pointed out before, you’d see this to be true.

Now to explain my previous post. This is a default cube. It has 24 vertices instead of just 8. If you pay close attention, it has 3 normals in every corner. 3x4x2 = 24

This is how normals would look if it only had 8 vertices.

It would render smoothly, like a block of weird soap.

Ya that’s what I was thinking. So basically I have to check for a range or round the float to an int then. Alrighty, thx orionsyndrome.

1 Like

To check whether a floating point value is a zero, you introduce a “good-enough” threshold. This is how this is normally done (or a variant thereof)

static public bool isZero(float n, float threshold = 1E-6f)
  => MathF.Abs(n) < threshold;

Now instead of doing y == 0f you do isZero(y)

It’s worth noting that a floating point can perfectly store exactly 0 or exactly 1. So if you’re the one writing exactly these values, you can truly test for them exactly (I.e. y == 0 or y == 1). Try to do this with a value such as 0.2 and you’ll see that it doesn’t work, as 0.2 cannot be perfectly stored in a floating point.

Likewise, if you’re computing a value, there are no such guarantees that it will land exactly on zero, depending on the order and quality of operations, and this is typical for geometry and linear algebra with 32-bit values, so you must apply the “good-enough” approach.

1 Like