практическая база по применению Macromedia Flash flash.plux.ru
главная | исходники | обучение | actionscript.txt | форум | об авторе | каталог
:: обучение : 3d обучение: основы 3d преобразовавний в flash
автор: Grigory A. Ryabov
- загрузить файл

Это - первая часть из курса статей "3d обучение". Здесь будут рассмотрены основные 3d преобразования и как их эффективней реализовать в flash mx.

Содержание.

Введение.
1. Основы.
1.1 - координаты точки
1.2 - система координат
2. Преобразование поворота.
2.1 - поворот вокруг координатных осей относительно центра координат
2.2 - поворот вокруг координатных осей относительно произвольной точки
2.3 - поворот вокруг произвольной оси относительно центра координат
2.4 - поворот вокруг произвольной оси относительно произвольной точки
3. Преобразование масштабирования.
3.1 - масштабирование относительно центра координат
3.2 - масштабирование относительно произвольной точки
4. Преобразование сдвига.
4.1 - сдвиг начала координат
5. Проецирование.
5.1 - перспективное проецирование
6. Комбинирование преобразований.
6.1 - последовательное выполнение сдвигов
6.2 - последовательное выполнение поворотов
6.3 - последовательное выполнение масштабирований
7. Реализация в flash
Заключение.



1. Основы.

Предполагаемые обозначения в тексте

x, y, z - координаты точки
x', y', z' - новые координаты точки
xp, yp - проецированные координаты точки
dist - дистанция от точки наблюдения (позиции камеры) до центра координат
cnx, cny - координаты центра клипа
sin(A), cos(A), tan(A) - синус, косинус и тангенс угла A
rad = Math.PI/180 - коэффициет перевода градусов в радианы и обратно

1.1 Координаты точки.

Точка в 3d пространстве имеет три координаты (x,y,z) - это расстояние от каждой из осей до самой точки. К примеру координаты (1, 2, 3) означают, что точка расположена на расстоянии 1 от оси X, на расстоянии 2 от оси Y и на расстоянии 3 от оси Z.

1.2 Система координат.

В нашем случае мы будем применять правую систему координат. Правая система координат наиболее часто применяется в машинной графике.

определение: Система координат называется правой, если для совмещения с положительной полуосью Y положительную полуось X требуется повернуть на +90° при этом направление движения расположенного вдоль оси Z и поворачивающегося против часовой стрелки правого винта и положительной полуоси Z совпадают.

2. Преобразование поворота.

Преобразование поворота играет важнейшую роль в 3d движениях. Без него невозможны плавные движения облёта, вращения и др. основополагающие движения камеры.

2.1 Поворот вокруг координатных осей относительно центра координат.

Самым лёгким поворотом является поворот вокруг координатной оси.

Формулы поворота вокруг координатных осей на угол A

- вокруг оси X
x' = x;
y' = y*cos(A)-z*sin(A);
z' = y*sin(A)+z*cos(A);


- вокруг оси Y
x' = x*cos(A)+z*sin(A);
y' = y;
z' = -x*sin(A)+z*cos(A);


- вокруг оси Z
x' = x*cos(A)-y*sin(A);
y' = x*sin(A)+y*cos(A);
z' = z;


2.2 Поворот вокруг координатных осей относительно произвольной точки.

Смысл поворота вокруг произвольной точки:
- делаем сдвиг начала координат в нужную точку
- производим поворот
- возвращаем центр координат обратно на место

Формулы поворота относительно точки M(x0,y0,z0) на угол A

- вокруг оси X
x' = x;
y' = y0+(y-y0)*cos(A)+(z0-z)*sin(A);
z' = z0+(y-y0)*sin(A)+(z-z0)*cos(A);


- вокруг оси Y
x' = x0+(x-x0)*cos(A)+(z-z0)*sin(A);
y' = y;
z' = z0+(x0-x)*sin(A)+(z-z0)*cos(A);


- вокруг оси Z
x' = x0+(x-x0)*cos(A)+(y0-y)*sin(A);
y' = y0+(x-x0)*sin(A)+(y-y0)*cos(A);
z' = z;


2.3 Поворот вокруг произвольной оси относительно центра координат.

Вращение вокруг произвольной оси немного сложнее. Его нужно непременно знать и понимать.

Формулы поворота на угол A вокруг произвольной оси (alpha, beta, gamma)

temp = 1.0-cos(A);

x' = x*(alpha*temp*alpha + cos(A)) + y*(beta*temp*alpha - sin(A)*gamma) + z*(gamma*temp*alpha + sin(A)*beta);

y' = x*(alpha*temp*beta + sin(A)*gamma) + y*(beta*temp*beta + cos(A)) + z*(gamma*temp*beta - sin(A)*alpha);

z' = x*(alpha*temp*gamma - sin(A)*beta) + y*(beta*temp*gamma + sin(A)*alpha) + z*(gamma*temp*gamma + cos(A));


2.4 Поворот вокруг произвольной оси относительно произвольной точки.

Смысл поворота относительно произвольной точки вокруг произвольной оси:
- делаем сдвиг начала координат в нужную точку
- производим поворот вокруг произвольной оси
- возвращаем центр координат обратно на место

Формулы поворота на угол A вокруг произвольной оси (alpha, beta, gamma) относительно точки M(x0, y0, z0)

temp = 1.0-cos(A);

x' = x0 + (x-x0)*(alpha*temp*alpha + cos(A)) + (y-y0)*(beta*temp*alpha - sin(A)*gamma) + (z-z0)*(gamma*temp*alpha + sin(A)*beta);

y' = y0 + (x-x0)*(alpha*temp*beta + sin(A)*gamma) + (y-y0)*(beta*temp*beta + cos(A)) + (z-z0)*(gamma*temp*beta - sin(A)*alpha);

z' = z0 + (x-x0)*(alpha*temp*gamma - sin(A)*beta) + (y-y0)*(beta*temp*gamma + sin(A)*alpha) + (z-z0)*(gamma*temp*gamma + cos(A));

3. Преобразование масштабирования.

Преобразование масштабирования это - изменение размера, изменение масштаба.

3.1 Масштабирование относительно центра координат.

Наипростейшим масштабированием является масштабирование относительно центра координат. Это достаточно часто используемое преобразование.

Sx, Sy, Sz - коэффициенты масштабирования по осям X, Y и Z

Формула масштабирования:

x' = x*Sx;
y' = y*Sy;
z' = z*Sz;

3.2 Масштабирование относительно произвольной точки.

Данное преобразование нужно в основном для увеличения отдельных деталей фигуры. Допустим вам нужно промасштабировать один кубик. Делаем сдвиг центра координат в центр кубика, масштабируем все точки куба, и возвращаем центр координат на место. Вуаля - кубик увеличился в размерах не сдвигаясь со своего места.

Смысл масштабирования относительно произвольной точки:
- делаем сдвиг начала координат в нужную точку
- производим масштабирование
- возвращаем центр координат обратно на место

Формула масштабирования относительно произвольной точки M(x0,y0,z0):

x' = x0+(x-x0)*Sx;
y' = y0+(y-y0)*Sy;
z' = z0+(z-z0)*Sz;


4. Преобразование сдвига.

Название говорит само за себя - изменение позиции точки.

4.1 Сдвиг начала координат.

incX, incY, incZ - величина сдвига по осям X, Y и Z;

Формула преобразования сдвига:

x' = x+incX;
y' = y+incY;
z' = z+incZ;


5. Проецирование.

Прошу не путать. Все вычисления производятся исключительно с нормальными координатами. Проецирование производится в самом конце, и оно не запоминается и не заменяет координат точки. Оно лишь нужно для визуально более реального изображения (получения перспективы) и перехода от 3d координат к 2d (монитор-то 2d, а не 3d)!

5.1 Перспективное проецирование.

Формула перспективного проецирования:

rx = cnx + x*dist/(dist+z);
ry = cny - y*dist/(dist+z);


6. Комбинирование преобразований.

Последовательное выполнение нескольких преобразований можно представить в виде единого преобразования. Это затрачивает меньше ресурсов машины.

6.1 Последовательное выполнение сдвигов.

Сдвиг аддитивен, т.е. последовательное выполнение сдвигов на расстояние (T1.x, T1.y, T1.z) и (T2.x, T2.y, T2.z) эквивалентно одному сдвигу на расстояние (T1.x + T2.x, T1.y + T2.y, T1.z + T2.z).

6.2 Последовательное выполнение поворотов.

Можно показать, что два последовательных поворота аддитивны. Например повернём точку на угол 45° и после этого повернём на угол -67°. Это эквивалентно повороту на угол (45° + (-67°)) = -22°.

6.3 Последовательное выполнение масштабирований.

Первое масштабирование с коэффициентами (Sx1, Sy1, Sz1) второе с коэффициентами (Sx2, Sy2, Sz2). Следует ожидать, что суммарное масштабирование будет мультипликативным. А именно последовательное выполнение этих масштабирований даст результат, эквивалентный масштабированию (Sx1*Sx2, Sy1*Sy2, Sz1*Sz2).

7. Реализация в flash.

А теперь реализуем всё это в flash mx.

Создадим специальную функцию vertex для координат точки. Для этой функции мы сделаем прототипы обработки точки - вращения, сдвига и др. 3d преобразований с точкой.

vertex = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};


Функция vertex будет запоминать координаты точки. Это - родитель остальных прототипов.

прототип поворота вокруг оси X

vertex.prototype.rotateX = function(sinX, cosX) {
var xp = this.x;
var yp = this.y*cosX-this.z*sinX;
var zp = this.y*sinX+this.z*cosX;
this.x = xp;
this.y = yp;
this.z = zp;
};


прототип поворота вокруг оси Y

vertex.prototype.rotateY = function(sinY, cosY) {
var xp = this.x*cosY+this.z*sinY;
var yp = this.y;
var zp = -this.x*sinY+this.z*cosY;
this.x = xp;
this.y = yp;
this.z = zp;
};

прототип поворота вокруг оси Z

vertex.prototype.rotateZ = function(sinZ, cosZ) {
var xp = this.x*cosZ-this.y*sinZ;
var yp = this.x*sinZ+this.y*cosZ;
var zp = this.z;
this.x = xp;
this.y = yp;
this.z = zp;
};


прототип поворота вокруг точки a по оси X

vertex.prototype.rotateX_around_point = function(sinX, cosX, a) {
var xp = this.x;
var yp = a.y+(this.y-a.y)*cosX-(this.z-a.z)*sinX;
var zp = a.z+(this.y-a.y)*sinX+(this.z-a.z)*cosX;
this.x = xp;
this.y = yp;
this.z = zp;
};


прототип поворота вокруг точки a по оси Y

vertex.prototype.rotateY_around_point = function(sinY, cosY, a) {
var xp = a.x+(this.x-a.x)*cosY+(this.z-a.z)*sinY;
var yp = this.y;
var zp = a.z-(this.x-a.x)*sinY+(this.z-a.z)*cosY;
this.x = xp;
this.y = yp;
this.z = zp;
};


прототип поворота вокруг точки a по оси Z

vertex.prototype.rotateZ_around_point = function(sinZ, cosZ, a) {
var xp = a.x+(this.x-a.x)*cosZ-(this.y-a.y)*sinZ;
var yp = a.y+(this.x-a.x)*sinZ+(this.y-a.y)*cosZ;
var zp = this.z;
this.x = xp;
this.y = yp;
this.z = zp;
};


прототип поворота вокруг произвольной оси a относительно центра координат

vertex.prototype.rotate_axis = function(a, sin, cos) {
var temp = 1.0-cos;
var t1 = a.x*temp;
var t2 = a.y*temp;
var t3 = a.z*temp;
var tsinx = sin*a.x;
var tsiny = sin*a.y;
var tsinz = sin*a.z;
var xp = this.x*(t1*a.x+cos)+this.y*(t2*a.x-tsinz)+this.z*(t3*a.x+tsiny);
var yp = this.x*(t1*a.y+tsinz)+this.y*(t2*a.y+cos)+this.z*(t3*a.y-tsinx);
var zp = this.x*(t1*a.z-tsiny)+this.y*(t2*a.z+tsinx)+this.z*(t3*a.z+cos);
this.x = xp;
this.y = yp;
this.z = zp;
};


прототип поворота вокруг произвольной оси a относительно точки b

vertex.prototype.rotate_axis_around_point = function(a, sin, cos, b) {
var temp = 1.0-cos;
var t1 = a.x*temp;
var t2 = a.y*temp;
var t3 = a.z*temp;
var tsinx = sin*a.x;
var tsiny = sin*a.y;
var tsinz = sin*a.z;
var tbx = this.x-b.x;
var tby = this.y-b.y;
var tbz = this.z-b.z;
var xp = b.x+tbx*(t1*a.x+cos)+tby*(t2*a.x-tsinz)+tbz*(t3*a.x+tsiny);
var yp = b.y+tby*(t1*a.y+tsinz)+tby*(t2*a.y+cos)+tbz*(t3*a.y-tsinx);
var zp = b.z+tbz*(t1*a.z-tsiny)+tby*(t2*a.z+tsinx)+tbz*(t3*a.z+cos);
this.x = xp;
this.y = yp;
this.z = zp;
};


прототип масштабирования, относительно центра координат

vertex.prototype.scale = function(sx, sy, sz) {
this.x *= sX;
this.y *= sY;
this.z *= sZ;
};


прототип масштабирования относительно точки a

vertex.prototype.scale_point = function(sx, sy, sz, a) {
this.x = a.x+(this.x-a.x)*sX;
this.y = a.y+(this.y-a.y)*sY;
this.z = a.z+(this.z-a.z)*sZ;
};


прототип масштабирования относительно точки a

vertex.prototype.scale_point = function(sx, sy, sz, a) {
this.x = a.x+(this.x-a.x)*sX;
this.y = a.y+(this.y-a.y)*sY;
this.z = a.z+(this.z-a.z)*sZ;
};


прототип масштабирования по оси X относительно центра координат

vertex.prototype.scaleX = function(sx) {
this.x *= sx;
};


прототип масштабирования по оси Y относительно центра координат

vertex.prototype.scaleY = function(sy) {
this.y *= sy;
};


прототип масштабирования по оси Z относительно центра координат

vertex.prototype.scaleZ = function(sz) {
this.z *= sz;
};


прототип преобразования сдвига

vertex.prototype.transpose = function(incX, incY, incZ) {
this.x += incX;
this.y += incY;
this.z += incZ;
};


прототип преобразования сдвига по оси X

vertex.prototype.transposeX = function(incX) {
this.x += incX;
};


прототип преобразования сдвига по оси Y

vertex.prototype.transposeY = function(incY) {
this.y += incY;
};

прототип преобразования сдвига по оси Z

vertex.prototype.transposeZ = function(incZ) {
this.z += incZ;
};


прототип преобразования перспективы

vertex.prototype.perspective = function() {
var perRatio = (this.z/dist+1);
this.rx = cnx+this.x/perRatio;
this.ry = cny-this.y/perRatio;
};


дополнительная функции для облегчения заливки грани куба

face = function (c, d, a) {
// размерность грани
this.d = d;
// чётность грани
this.c = c;
// массив точек грани
this.a = a;
};


прототип закраски грани

face.prototype.draw = function() {
_root.lineStyle(0, 0x000000, 100);
_root.beginFill(0x999999, 50);
_root.moveTo(v[this.a[0]].rx, v[this.a[0]].ry);
for (var i = 1; i<=(this.d); i++) {
_root.lineTo(v[this.a[i]].rx, v[this.a[i]].ry, v[this.a[i+1]].rx, v[this.a[i+1]].ry);
}
_root.endFill();
};


прототип заливки грани

face.prototype.fill = function() {
this.draw();
};


функция, выполняемая один раз, после загрузке клипа

_root.onLoad = function() {
// задаём координаты центра клипа
cnx = 250;
cny = 250;
// задаём дистанцию от точки наблюдения до центра координат
dist = 600;
// задаём размеры куба
var sx = 75;
var sy = 75;
var sz = 75;
// задам коэффициент перевода градусов в радианы
rad = Math.PI/180;
// инициализируем вершины куба
v = new Array();
v[0] = new vertex(sx, -sy, sz);
v[1] = new vertex(sx, sy, sz);
v[2] = new vertex(sx, sy, -sz);
v[3] = new vertex(sx, -sy, -sz);
v[4] = new vertex(-sx, -sy, -sz);
v[5] = new vertex(-sx, sy, -sz);
v[6] = new vertex(-sx, sy, sz);
v[7] = new vertex(-sx, -sy, sz);
// инициализируем грани куба
f = new Array();
f[0] = new face(1, 3, [0, 1, 2, 3]);
f[1] = new face(0, 3, [2, 3, 4, 5]);
f[2] = new face(1, 3, [4, 5, 6, 7]);
f[3] = new face(0, 3, [0, 1, 6, 7]);
f[4] = new face(1, 3, [0, 3, 4, 7]);
f[5] = new face(0, 3, [1, 2, 5, 6]);
// инициализируем произвольные оси вращения
axisX = new vertex(1.0, 0.0, 0.0);
axisY = new vertex(0.0, 1.0, 0.0);
axisZ = new vertex(0.0, 0.0, 1.0);
// задаём колличество вершин
amount = v.length - 1;
};
// ####################################################
// -- © 2002-2003 Grigory Ryabov / Nuran.
// -- http://www.ShockOne.com
// -- EASY 3d ENGINE ORIGINAL
// ####################################################
/*
Инструкция
---------

Поворот осей:
------------
- поворот вокруг оси X: клавиши J и L
- поворот вокруг оси Y: клавиши I и K
- поворот вокруг оси Z: клавиши U и O

Преобразование сдвига:
---------------------
- по оси X: клавиши A и D
- по оси Y: клавиши W и S
- по оси Z: клавиши Q и E

Масштабирование относительно центра координат:
--------------------------------------------
- по оси X: клавиши 1 и 2
- по оси Y: клавиши 3 и 4
- по оси Z: клавиши 5 и 6
- по всем осям: клавиши 9 и 0

Вращение вокруг произвольной точки:
---------------------------------
- по оси X: клавиши UP и DOWN
- по оси Y: клавиши LEFT и RIGHT
*/

функция, выполняемая каждый раз, при смене кадра

_root.onEnterFrame = function() {
// цикл перебора всех вершин и задание им преобразований
for (var i = 0; i<=amount; i++) {
if (Key.isDown(65)) {
// Клавиша A
v[i].transpose(-10, 0, 0);
}
if (Key.isDown(68)) {
// Клавиша D
v[i].transpose(10, 0, 0);
}
if (Key.isDown(87)) {
// Клавиша W
v[i].transpose(0, 10, 0);
}
if (Key.isDown(83)) {
// Клавиша S
v[i].transpose(0, -10, 0);
}
if (Key.isDown(81)) {
// Клавиша Q
v[i].transpose(0, 0, 10);
}
if (Key.isDown(69)) {
// Клавиша E
v[i].transpose(0, 0, -10);
}
if (Key.isDown(48)) {
// Клавиша 0
v[i].scale(0.9, 0.9, 0.9);
}
if (Key.isDown(57)) {
// Клавиша 9
v[i].scale(1.1, 1.1, 1.1);
}
if (Key.isDown(49)) {
// Клавиша 1
v[i].scale(0.9, 1, 1);
}
if (Key.isDown(50)) {
// Клавиша 2
v[i].scale(1.1, 1, 1);
}
if (Key.isDown(51)) {
// Клавиша 3
v[i].scale(1, 0.9, 1);
}
if (Key.isDown(52)) {
// Клавиша 4
v[i].scale(1, 1.1, 1);
}
if (Key.isDown(53)) {
// Клавиша 5
v[i].scale(1, 1, 0.9);
}
if (Key.isDown(54)) {
// Клавиша 6
v[i].scale(1, 1, 1.1);
}
if (Key.isDown(73)) {
// Клавиша I
var xa = 10*rad;
var sinX = Math.sin(xa);
var cosX = Math.cos(xa);
v[i].rotateX(sinX, cosX);
}
if (Key.isDown(75)) {
// Клавиша K
var xa = -10*rad;
var sinX = Math.sin(xa);
var cosX = Math.cos(xa);
v[i].rotateX(sinX, cosX);
}
if (Key.isDown(74)) {
// Клавиша J
var ya = 10*rad;
var sinY = Math.sin(ya);
var cosY = Math.cos(ya);
v[i].rotateY(sinY, cosY);
}
if (Key.isDown(76)) {
// Клавиша L
var ya = -10*rad;
var sinY = Math.sin(ya);
var cosY = Math.cos(ya);
v[i].rotateY(sinY, cosY);
}
if (Key.isDown(85)) {
// Клавиша V
var za = 10*rad;
var sinZ = Math.sin(za);
var cosZ = Math.cos(za);
v[i].rotateZ(sinZ, cosZ);
}
if (Key.isDown(79)) {
// Клавиша 0
var za = -10*rad;
var sinZ = Math.sin(za);
var cosZ = Math.cos(za);
v[i].rotateZ(sinZ, cosZ);
}
if (Key.isDown(38)) {
// Клавиша UP
var ya = 10*rad;
var sinY = Math.sin(ya);
var cosY = Math.cos(ya);
v[i].rotateX_around_point(sinY, cosY, new vertex(0, 0, 200));
}
if (Key.isDown(40)) {
// Клавиша DOWN
var ya = -10*rad;
var sinY = Math.sin(ya);
var cosY = Math.cos(ya);
v[i].rotateX_around_point(sinY, cosY, new vertex(0, 0, 200));
}
if (Key.isDown(37)) {
// Клавиша LEFT
var xa = 10*rad;
var sinX = Math.sin(xa);
var cosX = Math.cos(xa);
v[i].rotateY_around_point(sinX, cosX, new vertex(0, 0, 200));
}
if (Key.isDown(39)) {
// Клавиша RIGHT
var xa = -10*rad;
var sinX = Math.sin(xa);
var cosX = Math.cos(xa);
v[i].rotateY_around_point(sinX, cosX, new vertex(0, 0, 200));
}
// выполняем преобразование перспективы
v[i].perspective();
}
_root.clear();
// заливаем правую грань
f[0].fill();
// заливаем переднюю грань
f[1].fill();
// заливаем левую грань
f[2].fill();
// заливаем заднюю грань
f[3].fill();
// заливаем нижнюю грань
f[4].fill();
// заливаем верхнюю грань
f[5].fill();
};
stop();


Заключение.
С помошью этих формул можно производить любые 3d преобразования. Конечно, в матричных вычислениях это намного удобнее, и выглядело бы это всё лучше, но и ресурчов оно бы занимало намного больше.
В следующих статьях будет также: 3d движок на матрицах 4x4 с движениями произвольной камеры, 3d освещение, 3d сортировка, 3d удаление невидимых граней, 3d тень и так далее.

Удачи!

Всегда Ваш Григорий Рябов / Nuran.
Сделано специально для flasher.ru.

Продолжение следует...
Перепечатка данного материала строго запрещена!
flash.plux.ru 2003

:: обучение : 3d обучение: основы 3d преобразовавний в flash
главная | исходники | обучение | actionscript.txt | форум | об авторе | каталог