- •Лабораторная работа 1 Свободное движение графического объекта
- •Лабораторная работа 2 Ограниченное движение графического объекта
- •Лабораторная работа 3 Изменение траектории движения
- •Лабораторная работа 4 Дублирование графических объектов
- •Лабораторная работа 5 Построение геометрических фракталов
- •Лабораторная работа 6 Программное рисование двумерных объектов
- •Лабораторная работа 7 Столкновение множества графических объектов
- •Лабораторная работа 8 Моделирование системы гравитации
- •Лабораторная работа 9 Моделирование пружинной системы
- •Лабораторная работа 10 Программное рисование трёхмерных объектов
- •Лабораторная работа 11 Моделирование системы освещения
- •Лабораторная работа 12 Моделирование полигональных объектов
- •Лабораторная работа 13 Текстурирование трёхмерных объектов
Лабораторная работа 10 Программное рисование трёхмерных объектов
Цель работы:
1). Изучить: общие принципы программного построения простейших трёхмерных моделей; использование аффинных преобразований для получения проекций вращения трёхмерных моделей; использование фокусного расстояния при расчёте перспективного проецирования.
2). Создать приложение для управления вращением трёхмерного графического объекта вдоль осей X, Y и Z на плоском фоне.
Порядок выполнения работы:
1. Создайте новый проект с названием "lab10".
2. Используйте классы Main и Graphics2D из предыдущей работы.
3. По аналогии класса Graphics2D создайте новый класс Graphics3D для рисования трёхмерных графических объектов с помощью полигонов.
4. В конструкторе класса Graphics3D определите следующие прототипы:
MovieClip.prototype.vertex3D = new Array();
MovieClip.prototype.polygons = new Array();
MovieClip.prototype.setVertex = setVertex;
MovieClip.prototype.setPolygon = setPolygon;
MovieClip.prototype.render = render;
MovieClip.prototype.rotate = rotate;
5. Так же используются следующие глобальные переменные:
var vertex3D, polygons:Array;
var rgb, alpha:Number;
var matrix:Object;
6. Методы setVertex() и setPolygon() предназначены соответственно для заполнения массивов координат вершин и порядок построения полигонов по этим вершинам (четыре вершины – a, b, c, d):
public function setVertex(x:Number, y:Number, z:Number) {
vertex3D.push({x:x, y:y, z:z});
}
public function setPolygon(a:Number, b:Number, c:Number, d:Number, rgb, alpha) {
polygons.push({vertices:[a, b, c, d], rgb:rgb, alpha:alpha});
}
7. При рисовании граней трёхмерной модели на одном плоском графическом уровне, необходимо определять, какая из граней будет ближе к наблюдателю, чем все остальные так, чтобы не было перекрытия. Для этого используется массив depths для хранения информации о порядке граней:
public function render(focalLength:Number) {
...
var vertex2D:Array = new Array();
var depths:Array = new Array();
for (var i = 0; i<polygons.length; i++) {
var zDepth:Number = 0;
for (var j = 0; j<polygons[i].vertices.length; j++) {
var zVertex:Array = polygons[i].vertices[j];
if (!vertex2D[zVertex]) {
vertex2D[zVertex] = new Object();
var scale:Number = focalLength/(focalLength-vertex3D[zVertex].z);
vertex2D[zVertex].x = vertex3D[zVertex].x*scale;
vertex2D[zVertex].y = vertex3D[zVertex].y*scale;
}
zDepth += vertex3D[zVertex].z;
}
depths.push([polygons[i], zDepth]);
}
depths.sort(function (a:Number, b:Number) {
return a[1]>b[1];
});
for (var i = 0; i<depths.length; i++) {
var zPolygon:Array = depths[i][0].vertices;
this.moveTo(vertex2D[zPolygon[0]].x, vertex2D[zPolygon[0]].y);
this.beginFill(depths[i][0].rgb, depths[i][0].alpha);
for (var j = 1; j<zPolygon.length; j++) {
this.lineTo(vertex2D[zPolygon[j]].x,vertex2D[zPolygon[j]].y);
}
this.lineTo(vertex2D[zPolygon[0]].x, vertex2D[zPolygon[0]].y);
this.endFill();
}
}
8. Единственным аргументом метода render() является переменная focalLength, которая учитывается при расчёте перспективной проекции.
9. Теперь, например, для того, чтобы задать некоторый четырёхугольный полигон в трёхмерной системе координат, в классе Main необходимо сделать вызов следующих методов:
var cube:MovieClip = bounds.createEmptyMovieClip("cube", 1);
cube.setVertex(-75, -75, 75);
cube.setVertex(75, -75, 75);
cube.setVertex(75, -75, -75);
cube.setVertex(-75, -75, -75);
cube.setPolygon(0, 1, 2, 3, 0xff0000, 50);
10. Прорисовку графического объекта с помощью метода render() необходимо делать в каждом кадре, очищая перед выводом экран с помощью метода clear(), а также указывать тип линии рисования lineStyle().
11. Задайте рисование правильного куба в пространстве. Все грани закрасьте шестью основными цветами. Измените степень прозрачности (рис. 13).
Рис. 13. Куб с разной степенью прозрачности граней: 0% (слева), 100%(в центре) и 50% (справа)
12. Для того, чтобы вращать или перемещать трёхмерный объект в пространстве, необходимо использовать аффинные преобразования. Для этого необходимо создать новый класс Matrix, конструктор которого будет пустым, а основным методом будет transformMatrix() следующей структуры:
private function transformMatrix(matrix:Object) {
if (complete) {
var move:Object = new Object();
move.a = matrix.a*a+matrix.b*d+matrix.c*g;
...
a = move.a;
...
} else {
a = matrix.a;
...
}
complete = true;
}
13. Переменная matrix представляет собой объект с девятью свойствами: a, b, c, d, e, f, g, h, i для хранения текущей матрицы аффинных преобразований.
14. Три других метода rotateX(), rotateY() и rotateZ() соответственно будут представлять собой аффинные преобразования при вращении вдоль соответствующих осей. Из каждого такого метода должно быть задано обращение к методу transformMatrix() с соответствующей матрицей аффинных преобразований, например, для метода rotateX(angle):
sin = Math.sin(angle*Math.PI/180);
cos = Math.cos(angle*Math.PI/180);
transformMatrix({a:1, b:0, c:0, d:0, e:cos, f:sin, g:0, h:-sin, i:cos});
15. Для вызова этих методов из класса Main одновременно в классе Graphics3D создайте один метод rotate() с тремя аргументами – углами.
16. В самое начало метода render() необходимо добавить следующий фрагмент умножения матрицы трансформации на координаты вершин, взятых из массива vertex3D. При условии наличия заполненного объекта matrix должен выполняться цикл и последующее удаление объекта:
for (var i = 0; i<vertex3D.length; i++) {
var move:Object = vertex3D[i];
var x:Number = matrix.a*move.x+matrix.b*move.y+matrix.c*move.z;
...
move.x = x;
...
}
delete matrix;
17. Для того, чтобы более гибко управлять вращением куба, можно использовать обработку нажатий кнопки мыши и замедление вращения.
Также можно включить управление значением focalLength для метода render() с помощью колёсика прокрутки мыши на площади фонового объекта bounds, к которому должен быть прикреплён объект cube.
Контрольные вопросы:
1). Для чего используется переменная zDepth в методе render()?
2). Каким образом происходит сортировка элементов массива depths?
3). В чём смысл использования логической переменной complete?
4). Для чего используется переменная move’ в методе render()?
5). Что происходит при уменьшении значения focalLength?
