GAMES103-作业1-刚体模拟(个人解读)

本文详细介绍了Unity中的两个脚本,Rigid_Bunny采用传统刚体物理模拟,涉及冲量和惩罚方法;而Rigid_Bunny_by_Shape_Matching则使用形状匹配算法处理碰撞,包括粒子碰撞响应。着重解析了FixedUpdate中的碰撞检测和响应过程,以及ShapeMatching在更新位置和旋转中的应用。
摘要由CSDN通过智能技术生成

前提说明

前提说明:作业不是自己完成的,从网上找的答案,因为刚接触这个,通过这个作业了解下unity以及代码逻辑书写
参考代码:https://github.com/Housz/GAMES103-labs
参考代码逻辑:Games 103 Lecture 04 刚体碰撞检测 - 冰点蓝的文章 - 知乎 https://zhuanlan.zhihu.com/p/465304063

总体概述:

有Rigid_Bunny和Rigid_Bunny_by_Shape_Matching两个脚本文件

Rigid_Bunny:

基于传统的刚体物理模拟方法,采用了冲量和惩罚两种碰撞响应方式。

Rigid_Bunny_by_Shape_Matching:

模拟了一个立体兔子在有初始速度前提下,受重力作用的运动和碰撞行为。

粒子碰撞相应算法(Impulse Method)
刚体碰撞相应算法(Shape Matching)

详细介绍Rigid_Bunny

FixedUpdate()

在这里插入图片描述
步骤:
1.受重力因素更新速度
2.受外力因素更新速度
3.计算Inertia惯性张量(公式见上图)
4.计算碰撞(代码中用的Collision Impulse),调用Collision函数
5.更新v和w(角速度)
6.通过动态调整阈值(punish_threshold)来控制速度和角速度的增长速度,以保持运动的稳定性
7.更新位置和方向

void FixedUpdate()
{
    if (!launched)
    {
        return;
    }

    Mesh mesh = GetComponent<MeshFilter>().mesh;
    Vector3[] vertices = mesh.vertices;

    // ==============================
    // Part I: Update velocities
    // ==============================
    v += dt * gravity;              // add gravity acceleration
    v += dt * inv_mass * ext_force; // add acceleration
    ext_force = Vector3.zero;       // clean external force

    // Calculate inertia
    Quaternion R = transform.rotation;
    Matrix4x4 MR = Matrix4x4.Rotate(transform.rotation);
    I = MR * I_ref * MR.transpose;

    // ==============================
    // Part II: Collision Impulse
    // ==============================
    Collision(new Vector3(0, 0.01f, 0), new Vector3(0, 1, 0));      // floor
    Collision(new Vector3(2, 0, 0), new Vector3(-1, 0, 0));         // wall
    //Debug.Log("66666666666");
    /*
    if (ECOLLISION_TYPE.IMPULSE == collision_type)
    {
       // Debug.Log("Hello World");
        CalTorque();
    }
    */
    v *= linear_decay;              // add velocity decay
    w *= angular_decay;             // add angular decay

    // trick for oscillation
    if (v.magnitude < 0.16f)
    {
        punish_threshold = Mathf.Max(punish_threshold - 0.025f, 0.0f);
    }
    else
    {
        if (punish_threshold < 1.0f)
        {
            punish_threshold = Mathf.Min(punish_threshold + 0.025f, 1.0f);
        }
    }

    if (punish_threshold < 1.0f)
    {
        v *= punish_threshold;
        w *= punish_threshold;
    }

    // ==============================
    // Part III: Update position & orientation
    // ==============================
    // Update linear status
    Vector3 x = transform.position;
    x += dt * v;

    Quaternion q = new Quaternion(half_dt * w.x, half_dt * w.y, half_dt * w.z, 0.0f) * R;
    q = AddQuaternion(R, q).normalized;

    // ==============================
    // Part IV: Assign to the object
    // ==============================
    transform.position = x;
    transform.rotation = q;
}

Collision函数

步骤:
1.计算哪些点发生碰撞,并求平均
2.计算质心发生旋转后的位置,以及到边界的距离
3.更新速度vi
在这里插入图片描述
在这里插入图片描述

 void Collision(Vector3 P, Vector3 N)
 {
     Mesh        mesh        = GetComponent<MeshFilter>().mesh;
     Vector3[]   vertices    = mesh.vertices;
     Vector3     pos         = transform.position;

     Vector3     avgPos      = Vector3.zero;         // average position
     int         count       = 0;                    // collision count
     float       dist        = 0.0f;

     Quaternion  R           = transform.rotation;

     // test collision
     for (int i = 0; i < vertices.Length; i++)
     {
         Vector3 vert = pos + (R * vertices[i]);
         dist = Vector3.Dot(vert - P, N);
         if (dist > collision_epsilon)
         {
             continue;
         }

         avgPos += vertices[i];
         ++count;
     }

     // check collision
     if (count < 1)
     {
         return;
     }

     // calculate average position
     avgPos /= (float)count;
     Vector3 avgRi = R * avgPos;
     dist = Vector3.Dot((avgRi + pos) - P, N);

     // caculate velocity
     Vector3 vi = v + Vector3.Cross(w, avgRi);

     switch (collision_type)
     {
     case ECOLLISION_TYPE.IMPULSE:
         Collision_Impulse(dist, ref vi, ref N, ref avgRi);
         break;
     case ECOLLISION_TYPE.PENALTY:
         Collision_Penalty(dist, ref vi, ref N);
         break;
     default:
         break;
     }
 }

刚体碰撞相应算法(Impulse Method)

步骤:
1.更新每个顶点的位置,检测是否有碰撞
2.碰撞点的速度算出来,计算是否还继续往下掉(往内穿透)?
3.计算 v i n e w v_i^{new} vinew,求法向量和切向量的速度,然后关于摩擦的系数a计算,求新的两个方向的速度,然后加起来(向量的相加)
v i n e w v_i^{new} vinew是不存在的,只是中间变量,实际上应该更新v(线速度)和ω(角速度),用冲量来更新它们
4.计算K,求出冲量j
5.通过冲量来计算v和ω
总结:
因为一个模型的v是质心的速度,vi是模型上某一点的速度。我们不能直接去修改模型上某一点vi的速度,所以要借助j去修改v和w,达到修改vi的目的,求出整个兔子其他点的变化(即修改整体的运动状态)
在这里插入图片描述

在这里插入图片描述
前半部分用冲量法更新速度

void Collision_Impulse(float dist, ref Vector3 vi, ref Vector3 N, ref Vector3 avgRi)
{
    Debug.Log("Hello World");
    // Check velocity dir
    if (Vector3.Dot(vi, N) >= 0.0f)
    {
        return;
    }

    // -------------------------
    // compute the wanted Vi new
    // -------------------------
    Vector3 vn = Vector3.Dot(vi, N) * N;
    Vector3 vt = vi - vn;

    float a = 0.0f;
    if (0.0f != vt.magnitude)
    {
        a = 1.0f - mu_t * (1.0f + restitution) * vn.magnitude / vt.magnitude;
        a = Mathf.Max(a, 0.0f);
    }

    // chagne velocity
    Vector3 vn_new = -restitution * vn;
    Vector3 vt_new = a * vt;
    Vector3 vi_new = vn_new + vt_new;

    // -------------------------
    // compute the impulse j
    // -------------------------
    Matrix4x4 Ri = Get_Cross_Matrix(avgRi);
    Matrix4x4 inv_m = MulMatrix4x4(Matrix4x4.identity, inv_mass);
    Matrix4x4 rir = Ri * I.inverse * Ri;
    Matrix4x4 K = SubMatrix4x4(ref inv_m, ref rir);

    J = K.inverse * (vi_new - vi);
    v += inv_mass * J;

    Vector3 dw = I.inverse * Ri * J;
    w += dw;

    // -------------------------
    // change position
    // -------------------------
    Vector3 pos = transform.position;
    pos -= (dist - collision_epsilon) * N;
    transform.position = pos;
}

Penalty Method(版本3 Log-Barrier)

在这里插入图片描述

void Collision_Penalty(float dist, ref Vector3 vi, ref Vector3 N)
{
    Debug.Log("Hello World2");
    Vector3 vn = Vector3.Dot(vi, N) * N;
    Vector3 vt = vi - vn;
    v = -restitution * vn + mu_t * vt;

    float diff = dist - collision_epsilon;

    // Quadratic Penalty Method
    if (log_barrier_flag)
    {
        if (dist > 0.0f)
        {
            return;
        }

        const float penalty_rho = 4.0f;     // for Log-Barrier penalty method
        float rho = penalty_rho / (diff);
        ext_force += rho * mass * N;
        return;
    }

    const float penalty_k = 2000.0f;        // for quadratic penalty method
    ext_force += penalty_k * diff * mass * N;
}

详细介绍Rigid_Bunny_by_Shape_Matching

Get_Rotation()函数具体实现,暂且不管

粒子碰撞相应算法(Impulse Method)

在这里插入图片描述

void Collision(float inv_dt)
{
    Collision(new Vector3(0, 0.01f, 0), new Vector3(0, 1, 0));      // floor
    Collision(new Vector3(2, 0, 0), new Vector3(-1, 0, 0));         // wall
}

void Collision(Vector3 P, Vector3 N)
{
    for (int i = 0; i < Y.Length; i++)
    {
        float dist = Vector3.Dot(Y[i] - P, N);
        if (dist > collision_epsilon)
        {
            continue;
        }

        // -------------------------
        // compute the wanted vi new
        // -------------------------
        Vector3 vn = Vector3.Dot(V[i], N) * N;
        Vector3 vt = V[i] - vn;

        float a = 0.0f;
        if (0.0f != vt.magnitude)
        {
            a = 1.0f - mu_t * (1.0f + restitution) * vn.magnitude / vt.magnitude;
            a = Mathf.Max(a, 0.0f);
        }

        // chagne velocity
        Vector3 vn_new = -restitution * vn;
        Vector3 vt_new = a * vt;
        V[i] = vn_new + vt_new;

        // change position
        Y[i] -= (dist - collision_epsilon) * N;
    }
}

部分2 Shape Matching-刚体碰撞响应的仿真管线

在这里插入图片描述
FixedUpdate()函数

void FixedUpdate()
{
    if (!launched)
    {
        return;
    }

    const float dt          = 0.015f;
    const float inv_dt      = 1.0f / dt;

    //Step 1: run a simple particle system.
    for (int i = 0; i < V.Length; i++)
    {
        V[i] = V[i] + dt * gravity;     // add gravity acceleration
        V[i] *= linear_decay;           // add linear_decay
        Y[i] = X[i] + dt * V[i];
    }

    //Step 2: Perform simple particle collision.
    Collision(inv_dt);

    // Step 3: Use shape matching to get new translation c and 
    // new rotation R. Update the mesh by c and R.
    //Shape Matching (translation)
    Vector3 c = Vector3.zero;
    for (int i = 0; i < Y.Length; i++)
    {
        c += Y[i];
    }
    c /= (float)Y.Length;

    Matrix4x4 YCRt = Matrix4x4.zero;
    for (int i = 0; i < Q.Length; i++)
    {
        Vector3 d = Y[i] - c;

        YCRt[0, 0] += d[0] * Q[i][0];
        YCRt[0, 1] += d[0] * Q[i][1];
        YCRt[0, 2] += d[0] * Q[i][2];
        YCRt[1, 0] += d[1] * Q[i][0];
        YCRt[1, 1] += d[1] * Q[i][1];
        YCRt[1, 2] += d[1] * Q[i][2];
        YCRt[2, 0] += d[2] * Q[i][0];
        YCRt[2, 1] += d[2] * Q[i][1];
        YCRt[2, 2] += d[2] * Q[i][2];
    }
    YCRt[3, 3] = 1.0f;

    Matrix4x4 A = YCRt * QQt.inverse;

    //Shape Matching (rotation)
    Matrix4x4 R = Get_Rotation(A);

    Update_Mesh(c, R, inv_dt);
}

Update_Mesh函数()

void Update_Mesh(Vector3 c, Matrix4x4 R, float inv_dt)
{
    for (int i = 0; i < Q.Length; i++)
    {
        Vector3 x = (Vector3)(R * Q[i]) + c;
        V[i] += (x - Y[i]) * inv_dt;
        X[i] = x;
    }
    Mesh mesh = GetComponent<MeshFilter>().mesh;
    mesh.vertices = X;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值