23.htm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <!--
  2. demo.design 3D programming FAQ
  3. Idea, texts, screenshots:
  4. Andrew A. Aksyonoff,
  5. shodan@chat.ru
  6. Web-design, illustrations:
  7. Andrey Samoilov,
  8. asy@sense.simbirsk.su
  9. -->
  10. <html>
  11. <head>
  12. <title>demo.design 3D programming FAQ. Основы 3D графики. Матричные преобразования.</title>
  13. <link rel=stylesheet href="../style.css" type="text/css">
  14. </head>
  15. <script language="javascript">
  16. <!--//
  17. browser = navigator.appName;
  18. version = parseFloat(navigator.appVersion);
  19. if (browser == "Netscape" && version >= 3.0) { jsenabled = 1; } else
  20. if (browser == "Microsoft Internet Explorer" && version >= 3.0) { jsenabled = 1; } else { jsenabled = 0; }
  21. function swap(img,ref) { if (jsenabled) {document.images[img].src = ref;} }
  22. function loadtocache(img,ref) { cache[img] = new Image(); cache[img].src = ref; }
  23. if (jsenabled) {
  24. cache = new Array();
  25. loadtocache(0,"../img/xdl.gif");
  26. loadtocache(1,"../img/xfaq.gif");
  27. loadtocache(2,"../img/xlinks.gif");
  28. loadtocache(3,"../img/xauthor.gif");
  29. loadtocache(4,"../img/xe.gif");
  30. loadtocache(5,"../img/xprev.gif");
  31. loadtocache(6,"../img/xnext.gif");}
  32. //-->
  33. </script>
  34. <body bgcolor=white><center>
  35. <!-- Title -->
  36. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  37. <img src="../img/t.gif" width=500 height=1 alt=""><br>
  38. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  39. <img src="../img/t.gif" width=500 height=2 alt=""><br>
  40. <table width=500 cellpadding=0 cellspacing=0 border=0>
  41. <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>
  42. <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
  43. <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>
  44. <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>
  45. <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>
  46. </table>
  47. <img src="../img/t.gif" width=500 height=4 alt=""><br><img src="../img/b.gif" width=500 height=1 alt=""><br>
  48. <!-- Head -->
  49. <table width=500 cellpadding=0 cellspacing=10 border=0><td><div align=justify>
  50. <p class=title>
  51. <img src="../img/b2.gif" width=70 height=70 align=left hspace=0 alt="">
  52. <img src="../img/t.gif" width=5 height=70 align=left hspace=0 alt="">
  53. ОСНОВЫ 3D ГРАФИКИ<br>2.3. Матричные преобразования
  54. <!-- Article -->
  55. <p>Вообще говоря, лучше всего немного почитать любую книжку по линейной алгебре.
  56. Здесь будет только краткий рассказ о 3D преобразованиях, о том, как их делать
  57. с помощью матриц, и о том, что же такое матрицы и как с ними работать.
  58. <p>Введем несколько терминов. n-мерный вектор, он же вектор размерности n, он
  59. же вектор размера n: упорядоченный набор n действительных чисел. Вообще
  60. говоря, практически то же самое, что и обычный 1D-массив. Матрица размера
  61. m на n (будет обозначаться как m*n, mxn): таблица размера m на n, в каждой
  62. клетке которой - действительное число. Это уже 2D-массив. Всего лишь.
  63. Вот пример матрицы 3x3:
  64. <pre class=formula>
  65. [ 15 y*z 0.6 ]
  66. [ 7 -3 91 ]
  67. [ sin(x) 0.123 exp(t) ]
  68. </pre>
  69. <p>Займемся определением операций над векторами и матрицами. Вектор будем
  70. записывать в столбик и рассматривать его как матрицу размера n*1.
  71. <p>Операция скалярного произведения векторов: определена для двух векторов
  72. одинаковых размеров. Результат есть число, равное сумме произведений
  73. соответствующих элементов векторов. Пример:
  74. <pre class=formula>
  75. [ 1 ] [ 4 ]
  76. [ 2 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
  77. [ 3 ] [ 6 ]
  78. </pre>
  79. <p>Операция векторного произведения: определена для (n-1) вектора одинакового
  80. размера n. Результат - вектор, причем, что интересно, перпендикулярный всем
  81. множителям. Результат меняется от перестановки мест множителей!!! Формально
  82. определяется как определитель матрицы, первая строка которой есть все
  83. базисные вектора, а все последующие - соответствующие координаты всех
  84. множителей. Поскольку нам она будет требоваться только для 3D пространства,
  85. мы определим векторное произведение двух 3D векторов явно:
  86. <pre class=formula>
  87. [ Ax ] [ Bx ] | i j k | [ Ay*Bz-Az*By ]
  88. AxB = [ Ay ] x [ By ] = | Ax Ay Az | = [ Az*Bx-Ax*Bz ]
  89. [ Az ] [ Bz ] | Bx By Bz | [ Ax*By-Ay*Bx ]
  90. </pre>
  91. <p>Операция сложения двух матриц: определена для матриц одинаковых размеров.
  92. Каждый элемент суммы (то есть, каждое число в таблице) равняется сумме
  93. соответствующих элементов слагаемых-матриц. Пример:
  94. <pre class=formula>
  95. [ 1 x 500 ] [ 8 a 3 ] [ 9 a+x 503 ]
  96. [ 2 y 600 ] + [ 9 b 2 ] = [ 11 b+y 602 ]
  97. [ 3 z 700 ] [ 10 c 1 ] [ 13 c+z 701 ]
  98. </pre>
  99. <p>Операция умножения матрицы на число: определена для любой матрицы и любого
  100. числа; каждый элемент результата равняется произведению соответствующего
  101. элемента матрицы-множителя и числа-множителя.
  102. <p>Операция умножения двух матриц: определена для двух матриц таких размеров
  103. a*b и c*d, что b = c. Например, если b = c, но a != d, то при перестановке
  104. множителей операция будет вообще не определена. Результатом умножения матрицы
  105. A размером a*b на матрицу B размером b*d будет матрица C размером a*d, в
  106. которой элемент, стоящий в строке i и столбце j, равен произведению строки i
  107. матрицы A на столбец j матрицы B. Произведение строки на столбец определяется
  108. как сумма произведений соответствующих элементов строки и столбца. Чтобы
  109. было хоть чуть-чуть понятно, пример умножения строки на столбец (они должны
  110. быть равной длины, кстати; поэтому и такие ограничения на размеры матриц):
  111. <pre class=formula>
  112. [ 4 ]
  113. [ 1 2 3 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
  114. [ 6 ]
  115. </pre>
  116. <p>А чтобы перемножить две матрицы, надо эту операцию проделать для каждого
  117. элемента. Вот пример:
  118. <pre class=formula>
  119. [ 1 2 3 ] [ 0 3 ] [ 1*0+2*1+3*2 1*3+2*4+3*5 ]
  120. [ 4 5 6 ] * [ 1 4 ] = [ 4*0+5*1+6*2 4*3+5*4+6*5 ] = ...
  121. [ 7 8 9 ] [ 2 5 ] [ 7*0+8*1+9*2 7*3+8*4+9*5 ]
  122. </pre>
  123. <p>Умножение и сложение матриц обладают почти тем же набором свойств, что и
  124. обычные числа, хотя некоторые привычные свойства не выполняются (например,
  125. A*B != B*A); нам на самом деле понадобится знать, что произведение вида
  126. A*B*C*D*... не зависит от того, как расставить скобки. Или, если угодно,
  127. что
  128. <p class=expression>A*(B*C) = (A*B)*C.
  129. <p>Теперь забудем об этом на некоторое время и перейдем к преобразованиям.
  130. Любое движение (то есть преобразование пространства, сохраняющее расстояние
  131. между точками) в трехмерном пространстве, согласно теореме Шаля, может
  132. быть представлено в виде суперпозиции поворота и параллельного переноса,
  133. то есть последовательного выполнения поворота и параллельного переноса.
  134. Именно поэтому основная часть информация о поведении объекта - это его
  135. смещение, ось поворота и угол поворота. И именно поэтому нам достаточно
  136. знать, как сделать два преобразования - перенос и поворот.
  137. <p>Перенос точки (кстати, точки будут также рассматриваться как вектора с
  138. началом в начале координат и концом в собственно точке) с координатами
  139. (x,y,z) на вектор (dx,dy,dz) делается простым сложением всех координат.
  140. То есть результат - это (x+dx,y+dy,z+dz). Как бы сложили вектор-точку и
  141. вектор-перенос.
  142. <p>Поворот - занятие уже более интересное. Но тоже простое. Рассмотрим для
  143. примера поворот точки (x,y,z) относительно оси z. В этом случае z не
  144. меняется вообще, а (x,y) меняются так же, как и при 2D повороте относительно
  145. начала координат.
  146. <p>Посмотрим, какие будут координаты у точки A' - результата поворота A(x,y)
  147. на угол alpha.
  148. <center><img src="illu/illu23a.gif" width=180 height=180 alt="рисунок (illu/illu23a.gif)"></center>
  149. <p>Пусть r = sqrt(x*x+y*y). Пусть угол AOx равен phi, тогда из рисунка видно,
  150. что cos(phi)&nbsp;= x/r, sin(phi)&nbsp;= y/r. Угол A'OA равен по условию alpha.
  151. Отсюда
  152. <p class=expression>
  153. x' = r*cos(alpha+phi) = r*(cos(alpha)*cos(phi)-sin(alpha)*sin(phi)) =<br>
  154. &nbsp;&nbsp; = (r*cos(phi))*cos(alpha)-(r*sin(phi))*sin(alpha) =<br>
  155. &nbsp;&nbsp; = x*cos(alpha)-y*sin(alpha)<br>
  156. <p class=expression>
  157. y' = r*sin(alpha+phi) = r*(cos(alpha)*sin(phi)+sin(alpha)*cos(phi)) =<br>
  158. &nbsp;&nbsp; = (r*cos(phi))*sin(alpha)+(r*sin(phi))*cos(alpha) =<br>
  159. &nbsp;&nbsp; = x*sin(alpha)+y*cos(alpha)<br>
  160. <p>Для трехмерного случая, таким образом
  161. <p class=expression>x' = x*cos(alpha)-y*sin(alpha)<br>
  162. y' = x*sin(alpha)+y*cos(alpha)<br>
  163. z' = z<br>
  164. <p>Аналогичные формулы получатся и для других осей поворота (то есть Ox, Oy).
  165. Поворот относительно произвольной оси, проходящей через начало координат,
  166. можно сделать с помощью этих поворотов - сделать поворот относительно Ox
  167. так, чтобы ось поворота стала перпендикулярна Oy, затем поворот относительно
  168. Oy так, чтобы ось поворота совпала с Oz, сделать собственно поворот, а затем
  169. обратные повороты относительно Oy и Ox. Можно даже вывести формулы для
  170. такого поворота и убедиться в том, что они очень громоздкие.
  171. <p>Вспомним о матрицах и векторах и внимательно посмотрим на выведенные формулы
  172. для поворота. Можно заметить, что
  173. <pre class=formula>
  174. [ x' ] = [ cos(alpha) -sin(alpha) 0 ] [ x ]
  175. [ y' ] = [ sin(alpha) cos(alpha) 0 ] [ y ]
  176. [ z' ] = [ 0 0 1 ] [ z ]
  177. </pre>
  178. <p>То есть поворот на угол alpha задается одной и той же матрицей, и с помощью
  179. этой матрицы (умножая ее на вектор-точку) можно получить координаты
  180. повернутой точки. Пока никакого выигрыша не видно - здесь умножение матрицы
  181. на вектор требует больше операций, чем расчет x' и y' по формулам.
  182. <p>Удобство матриц для нас заключается как раз в свойстве A*(B*C) = (A*B)*C.
  183. Пусть мы делаем несколько поворотов подряд, например, пять (как раз столько,
  184. сколько надо для поворота относительно произвольной оси), и пусть они
  185. задаюся матрицами A, B, C, D, E (A - матрица самого первого поворота,
  186. E - последнего). Тогда для вектора p мы получаем
  187. <p class=expression>p' = E*(D*(C*(B*(A*p)))) = E*D*C*B*A*p = (E*D*C*B*A)*p =
  188. (E*(D*(C*(B*A))))*p = T*p,
  189. <p>где
  190. <p class=expression>T = (E*(D*(C*(B*A))))
  191. <p>матрица преобразования, являющегося комбинацией пяти поворотов. Посчитав
  192. один раз эту матрицу, можно в дальнейшем без проблем применить довольно
  193. сложное преобразование из пяти поворотов к любому вектору с помощью всего
  194. одного умножения матрицы на вектор.
  195. <p>Таким образом, можно задать любой поворот матрицей, и любая комбинация
  196. поворотов также будет задаваться матрицей, которую можно довольно легко
  197. посчитать. Но есть еще параллельный перенос, есть еще масштабирование.
  198. Что делать с ними?
  199. <p>На самом деле, эти преобразования тоже легко записываются в виде матриц.
  200. Только вместо матриц 3x3 и 3-мерных векторов используются так называемые
  201. однородные 4-мерные координаты и матрицы 4x4. При этом вместо векторов вида
  202. <pre class=formula>
  203. [ x ]
  204. [ y ]
  205. [ z ]
  206. </pre>
  207. <p>используются вектора вида
  208. <pre class=formula>
  209. [ x ]
  210. [ y ]
  211. [ z ]
  212. [ 1 ]
  213. </pre>
  214. <p>а вместо произвольных матриц 3x3 используются матрицы 4x4 такого вида:
  215. <pre class=formula>
  216. [ a b c d ]
  217. [ e f g h ]
  218. [ i j k l ]
  219. [ 0 0 0 1 ]
  220. </pre>
  221. <p>Видно, что если d = h = l = 0, то в результате применения всех операций
  222. получается то же самое, что и для матриц 3x3.
  223. <p>Матрица параллельного переноса теперь определяется как
  224. <pre class=formula>
  225. [ 1 0 0 dx ]
  226. [ 0 1 0 dy ]
  227. [ 0 0 1 dz ]
  228. [ 0 0 0 1 ]
  229. </pre>
  230. <p>Матрицу масштабирования можно определить и для матриц 3x3, и для матриц 4x4:
  231. <pre class=formula>
  232. [ kx 0 0 ] [ kx 0 0 0 ]
  233. [ 0 ky 0 ] или [ 0 ky 0 0 ]
  234. [ 0 0 kz ] [ 0 0 kz 0 ]
  235. [ 0 0 0 1 ]
  236. </pre>
  237. <p>где kx, ky, kz - коэффициенты масштабирования по соответствующим осям.
  238. <p>Таким образом, получаем следующее. Любое нужное нам преобразование
  239. пространства можно задать матрицей 4x4 определенной структуры, разной для
  240. разных преобразований. Результат последовательного выполнений нескольких
  241. преобразований совпадает с результатом одного преобразования T, которое
  242. также задается матрицей 4x4, вычисляемой как произведение матриц всех этих
  243. преобразований. Важен порядок умножения, так как A*B != B*A. Результат
  244. применения преобразования T к вектору [ x y z ] считается как результат
  245. умножения матрицы T на вектор [ x y z 1 ]. Вот и все.
  246. <p>Осталось только на примере показать, почему A*B != B*A. Пусть A - матрица
  247. переноса, B - поворота. Если мы сначала перенесем объект, а потом повернем
  248. относительно центра координат (это будет B*A), получим далеко не то, что
  249. будет, если сначала объект повернуть, а потом перенести (это уже A*B).
  250. </div>
  251. </td></table>
  252. <!-- Bottom Navigation -->
  253. <img src="../img/b.gif" width=500 height=1 alt=""><br><img src="../img/t.gif" width=500 height=2 alt=""><br>
  254. <table width=500 cellpadding=0 cellspacing=0 border=0>
  255. <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>
  256. <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
  257. <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>
  258. <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>
  259. <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>
  260. </table>
  261. <img src="../img/t.gif" width=500 height=4 alt=""><br>
  262. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  263. <img src="../img/t.gif" width=500 height=1 alt=""><br>
  264. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  265. </center></body>
  266. </html>