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;
}

最后效果如下:

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

相关推荐
超龄魔法少女13 小时前
[Unity] ShaderGraph动态修改Keyword Enum,实现不同效果一键切换
unity·技术美术·shadergraph
蔗理苦14 小时前
2024-12-24 NO1. XR Interaction ToolKit 环境配置
unity·quest3·xr toolkit
花生糖@14 小时前
Android XR 应用程序开发 | 从 Unity 6 开发准备到应用程序构建的步骤
android·unity·xr·android xr
向宇it14 小时前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎
虾球xz15 小时前
游戏引擎学习第55天
学习·游戏引擎
虾球xz17 小时前
游戏引擎学习第58天
学习·游戏引擎
ue星空18 小时前
虚幻引擎结构之UWorld
游戏引擎·虚幻
ue星空18 小时前
虚幻引擎结构之ULevel
游戏引擎·虚幻
向宇it19 小时前
【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么、主流的游戏引擎、为什么选择Unity
开发语言·unity·c#·游戏引擎
神洛华21 小时前
Y3地图制作1:水果缤纷乐、密室逃脱
编辑器·游戏引擎·游戏程序