Games 103 作业一

Games 103 作业一

整个作业一的内容其实就是要自己动手实现一遍ImpulseShape Matching这两个方法。作业中给的示例场景如下:

Games 103 作业一1

场景中有个兔子的刚体,我们要模拟的就是给兔子一个初始的速度,让其在重力的影响下,与两堵墙发生碰撞的效果。

先看Impulse方法的实现,实现思路就是主要参考PPT中第28页:

Games 103 作业一2

首先,我们需要对兔子mesh中的所有顶点进行遍历,找到与墙平面发生碰撞的所有顶点,检查任意顶点是否与墙平面发生碰撞也很简单,就是计算点到平面的距离,若为负值即发生碰撞,也就是PPT中的 ϕ ( x i ) < 0 \phi(\textbf{x}_i) < 0 ϕ(xi)<0

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

Vector3 avg = Vector3.zero;
int num = 0;
for(int i = 0; i < vertices.Length; i++)
{
    Vector3 vert = transform.TransformPoint(vertices[i]);
    if(Vector3.Dot(vert - P, N) < 0)
    {
        avg += vert;
        num++;
    }
}

如果这里得到的num为0,说明所有点都没有和平面发生碰撞,也就不必再继续计算了。下一步,我们计算发生碰撞的点的平均值,作为 x i \textbf{x}_i xi,而 R r i \textbf{R}\textbf{r}_i Rri其实就是模型中心到这个点的向量:

Games 103 作业一3

那么我们就可以得到 v i \textbf{v}_i vi,根据速度的方向来判断是否发生碰撞:

avg /= num;

Vector3 rri = avg - transform.position;
Vector3 vi = v + Vector3.Cross(w, rri);
float dot = Vector3.Dot(vi, N);
if(dot >= 0)
{
    return;
}

如果发生碰撞,则需要根据PPT中的公式,更新 v i \textbf{v}_i vi

Vector3 vni = dot * N;
Vector3 vti = vi - vni;
float a = Mathf.Max(1 - u_t * (1 + u_n) * Vector3.Magnitude(vni) / Vector3.Magnitude(vti), 0);
vni = -u_n * vni;
vti = a * vti;
Vector3 vinew = vni + vti;

有了新的 v i \textbf{v}_i vi之后,就可以根据它计算出当前的冲量 j \textbf{j} j

Matrix4x4 inv = Matrix4x4.Inverse(I_ref);
Matrix4x4 k1 = Matrix4x4.identity;
for(int i = 0; i < 4; i++)
{
	k1[i, i] = 1 / mass;
}
Matrix4x4 k2 = Get_Cross_Matrix(rri) * inv * Get_Cross_Matrix(rri);
Matrix4x4 k = new Matrix4x4();
for(int i = 0; i < 4; i++)
{
    for(int j = 0; j < 4; j++)
    {
        k[i, j] = k1[i, j] - k2[i, j];
    }
}

Vector3 J = Matrix4x4.Inverse(k) * (vinew - vi);

进而可以更新刚体的速度 v \textbf{v} v和角速度 w \textbf{w} w

v = v + J / mass;
Vector3 tmp1 = Vector3.Cross(rri, J);
Vector4 tmp2 = new Vector4(tmp1.x, tmp1.y, tmp1.z, 0);
Vector3 tmp3 = inv * tmp2;
w = w + new Vector3(tmp3.x, tmp3.y, tmp3.z);

最终就得到了下一时刻刚体的位置和旋转。实现的效果如下:

Games 103 作业一4

接下来就是Shape Matching方法了,它的原理就是让每个顶点先分别自由地计算各自的速度和位置,然后再根据刚体的约束,对顶点进行调整,得到每个顶点最终的速度和位置。实现思路也主要参考PPT中第39页:

Games 103 作业一5

每个顶点与平面发生碰撞的逻辑依旧可以用Impulse的方法来处理,只不过这里只需要计算出每个顶点碰撞后新的速度 v i \textbf{v}_i vi即可:

void Collision(float inv_dt)
{
    for(int i = 0; i < Y.Length; i++)
    {
        Collision_Impulse(i, new Vector3(0, 0.01f, 0), new Vector3(0, 1, 0));
        Collision_Impulse(i, new Vector3(2, 0, 0), new Vector3(-1, 0, 0));
    }
}

更新完位置后,需要计算当前时刻下刚体的中心,以及新的旋转:

Vector3 c=Vector3.zero;
for(int i=0; i<Y.Length; i++)
    c+=Y[i];
c/=Y.Length;
//Shape Matching (translation)
Matrix4x4 A = Matrix4x4.zero;
for(int i=0; i<Y.Length; i++)
{
    Vector3 yic = Y[i] - c;
    A[0, 0]+=yic[0]*Q[i][0];
    A[0, 1]+=yic[0]*Q[i][1];
    A[0, 2]+=yic[0]*Q[i][2];
    A[1, 0]+=yic[1]*Q[i][0];
    A[1, 1]+=yic[1]*Q[i][1];
    A[1, 2]+=yic[1]*Q[i][2];
    A[2, 0]+=yic[2]*Q[i][0];
    A[2, 1]+=yic[2]*Q[i][1];
    A[2, 2]+=yic[2]*Q[i][2];
}
A[3, 3]=1;
A *= Matrix4x4.Inverse(QQt);
//Shape Matching (rotation)
Matrix4x4 R = Get_Rotation(A);

然后,我们再反过来,根据当前的约束去计算每个顶点真正的位置,再和上一时刻的顶点的位置做差,就能得到每个顶点当前时刻的速度了:

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-X[i])*inv_dt;
        X[i]=x;
    }
    Mesh mesh = GetComponent<MeshFilter>().mesh;
    mesh.vertices=X;
}

最后运行的效果如下:

Games 103 作业一6

如果你觉得我的文章有帮助,欢迎关注我的微信公众号 我是真的想做游戏啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值