NeHe Tutorials Народный учебник по OpenGL
Урок 40. OpenGL

Rope Physics

 

Эмуляция Веревки

 

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

 

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

 

Тот масштаб пространства и времени, которые мы хотим наблюдать, связан с:

 

1. Математикой движения

2. Производительностью компьютера, который мы используем для моделирования

 

1. Математика Движения:

 

Здесь, математика движения называется "классической механикой", в которой массы рассматриваются как частицы и ускорение им придается силами, действующими во времени. В масштабе, который мы можем наблюдать невооруженным глазом, классическая механика, допустима для использования. Поэтому, мы можем использовать классическую механику для моделирования объектов и механизмов из нашей обычной жизни. В уроке 39, сила тяготения и сила упругости пружины применялась к массам в 1 кг при помощи классической механики. В этом уроке, мы будем использовать классическую механику для моделирования гибких движений веревки.

 

2. Производительность компьютера, который мы используем для моделирования:

 

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

 

Разработка физических данных для веревки:

 

Имея в наличии классическую механику (как математику движения) и компьютер с процессором не менее 500 МГц, мы будет проектировать физические данные для моделирования веревки. Во-первых, мы должны определить, как детально мы хотим наблюдать процесс движения веревки. При кодировании, мы будем использовать Physics1.h из урока 39. В Physics1.h, мы имеем класс массы (class Mass), который представляет массу как точечную частицу. Мы можем использовать этот класс массы. Если мы связываем массы, подобные точкам, друг с другом как при моделировании пружины, мы сможем сформировать физическую модель, чтобы эмулировать веревку. Исходя из этой модели, которую мы рассматриваем, мы определяем насколько точной будет эмуляция движений веревки. Мы сможем понять, что мы готовы воспроизвести покачивание или помахивание веревкой, но мы не сможем воссоздать скручивание веревки. (Чтобы понять, что такое скручивание веревки, возьмите веревку в руки и зажмите в ладони, и потирайте ладони друг об друга, тогда веревка будет скручиваться.) Мы не можем наблюдать скручивание, потому что мы используем в данной модели точки. Точки не могут вращаться вокруг оси, чтобы эмулировать скручивание веревки (примечание переводчика: но если вы будет каждый фрагмент веревки представлять не линией, а цилиндром, то вы вполне сможет эмулировать и скручивание веревки). Давайте будем использовать эту модель и примем что, движение веревки ограничено покачиванием и помахиванием. Давайте, также определим, что мы хотим наблюдать движение помахивания веревкой, с максимальной детализацией 10 см. Это означает, что у веревки будут нарушения непрерывности до 10 см. Я выбрал эти ограничения, потому что я хочу использовать в веревке приблизительно 50 или 100 частиц (из-за производительности), и я хочу, чтобы эта веревка была приблизительно от 3 до 4 метров длиной. Что означает, что есть приблизительно от 3 до 8 см между частицами веревки, что не превышает уровень нарушения непрерывности, который мы выбрали (10 см).

 

Определение уравнения движения:

 

Уравнение движения с точки зрения математики представляет из себя дифференциальное уравнение второго порядка и концептуально означает силы, действующие в физическом окружении. Давайте использовать концептуальное значение, потому что это звучит лучше. Определение уравнения движения означает определение сил. В модели веревки, силы будут действовать на частицы, которые составляют веревку. Первая сила будет упругое растяжение/натяжение пружины между этими частицами. Ниже, каждая частица помечена как "о", и пружина показана как "----":

 
O----O----O----O
1    2    3    4

 

Частица 1 связана с 2,  2 с 3, и 3 с 4. Мы имеем 4 частицы в этой веревке и 3 пружины. Пружина - источник силы между двумя частицами. Помните, что сила упругости сформулирована так:

 

Сила = -k * x
k: константа жесткости пружины
x: расстояние массы от точки до места присоединения

 

Формула для вычисления упругости пружины, которую мы будем использовать, будет похожа на эту, но немного другая. Если бы мы использовали указанную формулу, то при этом веревка бы сжималась! Поскольку, если x не ноль (x - расстояние между двумя связанными массами в нашей модели веревки), возникает сила. Поэтому все частицы веревки были бы стянуты друг другу, пока x не стало бы равным нолю. Это не то, что мы хотим. Вообразите, что веревка лежит на столе. Мы хотим, чтобы наша веревка осталась неизменной подобно веревке на столе. Так или иначе, мы должны сохранить в этом случае неизменной длину веревки. Чтобы сделать это, сила между двумя любыми частицами должна быть ноль, тогда x имеет положительное значение. Давайте перепишем формулу так:

 

Сила =-k * (x - d)
k: константа жесткости пружины
x: расстояние массы от точки до места присоединения
d: положительная константа, задающее расстояние, при котором пружина будет устойчивой

 

С этой формулой ясно, что, если расстояние между двумя массами равняется d, никакая сила не будет действовать. Пусть мы имеем 100 частиц. Если мы выбираем d как 5 см (0.05 метра), мы имеем устойчивую веревку в 5 метров, когда она лежит на столе. Когда x - больше чем d, пружина вытягивается, а когда меньше сжимается.

 

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

 

Класс пружины

 

Класс пружины связывает две массы и прикладывает силу к каждой из этих масс.

 

class Spring // Объект для представления пружины с внутренней силой трения двух связанных масс

             // Пружина нормальной длины (длина, при которой пружина не приводится

             // в действие любыми силами)

{

public:

  Mass* mass1;                // Первая масса – один конец пружины

  Mass* mass2;                // Вторая масса – другой конец пружины

 

  float springConstant;       // Константа жесткости пружины

  float springLength;         // Длина, при которой пружина не приводится в действие любыми силами

  float frictionConstant;     // Константа трения пружины

 

  Spring(Mass* mass1, Mass* mass2,

    // Конструктор

    float springConstant, float springLength, float frictionConstant)

  {

    this->springConstant = springConstant;

    this->springLength = springLength;

    this->frictionConstant = frictionConstant;

 

    this->mass1 = mass1;

    this->mass2 = mass2;

  }

 

  void solve()                // Метод решение: действие сил

  {

    Vector3D springVector = mass1->pos - mass2->pos; // Вектор между 2 массами

   

    float r = springVector.length(); // Расстояние между 2 массами

 

    Vector3D force;                  // Сила инициализируется нулевым значением

   

    if (r != 0) // Чтобы не было деления на ноль

      // Силы пружины добавляются в силу

      force += -(springVector / r) * (r - springLength) * springConstant;

    ...

 

В конструкторе задаются переменные mass1, mass2, и константы. Наиболее интересен метод solve(). В этом методе происходит применение сил. Чтобы применить силу, мы должны написать формулу пружины, которую мы получили:

 

force = -k * (x - d)

 

Трехмерный вектор, задающий расстояние между массами:

 

Vector3D springVector = mass1->pos - mass2->pos;   (Вектор между 2 массами)

 

найден. Затем нулевая сила создана:

 

Vector3D force;

 

Затем сила пружины добавлена к ней:

 

force += (springVector / r) * (r - springLength) * (-springConstant);

 

Чтобы реализовать формулу выше, мы, во-первых, получаем вектор единичной длины между массами:

 

(springVector / r)

 

Затем с используем этот вектор совместно с (x - d) частью формулы:

 

(springVector / r) * (r - springLength)

 

Далее мы умножаем вектор на:

 

(-springConstant)

 

Который соответствует -k в первоначальной формуле (минус означает натяжение, а не отталкивание). Мы завершили вычисление части силы связанной с натяжением. Давайте, вычислим трение. Это трение в пружине. Пружина имеет тенденцию терять энергию при действии сил действующих в ней. Если Вы применяете силу к массе в направлении противоположном тому, куда движутся массы, то вы заставите массы двигаться медленнее (т.е. как будто будет действовать сила трения). Поэтому, можно вычислить силу трения из скорости движения массы:

 

Сила трения = -k * скорость
k: константа, задающая величину трения
Скорость: скорость массы, на которую действует сила трения

 

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

 

    (void solve() continued)

 

    force += -(mass1->vel - mass2->vel) * frictionConstant; // Сила трения

    mass1->applyForce(force);  // Добавим силу к mass1

    mass2->applyForce(-force); // Добавим силу к mass2

  }                  // Конец метода solve

 

force += -(mass1->vel - mass2->vel) * frictionConstant;

 

Выше, сила трения, полученная из разницы скоростей масс, добавлена к силе пружины. Сила применяется к mass1:

 

mass1->applyForce(force);

 

И противоположная сила применяется к mass2:

 

mass2->applyForce(-force);

 

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

 

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

 

Сила трения = -k * скорость
k: константа, задающая величину трения
Скорость: скорость массы, на которую действует сила трения

 

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

 

Сила = (ускорение гравитации) * масса

 

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

 

Задание начальных значений для моделирования

 

Наша среда готова к моделированию. Единицами измерения расстояния будут метры, секунды (для времени), и кг (для веса).

 

Для того чтобы задать начальные значения, мы должны определить ориентацию веревки перед началом моделирования и определить некоторые константы. Пусть гравитация действует в отрицательном направлении y с 9.81 м/c/c. Пусть один конец веревки подвешен в 4 метрах над поверхностью. Пусть веревка лежит горизонтально до запуска симуляции. Чтобы это выполнить, мы должны разнести частицы на 5 см друг от друга (4 метра / 80 =0.05 метров = 5 см). Также зададим, что это нормальная длина пружины (длина при которой сила упругости равна нулю) - 5 см так, чтобы эта веревка была без напряжения в начале моделирования. Определим общую массу веревки равную 4 кг (тяжелая веревка, цепь). Тогда каждая масса будет весить 0.05 кг (50 граммов). До того как двигаться дальше, посмотрим, что у нас есть:

 

1. Гравитационное ускорение: 9.81 м/с/с в отрицательном направлении y
2. Число масс: 80
3. Нормальное расстояние между двумя соседними массами: 5 см (0.05 метров)
4. Вес массы: 50 граммов (0.05 кг)
5. Ориентация веревки: горизонтальная, без напряжения

 

Затем, мы можем найти константу пружины. Когда мы вешаем веревку за верхний конец, она конечно вытянется. Пружина наверху веревки больше всего вытянется. Я не хочу, чтобы пружина вытянулась больше, чем на 1 см (0.01 м). Вес, который эта пружина несет – это почти вся веревка (частица на верхнем конце исключительна). Сила равна:

 

f = (масса веревки) * (гравитационное ускорение) = (4 кг) * (9.81) ~ = 40 Н

 

Сила упругости пружины должна сбалансировать 40 N:

 

сила пружины = -k * x = -k * 0.01 м

 

Сумма этих сил должна быть равной нулю:

 

40 Н + (-k * 0.01 м) = 0

 

Отсюда мы вычисляем k:

 

k = 4000 Н / м

 

Для простоты примем, что k равно 10000 Н/м, что дает более жесткую веревку, которая приблизительно вытянется на 4 мм.

 

Чтобы найти константу трения в пружине, мы должны проделать более сложные вычисления. Поэтому, будем использовать значение, которое я подобрал экспериментальным путем:

 

springFrictionConstant = 0.2 Н/(м/с)

 

Константа трения пружины равна 0.2 Н/(м/с) и прекрасно подходит для нашей веревки, чтобы она выглядела реалистично (это мое мнение после того, как я провел моделирование).

 

Прежде, чем перейти к трению о воздух и силам, воздействующим от поверхности, давайте взглянем на класс RopeSimulation. Этот производный класс класса Simulation из Physics1.h, который мы рассмотрели в уроке 39. Класс Simulation имеет четыре метода для выполнения моделирования. Вот они:

 

1. virtual void init() --->  Сброс сил.

 

2. virtual void solve() --->  Намеченные силы применяются.
 

3. virtual void simulate(float dt) --->  Позиция и скорость итерационно меняются.
 

4. virtual void operate(float dt) --->  Методы 1., 2., и 3. вместе вызываются.

 

В классе RopeSimulation, мы переопределим метод solve() и simulate(float dt), поскольку мы имеем специальную реализацию этих методов для веревки. Мы применим метод solve(), и закрепим верхний конец веревки в методе simulate(float dt).

 

Класс RopeSimulation производный от класса Simulation (из Physics1.h). В нем моделируется веревка с точечными частицами, связанными пружинами. Пружина имеет внутреннее трение и нормальную длину. Один конец веревки привязан к точке пространства названном "Vector3D ropeConnectionPos". Эта точку можно сдвинуть методом "void setRopeConnectionVel (Vector3D ropeConnectionVel)". RopeSimulation создает трение о воздух и плоскую поверхность (или землю) с нормалью направленной в положительном направлении y. RopeSimulation вычисляет силу, приложенную к этой поверхности. В коде, поверхность – называется "земля".

 

Класс RopeSimulation начинается так:

 

class RopeSimulation : public Simulation // Объект моделирования веревка,

                                         // который взаимодействует с плоской поверхностью

                                         // и воздухом

{

public:

  Spring** springs; // Пружины связывают numOfMasses масс

 

  Vector3D gravitation; // Ускорение гравитации (Гравитация будет применена ко всем массам)

 

  Vector3D ropeConnectionPos; // Точка в пространстве, которая используется для задания позиции

                              // первой массы в пространстве (с индексом 0)

 

  Vector3D ropeConnectionVel; // Скорость перемещения ropeConnectionPos

                              // с помощью нее мы можем раскачивать веревку

 

  float groundRepulsionConstant; // Константа для представления того,

                                 // насколько сильно земля будет отталкивать массы

 

  float groundFrictionConstant; // Константа трения, которое возникает от земли, используется

                                // для скольжения веревки по земле

 

  float groundAbsorptionConstant; // Константа поглощения трения об землю, используется

                                  // для вертикальных столкновений веревки с землей

 

  float groundHeight; // Y координата земли (земля это плоская поверхность расположенная

                      // лицевой гранью с положительном направлении оси Y

 

  float airFrictionConstant; // Константа трения о воздух

 

Класс имеет конструктор с 11 параметрами:

 

  RopeSimulation(                  // Длинный предлинный конструктор

    int numOfMasses,               // 1. Число масс

    float m,                       // 2. Вес каждой массы

    float springConstant,          // 3. Насколько пружина тугая

    float springLength,            // 4. Длина спокойной пружины

    float springFrictionConstant,  // 5. Трение изнутри

    Vector3D gravitation,          // 6. Гравитация

    float airFrictionConstant,     // 7. Трение воздуха

    float groundRepulsionConstant, // 8. Отталкивание от земли

    float groundFrictionConstant,  // 9. Трение о землю

    float groundAbsorptionConstant,// 10. Поглощение земли

    float groundHeight             // 11. Высота земли(Y позиция)

    ) : Simulation(numOfMasses, m) // Суперкласс создает массы с весом m каждая

  {

    this->gravitation = gravitation;

   

    this->airFrictionConstant = airFrictionConstant;

 

    this->groundFrictionConstant = groundFrictionConstant;

    this->groundRepulsionConstant = groundRepulsionConstant;

    this->groundAbsorptionConstant = groundAbsorptionConstant;

    this->groundHeight = groundHeight;

 

    for (int a = 0; a < numOfMasses; ++a) // Начальные позиции масс

    {

      masses[a]->pos.x = a * springLength; // X-позиция masses[a] с расстоянием

                                           // springLength от его соседа

      masses[a]->pos.y = 0; // Y-позиция равна 0

      masses[a]->pos.z = 0; // Z-позиция равна 0

    }

 

    springs = new Spring*[numOfMasses - 1]; // Создание [numOfMasses - 1] точек для пружины

                    // ([numOfMasses - 1] пружин необходимы для numOfMasses)

   

    for (a = 0; a < numOfMasses - 1; ++a) // Создание пружин

    {

      // Пружина между массой "a" и массой "a + 1".

      springs[a] = new Spring(masses[a], masses[a + 1],

                              springConstant, springLength, springFrictionConstant);

    }

  }

 

Всего создается [numOfMasses - 1] пружин (вспомните рисунок: O----O----O----O). Массы первоначально горизонтальном положении. Затем применение сил реализовано в методе solve, уравнения движения будут решены в процессе моделирования. Метод solve выгляди так:

 

  void solve() // solve() переопределен, поскольку мы применяем силы

  {

    for (int a = 0; a < numOfMasses - 1; ++a) // Применение сил для всех пружин

    {

      springs[a]->solve();

    }

 

    for (a = 0; a < numOfMasses; ++a) // Цикл применения сил ко всем массам

    {

      masses[a]->applyForce(gravitation * masses[a]->m); // Сила гравитации

      // Трение о воздух

      masses[a]->applyForce(-masses[a]->vel * airFrictionConstant);

 

      if (masses[a]->pos.y < groundHeight) // Силы от земли, если массы коснулись земли

      {

        Vector3D v; // Временный вектор

 

        v = masses[a]->vel; // Взять скорость

        v.y = 0;            // Пренебречь компонент скорости в Y-направлении

 

        // Скорость в Y-направлении пренебрежем, поскольку мы будем применять силу трения

        // для создания эффекта скольжения. Скольжение параллельно земле. Скорость в Y-направлении

        // будет использоваться для эффекта поглощения

 

        // Сила трения о землю

        masses[a]->applyForce(-v * groundFrictionConstant);

 

        v = masses[a]->vel; // Взять скорость

        v.x = 0;            // Пренебречь x и z компонентами скорости

        v.z = 0;            // Мы будем использовать v в эффекте поглощения

       

        // Выше, мы получили скорость, которая вектор которой направлен вертикально

        // земле и это будет использовано в эффекте поглощения

 

        if (v.y < 0) // Пусть энергия поглощения только, затем масса сталкивается с землей

        {

          // Эффект поглощения

          masses[a]->applyForce(-v * groundAbsorptionConstant);

       

        // Земля будет отталкивать массы подобно пружине.

        // "Vector3D(0, groundRepulsionConstant, 0)" – создаем вектор в направлении

        // плоскости нормали с модулем groundRepulsionConstant.

        // (groundHeight - masses[a]->pos.y) – мы отталкиваем массу,

        // как только она сталкивается с землей

        Vector3D force = Vector3D(0, groundRepulsionConstant, 0) *

          (groundHeight - masses[a]->pos.y);

 

        masses[a]->applyForce(force); // Сила отталкивания земли

      }

    }

  }

 

Вначале, в коде выше, вычисляются все силы упругости пружин (порядок не имеет значения). Затем вычисляются силы общие для всех масс в цикле for(;;). Это силы гравитации, трения о воздух и силы от земли. Вычисление сил от земли выглядит немного запутанным, но это фактически столь же просто, как и вычисление других сил. Эффект скольжения веревки по земле обеспечивается силой трения, при вычислении которой пренебрегли скоростью в y направлении. Направление y – это направление вверх от лицевой стороны земли. Эффект скольжения не должен быть в направлении на лицевую сторону земли. Именно поэтому y опущено. Иначе дело обстоит с эффектом поглощения. Сила поглощения применяется только в направлении лицевой стороны земли. Исключение для эффекта поглощения состоит в том, что не надо применять силы, когда масса движется вдоль земли. Иначе веревка бы прилипала к земле, в то время как мы тянем ее вверх. Мы реализуем этот исключительный случай если v.y < 0. Наконец есть сила отталкивания от земли. Земля отталкивает массы точно так же как пружина, выталкивая массу наверх.

 

В классе RopeSimulation моделирование начинается с первой частицы веревки. Цель состоит в том, чтобы создать способ раскачивания веревки с верхнего конца. Для моделирования используются значения ropeConnectionVel и ropeConnectionPos.

 

  void simulate(float dt) // переопределено поскольку мы хотим моделировать веревку

  {

    Simulation::simulate(dt); // Моделирование масс

 

    ropeConnectionPos += ropeConnectionVel * dt; // Итерация изменения позиции

 

    if (ropeConnectionPos.y < groundHeight) // Веревка не будет двигаться под землей

    {

      ropeConnectionPos.y = groundHeight;

      ropeConnectionVel.y = 0;

    }

 

    masses[0]->pos = ropeConnectionPos; // Сдвиг верхней массы

    masses[0]->vel = ropeConnectionVel; // Изменение скорости верхней массы

  }

 

С помощью этого метода устанавливается ropeConnectionVel:

 

  void setRopeConnectionVel(Vector3D ropeConnectionVel)

  {

    this->ropeConnectionVel = ropeConnectionVel;

  }

 

Эта функция используется при моделировании. Используя клавиши мы задаем ropeConnectionVel, и мы можем перемещать веревку так, как если бы мы держали ее за один конец.

 

Есть некоторые константы, значение которых очень трудно вычислить прежде, чем мы запустим моделирование. Вот подходящие значения этих константы (взято из Physics2Application.cpp):

 

RopeSimulation* ropeSimulation =

  new RopeSimulation(

    80,                     // 80 частиц (масс)

    0.05f,                  // Каждая частица имеет вес в 50 грамм

    10000.0f,               // springConstant в веревке

    0.05f,                  // Нормальная длина пружины в веревке

    0.2f,                   // Константа внутреннего трения пружины

    Vector3D(0, -9.81f, 0), // Ускорение гравитации

    0.02f,                  // Константа трения о воздух

    100.0f,                 // Константа отталкивания земли

    0.2f,                   // Константа трения скольжения о землю

    2.0f,                   // Константа поглощения земли

    -1.5f);                 // Высота земли

 

Изменяя эти значения, Вы сможете попробовать различные варианты движения веревки. Отметьте, что "высота земли" равна -1.5 метром. Веревка вначале находится в y = 0. При этом мы видим веревку, которая раскачивается над землей, а затем сталкивается с ней. Вспомните, что в уроке 39 задавалось максимальное значение dt. В этом случае я нашел, что этот максимум dt должен быть равен 0.002 секунды. Если Ваши изменения в параметрах уменьшат максимум dt, то моделирование может быть неустойчивым, и моделирование не будет работать. В этом случае Вам надо найти новый максимум dt. Увеличение силы и/или уменьшение масс вызывают также появление неустойчивости, потому что ускорение при этом возрастает (вспомните "ускорение = сила/масса").

 

Так же как и в уроке 39, моделирование используется в файле приложения (Physics2Application.cpp):

 

float dt = milliseconds / 1000.0f; // Конвертирование миллисекунд в секунды

 

float maxPossible_dt = 0.002f; // Максимум dt, сек

                               // Это необходимо, чтобы не было потери точности dt

 

// Вычислим число итераций для обновления

int numOfIterations = (int)(dt / maxPossible_dt) + 1;

if (numOfIterations != 0) // Чтобы не было деления на ноль

  dt = dt / numOfIterations; // dt обновлено в зависимости от numOfIterations

 

for (int a = 0; a < numOfIterations; ++a) // "numOfIterations" итераций моделирования

  ropeSimulation->operate(dt);

 

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

 

Процедура моделирования загружается процессор. Поэтому, рекомендуется оптимизировать ваш транслятор. При компиляции в Visual C++, когда выбран режим окончательной сборки (Release), моделирование веревки выполняет в 10 раз быстрее, чем при компиляции в отладочном режиме (Debug). В отладочном режиме нужен минимум 500 МГц процессор. В режиме окончательной сборки требование гораздо меньше.

 

В этом уроке представлено полное моделирование. Есть и настройки, и теория, и проект, и реализация. Более продвинутое моделирование основывается на рассмотренном материале. Наиболее часто используются концепции, которые рассмотрены в этом примере с веревкой. Это также подходит и для физического моделирования в программировании игр. Пробуйте использовать физику в ваших программах и создать ваши собственные демонстрации и игры.

 

Комментарии или вопросы, пожалуйста, посылайте по адресу:

© Erkin Tunca
Jeff Molofee (NeHe)

PMG  15 марта 2004 (c)  Сергей Анисимов