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动态图片. 动画的主要元素 时 ...
随机推荐
- SpringBoot3整合SpringSecurity6(一)快速入门
大家好,我是晓凡. 写在前面 不知道小伙伴们在学SpringSecurity过程中有没有和我一样的经历和烦恼. ①看完一篇文章或者一个教程,感觉学会了.但是一到实际项目中就不知道怎么用: ②被Spri ...
- 关于composer报错The openssl extension is required for SSL/TLS protection but is not available问题
今天使用composer的时候得到了这个错误: The openssl extension is required for SSL/TLS protection but is not availabl ...
- SpringIntegrationRamble
目录 Why SpringIntegration Background Consolidate Architecture ESB service Popular Solutions Getting S ...
- Java--Calendar类,Date类的简单使用,日期的格式化
package demo; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; / ...
- 工具 | Hfish
0x00 简介 HFish是一款社区型免费蜜罐. 下载地址 HFish下载: HFish下载 0x01 功能说明 支持多种蜜罐服务 支持自定义Web蜜罐 支持流量牵引 支持端口扫描感知能力 支持多种告 ...
- 代码随想录第十五天 | Leecode 110. 平衡二叉树、257. 二叉树的所有路径、404. 左叶子之和、222. 完全二叉树的节点个数
Leecode 110. 平衡二叉树 题目描述 给定一个二叉树,判断它是否是 平衡二叉树(是指该树所有节点的左右子树的高度相差不超过 1.) 示例 1: 输入:root = [3,9,20,null, ...
- HarmonyOS NEXT开发实战教程--招聘app
这一周忙到起飞,只能在周末发个文章.今天的内容比较简单,是一个招聘app,适合新手友友参考,大佬们可以直接忽略. 看一下效果图: 这是一个比较常见的应用,大家做这类应用建议大家先分析一下应用和页面的结 ...
- ESP32 MQTT对接巴法云平台
ESP32 MQTT对接巴法云平台 MQTT(Message Queuing Telemetry Transport)是一种轻量级的 发布/订阅(Publish/Subscribe) 消息传输协议,专 ...
- JS高级用法:像大神一样玩转JavaScript
@charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...
- 关于I/O与并发
前言 由于笔者在之前发布的一文玩转NGINX中提到过I/O复用模型,在此另起一篇文章简述相关技术. 什么是I/O I/O输入/输出(Input/Output),分为IO设备和IO接口两个部分. 在PO ...