Games 103 作业二

Games 103 作业二

作业二其实就是要使用隐式积分和PBD两种方式来实现布料求解。难度相对于作业一来说要简单一些,在文档中基本把步骤都写清楚了。主要逻辑首先参考Lecture 05 PPT的第18页:

然后我们按照文档的步骤一步一步地来。注意0号顶点和20号顶点是不参与更新的,它们相当于就是定死了位置。第一步是初始化,这个很简单:

c# 复制代码
for (int i = 0; i < V.Length; i++)
{
    if (!Skip_Update(i))
    {
        V[i] *= damping;
    }
}
for(int i = 0; i < X.Length; i++)
{
    if (!Skip_Update(i))
    {
        X_hat[i] = X[i] + V[i] * t;
        X[i] = X_hat[i];
    }
}

第二步就是计算梯度。这里的梯度就是PPT里的
∇ F ( x ( k ) ) = 1 Δ t 2 M ( x ( k ) − x [ 0 ] − Δ t 2 v [ 0 ] ) − f ( x ( k ) ) \nabla F(\textbf{x}^{(k)}) = \dfrac{1}{\Delta t^2} \textbf{M} (\textbf{x}^{(k)} - \textbf{x}^{[0]} - \Delta t^2 \textbf{v}^{[0]}) - \textbf{f} (\textbf{x}^{(k)}) ∇F(x(k))=Δt21M(x(k)−x[0]−Δt2v[0])−f(x(k))

这里的f就是重力和弹簧间的弹力。重力是个常量好办,弹力的计算需要遍历所有的边,找到边的两个顶点,分别进行计算,参考PPT的第11页:

c# 复制代码
void Get_Gradient(Vector3[] X, Vector3[] X_hat, float t, Vector3[] G)
{
    //Momentum and Gravity.
    for (int i = 0; i < G.Length; i++)
    {
        if (!Skip_Update(i))
        {
            G[i] = mass * (X[i] - X_hat[i]) / (t * t) - mass * gravity;
        }
    }

    //Spring Force.
    for (int e = 0; e < L.Length; e++)
    {
        int i = E[e * 2 + 0];
        int j = E[e * 2 + 1];
        float x = Vector3.Distance(X[i], X[j]);
        Vector3 f = spring_k * (1 - L[e] / x) * (X[i] - X[j]);

        if (!Skip_Update(i))
        {
            G[i] += f;
        }

        if (!Skip_Update(j))
        {
            G[j] -= f;
        }
    }
}

然后需要计算:
∂ 2 F ( x ( k ) ) ∂ x 2 = 1 Δ t 2 M + H ( x ( k ) ) \dfrac{\partial^2F(\textbf{x}^{(k)})}{\partial\textbf{x}^2} = \dfrac{1}{\Delta t^2}\textbf{M} + \textbf{H}(\textbf{x}^{(k)}) ∂x2∂2F(x(k))=Δt21M+H(x(k))

作业文档中给了近似求解的方法,我们就不用算 H \textbf{H} H了。我们使用Chebyshev加速牛顿法迭代,可以参考PPT中第26页:

作业中是固定的32次迭代次数,这里的break判断就可以省略掉;另外,x的更新直接使用作业里的公式即可:

c# 复制代码
for(int k=0; k<32; k++)
{
    Get_Gradient(X, X_hat, t, G);

    if(k == 0)
    {
        omega = 1.0f;
    }
    else if(k == 1)
    {
        omega = 2.0f / (2.0f - rho * rho);
    }
    else
    {
        omega = 4.0f / (4.0f - rho * rho * omega);
    }
    
    //Update X by gradient.
    for(int i = 0; i < X.Length; i++)
    {
        if (!Skip_Update(i))
        {
            Vector3 old = X[i];
            X[i] = omega * (X[i] - 1 / (mass / (t * t) + 4 * spring_k) * G[i]) + (1 - omega) * last_X[i];
            last_X[i] = old;
        }
    }
}

迭代完别忘记更新下V,这里要使用+=,因为一开始算 x ~ \widetilde{x} x 的时候加过v了:

c# 复制代码
for(int i = 0; i < X.Length; i++)
{
    if (!Skip_Update(i))
    {
        V[i] += (X[i] - X_hat[i]) / t;
    }
}

最后的碰撞检测很简单,算一下点到球心的距离,如果小于半径就说明发生碰撞:

c# 复制代码
void Collision_Handling()
{
    Mesh mesh = GetComponent<MeshFilter> ().mesh;
    Vector3[] X = mesh.vertices;

    //Handle colllision.
    Vector3 c = sphere.transform.position;
    for(int i = 0; i < X.Length; i++)
    {
        if(!Skip_Update(i))
        {
            float d = Vector3.Distance(X[i], c);
            if (d < r)
            {
                V[i] = V[i] + 1 / t * (c + r * (X[i] - c) / d - X[i]);
                X[i] = c + r * (X[i] - c) / d;
            }
        }
    }

    mesh.vertices = X;
}

最后效果如下:

然后我们再看下PBD的实现。第一步就是让每个顶点自由更新:

c# 复制代码
for(int i=0; i<X.Length; i++)
{
    if(i==0 || i==20)	continue;
    //Initial Setup
    //...
    V[i] *= damping;
    V[i] += gravity * t;
    X[i] += V[i] * t;
}

接下来,通过若干次迭代施加约束,这里可以参考Lecture 06 PPT的第10页:

作业里 α \alpha α取的0.2:

c# 复制代码
void Strain_Limiting()
{
    Mesh mesh = GetComponent<MeshFilter> ().mesh;
    Vector3[] vertices = mesh.vertices;

    //Apply PBD here.
    //...
    Vector3[] sum_X = new Vector3[vertices.Length];
    int[] sum_N = new int[vertices.Length];

    for (int e = 0; e < L.Length; e++)
    {
        int i = E[e * 2 + 0];
        int j = E[e * 2 + 1];
        float x = Vector3.Distance(vertices[i], vertices[j]);
        Vector3 f = L[e] * (vertices[i] - vertices[j]) / x;

        sum_X[i] += 0.5f * (vertices[i] + vertices[j] + f);
        sum_N[i]++;
        sum_X[j] += 0.5f * (vertices[i] + vertices[j] - f);
        sum_N[j]++;
    }

    for(int i = 0; i < V.Length; i++)
    {
        if(i == 0 || i == 20)	continue;
        Vector3 f = (0.2f * vertices[i] + sum_X[i]) / (0.2f + sum_N[i]);
        V[i] += 1 / t * (f - vertices[i]);
        vertices[i] = f;
    }

    mesh.vertices = vertices;
}

最后效果如下:

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

相关推荐
June bug14 小时前
【领域知识】休闲游戏一次发版全流程:Google Play + Apple App Store
unity
星夜泊客16 小时前
C# 基础:为什么类可以在静态方法中创建自己的实例?
开发语言·经验分享·笔记·unity·c#·游戏引擎
dzj202117 小时前
PointerEnter、PointerExit、PointerDown、PointerUp——鼠标点击物体,则开始旋转,鼠标离开或者松开物体,则停止旋转
unity·pointerdown·pointerup
心前阳光18 小时前
Unity 模拟父子关系
android·unity·游戏引擎
在路上看风景21 小时前
26. Mipmap
unity
咸鱼永不翻身1 天前
Unity视频资源压缩详解
unity·游戏引擎·音视频
在路上看风景1 天前
4.2 OverDraw
unity
在路上看风景1 天前
1.10 CDN缓存
unity
ellis19701 天前
Unity插件SafeArea Helper适配异形屏详解
unity
nnsix1 天前
Unity Physics.Raycast的 QueryTriggerInteraction枚举作用
unity·游戏引擎