MrShoor
>Мой фреймворк заточен под правостороннюю СК
Чем ты ориентировался при ее выборе?
Мне, например, удобнее левосторонняя,
т.к. в школе была именно такая,
так сказать - СК которую мы всосали с молоком матери.
MrShoor
> Потому что -1 это для правосторонней системы координат.
MrShoor
> Мой фреймворк заточен под правостороннюю СК, поэтому там строго 1.
Так всё таки какая? Вот даже кто знает путается. Что разный код наваливают так вообще вставишь, и не работает. А оно просто под другую СК.
Я вот тоже для производительности в SIMD матрицы храню в таком представлении:
union { __m128 fmm[4]; f32 m[4][4]; struct { f32 _30, _20, _10, _00, _31, _21, _11, _01, _32, _22, _12, _02, _33, _23, _13, _03; }; };
Поэтому мой код нельзя просто взять и применить.
Truthfinder
> Так всё таки какая?
Опечатался, хотел сказать мой фреймворк заточен под левостороннюю.
У меня обратные матрицы для каждого случая считаются специальным методом.
(Это уменьшает погрешности, что критично при рендеринге ландшафтов.)
Тута расчёт обратной матрицы проекции в Direct3D, может, кому-то пригодится:
/// Constructs a symmetric view frustum using Direct3D's clip-space conventions (NDC depth in range [0..+1]). /// FoV_Y_radians - (full) vertical field of view, in radians (e.g. PI/2 radians or 90 degrees). /// mxNO_ALIAS void M44_ProjectionAndInverse_D3D(float FoV_Y_radians, float aspect_ratio, float near_clip, float far_clip, M44f *__restrict projectionMatrix, M44f *__restrict inverseProjectionMatrix ) { mxASSERT( FoV_Y_radians > 0); mxASSERT( aspect_ratio > 0); mxASSERT( near_clip > 0.0f); mxASSERT( near_clip != far_clip); //@todo: rearrange using algebraic rules for better precision? const double tanHalfFoV = tan( ( double)FoV_Y_radians * 0.5 ); const double V = 1.0f / tanHalfFoV; const double H = V / aspect_ratio; const double A = far_clip / ( far_clip - near_clip); const double B = -near_clip * A; projectionMatrix->r0 = Vector4_Set( H, 0, 0, 0 ); projectionMatrix->r1 = Vector4_Set( 0, 0, A, 1 ); projectionMatrix->r2 = Vector4_Set( 0, V, 0, 0 ); projectionMatrix->r3 = Vector4_Set( 0, 0, B, 0 ); inverseProjectionMatrix->r0 = Vector4_Set( 1/H, 0, 0, 0 ); inverseProjectionMatrix->r1 = Vector4_Set( 0, 0, 1/V, 0 ); inverseProjectionMatrix->r2 = Vector4_Set( 0, 0, 0, 1/B ); inverseProjectionMatrix->r3 = Vector4_Set( 0, 1, 0,-A/B ); } /* Multiplication by our projection matrix (NOTE: z corresponds to height, y - to depth): | H 0 0 0 | [x y z w] * | 0 0 A 1 | = | (x*H) (z*V) (y*A + w*B) (y) | | 0 V 0 0 | | 0 0 B 0 | */ /* Multiplication by inverse projection matrix (P^-1): | 1/H 0 0 0 | [x y z w] * | 0 0 1/V 0 | = | (x/H) (w) (y/V) (z/B - w*A/B) | | 0 0 0 1/B | | 0 1 0 -A/B | */
Прикола ради залез на википедию, и нашёл там вот такой интересный метод.
Для матриц 4*4:
Для матриц 3*3:
Кто-нибудь пробовал так делать? Какие впечатления - скорость на симд, численная стабильность? С братом ничего не случилось?
Delfigamer
> Кто-нибудь пробовал так делать? Какие впечатления - скорость на симд, численная
> стабильность? С братом ничего не случилось?
Даже если это корректные формулы, профит по количеству операций чота не сильно заметно. Отбрасываем обратный детерминант, он в обоих случаях есть. А вот насколько оставшееся выгоднее вычисления алгебраических дополнений не очевидно. Тут надо попробовать и посмотреть что получится. Но если бы это было уберрешение, я думаю его давно бы уже везде использовали.
Например тут надо получить A^2 и A^3, это два матричных умножения, далее 4 умножения матрицы на число и 3 матричные суммы. Я скалярные операции даже не смотрю. При этом много работы с памятью (I, A, A^2, A^3), что вредно на современных CPU. Выгоднее загрузить одну матрицу и много считать, чем мало считать, но много работать с памятью.
eDmk
> Инверсия матрицы 4x4
Отлично! Скоро увидим подсказки, как сложить два вектора, а еще через пару лет узнаем, что дважды два = четыре.
Truthfinder
> Например тут надо получить A^2 и A^3, это два матричных умножения, далее 4 умножения матрицы на число и 3 матричные суммы.
Такое обычно считается схемой Горнера.
}:+()___ [Smile]
> Такое обычно считается схемой Горнера.
А можно примерчик, ну или линк для матриц?
0xc0de
> Отлично! Скоро увидим подсказки, как сложить два вектора
Когда я пришел на GameDev, то не знал как сложить 2 вектора и что такое вектор вообще :)
Для новичков норм.
Truthfinder
> А можно примерчик, ну или линк для матриц?
\(\alpha I+\beta A+\gamma A^2+\delta A^3=\alpha I+(\beta I+(\gamma I+\delta A)A)A.\)
Правда, в данном конкретном случае, наверное, не поможет, ибо там \(A^2\) и \(A^3\) в нескольких местах стоят.
eDmk
> Для новичков норм.
Для новичков в последних Дельфях есть операции с матрицами прямо "из коробки", System.Math.Vectors.
Можно найти и с SIMD: https://github.com/neslib/FastMath
+ традиционный источник копипаста - GLScene (GLVectorGeometry).
invis
> в последних Дельфях
Не у всех последняя дельфя. Она денег стоит.
>+ традиционный источник копипаста
Это у вас принято воровать, а мы по статьям и книжкам как обычно.
Я например читал статью с сайта.
Мне просто непонятно, чем вы недовольны?
Я подсказываю потенциальным новичкам (другой вопрос, что на Дельфи? 3D? anyone?), какие ещё есть варианты. Может и ты что-то новое узнаешь - например, что последняя Дельфя бесплатна, если на ней зарабатываешь не больше 5000$ в год.
Копипаст из других библиотек никогда по-моему воровством не считался, если указываешь источник. Ну скажем так, из библиотек уровня GLScene. Если это что-то серьёзное/продвигаемое крупной компанией/купленное за деньги, тогда лицензию надо читать.
И кстати, стандартный дельфийский модуль явно скопирован с GLScene, по названиям функций видно.
invis
> Для новичков в последних Дельфях есть операции с матрицами прямо "из коробки",
> System.Math.Vectors.
Я вот тоже не знал. Поэтому сейчас заглянул в этот юнит. О боги. Там ушибленная на всю голову математика. Человек писавший её явно никогда не занимался компьютерной графикой, и понятия не имеет что удобно, а что нет.
Итак, что сразу бросается в глаза:
TEpsilon = record const Matrix = 1E-5; Vector = 1E-4; end;
Чиво? Разные эпсилоны для матрицы и для вектора? Лул.
Далее имеем:
TVector = recordкоторый:
case Integer of 0: (V: TVectorArray;); 1: ( X: Single; Y: Single; W: Single;); end;
И:
TPoint3D = recordкоторый:
case Integer of 0: (V: TPoint3DArray;); 1: ( X: Single; Y: Single; Z: Single;); end;
Спрашивается, зачем? Типа точка и вектор разные сущности? А вот нифига. Потому что у точки и у вектора список методов почти одинаковый. У точки мы можем найти Length, сделать для точки Normalize и получить точку... это фейспалм.
А еще есть тип:
TVector3D = recordУгадаете что это? Это vec4:
case Integer of 0: (V: TVector3DType;); 1: ( X: Single; Y: Single; Z: Single; W: Single;); end;
Так же у нас есть:
TMatrix = recordи:
TMatrix3D = recordРазница в том, что одна из них - это матрица 3*3, а другая 4*4. Удобные названия, ничего не скажешь.
А еще есть:
TQuaternion3D = recordЗачем приписка 3D я не понимаю. Очень полезный кватернион, на который даже нельзя умножить вектор. Можно только в матрицу сконвертить, а потом матрицу уже умножать.
И вообще вся математика крайне куцая. Нельзя даже покомпонентно 2 вектора умножить. Зато 100500 аналогов одинаковых функций, которые сразу объявили deprecated. В новом! юните слово deprecated встречается 12 раз. Вы просто оцените эту красоту:

Это же долбаный позор.
Тема в архиве.