前言

An object at rest remains at rest, unless acted upon by an exterior force. An object in motion remains in motion, at a constant speed, and in the same direction, unless acted upon by an exterior force

欧拉积分

欧拉积分其实分为隐式欧拉隐式欧拉,为了简单开发,这里使用显式欧拉方法进行积分。当然这里说的简单开发,可不是简单了一点,实际隐式欧拉的求解相比显示欧拉的求解更加复杂,而且对于游戏来说也无必要。欧式积分,其实可以看成我们假定在一个规定的时间,物体的速度是一致的。那么在这个一致速度的时间范围内,我们就能很好去求解质点下一刻的状态。

在显式欧拉积分中,下一时刻的位置与当前的速度和当前的位置有关。
不过欧拉方法中,有一些是我们不希望的。由于我们并不是完全按照物理模型去仿真,因此在每一步计算的时候总会有一些误差。在欧拉积分中,每一步会逐步的累计误差。

积分公式

Unity布料如何添加风_游戏引擎
Unity布料如何添加风_笔记_02
公式中,Unity布料如何添加风_游戏引擎_03是位置,Unity布料如何添加风_笔记_04是速度

C#代码分析

// Euler
    // Most integral mathod have convergance feature
    // So In Explicit Euler Mathod , 
    // I give a air resistance to speed up the aconvergance
    public void ExplicitEulerMathod(float deltaTime)
    {
        foreach (var item in Springs)
        {
            // euler
            // spring force

            Vector3 spingforce = Springk * item.GetCurentLength().normalized * (item.GetCurentLength().magnitude - item.RestLength);
            item.Mass1.Force += spingforce;
            item.Mass2.Force -= spingforce;
            Debug.DrawLine(item.Mass1.Position, item.Mass2.Position);
        }

        foreach (var item in Masses)
        {
            if (!item.isPined)
            {
                item.Force += Gravity;
                item.Force -= item.Velocity * AirResistanceFactor;

                item.Velocity += (item.Force / item.MassValue) * deltaTime;
                item.Position += item.Velocity * deltaTime;
            }
            item.Force = Vector3.zero;
        }
    }

Verlet积分

Verlet积分参考 Verlet积分会使用上一个时刻的状态来推下一个时刻的状态。Verlet是一种基于位置的积分,在计算时,上一时刻和当前时刻的位置很重要,然而速度在这里却不那么重要,但是我们可以计算速度,以方便用来计算其他的参数,例如空气阻力。
当我们计算得到下一个时刻的位置,我们需要根据弹簧质点的特性对当前位置进行约束。约束方法就是,假设我们的弹簧是一个弹性系数无限大,不可拉伸的弹簧。当我们计算得到下一个时刻的位置的时候,就需要进行位置修正。

积分公式

Unity布料如何添加风_ci_05

C#代码分析

// Verlet mathod
    // 
    public void VerletMathod(float deltaTime)
    {
        // first , get the final position of mass
        // second , constraint the positon to the rest length

        foreach (var item in Masses)
        {
            if (!item.isPined)
            {
                Vector3 tempPosition = item.Position;

                item.Force = Gravity;
                // Verlet integration
                item.Position += (1f - AirResistanceFactor) * (item.Position - item.LastPosition) + (item.Force / item.MassValue) * deltaTime * deltaTime;

                item.LastPosition = tempPosition;
            }
        }
        foreach (var item in Springs)
        {
            Vector3 subtract = item.Mass1.Position - item.Mass2.Position;
            subtract = subtract.normalized * (subtract.magnitude - item.RestLength);

            if (item.Mass1.isPined)
            {
                item.Mass2.Position += subtract;
            }
            else if (item.Mass2.isPined)
            {
                item.Mass1.Position -= subtract;
            }
            else
            {
                item.Mass1.Position -= subtract/2.0f;
                item.Mass2.Position += subtract/2.0f;
            }
            // Draing line on scene view
            Debug.DrawLine(item.Mass1.Position, item.Mass2.Position);
        }
    }

总结

从仿真效果来看,不同的算法效果都还不错。。但是仿真内容下一篇笔记将会记录使用龙格库塔积分进行计算的方法。

参考

[1]Verlet Integration : http://datagenetics.com/blog/july22018/index.html
[2]Git Repository:https://github.com/HanochZhu/PhysicSimulation
先留个坑,有空来写。