Обратная Матрица (Inverse Matrix)

от автора

Всем привет. При изучении 3D в программировании мы пользуемся математикой — линейная алгебра, матрицы, векторы, кватернионы и т.д. В какой-то момент становится интересно как устроено пространство 3D, какие принципы и основы заложены в фундамент отображения моделей. Так же, просто на отображении 3D не возможно остановиться — хочется добавить свет, тень, как минимум. Для расчета света нам надо отправить в шейдер матрицу модели рисуемого объекта — текущего, нормали плоскостей (например на триангулированный объект на каждый треугольник по нормали выходит).

Так же по расчету света необходимо добавить эту же текущую модель, в виде обратной матрицы и транспонированную.

В этой статье покажу как я решил такую задачу — комплексное решение приводящее к обратной матрице (размер 3х3).

Рабочее пространство

Все расчеты буду вести в среде Линукс, 14.2 gcc.

Давайте посмотрим, как можно это решить

Так же подготовим ориентиры которыми будем пользоваться

Обозначение M - матрица пример, Transpose - матрица транспонирования, Minor - матрица минор, Cofactor - матрица знаков, DetM - определитель, M в степени -1 - обратная матрица - искомая, Maj - матрица после (Minor->Cofactor->Transpose) матрица сопряжения

Обозначение M — матрица пример, Transpose — матрица транспонирования, Minor — матрица минор, Cofactor — матрица знаков, DetM — определитель, M в степени -1 — обратная матрица — искомая, Maj — матрица после (Minor->Cofactor->Transpose) матрица сопряжения

Так же будем пользоваться транспонированием результирующей матрицы.

Когда мы посчитаем матрицу Minor и применим знаки матрицы Cofactor и применим матрицу Транспонирования — это можно назвать матрицей Сопряжения(Ajoint или она же где-то может называться Ajugate).

Примерно перед применением Транспонирования мы можем проверить определитель (Determinant, Det), определитель нужно проверить на не ноль далее ниже мы рассмотрим случаи 0 и не 0.

Определитель является важной составляющей при расчете обратной матрицы, так как при нахождении обратной матрицы на определитель надо поделить.

Перейдём к реализации

Скрытый текст
#pragma GCC optimize ("Ofast,unroll-loops,-ffast-math") #pragma GCC target   ("sse,sse4.2") #include <xmmintrin.h> #include <smmintrin.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> //обозначим необходимый минимум по структурам typedef float f32;  struct vec3_t {     f32 x;     f32 y;     f32 z; }; typedef struct vec3_t vec3;  struct mat3_t {     f32 v[9]; }; typedef struct mat3_t mat3;  //функция печати матрицы void printMatrix(const mat3 m) {   printf("%f %f %f\n%f %f %f\n%f %f %f\n\n",  m.v[0],m.v[1],m.v[2],  m.v[3],m.v[4],m.v[5],  m.v[6],m.v[7],m.v[8]  ); }  //установка матрицы через 3 вектора const mat3 setMatrix(const vec3 v,const vec3 v1,const vec3 v2) {   return (mat3)     {       v.x,v.y,v.z,       v1.x,v1.y,v1.z,       v2.x,v2.y,v2.z,     }; }  //матрица транспонирования const mat3 Transposedm3(const mat3 m) {     return (mat3)       { m.v[0], m.v[3],m.v[6], m.v[1], m.v[4],m.v[7], m.v[2], m.v[5],m.v[8]       }; }  //матрица минора  mat3 Minorm3(mat3 m) {   return (mat3)     {       (m.v[4]*m.v[8]-m.v[5]*m.v[7]),       (m.v[3]*m.v[8]-m.v[5]*m.v[6]),       (m.v[3]*m.v[7]-m.v[4]*m.v[6]),        (m.v[1]*m.v[8]-m.v[2]*m.v[7]),       (m.v[0]*m.v[8]-m.v[2]*m.v[6]),       (m.v[0]*m.v[7]-m.v[1]*m.v[6]),        (m.v[1]*m.v[5]-m.v[2]*m.v[4]),       (m.v[0]*m.v[5]-m.v[2]*m.v[3]),       (m.v[0]*m.v[4]-m.v[1]*m.v[3]),     }; }  //применение знаковой матрицы mat3 Cofactorm3(mat3 m) {   return (mat3)     {        m.v[0],-m.v[1],m.v[2],       -m.v[3], m.v[4],-m.v[5],        m.v[6],-m.v[7],m.v[8]     }; }  //результирующая функция Инверсной матрицы const mat3 Inversem3(mat3 m)  {   mat3 as;   as=Minorm3(m);   as=Cofactorm3(as);    float DetA=(m.v[0]*as.v[0])+(m.v[1]*as.v[1])+(m.v[2]*as.v[2]);   printf("Det %f\n\n",DetA);   //проверка !0   DetA=1/DetA;         as=Transposedm3(as);      return (mat3)     {       as.v[0]*DetA,as.v[1]*DetA,as.v[2]*DetA,       as.v[3]*DetA,as.v[4]*DetA,as.v[5]*DetA,       as.v[6]*DetA,as.v[7]*DetA,as.v[8]*DetA     }; }  int main() {      //printf("Chapter: Find Inverse Matrix!\nExample Matrix for start calculate:\n\n");   mat3 matA;    matA=setMatrix(  (vec3){1,2,3},  (vec3){4,5,6},  (vec3){7,8,9}                 );   printMatrix(matA);    mat3 as;    as=Inversem3(matA);   printf("Inverse Result: \n");   printMatrix(as);    printf("\n\n");   return 0;    } 

скомпилируем

gcc -Ofast -ffast-math -o test main.c -lm

результат

случай 0

случай 0
случай не 0

случай не 0

теперь мы умеем находить Обратную матрицу.

Ресурсы

https://www.mathsisfun.com/algebra/matrix-calculator.html

https://www.geeksforgeeks.org/inverse-of-3×3-matrix/

https://ru.wikipedia.org/wiki/Обратная_матрица

https://ru.wikipedia.org/wiki/Определитель


ссылка на оригинал статьи https://habr.com/ru/articles/865192/