| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- <!--
- demo.design 3D programming FAQ
- Idea, texts, screenshots:
- Andrew A. Aksyonoff,
- shodan@chat.ru
- Web-design, illustrations:
- Andrey Samoilov,
- asy@sense.simbirsk.su
- -->
- <html>
- <head>
- <title>demo.design 3D programming FAQ. Основы 3D графики. Матричные преобразования.</title>
- <link rel=stylesheet href="../style.css" type="text/css">
- </head>
- <script language="javascript">
- <!--//
- browser = navigator.appName;
- version = parseFloat(navigator.appVersion);
- if (browser == "Netscape" && version >= 3.0) { jsenabled = 1; } else
- if (browser == "Microsoft Internet Explorer" && version >= 3.0) { jsenabled = 1; } else { jsenabled = 0; }
- function swap(img,ref) { if (jsenabled) {document.images[img].src = ref;} }
- function loadtocache(img,ref) { cache[img] = new Image(); cache[img].src = ref; }
- if (jsenabled) {
- cache = new Array();
- loadtocache(0,"../img/xdl.gif");
- loadtocache(1,"../img/xfaq.gif");
- loadtocache(2,"../img/xlinks.gif");
- loadtocache(3,"../img/xauthor.gif");
- loadtocache(4,"../img/xe.gif");
- loadtocache(5,"../img/xprev.gif");
- loadtocache(6,"../img/xnext.gif");}
- //-->
- </script>
- <body bgcolor=white><center>
- <!-- Title -->
- <img src="../img/b.gif" width=500 height=1 alt=""><br>
- <img src="../img/t.gif" width=500 height=1 alt=""><br>
- <img src="../img/b.gif" width=500 height=1 alt=""><br>
- <img src="../img/t.gif" width=500 height=2 alt=""><br>
- <table width=500 cellpadding=0 cellspacing=0 border=0>
- <td><img src="../img/t.gif" width=5 height=1 alt=""><a href="../main.htm" onmouseover="swap('logo','../img/xe.gif');" onmouseout="swap('logo','../img/e.gif');"><img src="../img/e.gif" name=logo width=60 height=50 hspace=10 border=0 alt=" в самое начало "></a></td>
- <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
- <td align=center><p class=navy><a href="../download.htm" onmouseover="swap('dl','../img/xdl.gif');" onmouseout="swap('dl','../img/dl.gif');"><img src="../img/dl.gif" name=dl width=40 height=40 border=0 hspace=5 alt=" download "></a><br>download</td>
- <td align=center><p class=navy><a href="../links.htm" onmouseover="swap('links','../img/xlinks.gif');" onmouseout="swap('links','../img/links.gif');"><img src="../img/links.gif" name=links width=40 height=40 border=0 hspace=5 alt=" коллекция линков "></a><br>links</td>
- <td align=center><p class=navy><a href="../author.htm" onmouseover="swap('author','../img/xauthor.gif');" onmouseout="swap('author','../img/author.gif');"><img src="../img/author.gif" name=author width=40 height=40 border=0 hspace=5 alt=" автора! "></a><br>author</td>
- </table>
- <img src="../img/t.gif" width=500 height=4 alt=""><br><img src="../img/b.gif" width=500 height=1 alt=""><br>
- <!-- Head -->
- <table width=500 cellpadding=0 cellspacing=10 border=0><td><div align=justify>
- <p class=title>
- <img src="../img/b2.gif" width=70 height=70 align=left hspace=0 alt="">
- <img src="../img/t.gif" width=5 height=70 align=left hspace=0 alt="">
- ОСНОВЫ 3D ГРАФИКИ<br>2.3. Матричные преобразования
- <!-- Article -->
- <p>Вообще говоря, лучше всего немного почитать любую книжку по линейной алгебре.
- Здесь будет только краткий рассказ о 3D преобразованиях, о том, как их делать
- с помощью матриц, и о том, что же такое матрицы и как с ними работать.
- <p>Введем несколько терминов. n-мерный вектор, он же вектор размерности n, он
- же вектор размера n: упорядоченный набор n действительных чисел. Вообще
- говоря, практически то же самое, что и обычный 1D-массив. Матрица размера
- m на n (будет обозначаться как m*n, mxn): таблица размера m на n, в каждой
- клетке которой - действительное число. Это уже 2D-массив. Всего лишь.
- Вот пример матрицы 3x3:
- <pre class=formula>
- [ 15 y*z 0.6 ]
- [ 7 -3 91 ]
- [ sin(x) 0.123 exp(t) ]
- </pre>
- <p>Займемся определением операций над векторами и матрицами. Вектор будем
- записывать в столбик и рассматривать его как матрицу размера n*1.
- <p>Операция скалярного произведения векторов: определена для двух векторов
- одинаковых размеров. Результат есть число, равное сумме произведений
- соответствующих элементов векторов. Пример:
- <pre class=formula>
- [ 1 ] [ 4 ]
- [ 2 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
- [ 3 ] [ 6 ]
- </pre>
- <p>Операция векторного произведения: определена для (n-1) вектора одинакового
- размера n. Результат - вектор, причем, что интересно, перпендикулярный всем
- множителям. Результат меняется от перестановки мест множителей!!! Формально
- определяется как определитель матрицы, первая строка которой есть все
- базисные вектора, а все последующие - соответствующие координаты всех
- множителей. Поскольку нам она будет требоваться только для 3D пространства,
- мы определим векторное произведение двух 3D векторов явно:
- <pre class=formula>
- [ Ax ] [ Bx ] | i j k | [ Ay*Bz-Az*By ]
- AxB = [ Ay ] x [ By ] = | Ax Ay Az | = [ Az*Bx-Ax*Bz ]
- [ Az ] [ Bz ] | Bx By Bz | [ Ax*By-Ay*Bx ]
- </pre>
- <p>Операция сложения двух матриц: определена для матриц одинаковых размеров.
- Каждый элемент суммы (то есть, каждое число в таблице) равняется сумме
- соответствующих элементов слагаемых-матриц. Пример:
- <pre class=formula>
- [ 1 x 500 ] [ 8 a 3 ] [ 9 a+x 503 ]
- [ 2 y 600 ] + [ 9 b 2 ] = [ 11 b+y 602 ]
- [ 3 z 700 ] [ 10 c 1 ] [ 13 c+z 701 ]
- </pre>
- <p>Операция умножения матрицы на число: определена для любой матрицы и любого
- числа; каждый элемент результата равняется произведению соответствующего
- элемента матрицы-множителя и числа-множителя.
- <p>Операция умножения двух матриц: определена для двух матриц таких размеров
- a*b и c*d, что b = c. Например, если b = c, но a != d, то при перестановке
- множителей операция будет вообще не определена. Результатом умножения матрицы
- A размером a*b на матрицу B размером b*d будет матрица C размером a*d, в
- которой элемент, стоящий в строке i и столбце j, равен произведению строки i
- матрицы A на столбец j матрицы B. Произведение строки на столбец определяется
- как сумма произведений соответствующих элементов строки и столбца. Чтобы
- было хоть чуть-чуть понятно, пример умножения строки на столбец (они должны
- быть равной длины, кстати; поэтому и такие ограничения на размеры матриц):
- <pre class=formula>
- [ 4 ]
- [ 1 2 3 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
- [ 6 ]
- </pre>
- <p>А чтобы перемножить две матрицы, надо эту операцию проделать для каждого
- элемента. Вот пример:
- <pre class=formula>
- [ 1 2 3 ] [ 0 3 ] [ 1*0+2*1+3*2 1*3+2*4+3*5 ]
- [ 4 5 6 ] * [ 1 4 ] = [ 4*0+5*1+6*2 4*3+5*4+6*5 ] = ...
- [ 7 8 9 ] [ 2 5 ] [ 7*0+8*1+9*2 7*3+8*4+9*5 ]
- </pre>
- <p>Умножение и сложение матриц обладают почти тем же набором свойств, что и
- обычные числа, хотя некоторые привычные свойства не выполняются (например,
- A*B != B*A); нам на самом деле понадобится знать, что произведение вида
- A*B*C*D*... не зависит от того, как расставить скобки. Или, если угодно,
- что
- <p class=expression>A*(B*C) = (A*B)*C.
- <p>Теперь забудем об этом на некоторое время и перейдем к преобразованиям.
- Любое движение (то есть преобразование пространства, сохраняющее расстояние
- между точками) в трехмерном пространстве, согласно теореме Шаля, может
- быть представлено в виде суперпозиции поворота и параллельного переноса,
- то есть последовательного выполнения поворота и параллельного переноса.
- Именно поэтому основная часть информация о поведении объекта - это его
- смещение, ось поворота и угол поворота. И именно поэтому нам достаточно
- знать, как сделать два преобразования - перенос и поворот.
- <p>Перенос точки (кстати, точки будут также рассматриваться как вектора с
- началом в начале координат и концом в собственно точке) с координатами
- (x,y,z) на вектор (dx,dy,dz) делается простым сложением всех координат.
- То есть результат - это (x+dx,y+dy,z+dz). Как бы сложили вектор-точку и
- вектор-перенос.
- <p>Поворот - занятие уже более интересное. Но тоже простое. Рассмотрим для
- примера поворот точки (x,y,z) относительно оси z. В этом случае z не
- меняется вообще, а (x,y) меняются так же, как и при 2D повороте относительно
- начала координат.
- <p>Посмотрим, какие будут координаты у точки A' - результата поворота A(x,y)
- на угол alpha.
- <center><img src="illu/illu23a.gif" width=180 height=180 alt="рисунок (illu/illu23a.gif)"></center>
- <p>Пусть r = sqrt(x*x+y*y). Пусть угол AOx равен phi, тогда из рисунка видно,
- что cos(phi) = x/r, sin(phi) = y/r. Угол A'OA равен по условию alpha.
- Отсюда
- <p class=expression>
- x' = r*cos(alpha+phi) = r*(cos(alpha)*cos(phi)-sin(alpha)*sin(phi)) =<br>
- = (r*cos(phi))*cos(alpha)-(r*sin(phi))*sin(alpha) =<br>
- = x*cos(alpha)-y*sin(alpha)<br>
- <p class=expression>
- y' = r*sin(alpha+phi) = r*(cos(alpha)*sin(phi)+sin(alpha)*cos(phi)) =<br>
- = (r*cos(phi))*sin(alpha)+(r*sin(phi))*cos(alpha) =<br>
- = x*sin(alpha)+y*cos(alpha)<br>
- <p>Для трехмерного случая, таким образом
- <p class=expression>x' = x*cos(alpha)-y*sin(alpha)<br>
- y' = x*sin(alpha)+y*cos(alpha)<br>
- z' = z<br>
- <p>Аналогичные формулы получатся и для других осей поворота (то есть Ox, Oy).
- Поворот относительно произвольной оси, проходящей через начало координат,
- можно сделать с помощью этих поворотов - сделать поворот относительно Ox
- так, чтобы ось поворота стала перпендикулярна Oy, затем поворот относительно
- Oy так, чтобы ось поворота совпала с Oz, сделать собственно поворот, а затем
- обратные повороты относительно Oy и Ox. Можно даже вывести формулы для
- такого поворота и убедиться в том, что они очень громоздкие.
- <p>Вспомним о матрицах и векторах и внимательно посмотрим на выведенные формулы
- для поворота. Можно заметить, что
- <pre class=formula>
- [ x' ] = [ cos(alpha) -sin(alpha) 0 ] [ x ]
- [ y' ] = [ sin(alpha) cos(alpha) 0 ] [ y ]
- [ z' ] = [ 0 0 1 ] [ z ]
- </pre>
- <p>То есть поворот на угол alpha задается одной и той же матрицей, и с помощью
- этой матрицы (умножая ее на вектор-точку) можно получить координаты
- повернутой точки. Пока никакого выигрыша не видно - здесь умножение матрицы
- на вектор требует больше операций, чем расчет x' и y' по формулам.
- <p>Удобство матриц для нас заключается как раз в свойстве A*(B*C) = (A*B)*C.
- Пусть мы делаем несколько поворотов подряд, например, пять (как раз столько,
- сколько надо для поворота относительно произвольной оси), и пусть они
- задаюся матрицами A, B, C, D, E (A - матрица самого первого поворота,
- E - последнего). Тогда для вектора p мы получаем
- <p class=expression>p' = E*(D*(C*(B*(A*p)))) = E*D*C*B*A*p = (E*D*C*B*A)*p =
- (E*(D*(C*(B*A))))*p = T*p,
- <p>где
- <p class=expression>T = (E*(D*(C*(B*A))))
- <p>матрица преобразования, являющегося комбинацией пяти поворотов. Посчитав
- один раз эту матрицу, можно в дальнейшем без проблем применить довольно
- сложное преобразование из пяти поворотов к любому вектору с помощью всего
- одного умножения матрицы на вектор.
- <p>Таким образом, можно задать любой поворот матрицей, и любая комбинация
- поворотов также будет задаваться матрицей, которую можно довольно легко
- посчитать. Но есть еще параллельный перенос, есть еще масштабирование.
- Что делать с ними?
- <p>На самом деле, эти преобразования тоже легко записываются в виде матриц.
- Только вместо матриц 3x3 и 3-мерных векторов используются так называемые
- однородные 4-мерные координаты и матрицы 4x4. При этом вместо векторов вида
- <pre class=formula>
- [ x ]
- [ y ]
- [ z ]
- </pre>
- <p>используются вектора вида
- <pre class=formula>
- [ x ]
- [ y ]
- [ z ]
- [ 1 ]
- </pre>
- <p>а вместо произвольных матриц 3x3 используются матрицы 4x4 такого вида:
- <pre class=formula>
- [ a b c d ]
- [ e f g h ]
- [ i j k l ]
- [ 0 0 0 1 ]
- </pre>
- <p>Видно, что если d = h = l = 0, то в результате применения всех операций
- получается то же самое, что и для матриц 3x3.
- <p>Матрица параллельного переноса теперь определяется как
- <pre class=formula>
- [ 1 0 0 dx ]
- [ 0 1 0 dy ]
- [ 0 0 1 dz ]
- [ 0 0 0 1 ]
- </pre>
- <p>Матрицу масштабирования можно определить и для матриц 3x3, и для матриц 4x4:
- <pre class=formula>
- [ kx 0 0 ] [ kx 0 0 0 ]
- [ 0 ky 0 ] или [ 0 ky 0 0 ]
- [ 0 0 kz ] [ 0 0 kz 0 ]
- [ 0 0 0 1 ]
- </pre>
- <p>где kx, ky, kz - коэффициенты масштабирования по соответствующим осям.
- <p>Таким образом, получаем следующее. Любое нужное нам преобразование
- пространства можно задать матрицей 4x4 определенной структуры, разной для
- разных преобразований. Результат последовательного выполнений нескольких
- преобразований совпадает с результатом одного преобразования T, которое
- также задается матрицей 4x4, вычисляемой как произведение матриц всех этих
- преобразований. Важен порядок умножения, так как A*B != B*A. Результат
- применения преобразования T к вектору [ x y z ] считается как результат
- умножения матрицы T на вектор [ x y z 1 ]. Вот и все.
- <p>Осталось только на примере показать, почему A*B != B*A. Пусть A - матрица
- переноса, B - поворота. Если мы сначала перенесем объект, а потом повернем
- относительно центра координат (это будет B*A), получим далеко не то, что
- будет, если сначала объект повернуть, а потом перенести (это уже A*B).
- </div>
- </td></table>
- <!-- Bottom Navigation -->
- <img src="../img/b.gif" width=500 height=1 alt=""><br><img src="../img/t.gif" width=500 height=2 alt=""><br>
- <table width=500 cellpadding=0 cellspacing=0 border=0>
- <td><img src="../img/t.gif" width=5 height=1 alt=""><a href="../main.htm" onmouseover="swap('logo2','../img/xe.gif');" onmouseout="swap('logo2','../img/e.gif');"><img src="../img/e.gif" name=logo2 width=60 height=50 hspace=10 border=0 alt=" в самое начало "></a></td>
- <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
- <td align=center><p class=navy><a href="22.htm" onmouseover="swap('prev','../img/xprev.gif');" onmouseout="swap('prev','../img/prev.gif');"><img src="../img/prev.gif" name=prev width=40 height=40 border=0 hspace=5 alt=" предыдущая статья "></a><br>previous</td>
- <td align=center><p class=navy><a href="../content.htm" onmouseover="swap('faq','../img/xfaq.gif');" onmouseout="swap('faq','../img/faq.gif');"><img src="../img/faq.gif" name=faq width=40 height=40 border=0 hspace=5 alt=" содержание "></a><br>content</td>
- <td align=center><p class=navy><a href="24.htm" onmouseover="swap('next','../img/xnext.gif');" onmouseout="swap('next','../img/next.gif');"><img src="../img/next.gif" name=next width=40 height=40 border=0 hspace=5 alt=" следующая статья "></a><br>next</td>
- </table>
- <img src="../img/t.gif" width=500 height=4 alt=""><br>
- <img src="../img/b.gif" width=500 height=1 alt=""><br>
- <img src="../img/t.gif" width=500 height=1 alt=""><br>
- <img src="../img/b.gif" width=500 height=1 alt=""><br>
- </center></body>
- </html>
|