虚幻4渲染编程(物理模拟篇)【第一卷:Introduce my Physic plugin】

2023-10-17 21:59

本文主要是介绍虚幻4渲染编程(物理模拟篇)【第一卷:Introduce my Physic plugin】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

MY BLOG DIRECTORY:

YivanLee:专题概述及目录

INTRODUCTION:

虽然虚幻4给我们提供了物理的解决方案,但是这些方案其实还是不够完善,很多上层功能性的物理特性还是需要基于Unreal的物理接口再次开发的。这卷我将基于Unreal的物理引擎接口开发更加上层的物理特性。

以下的效果是我开发的PhyGX插件目前的部分功能。比如我们有时候需要一个物理蜘蛛网:

再比如我们有时候需要一片物理草地

或者需要什么更上层的功能,这时候就需要对引擎的物理功能根据项目需求进行拓展。为什么叫PhyGX呢,因为接下来我开发的物理插件就叫PhyGX(自己瞎取的名)。


MAIN CONTENT:

其实在我之前的文章里也有提到过,首先我们构造出一些动力学粒子和约束,然后结算它们,把最后的解算结果用来构建动态网格,然后把网格数据塞的渲染管线里就好了。这个可以看我之前的文章:

YivanLee:虚幻4渲染编程(图元汇编篇)【第五卷:游戏中的动力学模拟】

YivanLee:虚幻4渲染编程(环境模拟篇)【第五卷:可交互物理植被模拟 - 上】

刚体运动模拟就是使用韦尔莱算法,如果想模拟软体就是需要在约束上下功夫了。目前我实现的约束有四种:

DistanceConstraint,AngularConstraint,DistanceAngularConstraint,PinConstraint

VeletParticle.h

        #pragma  once#include "EngineMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Engine/EngineTypes.h"/
//PinConstraint
struct FPinConstraint
{FPinConstraint(){bFree = true;bLockX = false;bLockY = false;bLockZ = false;}FPinConstraint(bool bfreeval, bool blockxval, bool blockyval , bool blockzval):bFree(bfreeval),bLockX(blockxval),bLockY(blockyval),bLockZ(blockzval){}bool bFree;bool bLockX;bool bLockY;bool bLockZ;
};/
//Verlet Paticle
struct FVerletParticle
{
public:FVerletParticle() {bUseLocalForce = false;ParticleFraction = 1.0f;}void InitVerletParticle(FVector CurPosValue, FVector OldPosValue, bool bFreeValue = true, bool bLockXValue = false, bool bLockYValue = false, bool bLockZValue = false,float Fraction = 1.0f){CurPos = CurPosValue;OldPos = OldPosValue;PinCons.bFree = bFreeValue;PinCons.bLockX = bLockXValue;PinCons.bLockY = bLockYValue;PinCons.bLockZ = bLockZValue;ParticleFraction = Fraction;}// If Using this function vertion particle will use it's own force directionvoid InitVerletParticle(FVector CurPosValue,FVector OldPosValue,FVector Force,bool bFreeValue = true,bool bLockXValue = false,bool bLockYValue = false,bool bLockZValue = false,float Fraction = 1.0f){CurPos = CurPosValue;OldPos = OldPosValue;LocalForceDir = Force;PinCons.bFree = bFreeValue;PinCons.bLockX = bLockXValue;PinCons.bLockY = bLockYValue;PinCons.bLockZ = bLockZValue;ParticleFraction = Fraction;bUseLocalForce = true;}FVector CurPos;FVector OldPos;FVector LocalForceDir;FPinConstraint PinCons;float ParticleFraction;void SolvePinConstraint();bool GetbUseLocalForce() { return bUseLocalForce; }private:bool bUseLocalForce;};/
//DistanceConstraint
struct FDistanceConstraint
{FDistanceConstraint(){}FDistanceConstraint(FVerletParticle* A, FVerletParticle* B, float DesiDistanceValue):ParticleA(A), ParticleB(B), DistanceLength(DesiDistanceValue){}//The Range of the SoftFactor is [0, 1]void InitDistanceConstraint(FVerletParticle& A, FVerletParticle& B, float distance, float SoftFactor = 1.0f){ParticleA = &A;ParticleB = &B;DistanceLength = distance;SoftStrenth = SoftFactor;}void SolveDistanceConstraint();FVerletParticle* ParticleA;FVerletParticle* ParticleB;float DistanceLength;//The Range of the SoftStrenth is [0, 1]float SoftStrenth;
};/
//AngularConstraint
struct FAngularConstraint
{FAngularConstraint(){}FAngularConstraint(FVerletParticle* A, FVerletParticle* B, FVerletParticle* C, float AngularSizeValue):ParticleA(A), ParticleB(B), ParticleC(C), AngularSize(AngularSizeValue){}//If we don't use distance angular constraint,use this function to initvoid InitAngularConstraint(FVerletParticle& A, FVerletParticle& B, FVerletParticle& C, float AngularSizeValue){ParticleA = &A;ParticleB = &B;ParticleC = &C;AngularSize = AngularSizeValue;bUseDistanceAngularConstraint = false;}//If we use distance angular constraint, we shoule use this vertion function to initvoid InitAngularConstraint(FVerletParticle& A, FVerletParticle& B, FVerletParticle& C,float AngularSizeValue,float EdgeASize, float EdgeBSize, float angularsoft){ParticleA = &A;ParticleB = &B;ParticleC = &C;AngularSize = AngularSizeValue;AngularSoftStrenth = angularsoft;bUseDistanceAngularConstraint = true;DistanceAngularEdgeALength = EdgeASize;DistanceAngularEdgeBLength = EdgeBSize;}//This is first type angular constraint,it is not a good solve wayvoid SolveAngularConstraint();//The second Solve Angular constraint way, very simple but usefulvoid SolveDistanceAngularConstraint();//These data is for distance angular constraint//The range of the AngularSoftStrenth is [0, 1]float AngularSoftStrenth;float DistanceAngularEdgeALength;float DistanceAngularEdgeBLength;bool bUseDistanceAngularConstraint;//Three particle pointer of this angular constraintFVerletParticle* ParticleA;FVerletParticle* ParticleB;FVerletParticle* ParticleC;//The angle to limitedfloat AngularSize;};

VeletParticle.cpp

        #include "VerletParticle.h"void FVerletParticle::SolvePinConstraint()
{if (PinCons.bLockX){CurPos.X = OldPos.X;}if (PinCons.bLockY){CurPos.Y = OldPos.Y;}if (PinCons.bLockZ){CurPos.Z = OldPos.Z;}
}void FDistanceConstraint::SolveDistanceConstraint()
{if ((ParticleA != nullptr && ParticleB != nullptr)&&(ParticleA->PinCons.bFree || ParticleB->PinCons.bFree)){// Find current vector between particlesFVector Delta = ParticleB->CurPos - ParticleA->CurPos;// float CurrentDistance = Delta.Size();float ErrorFactor = (CurrentDistance - DistanceLength) / CurrentDistance * SoftStrenth;// Only move free particles to satisfy constraintsif (ParticleA->PinCons.bFree && ParticleB->PinCons.bFree){ParticleA->CurPos += ErrorFactor * 0.5f * Delta;ParticleB->CurPos -= ErrorFactor * 0.5f * Delta;}else if (ParticleA->PinCons.bFree){ParticleA->CurPos += ErrorFactor * Delta;}else if (ParticleB->PinCons.bFree){ParticleB->CurPos -= ErrorFactor * Delta;}}
}void FAngularConstraint::SolveAngularConstraint()
{if ((ParticleA != nullptr && ParticleB != nullptr && ParticleC !=nullptr) && (ParticleA->PinCons.bFree || ParticleC->PinCons.bFree)){FVector a = ParticleA->CurPos - ParticleB->CurPos;FVector b = ParticleC->CurPos - ParticleB->CurPos;float theta = acos(dot(a, b) / (length(a) * length(b))) * 180.0f / 3.1415926f;if (theta > AngularSize) return;float dif = theta - AngularSize;float ErrorFactor = (theta - AngularSize) / (theta + 0.0001);if (dif < -360.0f){dif += 360;}else if (dif > 360.0f){dif -= 360.0f;}FVector Axi = cross(a, b).GetSafeNormal();float ConstraintStrenth = 0.007f;if (ParticleA->PinCons.bFree && ParticleC->PinCons.bFree){ParticleA->CurPos += (ParticleA->CurPos.RotateAngleAxis(dif * ErrorFactor * 0.5f, Axi)) * ConstraintStrenth * 0.5f;ParticleC->CurPos -= (ParticleC->CurPos.RotateAngleAxis(-dif * ErrorFactor * 0.5f, Axi)) * ConstraintStrenth * 0.5f;}else if (ParticleA->PinCons.bFree){ParticleA->CurPos += (ParticleA->CurPos.RotateAngleAxis(dif * ErrorFactor, Axi)) * ConstraintStrenth;}else if (ParticleC->PinCons.bFree){ParticleC->CurPos -= (ParticleC->CurPos.RotateAngleAxis(-dif * ErrorFactor, Axi)) * ConstraintStrenth;}}
}void FAngularConstraint::SolveDistanceAngularConstraint()
{if ((ParticleA != nullptr && ParticleB != nullptr && ParticleC != nullptr)&& (ParticleA->PinCons.bFree || ParticleC->PinCons.bFree)){float la = DistanceAngularEdgeALength;float lb = DistanceAngularEdgeBLength;float lc = sqrt(la*la + lb * lb - 2 * la*lb*cos(AngularSize));// Find current vector between particlesFVector Delta = ParticleA->CurPos - ParticleC->CurPos;// float CurrentDistance = Delta.Size();float ErrorFactor = (CurrentDistance - lc) / CurrentDistance * AngularSoftStrenth;// Only move free particles to satisfy constraintsif (ParticleA->PinCons.bFree && ParticleC->PinCons.bFree){ParticleA->CurPos -= ErrorFactor * 0.5f * Delta;ParticleC->CurPos += ErrorFactor * 0.5f * Delta;}else if (ParticleA->PinCons.bFree){ParticleA->CurPos -= ErrorFactor * Delta;}else if (ParticleC->PinCons.bFree){ParticleC->CurPos += ErrorFactor * Delta;}}
}

这便是构造物体最基础的Particle和四种Constraint的代码了,目前还非常简单,没有加入太多的物理量。

有了最基础的Particle,下一步就是构造动力学网格ParticleNet

v2-96f3a1f7669f4d84b33f2bd2f632b860_b.jpg

v2-56162d7d13ce0b2eba732ffa518e7290_b.jpg

ParticleNet由各种Constraint,一堆约束和一堆渲染所用的数据组成,在ParticleNet里需要完成粒子网格的构建和动态模型的构建,然后在把这些数据塞给SceneProxy即可

v2-276a3fc3e1270943bcd697669f7b47d5_b.jpg

v2-04ada33fd297750a8b18dec8e09643fc_b.jpg

最后再在MeshComponent中把SceneProxy和ParticleNet组合到一起,在Tick函数中不停让ParticleNet更新数据

v2-388651e2cb29dd918dda4aea60197bf7_b.jpg

ParticleNet需要对Point的位置进行动力学解算,下面这段解算公式是通用的。代码中的Force可以是任何力,默认情况下物体受到一个重力。对于重力,这个值是一直不变的,但是可以把它的值进行动态变换。

        void FPhyGXParticleNet::VerletInstigation()
{for (int32 ParticleIndex = 0; ParticleIndex < Particles.Num(); ParticleIndex++){FVerletParticle& Particle = Particles[ParticleIndex];if (Particle.PinCons.bFree){FVector Force = FVector(0, 0, 0);if (Particle.GetbUseLocalForce() == true){//Local forceForce = Particle.LocalForceDir;}//Use global force to caculateelseForce = ForceDir;// Find velconst FVector Vel = Particle.CurPos - Particle.OldPos;// Update positionconst FVector NewPosition = Particle.CurPos + (Vel + (SubstepTimeSqr * Force)) * Particle.ParticleFraction;Particle.OldPos = Particle.CurPos;Particle.CurPos = NewPosition;}}
}

在SendRenderDynamicData_Concurrent()函数中把ParticleNet的渲染数据塞给SceneProxy

v2-3d1002277221eec9b4ca4873c3423a83_b.jpg

这样就可以在UE中进行我们自己的物理引擎插件的开发了。完整代码我会在后面的章节中放出。


SUMMARY AND OUTLOOK:

如果不进行VB和IB的Buffer拷贝修改操作的话,其实仅仅是模拟物理那部分计算量还是比较小的。

Enjoy it。


NEXT:

YivanLee:虚幻4渲染编程(物理模拟篇)【第二卷:Soft Body Simulation】

这篇关于虚幻4渲染编程(物理模拟篇)【第一卷:Introduce my Physic plugin】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/228204

相关文章

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点