Source
- URL: https://x.com/SebAaltonen/status/1866131513852477814
- Author: Sebastian Aaltonen (@SebAaltonen)
- Posted: 2024-12-09 14:43:18
Thread
1/ @SebAaltonen
Bug report: I want to scale an object to be flat. I set the X-axis scale to zero. Lighting breaks!
When the X-axis in the scale matrix is zero, that matrix can’t be inverted. Divide by zero is not legal. So, we can’t calculate the inverse-transpose matrix for normal transform.
2/ @BrookeHodgman
@SebAaltonen
When computing the normal-transformation matrix, you could detect this special case and emit a matrix that just results in 1,0,0,0 for all inputs 😎
3/ @SebAaltonen
@BrookeHodgman
Unfortunately a 3x3 matrix that does this doesn’t exist. Also that’s not correct either, you want either (-1,0,0) or (+1,0,0) output depending on which side the normal is.
4/ @EricLengyel
The 3x3 matrix you want does exist. It’s got a one in the upper-left corner and zeros everywhere else. For an input normal of (x, y, z), the result will be (x, 0, 0), which will still point in the right direction. It just needs renormalized, which is to be expected.
5/ @BrookeHodgman
@EricLengyel @SebAaltonen
That can output 0,0,0 (and then normalise to NaN) for some inputs though, so isn’t quite the same as above 😓
6/ @EricLengyel
It is provably the correct matrix to use for transforming normals when there’s a zero scale in the x direction. Any surface having a normal with zero x component would no longer exist after the scale is applied, so a NaN normal actually makes sense and would only appear on degenerate triangles that get culled anyway. To avoid interpolating with a NaN normal at a vertex, the scale transform would need to be applied after interpolation occurs.
7/ @BrookeHodgman
@EricLengyel @SebAaltonen
Yeah it’s correct in theory… But in practice (with interpolation like you mention or normal maps, etc) you often do see surfaces that have “wrong” normals so have to worry about handling those NaNs 😓
You don’t want an unlucky normal map sample to result in a random black pixel
8/ @SebAaltonen
@BrookeHodgman @EricLengyel
Random NaN pixel would be even worse. It will propagate and ruin the whole image.
9/ @EricLengyel
@SebAaltonen @BrookeHodgman
A normal map would need to contain vectors tangent to the surface for this to happen. So just don’t allow it! It can’t occur if the normals are calculated from a height field anyway.
10/ @SebAaltonen
@EricLengyel @BrookeHodgman
You don’t even need a normal map to see “backfacing” pixels. Interpolated normal makes it possible. Let’s say you have a smoothed cube. Normal vectors on the same triangle have 90-degree angle between them. Let’s say we are flattening such cube…
11/ @SebAaltonen
@EricLengyel @BrookeHodgman
If we flatten vertex normals, then we get triangles with (1,0,0) and (-1,0,0) normals and interpolating those will result in (0,0,0) in the midpoint. Cube could be slightly rotated so that these triangles are visible.
12/ @SebAaltonen
@EricLengyel @BrookeHodgman
If you flatten per pixel, then you will get a triangle that is facing (-1,0,0) halfway and facing (+1,0,0) halfway, and there’s a possibility that the center pixel hits (0,0,0) exactly it’s normal vector.x = 0.
13/ @SebAaltonen
@EricLengyel @BrookeHodgman
The same can happen in completely flat surfaces with normal map. The same slightly rotated cube, but with flat faces. Now the normal map makes some pixels have negative X normal (most are positive), and there can be some with normal.X=0, which causes problems.
14/ @SebAaltonen
@EricLengyel @BrookeHodgman
A simple example with planar surface. You have a normal map on the plane that has half-sphere bumps. If this surface is flattened in X direction, then bumps of course disappear, as bumps are flattened too. But if you rotate it slightly, then some of those bump pixels face -X.
15/ @SebAaltonen
@EricLengyel @BrookeHodgman
This kind of normal map
16/ @SebAaltonen
@EricLengyel @BrookeHodgman
The flattening matrix still works, but randomly some pixels have normal.x = 0 exactly, and those will have invalid normals after renormalize. Maybe we just nudge the math a bit to make them choose either +X or -X path.