GAMES 103 动画基础作业1 Shape Matching 浅浅解析
简介
作业1简单实现了一个以一定初始速度和角速度的模型和墙壁碰撞的效果.
总共讲解了三种算法
impulse (脉冲法)
Shape Matching(基于形状保持的算法, 不包含物理特性)
Penalty methods
Shape Matching 可以说是最简单的方法之一
因为完全不涉及角速度. 基础逻辑理论就是, 当模型和墙壁发生碰撞的时候.这些碰撞的粒子会产生相反的形变但是. 我们强制将其整理保持形状的一致性. 就是先形变. 再变回来.
由于形变的步骤. 是中间步骤. 不会展示出来. 所以效果还是挺好的.
Image



TIPS
code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rigid_Bunny_by_Shape_Matching : MonoBehaviour
{
public bool launched = false;
Vector3[] X; // world position
Vector3[] Y; // temp world position
Vector3[] Q; // Local coordinates
Vector3[] V; // speed
Vector3[] OldX;
Matrix4x4 QQt = Matrix4x4.zero;
Vector3 G = new Vector3(0.0f, -9.8f, 0.0f);
float linear_decay = 0.999f;
Vector3 ground = new Vector3(0, 0.01f, 0);
Vector3 groundNormal = new Vector3(0, 1, 0);
Vector3 wall = new Vector3(2.01f, 0, 0);
Vector3 wallNormal = new Vector3(-1, 0, 0);
float mu_T = 0.5f; // μ_T may be coefficient of air resistance
float mu_N = 5.0f; // μ_N may be Coefficient of Restitution
float m_timer = 0;
// Start is called before the first frame update
void Start()
{
Mesh mesh = GetComponent<MeshFilter>().mesh;
V = new Vector3[mesh.vertices.Length];
X = mesh.vertices;
Y = mesh.vertices;
OldX = mesh.vertices;
Q = mesh.vertices;
//Centerizing Q.
Vector3 c=Vector3.zero;
for(int i=0; i<Q.Length; i++)
c+=Q[i];
c/=Q.Length;
Debug.Log("C: " + c);
for(int i=0; i<Q.Length; i++)
Q[i]-=c;
//Get QQ^t ready.
for(int i=0; i<Q.Length; i++)
{
QQt[0, 0]+=Q[i][0]*Q[i][0];
QQt[0, 1]+=Q[i][0]*Q[i][1];
QQt[0, 2]+=Q[i][0]*Q[i][2];
QQt[1, 0]+=Q[i][1]*Q[i][0];
QQt[1, 1]+=Q[i][1]*Q[i][1];
QQt[1, 2]+=Q[i][1]*Q[i][2];
QQt[2, 0]+=Q[i][2]*Q[i][0];
QQt[2, 1]+=Q[i][2]*Q[i][1];
QQt[2, 2]+=Q[i][2]*Q[i][2];
}
QQt[3, 3]=1;
for(int i=0; i<X.Length; i++)
V[i][0]=4.0f;
Update_Mesh(transform.position, Matrix4x4.Rotate(transform.rotation), 0);
transform.position=Vector3.zero;
transform.rotation=Quaternion.identity;
}
Matrix4x4 vector3x1dotvector1x3(Vector3 A, Vector3 B)
{
Matrix4x4 rlt = Matrix4x4.zero;
rlt[3, 3] = 1.0f;
rlt[0, 0] = A[0] * B[0];
rlt[0, 1] = A[0] * B[1];
rlt[0, 2] = A[0] * B[2];
rlt[1, 0] = A[1] * B[0];
rlt[1, 1] = A[1] * B[1];
rlt[1, 2] = A[1] * B[2];
rlt[2, 0] = A[2] * B[0];
rlt[2, 1] = A[2] * B[1];
rlt[2, 2] = A[2] * B[2];
return rlt;
}
// Polar Decomposition that returns the rotation from F.
Matrix4x4 Get_Rotation(Matrix4x4 F)
{
Matrix4x4 C = Matrix4x4.zero;
for(int ii=0; ii<3; ii++)
for(int jj=0; jj<3; jj++)
for(int kk=0; kk<3; kk++)
C[ii,jj]+=F[kk,ii]*F[kk,jj];
Matrix4x4 C2 = Matrix4x4.zero;
for(int ii=0; ii<3; ii++)
for(int jj=0; jj<3; jj++)
for(int kk=0; kk<3; kk++)
C2[ii,jj]+=C[ii,kk]*C[jj,kk];
float det = F[0,0]*F[1,1]*F[2,2]+
F[0,1]*F[1,2]*F[2,0]+
F[1,0]*F[2,1]*F[0,2]-
F[0,2]*F[1,1]*F[2,0]-
F[0,1]*F[1,0]*F[2,2]-
F[0,0]*F[1,2]*F[2,1];
float I_c = C[0,0]+C[1,1]+C[2,2];
float I_c2 = I_c*I_c;
float II_c = 0.5f*(I_c2-C2[0,0]-C2[1,1]-C2[2,2]);
float III_c = det*det;
float k = I_c2-3*II_c;
Matrix4x4 inv_U = Matrix4x4.zero;
if(k<1e-10f)
{
float inv_lambda=1/Mathf.Sqrt(I_c/3);
inv_U[0,0]=inv_lambda;
inv_U[1,1]=inv_lambda;
inv_U[2,2]=inv_lambda;
}
else
{
float l = I_c*(I_c*I_c-4.5f*II_c)+13.5f*III_c;
float k_root = Mathf.Sqrt(k);
float value=l/(k*k_root);
if(value<-1.0f) value=-1.0f;
if(value> 1.0f) value= 1.0f;
float phi = Mathf.Acos(value);
float lambda2=(I_c+2*k_root*Mathf.Cos(phi/3))/3.0f;
float lambda=Mathf.Sqrt(lambda2);
float III_u = Mathf.Sqrt(III_c);
if(det<0) III_u=-III_u;
float I_u = lambda + Mathf.Sqrt(-lambda2 + I_c + 2*III_u/lambda);
float II_u=(I_u*I_u-I_c)*0.5f;
float inv_rate, factor;
inv_rate=1/(I_u*II_u-III_u);
factor=I_u*III_u*inv_rate;
Matrix4x4 U = Matrix4x4.zero;
U[0,0]=factor;
U[1,1]=factor;
U[2,2]=factor;
factor=(I_u*I_u-II_u)*inv_rate;
for(int i=0; i<3; i++)
for(int j=0; j<3; j++)
U[i,j]+=factor*C[i,j]-inv_rate*C2[i,j];
inv_rate=1/III_u;
factor=II_u*inv_rate;
inv_U[0,0]=factor;
inv_U[1,1]=factor;
inv_U[2,2]=factor;
factor=-I_u*inv_rate;
for(int i=0; i<3; i++)
for(int j=0; j<3; j++)
inv_U[i,j]+=factor*U[i,j]+inv_rate*C[i,j];
}
Matrix4x4 R=Matrix4x4.zero;
for(int ii=0; ii<3; ii++)
for(int jj=0; jj<3; jj++)
for(int kk=0; kk<3; kk++)
R[ii,jj]+=F[ii,kk]*inv_U[kk,jj];
R[3,3]=1;
return R;
}
// Update the mesh vertices according to translation c and rotation R.
// It also updates the velocity.
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;
}
void Collision(float inv_dt)
{
for(int i=0; i<Q.Length; i++)
{
if(Vector3.Dot(X[i] - ground, groundNormal) < 0 && Vector3.Dot(V[i], groundNormal) < 0)// collision with ground
{
Vector3 VN = Vector3.Dot(V[i], groundNormal) * groundNormal;
Vector3 VT = V[i] - VN;
float a = Mathf.Max(0, 1.0f - mu_T * (1.0f + mu_N)) * Vector3.Magnitude(VN) / Vector3.Magnitude(VT);
V[i] = -1.0f * mu_N * VN + 2.0f * a * VT;
}
else if(Vector3.Dot(X[i] - wall, wallNormal) < 0 && Vector3.Dot(V[i], wallNormal) < 0) // collision with wall
{
Vector3 VN = Vector3.Dot(V[i], wallNormal) * wallNormal;
Vector3 VT = V[i] - VN;
float a = Mathf.Max(0, 1.0f - mu_T * (1.0f + mu_N)) * Vector3.Magnitude(VN) / Vector3.Magnitude(VT);
V[i] = -1.0f * mu_N * VN + 2.0f * a * VT;
}
}
}
// Update is called once per frame
void Update()
{
if (Input.GetKey("l"))
{
launched = true;
for(int i=0; i<V.Length; i++)
{
V[i] = new Vector3(5.0f, 2.0f, 0.0f);
}
}
if (Input.GetKey("r"))
{
launched = false;
for (int i = 0; i < V.Length; i++)
{
V[i] = new Vector3(4.0f, 0.0f, 0.0f);
}
Update_Mesh(new Vector3(0, 0.6f, 0), Matrix4x4.Rotate(transform.rotation), 0);
}
if(!launched)
{
return;
}
//m_timer += Time.time;
//if (m_timer <= 500)
//{
// return;
//}
//else
//{
// m_timer = 0;
//}
float dt = 0.015f;
//Step 1: run a simple particle system.
for(int i=0; i<V.Length; i++)
{
V[i] = V[i] + G * dt;
V[i] *= linear_decay;
}
//Step 2: Perform simple particle collision.
Collision(1/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)
for(int i=0; i<V.Length; i++)
{
Y[i] = X[i] + V[i] * dt;
}
// calc c
Vector3 c = new Vector3(0, 0, 0);
for(int i=0; i<V.Length; i++)
{
c += Y[i];
}
c = c / V.Length;
// calc A
Matrix4x4 A = Matrix4x4.zero;
A[3, 3] = 1.0f;
for (int i=0; i<V.Length; i++)
{
Matrix4x4 o = vector3x1dotvector1x3(Y[i] - c, Q[i]);
A[0, 0] += o[0, 0];
A[0, 1] += o[0, 1];
A[0, 2] += o[0, 2];
A[1, 0] += o[1, 0];
A[1, 1] += o[1, 1];
A[1, 2] += o[1, 2];
A[2, 0] += o[2, 0];
A[2, 1] += o[2, 1];
A[2, 2] += o[2, 2];
}
A = A * QQt.inverse;
//Shape Matching (rotation)
// calc R
Matrix4x4 R = Matrix4x4.zero;
R = Get_Rotation(A);
Update_Mesh(c, R, 1/dt);
}
}
TRICK
PIPELINE

计算 C (模型质心坐标)
计算 A (通过A计算得到R, 即 旋转矩阵) == 从A得到R. 老师已经提供了.
更新顶点坐标
Trick
在处理碰撞的函数中. 里面的参数是自己调整的参数. 比如 5.0f 逻辑上 μ_T μ_N 都应该是一个小于1的数. 但是, 测试后感觉效果不是特别好. 如果你知道为什么的话, 请留言.
请看完bilibli 4个视屏后开始自己的作业.
参考链接
配置VS 作为 Unity 的配置环境
https://blog.csdn.net/qq_34405576/article/details/105572069
源码参考师弟写出来的, 大部分是抄的嘿嘿~~ 侵权删除.
GAMES 103 动画基础作业1 Shape Matching 浅浅解析的更多相关文章
- iOS开发UI篇—核心动画(基础动画)
转自:http://www.cnblogs.com/wendingding/p/3801157.html 文顶顶 最怕你一生碌碌无为 还安慰自己平凡可贵 iOS开发UI篇—核心动画(基础动画) iOS ...
- 【腾讯GAD暑期训练营游戏程序开发】游戏中的动画系统作业
游戏中的动画系统作业说明文档 一.实现一个动画状态机:至少包含3组大的状态节点
- 《Programming WPF》翻译 第8章 1.动画基础
原文:<Programming WPF>翻译 第8章 1.动画基础 动画包括在一段时间内改变用户界面的某些可见的特征,如它的大小.位置或颜色.你可以做到这一点,非常困难的通过创建一个tim ...
- 【2017-04-01】JS字符串的操作、时间日期的操作、函数、事件、动画基础
一.字符串的操作 1.转大写: s.toLowerCase(); 2.转大写: s.toUpperCase(); 3.字符串的截取: s.substr(3,4); -从索引3开始截取,截取4 ...
- 炫丽的倒计时效果Canvas绘图与动画基础
前言 想要在自己做的网页中,加入canvas动画效果,但是发现模板各种调整不好,觉得还是要对canvas有所了解,才可以让自己的网页变得狂拽炫酷吊炸天! 一.绘制基础 1 <!DOCTYPE h ...
- 动画基础--基于Core Animation(3)
参考:https://zsisme.gitbooks.io/ios-/content/ 前面的文章动画基础--基于Core Animation(1),动画基础--基于Core Animation(2) ...
- 动画基础--基于Core Animation(2)
参考:https://zsisme.gitbooks.io/ios-/content/ 前面的文章动画基础--基于Core Animation(1)提到了图层的基本概念以及可动画参数几何学等知识. 本 ...
- iOS 动画基础总结篇
iOS 动画基础总结篇 动画的大体分类(个人总结可能有误) 分类.png UIView 动画 属性动画 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 ...
- HTML5+JavaScript动画基础 完整版 中文pdf扫描版
<HTML5+JavaScript动画基础>包括了基础知识.基础动画.高级动画.3D动画和其他技术5大部分,分别介绍了动画的基本概念.动画的JavaScript基础.动画中的三角学.渲染技 ...
- Expression Blend学习动画基础
原文:Expression Blend学习动画基础 什么是动画(Animation)? 动画就是时间+换面的组合,画面跟着时间变化.最常见的是flash的动画,还有GIF动态图片. 动画的主要元素 时 ...
随机推荐
- nginx禁止IP访问,仅供域名访问(域名访问限制不严格漏洞)
域名访问限制不严格漏洞解决 nginx添加相关配置 通过default_server,在http中最前面加上该配置 server { listen 80 default_server; server_ ...
- mybatis——分页插件PageHelper的使用
项目开发中涉及列表查询时,经常会需要对查询结果进行分页处理:常用的一个插件--PageHelper,是国内非常优秀的一款开源的mybatis分页插件,它支持基本主流与常用的数据库,一致支持mysql. ...
- Robot Framework原生库的编辑与应用
RF有一些操作指令不存在,需要自己添加方法,比如selenium里有click_and_hold指令(鼠标保持点击状态)而RF内没有.所以需要在库文件里加入这个方法 C:\Python27\Lib\s ...
- kettle介绍-Step之Write to log
Write to log写日志介绍 写日志步骤是将输入步骤的信息打印在日志窗口,供用户直接查看 Step name:步骤的名称,在单一转换中,名称必须唯一. Log level:设置日志的显示级别. ...
- 基于docker的AI-Codereview-Gitlab部署实战
AI-Codereview-Gitlab是一个基于大模型的自动化代码审查工具,帮助开发团队在代码合并或提交时,快速进行智能化的审查(Code Review),提升代码质量和开发效率. 主要功能 多模型 ...
- 基于Kubernetes可扩展的Selenium 并行自动化测试部署及搭建(3)——基于k8s的selenium grid集群搭建
本篇主要讲解如何使用k8s搭建selenium grid集群 Selenium Grid集群部署 1. 首先我们将通过 Kubernetes 服务进行通信以到达hub和nodes.Kubernete ...
- 智表ZCELL产品V2.0发版小感
不知不觉,智表从诞生以来已经两年多了,首先感谢各位用户的支持,没有大家的支持,智表这个产品估计早就已经终止了,正是大家的支持和鼓励,智表才一步一步走到了今天.智表最初是从一个简单不能再简单的table ...
- java从小白到老白⑤——传智播客27版笔记
今天主要说些内部类的相关基础知识,如果能做出下面这个小题目,再下面的内容不看也罢(面试题答案在最后) 内部类面试题:补全下列代码,实现目标输出,其中描述阶段的数字只能调用已有变量,不能用其他方式 pu ...
- post 报头注入
1.user-agent 注入: 使用情况:万能密码无法绕过安全验证,用户名无法注入,通过查看源代码分析执行的动作. bp抓包修改user_agent的数据如下 User-Agent: ' or ud ...
- Spring注解之@Value注解读取配置文件属性和设置默认值
概述 在Spring 组件中,通常使用@Value注解读取 properties 文件的配置值.但如果在配置文件或启动参数中未指定对应的参数值,则项目在启动的时候会抛出异常,导致服务启动失败,异常 ...