Войти
ПрограммированиеФорумФизика

Физика "на пальцах" (комментарии) (21 стр)

Страницы: 118 19 20 21 22 23 Следующая »
#300
23:23, 14 апр. 2014

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

После проникновения уже скорость нарушилась. Например, рассмотрим случай абсолютно упругого удара и на шары действую силы, которые увеличивают их скорость. В момент непосредственного контакта они будут иметь скорости V1 и V2 , а в проникшем состоянии уже будут скорости V1' и V2' , которые будут больше из-за того, что на них действует сила. Получается, что в этом примере будет подкачка энергии. Есть ошибки в рассуждениях?

#301
23:31, 14 апр. 2014

ничё не понял, в чём проблема. псевдоскорости и вообще любой алгоритм расталкивания призван решить один из двух случаев, когда одного решения импульсов мало:
1) тела движутся слишком быстро и контакт обнаруживается слишком поздно, когда тела уже глубоко проникли. пример - шар на предыдущем шаге по времени находится на расстоянии 1м от стены, а на текущем -10см(уже проник внутрь), либо тела создали пересекающимися.
2) если долго-долго интегрировать скорость, то постепенно накапливается ошибка. тот же маятник постепенно сходит с оси. псевдоскорости нужны, чтобы этого не допускать.

во всех остальных случаях(особенно - если применять псевдоскорости, когда импульсы ещё не сошлись), псевдоскорости могут только навредить и уменьшить стабильность, но любой velocity-based correction навредит при этом куда больше.

#302
14:17, 15 апр. 2014

Suslik
проблема вот в чём. Рассмотрим случай абсолютно упругого удара. Пусть бокс падает на статичный бокс с какой-то высоты. В силу дискретности определения коллизий подвижный бокс проникнет в неподвижный и будем иметь скорость, большую, чем при первоначальном контакте. Солвер для скоростей изменит только направление движение, т.к. это случай абсолютно упругого удара. А солвер для псевдоскоростей вытолкнет бокс из неподвижного (или оставит в рамках depth skin --- разрешённой глубины проникновения). С течением времени бокс будет подпрыгивать на большую высоту, чем первоначальная, тем самым не выполняется закон сохранения энергии.

#303
20:40, 15 апр. 2014

str_evg
> С течением времени бокс будет подпрыгивать на большую высоту, чем
> первоначальная, тем самым не выполняется закон сохранения энергии.
обычно это не проблема, так как абсолютно упругий удар редко нужен в околоигровой физике. ну и, ясное дело, расчёт псевдоимпульсов нарушает закон сохранения потенциальной энергии - выталкивая тело из земли, мы его поднимаем, придавая потенциальную энергию. пойнт в том, что если бы мы его выталкивали, используя velocity-based баумгарте, оно бы получило куда более ощутимую кинетическую энергию и результат было бы трудно не заметить.

#304
15:12, 16 апр. 2014

Suslik
> обычно это не проблема, так как абсолютно упругий удар редко нужен в
> околоигровой физике. ну и, ясное дело, расчёт псевдоимпульсов нарушает закон
> сохранения потенциальной энергии - выталкивая тело из земли, мы его поднимаем,
> придавая потенциальную энергию. пойнт в том, что если бы мы его выталкивали,
> используя velocity-based баумгарте, оно бы получило куда более ощутимую
> кинетическую энергию и результат было бы трудно не заметить
похоже, что так.

не очень прозрачен вопрос о warmstarting-е для джойнтов, например для того же маятника. Со стабилизацией Баумгарте warmstarting подкачивал энергию в систему, поэтому от него пришлось отказаться. Со split impulses я пока ещё не проверял, но уверенности в его профите нет.
Стек из боксов -- это задача статики, поэтому применение накопленного импульса с предыдущего кадра более или менее очевидно. А вот будет ли улучшена сходимость для сложной конфигурации, например, как в демке Bendera в IBDS с 127 телами и шарнирами?

#305
12:06, 5 мая 2014

str_evg
> не очень прозрачен вопрос о warmstarting-е для джойнтов, например для того же
> маятника. Со стабилизацией Баумгарте warmstarting подкачивал энергию в систему,
> поэтому от него пришлось отказаться. Со split impulses я пока ещё не проверял,
> но уверенности в его профите нет.
Cмоделировал несколько сцен с осевыми шарнирами (hinge joint) с разрешением пересечением звеньев между собой и со стабилизацией на основе split impulses. Warmstarting ускоряет сходимость и без подкачки энергии в мех. систему.

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

Suslik
Если накапливать импульсы, которые обеспечивают выполнение мех. связей для шарниров, то в случае наличия разрыва по скоростям (как для случая удара), что нужно делать с накопленными импульсами? Я пробовал обнулять накопленные импульсы, но это не помогло.

#306
15:18, 5 мая 2014

str_evg
ничего делать не нужно, взрываться не должно. видео можешь записать?

#307
17:18, 5 мая 2014

Kак я понял в статье описывается impulse based physics.
Касательно псевдоскоростей, то несколько похожий подход используется в в этой статье: http://i31www.ira.uka.de/~jbender/Papers/CASA2014.pdf
Но псевдоскорости там считаются обратным методом, высчитывается необходимое смещение для разрешения столкновения и вектор смещения делится на дельтa t.

#308
11:51, 6 мая 2014

Suslik
> ничего делать не нужно, взрываться не должно. видео можешь записать?
хм, вот как. Я тоже изначально так думал по причине того, что метод Гаусса-Зейделя должен сходиться для любой начальной итерации. Заменил осевые на сферические шарниры, мех. система не взрывается. Это значит, что у меня проблемы с ограничениями для осевого шарнира

Спасибо за ответы, буду искать ошибки

#309
23:27, 1 окт. 2014

помогите решить задачу:
написал по статье простейший солвер для кучи шариков, летающих в прямоугольном контейнере (2D), без трения
для неупругих столкновений все идеально работает, но как только делаю bounce>0 - возникает неустойчивость, шарики быстро разлетаются. выключаю гравитацию - работает более менее стабильно, пока остается много свободного места в контейнере, но при увеличении количества шариков опять начинают разлетаться. Если выключить warmstarting - разлета нет, но шарики начинают проваливаться друг в друга (при включенной гравитации). Увеличение количества итераций проблемы не решает при разумных значениях, а при слишком больших значениях начинаются тормоза.
Подскажите, как оставить включенным warmstarting, но увеличить стабильность системы при bounce>0

#310
23:32, 1 окт. 2014

psmith4320
покажи код. вообще будь готов к тому, что bounce > 0 будет вести себя существенно менее устойчиво.

внимательно смотри, когда считаешь initialVelocityProjection - её можно считать перед решением всех джойнтов, а можно - во время каждого. ещё можно так:

... + max(0.0f, initialVelocityProjection * bounce - velocityDelta);

#311
1:49, 2 окт. 2014

перенес расчет initialVelocityProjection, считаю перед каждым решением. Гораздо устойчивее, теперь не разлетаются, а только начинают дрожать при большой гравитации. но визуально выглядит, как будто bounce стал близок к нулю.

#312
1:51, 2 окт. 2014

код на java

public class Solver {
    private static boolean useWarmStart = true;
    private static int pIterationCount = 8;
    private static int pvIterationCount = 3;
    private static float gravityScale = 1f;
    private static float fallTime = 1f; // желаемое время падения от worldTop до worldBottom при gravityScale = 1f (сек.)
    private static int sheresCountForWidth = 25; // для вычисления радиуса
    private static float damping = 0.5f; // в радиус/сек.
    private static float bounce = 1.9f;
    private static float ERP = 0.2f;

    private final RGroup rgroup;
    private float worldLeft;
    private float worldRight;
    private float worldBottom;
    private float worldTop;
    private static float gravityK;
    private static float radius;
    private static float dampingScaled;

    public Solver (final RGroup rgroup) {
        this.rgroup = rgroup;
    }

    public void setView (float worldLeft, float worldRight, float worldBottom, float worldTop) {
        this.worldLeft = worldLeft;
        this.worldRight = worldRight;
        this.worldBottom = worldBottom;
        this.worldTop = worldTop;

        gravityK = (worldTop-worldBottom)*2f/fallTime/fallTime/9.81f*gravityScale;
        radius = (worldRight-worldLeft)/((float)sheresCountForWidth)*0.5f;
        dampingScaled = damping*radius;

        final int spriteCount = rgroup.getSpriteCount();
        for (int i=0; i<spriteCount; i++) ((SphereSprite)rgroup.getSprite(i)).setView((worldLeft + worldRight)*0.5f, (worldBottom + worldTop)*0.5f);
        cellsXCnt = (int)((worldRight - worldLeft) / radius * 0.5f);
        cellsYCnt = (int)((worldTop - worldBottom) / radius * 0.5f);
        cellXSize = cellsXCnt / (worldRight - worldLeft);
        cellYSize = cellsYCnt / (worldTop - worldBottom);
        cells = new Cell[cellsXCnt][cellsYCnt];
        for (int x=0; x<cellsXCnt; x++) {
            for (int y=0; y<cellsYCnt; y++) {
                cells[x][y] = new Cell();
                cells[x][y].spheres.clear();
            }
        }
    }

    public static class SphereSprite extends Sprite {
        private static int indexSequence;
        private final int index;
        private final Vec2 position = new Vec2();
        private final Vec2 velocity = new Vec2();
        private final Vec2 force = new Vec2();
        private float r;
        private float invMass;
        private WallContact wallContact = new WallContact();
        private SparseArray<SphereContact> sphereContacts = new SparseArray<SphereContact>();

        public SphereSprite (RGroup rgroup, Area area) {
            super(rgroup, area);
            index = indexSequence; indexSequence++;
            r = radius;
            invMass = getInvMass();
            velocity.x = (Random.nextFloat()-0.5f)*2f;
            velocity.y = (Random.nextFloat()-0.5f)*2f;
        }

        private float getInvMass() {
            return 1/r/r/r;
        }

        private void applyForces (final float deltaTime) {
            if (invMass == 0) return;
            velocity.x += (Accelerometer.getX()*gravityK+force.x)*deltaTime;
            velocity.y += (Accelerometer.getY()*gravityK+force.y)*deltaTime;
        }

        private void applyImpulse (final Vec2 normal, final float impulse) {
            if (invMass == 0) return;
            velocity.add(normal, impulse*invMass);
        }

        private void applyPV (final Vec2 normal, final float pv) {
            if (invMass == 0) return;
            position.add(normal, pv*invMass);
        }

        private void applyVelocity (final float deltaTime) {
            if (invMass == 0) return;
            position.add(velocity, deltaTime);
        }

        public void setView (float x, float y) {
            position.x = x;
            position.y = y;
            center(r).move(position);
        }

        @Override
        public void update (final float deltaTime) {
            center(r).move(position);
        }
    }

    private abstract static class Contact {
        protected final Vec2 normal = new Vec2();
        protected float p;
        protected float pv;
        protected float initialVelocityProjection;
        protected float invB;
        protected abstract void resolveP();
        protected abstract void resolvePV();
    }

    private static class WallContact extends Contact {
        private boolean active;
        private SphereSprite sphere;
        private Solver solver;
        private boolean check (final SphereSprite sphere, final Solver solver) {
            normal.set(solver.wallDistance(sphere));
            if (normal.x != 0f || normal.y != 0f) {
                normal.norm();
                   init(sphere, solver, useWarmStart && active);
            }
            else
                active = false;
            return active;
        }
        private void init (final SphereSprite sphere, final Solver solver, final boolean warmStart) {
            active = true;
            this.sphere = sphere;
            this.solver = solver;
            invB = 1f/sphere.invMass;
            pv = 0f;
            if (!warmStart)
                p = 0f;
            else
                sphere.applyImpulse(normal, p);
        }
        @Override
        protected void resolveP() {
            initialVelocityProjection = -(sphere.velocity.mul(normal)*bounce)-dampingScaled;
            initialVelocityProjection = initialVelocityProjection<0f?0f:initialVelocityProjection;
            float lambda = (initialVelocityProjection-sphere.velocity.mul(normal))*invB;
            if (p + lambda < 0f) lambda = -p;
            p += lambda;
            sphere.applyImpulse(normal, lambda);
        }
        @Override
        protected void resolvePV() {
            normal.set(solver.wallDistance(sphere));
            float lambda = ERP*normal.norm()*invB;
            if (pv + lambda < 0f) lambda = -pv;
            pv += lambda;
            sphere.applyPV(normal, lambda);
        }
    }

    private static class SphereContact extends Contact {
        private SphereSprite sphere1;
        private SphereSprite sphere2;
        private void init (final SphereSprite sphere1, final SphereSprite sphere2, final boolean warmStart) {
            this.sphere1 = sphere1;
            this.sphere2 = sphere2;
            invB = 1f/(sphere1.invMass+sphere2.invMass);
            pv = 0f;
            normal.set(sphere2.position).sub(sphere1.position).norm();
            if (!warmStart)
                p = 0f;
            else {
                sphere1.applyImpulse(normal,-p);
                sphere2.applyImpulse(normal, p);
            }
        }
        @Override
        protected void resolveP() {
            initialVelocityProjection = -(sphere2.velocity.mul(normal)-sphere1.velocity.mul(normal))*bounce-dampingScaled;
            initialVelocityProjection = initialVelocityProjection<0f?0f:initialVelocityProjection;
            float lambda = (initialVelocityProjection-sphere2.velocity.mul(normal)+sphere1.velocity.mul(normal))*invB;
            if (p + lambda < 0f) lambda = -p;
            p += lambda;
            sphere1.applyImpulse(normal,-lambda);
            sphere2.applyImpulse(normal, lambda);
        }
        @Override
        protected void resolvePV() {
            float lambda = ERP*(sphere2.r+sphere1.r-normal.set(sphere2.position).sub(sphere1.position).norm())*invB;
            if (pv + lambda < 0f) lambda = -pv;
            pv += lambda;
            sphere1.applyPV(normal,-lambda);
            sphere2.applyPV(normal, lambda);
        }
    }

    private final Vec2 normal = new Vec2();

    private Vec2 wallDistance (SphereSprite sphere) {
        normal.zero();
        if (sphere.r - sphere.position.x + worldLeft >= 0f)
            normal.x = sphere.r - sphere.position.x + worldLeft;
        else if (sphere.position.x + sphere.r - worldRight >= 0f)
            normal.x = -(sphere.position.x + sphere.r - worldRight);
        if (sphere.r - sphere.position.y + worldBottom >= 0f)
            normal.y = sphere.r - sphere.position.y + worldBottom;
        else if (sphere.position.y + sphere.r - worldTop >= 0f)
            normal.y = -(sphere.position.y + sphere.r - worldTop);
        return normal;
    }

    private final ArrayList<Contact> activeContacts = new ArrayList<Contact>();
    private final ArrayList<SphereContact> freeSphereContacts = new ArrayList<SphereContact>();

    public void checkWallContact (SphereSprite sphere) {
        if (sphere.wallContact.check(sphere, this)) activeContacts.add(sphere.wallContact);
    }

    public void checkSphereContact (SphereSprite sphere1, SphereSprite sphere2) {
        int key;
        SparseArray<SphereContact> contacts;
        if (sphere1.index<sphere2.index) {
            key = sphere2.index;
            contacts = sphere1.sphereContacts;
        }
        else {
            key = sphere1.index;
            contacts = sphere2.sphereContacts;
        }
        SphereContact contact = contacts.get(key);
        if (sphere2.position.distSquared(sphere1.position) <= (sphere2.r + sphere1.r)*(sphere2.r + sphere1.r)) {
            if (contact == null) {
                final int cnt = freeSphereContacts.size();
                if (cnt == 0)
                    contact = new SphereContact();
                else
                    contact = freeSphereContacts.remove(cnt-1);
                contacts.put(key, contact);
                contact.init(sphere1, sphere2, false);
            }
            else
                contact.init(sphere1, sphere2, useWarmStart);
            activeContacts.add(contact);
        }
        else if (contact != null) {
            contacts.delete(key);
            freeSphereContacts.add(contact);
        }
    }
#313
1:52, 2 окт. 2014
//продолжение
    private static class Cell {
        private final ArrayList<SphereSprite> spheres = new ArrayList<SphereSprite>();
    }

    private Cell[][] cells;
    private int cellsXCnt;
    private int cellsYCnt;
    private float cellXSize;
    private float cellYSize;

    public void step (final float deltaTime) {
        activeContacts.clear();

        for (int x=0; x<cellsXCnt; x++) {
            for (int y=0; y<cellsYCnt; y++) {
                cells[x][y].spheres.clear();
            }
        }

        final int spriteCount = rgroup.getSpriteCount();

        for (int i=0; i<spriteCount; i++) {
            final SphereSprite sphere = (SphereSprite)rgroup.getSprite(i);
            checkWallContact(sphere);
            int x = (int)((sphere.position.x - worldLeft) * cellXSize);
            int y = (int)((sphere.position.y - worldBottom) * cellYSize);
            if (x < 0) x = 0; if (x >= cellsXCnt) x = cellsXCnt - 1;
            if (y < 0) y = 0; if (y >= cellsYCnt) y = cellsYCnt - 1;
            cells[x][y].spheres.add(sphere);
        }

        for (int x=0; x<cellsXCnt; x++) {
            for (int y=0; y<cellsYCnt; y++) {
                ArrayList<SphereSprite> spheres1 = cells[x][y].spheres;
                ArrayList<SphereSprite> spheres2;
                int cnt1 = spheres1.size();
                int cnt2;
                for (int i=0; i<cnt1; i++) {
                    final SphereSprite sphere1 = (SphereSprite)spheres1.get(i);

                    if (i>0) for (int j=0; j<i; j++) checkSphereContact(sphere1, (SphereSprite)spheres1.get(j));

                    if (x<cellsXCnt-1) {
                        if (y>0) {
                            spheres2 = cells[x+1][y-1].spheres;
                            cnt2 = spheres2.size();
                            for (int j=0; j<cnt2; j++) checkSphereContact(sphere1, (SphereSprite)spheres2.get(j));
                        }

                        spheres2 = cells[x+1][y].spheres;
                        cnt2 = spheres2.size();
                        for (int j=0; j<cnt2; j++) checkSphereContact(sphere1, (SphereSprite)spheres2.get(j));

                        if (y<cellsYCnt-1) {
                            spheres2 = cells[x+1][y+1].spheres;
                            cnt2 = spheres2.size();
                            for (int j=0; j<cnt2; j++) checkSphereContact(sphere1, (SphereSprite)spheres2.get(j));
                        }
                    }

                    if (y<cellsYCnt-1) {
                        spheres2 = cells[x][y+1].spheres;
                        cnt2 = spheres2.size();
                        for (int j=0; j<cnt2; j++) checkSphereContact(sphere1, (SphereSprite)spheres2.get(j));
                    }
                }
            }
        }

        final int cnt = activeContacts.size();
        for (int i=0; i<spriteCount; i++) ((SphereSprite)rgroup.getSprite(i)).applyForces(deltaTime);
        for (int j=0; j<pIterationCount; j++) for (int i=0; i<cnt; i++) activeContacts.get(i).resolveP();
        for (int j=0; j<pvIterationCount; j++) for (int i=0; i<cnt; i++) activeContacts.get(i).resolvePV();
        for (int i=0; i<spriteCount; i++) ((SphereSprite)rgroup.getSprite(i)).applyVelocity(deltaTime);
    }
}
#314
2:18, 2 окт. 2014

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

Страницы: 118 19 20 21 22 23 Следующая »
ПрограммированиеФорумФизика

Тема в архиве.