当前位置: 首页 > article >正文

Games 103 作业二

Games 103 作业二

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

Games 103 作业二1

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

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页:

Games 103 作业二2

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)}) x22F(x(k))=Δt21M+H(x(k))
作业文档中给了近似求解的方法,我们就不用算 H \textbf{H} H了。我们使用Chebyshev加速牛顿法迭代,可以参考PPT中第26页:

Games 103 作业二3

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

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了:

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

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

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

最后效果如下:

Games 103 作业二4

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

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页:

Games 103 作业二5

作业里 α \alpha α取的0.2:

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

最后效果如下:

Games 103 作业二6

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


http://www.kler.cn/a/107198.html

相关文章:

  • 数据集标注txt文件读取小工具
  • Golang | Leetcode Golang题解之第559题N叉树的最大深度
  • ubuntu中apt-get的默认安装路径。安装、卸载以及查看的方法总结
  • AWS认证SAA-C0303每日一题
  • 【ChatGPT】 如何让ChatGPT分析数据并得出结论
  • 移动端【01】面试系统的MVVM重构实践
  • 10款轻量型的嵌入式GUI库分享
  • C++前缀和算法的应用:使数组相等的最小开销
  • 基于Python Django 的微博舆论、微博情感分析可视化系统(V2.0)
  • 如何快速安装MONAI(莫奈)医学标注软件
  • Xray的简单使用
  • Spring 更简单的读取和存储对象
  • docker - window Docker Desktop升级
  • redis集群的多key原子性操作如何实现?
  • Oracle RU 19.21及 datapatch -sanity_checks
  • js中HTMLCollection如何循环
  • 基于springboot实现校园志愿者管理系统项目【项目源码+论文说明】计算机毕业设计
  • 【ARMv8 SIMD和浮点指令编程】NEON 存储指令——如何将数据从寄存器存储到内存?
  • 系列二十二、如何在Spring中所有的bean都创建完成后做扩展
  • MySQL篇---第四篇
  • 论文阅读——InstructGPT
  • 数据库MySQL(四):表中字段约束和外键约束
  • 磁场设备主要有哪些
  • DBA笔记(1)
  • 云服务器搭建Zookeeper集群
  • nginx 动静分离 防盗链