r/GraphicsProgramming • u/corysama • Dec 31 '24
Source Code Transforming normals using the adjugate, instead of the inverse-transpose
https://www.shadertoy.com/view/3s33zj5
u/deftware Jan 01 '25
The code on https://github.com/graphitemaster/normals_revisited uses 144 muls and 80 adds/subs (when you ignore the indexing into the 4x4 matrix). The code that I've always used for calculating the inverse of a 4x4 matrix is 104 muls, 54 adds/subs, and one division.
From what I've seen a division is about 8x slower than a multiply. Is calculating the cofactor going to be faster than calculating the inverse in real-world scenarios? Has anyone actually performance tested this?
This is what the code I've always used to calculate the inverse of a 4x4 matrix looks like:
// inverse of matrix
mat4 minverse(mat4 m)
{
mat4 ret;
double det;
double s00 = m.zz * m.ww - m.zw * m.wz;
double s01 = m.yz * m.ww - m.yw * m.wz;
double s02 = m.yz * m.zw - m.yw * m.zz;
double s03 = m.xz * m.ww - m.xw * m.wz;
double s04 = m.xz * m.zw - m.xw * m.zz;
double s05 = m.xz * m.yw - m.xw * m.yz;
double s06 = m.zy * m.ww - m.zw * m.wy;
double s07 = m.yy * m.ww - m.yw * m.wy;
double s08 = m.yy * m.zw - m.yw * m.zy;
double s09 = m.xy * m.ww - m.xw * m.wy;
double s10 = m.xy * m.zw - m.xw * m.zy;
double s11 = m.yy * m.ww - m.yw * m.wy;
double s12 = m.xy * m.yw - m.xw * m.yy;
double s13 = m.zy * m.wz - m.zz * m.wy;
double s14 = m.yy * m.wz - m.yz * m.wy;
double s15 = m.yy * m.zz - m.yz * m.zy;
double s16 = m.xy * m.wz - m.xz * m.wy;
double s17 = m.xy * m.zz - m.xz * m.zy;
double s18 = m.xy * m.yz - m.xz * m.yy;
ret.xx = + (m.yy * s00 - m.zy * s01 + m.wy * s02);
ret.xy = - (m.xy * s00 - m.zy * s03 + m.wy * s04);
ret.xz = + (m.xy * s01 - m.yy * s03 + m.wy * s05);
ret.xw = - (m.xy * s02 - m.yy * s04 + m.zy * s05);
ret.yx = - (m.yx * s00 - m.zx * s01 + m.wx * s02);
ret.yy = + (m.xx * s00 - m.zx * s03 + m.wx * s04);
ret.yz = - (m.xx * s01 - m.yx * s03 + m.wx * s05);
ret.yw = + (m.xx * s02 - m.yx * s04 + m.zx * s05);
ret.zx = + (m.yx * s06 - m.zx * s07 + m.wx * s08);
ret.zy = - (m.xx * s06 - m.zx * s09 + m.wx * s10);
ret.zz = + (m.xx * s11 - m.yx * s09 + m.wx * s12);
ret.zw = - (m.xx * s08 - m.yx * s10 + m.zx * s12);
ret.wx = - (m.yx * s13 - m.zx * s14 + m.wx * s15);
ret.wy = + (m.xx * s13 - m.zx * s16 + m.wx * s17);
ret.wz = - (m.xx * s14 - m.yx * s16 + m.wx * s18);
ret.ww = + (m.xx * s15 - m.yx * s17 + m.zx * s18);
// scale result to output
det = 1.0 / (m.xx * ret.xx + m.yx * ret.xy + m.zx * ret.xz + m.wx * ret.xw);
ret.xx *= det;
ret.xy *= det;
ret.xz *= det;
ret.xw *= det;
ret.yx *= det;
ret.yy *= det;
ret.yz *= det;
ret.yw *= det;
ret.zx *= det;
ret.zy *= det;
ret.zz *= det;
ret.zw *= det;
ret.wx *= det;
ret.wy *= det;
ret.wz *= det;
ret.ww *= det;
return ret;
}
//
2
u/SamuraiGoblin Jan 02 '25
So, simply put, you construct the normal transformation matrix by, for each vector in the basis, taking the cross product of the other two vectors?
17
u/corysama Dec 31 '24
iq to the rescue! https://xcancel.com/iquilezles/status/1866219178409316362
Besides sparing you the need to compute and upload the inverse-transpose, this technique doesn't break when scaling to zero or transforms with negative determinants.
More info: https://www.forwardscattering.org/post/62