Опубликован: 07.11.2006 | Доступ: свободный | Студентов: 3400 / 338 | Оценка: 3.94 / 3.71 | Длительность: 37:11:00
Лекция 11:

Реализация 3D-графики с помощью рисования API

Сортировка глубины

Одним из усовершенствований, которое нам будет необходимо сделать, когда мы дойдем до заливки моделей цветом, является сортировка глубины, или метод z-сортировки. Это метод размещения моделей или сторон куба, которые находятся ближе к наблюдателю, перед объектами и сторонами, которые находятся дальше. Как правило, не нужно, чтобы объект со значением координаты z=-200 был впереди объекта с координатой z=50. Во Flash 5 для этого нужно было бы поменять глубины фильмов вершин. Теперь, во Flash MX, мы можем просто создать порядок рисования, начиная с самых дальних сторон и далее по направлению к наблюдателю, прорисовывая стороны, которые будут располагаться за ними. Это все будет реализовываться в функции render, как показано в следующем упражнении.

Закрашивание сторон

  1. Сохраните фильм в файле spinning_cube_3.fla. Обновите функцию render, добавив в нее следующий код.
    render = function (model) { 
      if (transformMatrix) {
        for (var i = 0; i<model.vertexList.length; i++) {
          var vert = model.vertexList[i];
          var x = transformMatrix.a*vert.x+transformMatrix.b*vert.y 
          +transformMatrix.c*vert.z;
          var y = transformMatrix.d*vert.x+transformMatrix.e*vert.y 
          +transformMatrix.f*vert.z;
          var z = transformMatrix.g*vert.x+transformMatrix.h*vert.y 
          +transformMatrix.i*vert.z; 
          vert.x = x; 
          vert.y = y; 
          vert.z = z;
        }
        delete transformMatrix;
      }
      center.clear ();
      center.lineStyle(2, 0, 100); 
      verts2D = []; 
      depthArray = [];
      for (var i = 0; i<model.side.length; i++) {
        var zDepth = 0;
        for (var j = 0; j<model.side[i].length; j++) {
          var whichVert = model.side[i] [j]; 
          if (verts2D[whichVert] == undefined) { 
            verts2D[whichVert] = {}; 
            var scale = focalLength/(focalLength 
            -model.vertexList[whichVert].z);
            verts2D[whichVert].x = model.vertexList[whichVert].x*scale;
            verts2D[whichVert].y = model.vertexList[whichVert].y*scale;
          }
          zDepth += model.vertexList[whichVert].z;
        }
        depthArray.push([model.side[i], zDepth]);
      }
      depthArray.sort(function (a, b) { return a[1]>b[1];});
      for (var 1 = 0; i<depthArray.length; i++) {
        var sideVerts = depthArray[i][0];
        center.moveTo(verts2D[sideVerts[0]].x,verts2D [sideVerts [0]].y); 
        center.beginFill0x666666, 100);
        for (var j =1; j<sideVerts.length; j++) {
          center.lineTo(verts2D[sideVerts[j]].x,verts2D [sideVerts [j]].y);
        }
        center.lineTo(verts2D[sideVerts[0]].x,verts2D[sideVerts [0]].y);
        center.endFill();
      }
    };
    Пример 10.2.

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

    Сначала мы создаем массив с именем depthArray. Он содержит ссылки на стороны нашей модели и общую позицию z каждой вершины в данной стороне (это даст вполне подходящий уровень z для наших целей). После этого мы обрабатываем циклом каждую сторону нашей модели и преобразовываем ее вершины в двухмерные координаты (которые ничем не отличаются от оных в нашей предыдущей версии), а также добавляем значение z каждой вершины к этой величине zDepth. Выражение if используется таким образом, что не выполняется никаких необязательных операций по замене более чем один раз для вершины, что могло бы иметь место, так как стороны имеют общие вершины с другими сторонами.

    Как только вершины преобразованы и величина zDepth каждой стороны записана, мы вставляем ссылку на каждую сторону (которая, как вы знаете, содержит вершины этой стороны) и ее значение zDepth в массив depthArray, который сразу сортируется с помощью функции сортировки. Мы использовали практически идентичную функцию сортировки в предыдущей лекции, но здесь мы можем включать ее непосредственно в аргументы сортировки вместо вызова ее извне. Как только depthArray становится упорядоченным (в направлении от самой дальней стороны к самой ближней), мы обрабатываем его циклом и затем рисуем наши линии и заливки.

    В коде выше вас могут вводить в заблуждение две переменныe: whichVert и sideVerts, поэтому давайте рассмотрим их более детально. whichVert содержит просто номер вершины для стороны в модели. Поэтому для side[0], которая является массивом чисел [0,1,2,3], whichVert будет принимать значение каждого индекса на соответствующей итерации цикла. sideVerts, с другой стороны, будет содержать весь массив номеров вершин. Помните, что каждый индекс depthArray содержит ссылку на одну из сторон модели и ее zDepth. Выяснив первую позицию индекса в определенном индексе depthArray, мы можем получить список вершин этой стороны, чтобы затем поместить их в sideVerts.

  2. Запустите ваш фильм. Теперь стороны куба закрашены, и все благодаря рисованию API!

    spinning_cube_3.swf демонстрирует модель куба с закрашенными гранями и видимыми ребрами, полностью созданную с помощью рисования API. Сортировка глубины каждого полигона позволяет ближним сторонам отображаться перед более дальними сторонами. Куб вращается вокруг всех трех осей в каждом кадре с помощью матриц преобразования 3х3.

  3. Сохраните фильм и оставьте его открытым для следующего упражнения.

Выбраковка задних поверхностей

Выбраковка задних поверхностей - это предотвращение прорисовки полигонов, не видимых наблюдателю, что сохраняет процессорное время (на самом деле нормаль полигона направлена от пользователя, но, к сожалению, здесь нет возможности вдаваться в такие подробности). Способ определения того, виден полигон наблюдателю или нет, заключается в проверке экранных координат его вершин (но не трехмерных координат!), так как это положение модели относительно экрана и наблюдателя, что является важным определяющим фактором видимости полигонов. Нам также необходимо знать порядок расположения вершин - по часовой или против часовой стрелки. В нашем методе необходимо, чтобы вершины были определены для стороны в направлении против часовой стрелки относительно центра объекта, чтобы они были видны под корректными углами.

Все это звучит немного запутанно, не так ли? Как только вы поймете смысл происходящего, вы сразу поймете, что сложного здесь ничего нет. Поверьте мне. На самом деле, проверка является наиболее замысловатой частью. Процесс, используемый мной для определения порядка вершин, заключается в мысленном представлении полигона (или стороны), которая "смотрит" прямо на меня с левой от центра стороны по ходу движения. В случае с нашей моделью, это было бы мысленное вращение куба с отображением каждой стороны так, чтобы я мог лучше наблюдать порядок вершин. Если после этого поместить вершины в массив side в порядке против часовой стрелки в текущем отображении модели, сторона будет полностью видна наблюдателю, когда будет находиться с левой стороны от центра. Для лучшей визуализации этого процесса мы рассмотрим модель куба с тремя из сторон, определенными таким образом, что противоположная сторона куба также видна наблюдателю.


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


Ага! По часовой стрелке! Это потому, что в данном положении куба нам не нужно прорисовывать эти стороны, так как они находятся внутри куба. Именно по этой причине я использую процедуру, описанную выше, и заключающуюся в мысленном повороте модели так, чтобы мне стала видна каждая сторона объекта перед записью порядка вершин. Если повернуть side[5] так, чтобы она "смотрела" на меня слева от центра, тогда порядок ее вершин будет определяться в направлении против часовой стрелки.

Итак, вершины, расположенные на экране по часовой стрелке, не должны прорисовываться. Теперь нужно определить порядок, в котором отображаются двухмерные координаты вершин в данный момент. (Для получения более полной информации можете обратиться к сайту Pavils Jurjans www.jurjans.lv/flash, чтобы ознакомиться с открытым исходным кодом и материалом по этой интересной теме.)

Прорисовка только необходимых объектов

  1. Сохраните фильм из предыдущего упражнения в файле spinning_cube_4.fla. Создайте новую функцию над функцией render (на самом деле, не имеет значения, в каком месте кода вы ее расположите, однако, расположив ее в указанном месте, вы сохраните логический порядок и структуру вашего кода).
    backface - function (x, y) { 
      var cax = x[2]-x[0]; 
      var cay = y[2]-y[0]; 
      var bcx = x[l]-x[2]; 
      var bey = y[l]-y[2]; 
      return (cax*bcy<cay*bcx);
    };

    Здесь мы будем отправлять этой функции три вершины стороны. Используя экранные координаты трех вершин в формуле выше, можно определить, в каком порядке расположены вершины на экране - по часовой или против часовой стрелки. Нам нужны значения "истина" и "ложь". Значение "истина" имеет место, когда сторона "отвернута" от пользователя, а значение "ложь" (т.е. не "задняя" сторона) - если сторона повернута в направлении пользователя.

  2. Для вызова этой функции мы просто добавим эти несколько новых строк в функцию render.
    depthArray.sort(function (a, b) { return a [1] >b [1] ;}); 
      for (var i = 0; i<depthArray.length; i++) {
        var sideVerts = depthArray[i][0];
          if (!backface([verts2D[sideVerts[0]].x, 
          verts2D[sideVerts[1]] .x,
          verts2D [sideVerts [2]] .x], [verts2D [sideVerts [0]] .y, 
          verts2D [sideVerts [1]] .y, verts2D [sideVerts [2]] .y])) { 
          center.moveTo(verts2D[sideVerts[0]].x, 
            verts2D [sideVerts [0]] .y); 
          center.beginFill0x666666, 100); 
          for (var j = 1; j<sideVerts.length; j++) {
            center.lineTo(verts2D[sideVerts[j]] .x, 
              verts2D [sideVerts [j]] .y);
          }
          center.lineTo(verts2D[sideVerts[0]] .x, 
            verts2D [sideVerts [0]] .y); 
          center.endFill(); 
        } 
      }
    };

    Здесь мы добавляем рисование линий и методы рисования в условное выражение, которое проверяет, повернута ли сторона лицевой поверхностью в направлении от пользователя. Если здесь не обрабатывать каждую вершину в вызове функции, пришлось бы это сделать в функции backface. Я думаю, что так понятнее. Мы помещаем координаты x и y первых трех вершин в отдельные массивы при вызове функции backface (посмотрите еще раз на эту функцию, чтобы увидеть, как осуществляется доступ к этим массивам после их отправки). Если функция backface определяет, что данная сторона не является противоположной (возвращается значение "ложь"), то эта сторона прорисовывается.

  3. Запустите фильм. Применения сортировки по оси z не заметно.

    К нашему вращающемуся кубу применен процесс отмены прорисовки противоположных сторон. С его помощью стороны, находящиеся вне поля зрения наблюдателя, не прорисовываются за ненадобностью.

  4. Однако, если вернуться к коду и заменить данные интенсивности заливки на 0 посредством замены вызова beginFill() на center.beginFill(0x666666, 0);, вы увидите, что отмена прорисовки действительно применена к противоположным сторонам куба.

  5. Сохраните ваш фильм и оставьте его открытым. Мы будем обновлять его в следующем упражнении.
Игорь Хан
Игорь Хан

у меня аналогичная ситуация. Однако, если взять пример из приложения (ball_motion_04_click for trial.fla) то след остается. при этом заметил, что в моем проекте в поле "One item in library" виден кружок, в то время как в приложенном примере такого кружка нет.

Вопрос знатокам, что не так?

Александр Коргапольцев
Александр Коргапольцев

объект созданый мной упорно не желает оставлять след(единственное что добился, так это то что шарик резво гоняется за курсором) функция duplicateMovieClip остаётся не активной, т.е. следа от объекта не остаётся, но если я тоже самый код вбиваю в учебный файл всё работает, не могу понять где я ошибаюсь и почему в документе созданном заново, не работает код начиная от функции duplicateMovieClip?