Pull to refresh

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

Level of difficultyEasy
Reading time3 min
Views3.3K

Всем привет. При изучении 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-3x3-matrix/

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

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

Tags:
Hubs:
Total votes 6: ↑3 and ↓30
Comments41
1

Articles