2
mycondingЦитата:
Нописал бы кно нормальные tutorial по 3d
Этакая мини-лекция по лин. алгебре.
Начнем издалека. Под матрицей поворота я подразумеваю любую квадратную матрицу с детерминантом
(изометрически сохраняющую длины, углы, объемы), которая обращается простым транспонированием (ортогональность). Технически выражаясь, матрица поворота суть элемент группы
. Известно, что такие преобразования переводят прямые в прямые, поэтому (хоть я уже и упоминал это свойство в начале темы, но все равно повторюсь), чтобы вращать любой объект достаточно действовать матрицей поворота на его "контрольные" точки. Т.е. вопрос типа "как вращать линию" ничем не отличается от вопроса "как вращать радиус-вектор".
Почему я не люблю словосочетание "матрица ориентации"? Просто, иногда под ориентацией понимают "праволевость" (знак определителя матрицы, составленной из элементов базиса) системы координат. Зачем лишняя путаница? Давайте пока будем говорить именно о матрицах поворота.
Что делает матрица поворота? Эта матрица описывает поворот системы координат (т.е. поворачивает координатные оси) вокруг её начала. Координаты любого вектора относительно старой системы координат остаются теми-же относительно новой координатной системы. Получается, что вектор в новой системе координат (после поворота) уже известен, так как покомпонентно равен вектору в старой системе координат; однако в практических ситуациях важно знать чему равны координаты вектора из новой системы координат, в старой системе координат. Для этого достаточно умножить вектор слева на матрицу поворота, это преобразование можно назвать поворотом вектора.
Для наглядности проиллюстрирую вышесказанное алгебраически. Пусть полем боя будет евклидово пространство
. В нем выбирается стандартный ортонормированный базис (система линейно независимых векторов)
. Тогда любой другой вектор
можно представить в виде линейной комбинации (разложения)
, где
-- просто числа, суть координаты (компоненты) вектора
.
Чем тогда будет поворот системы координат? Поворот будет просто сменой базиса
на новый (тоже ортонормированный) базис
. При этом под поворотом вектора следует понимать преобразование
, такое, что, как я писал чуть выше, вектор
в старом базисе покомпонентно равен вектору
в новом базисе. То есть,
раскладывается по базису
как
, и вектор
раскладывается по новому базису
как
(n.b., координаты те же, базис -- другой).
Задача заключается в отыскании разложения вектора
в старом базисе, т.е. нужно найти числа
в разложении
. Как это сделать? Для этого можно воспользоваться таким приемом. Разложим новый базис по элементам старого, т.е.
,
и
. В этих разложениях девять чисел
как раз таки и составляют матрицу поворота
.
Как использовать эти числа? Просто подставим только что полученные разложения для
в разложение
, что немедленно даст искомое разложение для
в старом базисе, которое хоть и громоздко, но эквивалентно краткому матрично-векторному выражению
, переводящему тройку
в тройку
. Именно так можно объяснить связь поворотов с умножениями на матрицы.
Последовательное применение матриц поворота, как я уже говорил, эквивалентно применению их матричного произведения, поэтому хранить достаточно не все матрицы для каждого элементарного поворота, а лишь одну. Ради экономии.
Когда мы вращаем систему координат, вращение происходит вокруг какой-то оси (не обязательно совпадающей с одной из координатных осей). Эта ось является неподвижной точкой преобразования
(или элементом ядра преобразования
, где
-- единичная матрица). Поворот вокруг такой оси будет соответствовать двумерному повороту в плоскости, ортогональной оси вращения, а такой 2D-поворот можно описать, как известно, углом.
Любой поворот, таким образом, можно задать осью вращения и углом, что достаточно удобно (это, наверное, самый экономичный по памяти способ описания вращения; нужно хранить лишь три числа, вместо девяти, т.е. направление оси задается вектором, а угол его длиной). Однако, мне лень выписывать формулы для компонентов соответствующей матрицы (к тому же, само понятие произвольной оси вращения почти не имеет никакого смысла в пространствах других размерностей), поэтому далее элементарные повороты будут задаваться лишь относительно (вокруг) координатных осей (формулы я уже приводил), а произвольные повороты будут получаться в виде композиции элементарных (под композицией понимается последовательное применение или перемножение соответствующих матриц). Ранее я вводил следующие обозначения для элементарных поворотов,
,
,
, где углы
и
обычно кличут эйлеровыми углами (хотя названия могут быть разными). На самом деле, достаточно использовать не все три, а лишь два элементарных поворота (например можно описать произвольный поворот как композицию
).
Мы здесь немного поспорили с
ИСН по поводу коммутативности поворотов. Возможно, мы просто говорили о разных вещах, но на всякий случай попробую прояснить ситуацию. Итак, повороты на плоскости коммутируют, это известно. В трехмерном же случае повороты уже не перестановочны, и я с этим полностью согласен. Но когда я говорил о накоплении углов, я имел ввиду именно накопление поворотов относительно разных координатных осей. Каждый такой элементарный поворот, как я уже писал, суть поворот в плоскости, перепендикулярной оси вращения, именно в плоскости, поэтому такие элементарные повороты "двумерны" и допускают скалярное накопление соответствующих углов. Другими словами, эйлеровы углы независимы (друг от друга), а так называемые эйлеровы повороты, представляющие собой изменения одного из эйлеровых углов при фиксированных двух других углах, коммутируют. Готов отдельно подискутировать на эту тему (возможно я в чем-то и заблуждаюсь, это не исключено).
Несмотря на очевидные достоинства эйлеровых углов и естественность их применения, скажем, в авиасимуляторах, задание положения (поворота) тремя углами влечет за собой некоторые проблемы. Здесь и неоднозначности в интерпретации, и возможная потеря одной из степеней свободы (cf. gimbal lock, i.e. опаньки карданову подвесу). Поэтому в реальных приложениях зачастую используют другие интересные, хотя, возможно, и несколько менее "интуитивные" объекты вроде кватернионов, билинейных форм, тензоров, и пр.
Например, кватернионы (aka кватернионы поворота aka единичные кватернионы aka версоры) имеют огромные преимущества перед другими, до этого рассмотренными методами поворотов. Кватернионы позволяют избежать gimbal lock'а, избежать лишних артефактов трансформаций (композиция матриц поворота замкнута, т.е. вновь является матрицей поворота, но это выполняется лишь в теории, de-facto же из-за глюков машинной арифметики в матрицу "подмешиваются" и другие преобразования, масштабирование, скос; кватернионы конечно тоже портятся из-за ошибок округления, но восстановить кватернион гораздо проще чем матрицу), "жрут" меньше памяти (четыре числа vs. девять), а также имеют ряд других практически важных плюсов, в частности позволяют качественно интерполировать повороты.
Позже я могу рассказать о кватернионах (или других подобных технологиях) поподробнее. А когда решиться вопрос с поворотами могу также рассказать подробнее и об удалении невидимых граней и об освещении и о текстурировании. Но пока давайте разберемся с трансформациями координат, а именно поговорим об использовании трансформаций вообще. Действительно, коль скоро задаются вопросы типа "что на что множить", значит есть какая-то путаница в самом понимании целей и средств.
Забудьте на некоторое время про матрицы, кватернионы и прочую конкретику. Мыслите о трансформациях как об абстрактных предписаниях типа "повернуть на прямой угол", "вперед на два шага", "увеличить в три раза" и т.д. Если вам нужно "повернуть", а потом "переместить", то соответствующие предписания необходимо "склеить", получив в результате список-композицию вроде "повернуть на прямой угол -> вперед на два шага".
Усложним задачу. Представьте, что у вас есть несколько объектов и вы хотите манипулировать не только всей сценой (работая в мировых координатах), но и отдельными объектами (работая в их локальных координатах). Т.е. например вы хотите повернуть некоторый объект вокруг его геометрического центра, но объект находится вдалеке от точки-начала координат, а вы умеется вращать объекты только вокруг этой точки.
Решение очевидно. Вы создаете пустой список предписаний, он будет описывать преобразования всей сцены (например поворот), добавляете в него какие-нибудь команды (т.е. создаете вспомогательные временные списки, содержащие лишь по одной команде, а затем приклеиваете эти списки к основному пополняемому). После этого вам нужно создать новый список, в котором будут находиться преобразования, касающиеся лишь выбранного объекта. Этот новый список изначально является не пустым, а копией первого. Вы добавляете команды в новый список, а затем выполняете его, после чего выбрасываете за ненадобностью. В самую последнюю очередь вы вополняете глобальный список.
Такой подход легко обобщается на любую глубину иерархической трансформации. Для этого заводится стек списков. Изначально в нем находится пустой список. когда нужно поработать с локальными координатами, в стек преобразований добавляется (push) новый список (копия уже лежащего на вершине стека). Команды добавляются к этому новому списку, находящемуся на вершине стека, после их выполнения текущий список удаляется со стека (pop). Таким образом операция push сохраняет имеющуюся координатную систему, а pop -- восстанавливает её.
Между списками преобразований и матрицами существует прямая аналогия. Списки соответствуют матрицам, пустой список суть единичная матрица, склеивание списков реализуется как умножение матриц друг на друга, выполнение списков -- как умножение матрицы на вектор. Все просто.
Что вам нужно сделать, чтобы добавить поддержку матричных трансформаций? Сначала подумайте, как будете хранить матрицы. Если работать с однородными координатами (для кодирования матрицами любых аффинных трансформаций, а не только линейных), то матрицы можно хранить в виде двумерных массивов
. В "левой верхней" части будут храниться матрицы линейных преобразований (
), при этом в "нижнем правом" элементе будет единица, а остальные элементы можно забить нулями (для начала).
Затем реализуйте алгоритмы умножения матрицы на вектор (вектора в однородных координатах тоже придется расширить лишней единицей) и матрицы на матрицу.
При рендеринге нужно будет перемножать матрицы элементарных преобразований (друг на друга, т.е. "склеивать списки") а затем "выполнять список" умножая полученную матрицу на все контрольные точки (вектора) вашей сцены. Заметьте, пока вы не захотите реализовать анимацию, геометрия сцены должна оставаться статичной, т.е. результат умножения матрицы на вектор нужно не сохранять, а использовать сразу (на самом деле сохранять можно и даже нужно для достижения высокой производительности, но сохранять нужно отдельно от основной геометрии).
Стеки матриц позволят вам работать с локальными системами координат, поэтому их можно начать проектировать/реализовывать с самого начала. Слишком большими их делать не придется, обычно одновременно в стеке хранится не больше десятка матриц.
Просто напишите это все и запустите. Если заработает сразу -- хорошо, если не заработает -- оформляйте исходники и выкладывайте критичные фрагменты кода здесь, уверен, вам что-нибудь подскажут/помогут.
2
Qx15(RUS)Цитата:
А у меня вопрос, как разделить матрицу A на матрицу B?
Как такового, матричного деления нет, есть только обращение матрицы. Иногда обратную матрицу можно посчитать сравнительно легко (как, например, в случае с обсуждаемыми здесь матрицами поворота), но в общем случае это не так. Пусть есть квадратная обратимая (имеющая ненулевой определитель) матрица
. Обратная матрица
может быть найдена как
, где
-- присоединенная матрица, составленная из алгебраических дополнений матрицы
, траспонированной по отношению к
:
, где
-- минор элемента
, являющийся детерминантом матрицы, составленной из элементов исходной матрицы, за исключением элементов строки и столбца, на "пересечении" которых находится элемент, минор которого отыскивается.
Вам этого способа вполне достаточно для отыскания обратных матриц, нужно только уметь считать определитель.