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

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

Подсветка полигонов

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

Здесь появится более сложная математика, поэтому с нее я и начну этот параграф. Перед знакомством с Flash вся моя математика после высшего учебного заведения сводилась к подсчету счетов и сумм в ресторане. Формулы, которые будут использоваться здесь, являются результатом многих ночей, проведенных за компьютером на математических сайтах, а также помощи ahab - модератора форумов на сайте www.were-here.com. Мне также помогли друзья с более глубокими знаниями математики. Я хочу попросить вас использовать все доступные источники, которые могут оказать вам помощь. Не забывайте о книгах - в них есть все нужные формулы. Невероятные эффекты Flash будут достигнуты посредством комбинирования этих ресурсов с вашей изобретательностью, а не только с помощью математических выкладок.

Подсветка куба

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

  1. Сохраните фильм в файле spinning_cube_5.fla. Сразу в начале кода, перед всей имеющейся программой, введите следующее.
    LightSource = function(x, y, z, brightness) { 
      this.x = x; 
      this.y = y; 
      this.z = z;
      this.brightness = brightness; 
      this.calcMag = function() { 
        this.magnitude = Math.sqrt
         (this.x*this.x+this.y*this.y+this.z*this.z);
      };
      this.calcMag();
    };

    Функция-конструктор LightSource создает новый источник света для нашего мира. Единственными ее параметрами являются позиция и яркость, которые устанавливаются при создании объекта (в конечном счете вам понадобится настраивать эти параметры с помощью методов). Метод calcMag вычисляет магнитуду источника света (расстояние от центра пространства до источника). Это значение понадобится нам позже, и нам также понадобится пересчитывать его при изменении параметров x, y или z.

  2. Чтобы создать инстанс LightSource, введите следующий код после строки focalLength = 400; (она находится сразу после секции, к которой мы создаем фильм center, рядом с началом кода).
    light = new LightSource (-20000, -20000, 20000, 100);

    Итак, light теперь является LightSource, который используется в сцене, и если нам понадобится получить доступ к любому из его параметров, мы будем использовать выражение light.параметр. Мы установили его позицию на -20,000 по осям x и y, и на позицию 20,000 по оси z. При таком положении источника объект будет традиционно освещен спереди, сверху и слева. Мы также установили его яркость на значение 100. Потенциальные улучшения нашего исходного объекта LightSource могут включать в себя дополнительные методы для изменения яркости или для присвоения цвета нашему источнику.

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

  3. Измените шесть строк кода, которые вставляют номера вершин в массив side для чтения.
    cube.side = [];
    cube.side.push({vertices:[0,1,2,3], sideColor:0x6600CC});
    cube.side.push({vertices:[2,1,5,6], sideColor:0x6600CC});
    cube.side.push({vertices:[1,0,4,5], sideColor:0x6600CC});
    cube.side.push({vertices:[5,4,7,6], sideColor:0x6600CC});
    cube.side.push({vertices:[0,3,7,4], sideColor:0x6600CC});
    cube.side.push({vertices:[3,2,6,7], sideColor:0x6600CC});

    Вместо обычного добавления чисел в этот параметр, мы вставляем объекты, содержащие сами параметры: vertices для содержания номеров вершин и sideColor для содержания шестнадцатеричного значения цвета стороны.

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

  4. Внесите следующие изменения во вторую часть функции render.
    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].vertices.length; j++) {
          var whichVert = model.side[i].vertices[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 i = 0; i<depthArray.length; i++) {
        var sideVerts = depthArray[i][0].vertices; 
        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.beginFill(depthArray[i][0].sideColor, 90); 
          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.3.

    Это небольшое обновление открывает новые возможности. У нас есть доступ к параметру стороны vertices, где хранятся номера вершин. Вместо предоставления цвета заливки в методе beginFill мы будем вызывать функцию, которой будем отправлять ссылку на текущую сторону (она хранится в массиве depthArray[i] [0] ) и ссылку на саму модель. Она будет возвращать цвет, необходимый параметр для beginFill. Теперь напишем эту функцию.

  5. Добавьте следующую новую функцию прямо под функцией render.
    getSideColor = function (model, side) {
      var col = side.sideColor.toString(16); 
      while (col.length < 6) {
        col = "0" + col
      }
      var verts = [model.vertexList[side.vertices[0]], 
      model.vertexList[side.vertices[1]], 
      model.vertexList[side.vertices[2]]]; 
      var lightFactor = factorLightAngle(verts); 
      var r = parseInt(col.substr(0, 2), 16)*lightFactor; 
      var g = parseInt(col.substr(2, 2), 16)*lightFactor; 
      var b = parseInt(col.substr(4, 2), 16)*lightFactor; 
      return r << 16 | g << 8 | b;
    };

    getSideColor почти идентична функции convertToRGB из предыдущей лекции (там о ней было рассказано более подробно). Большая часть функции разделяет цвет на его красную, зеленую и синюю составляющие, чтобы можно было изменять каждое значение по отдельности. Отличаются лишь те строки, которые вставляют три вершины нашей стороны во временный массив переменных и затем отправляют этот массив функции с именем factorLightAngle. Посредством прямой отправки вершин (помните, объекты хранятся в vertexList нашей модели), factorLightAngle будет легче взаимодействовать с номерами.

    Осталось разобраться лишь с одной функцией - factorLightAngle - но в ней-то и заложен весь смысл. В ней вас также поджидают сложные математические выражения, поэтому соберитесь - и вперед!

  6. Добавьте эти строки сразу после последней функции.
    factorLightAngle = function (vertices) {
      var U = [(vertices[0].x-vertices[1].x), (vertices[0].y
      vertices [1].y), (vertices[0].z-vertices[1].z)];
      var V = [(vertices[1].x-vertices[2].x), (vertices[1].y-
      vertices[2].y), (vertices[1].z-vertices[2].z)];
      var p = [((U[1]*V[2])-(U[2]*V[1])), 
        - ( (U[0]*V[2]) - (U[2]*V[0])),
      ((U[0]*V[1])-(U[1]*V[0]))];
      var magP = Math, sqrt ((p[0]*p[0]) + 
         (p[1]*p[1]) + (p[2]*p[2]));
      var dP = ((p[0]*light.x) + (p[l]*light.y) +
          (p[2]*light.z));
      return ((Math.acos(dP/
        (magP*light.magnitude))/Math.PI)
       *light.brightness/100);
    };
Игорь Хан
Игорь Хан

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

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

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

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