Использование треугольников для создания трехмерных эффектов

Flash Player 10 и более поздних версий, Adobe AIR 1.5 и более поздних версий

В ActionScript преобразования растрового изображения выполняются с помощью метода Graphics.drawTriangles() , так как трехмерные модели представляются в виде набора треугольников в пространстве. (Однако проигрыватель Flash Player и среда AIR не поддерживают буфер глубины, поэтому экранные объекты по существу остаются плоскими, то есть двухмерными. Подробнее см. в разделе « Трехмерные экранные объекты во Flash Player и среде выполнения AIR ».) Метод Graphics.drawTriangles() напоминает метод Graphics.drawPath() , так как он принимает набор координат для рисования контура треугольника.

Сведения об использовании метода Graphics.drawPath() см. в разделе « Создание контуров ».

Метод Graphics.drawTriangles() использует Vector.<Number> для указания местоположения точек для контура треугольника:

drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void

Первым и единственным обязательным параметром метода drawTriangles() является параметр vertices . Этот параметр представляет собой вектор чисел, определяющих координаты точек, через которые рисуются треугольники. Каждые три группы координат (шесть чисел) представляют контур треугольника. Если не задан параметр indices , длина вектора всегда должна быть кратна шести, так как каждому треугольнику требуется три пары координат (три набора из пары значений x/y). Например:

graphics.beginFill(0xFF8000); 
graphics.drawTriangles( 
    Vector.<Number>([ 
        10,10,  100,10,  10,100, 
        110,10, 110,100, 20,100]));

Ни один из этих треугольников не имеет общих точек с остальными, но при наличии таких точек второй параметр метода drawTriangles() , indices , можно было бы использовать для повторного использования значений в векторе vertices для нескольких треугольников.

При использовании параметра indices следует помнить, что значения indices являются индексами точек, а не элементами массива vertices . Другими словами, индекс в векторе vertices , определенный параметром indices , на самом деле является настоящим индексом, разделенным на 2. Например, для третьей точки вектора vertices требуется использовать значение 2 для indices , несмотря на то, что первое числовое значение этой точки начинается в индексе вектора 4.

Например, с помощью параметра indices требуется объединить два треугольника так, чтобы у них была общая гипотенуза.

graphics.beginFill(0xFF8000); 
graphics.drawTriangles( 
    Vector.<Number>([10,10, 100,10, 10,100, 100,100]), 
    Vector.<int>([0,1,2, 1,3,2]));

Обратите внимание, что квадрат нарисован с помощью двух треугольников, и были указаны всего четыре точки в векторе vertices . При использовании indices , две точки, общие для двух треугольников, используются повторно при построении каждого из них. Это сокращает общее число вершин с 6 (12 чисел) до 4 (8 чисел).

Квадрат, полученный при построении двух треугольников с помощью параметра vertices

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

К треугольникам можно применять все возможные заливки. Заливки применяются к полученной сетке с треугольными ячейками так же, как к любой другой фигуре.

Преобразование растровых изображений

Преобразования растровых изображений создают иллюзию перспективы или «текстуры» на трехмерном объекте. В частности, можно исказить растровое изображение в направлении точки схода, чтобы изображение зрительно сжималось по мере удаления от точки зрения. Также можно использовать двухмерное растровое изображение для создания поверхности трехмерного объекта, придавая вид текстуры или «оболочки» этому объекту.

Двухмерная поверхность с использованием точки схода и трехмерный объект, заключенный в оболочку растровым изображением.

UV-наложение

При работе с текстурами необходимо использовать параметр uvtData метода drawTriangles() . Этот параметр позволяет настраивать UV-наложение для растровых заливок.

UV-наложение — это метод текстурирования объектов. В нем используются два значения: значение U по горизонтали (x) и значение V по вертикали (y). Эти значения представлены не в пикселах, а в процентах. Значения 0 U и 0 V определяют верхнюю левую точку изображения, а 1 U и 1 V — нижнюю правую.

Точки UV 0 и 1 на растровом изображении

Можно указать UV-координаты для векторов треугольника для связывания их с соответствующими точками на изображении.

UV-координаты треугольной области растрового изображения

UV-значения согласуются с точками треугольника.

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

При применении преобразований ActionScript к треугольнику, связанному с растровым изображением, это изображение накладывается на треугольник на основе UV-значений. Поэтому вместо использования вычислений матрицы трехмерный эффект создается путем указания или изменения UV-значений.

Метод Graphics.drawTriangles() также принимает дополнительный элемент информации для трехмерных трансформаций: значение T. Значение T в uvtData представляет трехмерную перспективу, а именно коэффициент масштабирования для связанной с ним вершины. UVT-наложение добавляет поправку перспективы к UV-наложению. Например, при помещении объекта в трехмерном пространстве в отдалении от точки зрения так, что он зрительно уменьшается на 50% по сравнению с исходным размером его значение Т будет равно 0,5. Так как треугольники рисуются для представления объектов в трехмерном пространстве, их значение Т определяется местоположением по оси z. Ниже приводится уравнение для определения значения T.

T = focalLength/(focalLength + z);
В этом уравнении focalLength представляет фокусное расстояние или вычисленное местоположение «экрана», определяющее степень перспективы, применяемой к данному виду.
Фокусное расстояние и значение z
A.
точка зрения

Б.
экран

В.
трехмерный объект

Г.
значение focalLength

Д.
значение z

Значение Т используется для масштабирования простых фигур, чтобы они казались дальше. Как правило, это значение используется для преобразование трехмерных точек в двухмерные. В случае UVT-данных это значение также используется для масштабирования растрового изображения между точками в пределах треугольника с перспективой.

При определении UVT-значений значение T следует непосредственно за значениями UV, определенными для вершины. При добавлении T каждые три значения параметра uvtData (U, V и T) соответствуют каждым двум значениями в параметре vertices (x и y). При использовании только значений UV uvtData.length == vertices.length. При включении значения T uvtData.length = 1.5*vertices.length.

Следующий пример демонстрирует плоскость, которая поворачивается в трехмерном пространстве с использованием данных UVT. В этом примере используется изображение ocean.jpg и вспомогательный класс ImageLoader, предназначенный для загрузки изображения ocean.jpg и его назначения объекту BitmapData.

Ниже приводится исходный код класса ImageLoader (необходимо сохранить его в файле с именем ImageLoader.as).

package { 
    import flash.display.* 
    import flash.events.*; 
    import flash.net.URLRequest; 
    public class ImageLoader extends Sprite { 
        public var url:String; 
        public var bitmap:Bitmap; 
    public function ImageLoader(loc:String = null) { 
        if (loc != null){ 
            url = loc; 
            loadImage(); 
        } 
    } 
    public function loadImage():void{ 
        if (url != null){ 
            var loader:Loader = new Loader(); 
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete); 
            loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIoError); 
             
                var req:URLRequest = new URLRequest(url); 
                loader.load(req); 
            } 
        } 
         
    private function onComplete(event:Event):void { 
            var loader:Loader = Loader(event.target.loader); 
            var info:LoaderInfo = LoaderInfo(loader.contentLoaderInfo); 
            this.bitmap = info.content as Bitmap; 
            this.dispatchEvent(new Event(Event.COMPLETE)); 
    } 
         
    private function onIoError(event:IOErrorEvent):void { 
            trace("onIoError: " + event); 
    } 
    } 
}

А этот код ActionScript использует треугольники, UV-наложение и значения T для создания иллюзии, что изображение сжимается в направлении точки схода и поворачивается. Требуется сохранить этот код в файле с именем Spinning3dOcean.as:

package { 
    import flash.display.* 
    import flash.events.*; 
    import flash.utils.getTimer; 
     
    public class Spinning3dOcean extends Sprite { 
        // plane vertex coordinates (and t values) 
        var x1:Number = -100,    y1:Number = -100,    z1:Number = 0,    t1:Number = 0; 
        var x2:Number = 100,    y2:Number = -100,    z2:Number = 0,    t2:Number = 0; 
        var x3:Number = 100,    y3:Number = 100,    z3:Number = 0,    t3:Number = 0; 
        var x4:Number = -100,    y4:Number = 100,    z4:Number = 0,    t4:Number = 0; 
        var focalLength:Number = 200;      
        // 2 triangles for 1 plane, indices will always be the same 
        var indices:Vector.<int>; 
         
        var container:Sprite; 
         
        var bitmapData:BitmapData; // texture 
        var imageLoader:ImageLoader; 
        public function Spinning3dOcean():void { 
            indices =  new Vector.<int>(); 
            indices.push(0,1,3, 1,2,3); 
             
            container = new Sprite(); // container to draw triangles in 
            container.x = 200; 
            container.y = 200; 
            addChild(container); 
             
            imageLoader = new ImageLoader("ocean.jpg"); 
            imageLoader.addEventListener(Event.COMPLETE, onImageLoaded); 
        } 
        function onImageLoaded(event:Event):void { 
            bitmapData = imageLoader.bitmap.bitmapData; 
            // animate every frame 
            addEventListener(Event.ENTER_FRAME, rotatePlane); 
        } 
        function rotatePlane(event:Event):void { 
            // rotate vertices over time 
            var ticker = getTimer()/400; 
            z2 = z3 = -(z1 = z4 = 100*Math.sin(ticker)); 
            x2 = x3 = -(x1 = x4 = 100*Math.cos(ticker)); 
             
            // calculate t values 
            t1 = focalLength/(focalLength + z1); 
            t2 = focalLength/(focalLength + z2); 
            t3 = focalLength/(focalLength + z3); 
            t4 = focalLength/(focalLength + z4); 
             
            // determine triangle vertices based on t values 
            var vertices:Vector.<Number> = new Vector.<Number>(); 
            vertices.push(x1*t1,y1*t1, x2*t2,y2*t2, x3*t3,y3*t3, x4*t4,y4*t4); 
            // set T values allowing perspective to change 
            // as each vertex moves around in z space 
            var uvtData:Vector.<Number> = new Vector.<Number>(); 
            uvtData.push(0,0,t1, 1,0,t2, 1,1,t3, 0,1,t4); 
             
            // draw 
            container.graphics.clear(); 
            container.graphics.beginBitmapFill(bitmapData); 
            container.graphics.drawTriangles(vertices, indices, uvtData); 
        } 
    } 
}

Для тестирования этого примера необходимо сохранить эти два файла классов в том же каталоге, что и изображение с именем ocean.jpg. Можно видеть, как исходное растровое изображение преобразуется, как будто оно исчезает в перспективе и поворачивается в трехмерном пространстве.

Отбор

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

Б.
трехмерный объект

В.
обратная сторона трехмерного объекта

Все треугольники по своей сути всегда визуализируются независимо от размера, формы и позиции. Отбор обеспечивает правильную визуализацию трехмерного объекта в проигрывателе Flash Player или среде AIR. Кроме того, с целью экономии на циклах визуализации иногда требуется, чтобы средство визуализации пропускало некоторые треугольники. Рассмотрим куб, поворачивающийся в пространстве. В любой момент времени вы видите только три стороны этого куба, так как остальные стороны обращены в другую сторону и не видны с данной точки обзора. Так как эти стороны не будут видны, средству визуализации не нужно их рисовать. Проигрыватель Flash Player или среда AIR визуализирует без отбора как переднюю, так и заднюю сторону.

У куба есть стороны, которые не видны с текущей точки зрения

Поэтому метод Graphics.drawTriangles() имеет четвертый параметр для указания значения отбора:

public function drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void

Параметр отбора представляет значение из класса перечисления TriangleCulling : TriangleCulling.NONE , TriangleCulling.POSITIVE и TriangleCulling.NEGATIVE . Эти значения зависят от направления контура треугольника, определяющего поверхность объекта. API-интерфейс ActionScript для определения отбора предполагает, что все треугольники трехмерной фигуры, обращенные в сторону точки зрения, рисуются с одинаковым направлением контура. Когда поверхность треугольника поворачивается, направление контура меняется. На этом этапе выполняется отбор треугольника (то есть он исключается из визуализации).

Таким образом, параметр TriangleCulling со значением POSITIVE убирает треугольники с положительным направлением контура (по часовой стрелке). Параметр TriangleCulling со значением NEGATIVE убирает треугольники с отрицательным направлением контура (против часовой стрелки). В случае c кубом, когда стороны, обращенные вперед, имеют положительное направление контура, стороны, обращенные назад, имеют отрицательное направление контура.

Куб без оболочки для демонстрации направления контура. Куб в оболочке; направление контура задней стороны повернуто в обратную сторону

Для просмотра принципа работы отбора необходимо начать с предыдущего примера из раздела « UV-наложение », установить для параметра отбора метода drawTriangles() значение TriangleCulling.NEGATIVE .

container.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NEGATIVE);

Обратите внимание, что при повороте объекта, «обратная» сторона изображения не визуализируется.