UE的Blend Profile

news2024/11/27 18:33:40

参考:Animation Blend Profile in Unity
参考:Unreal Engine - How to make a Blend Profile
参考:blend-masks-and-blend-profiles-in-unreal-engine


什么是Blend Profile

Animation Blend Profile is a UE4 animation system feature that enables us to set blend speed of each bone separately.

这个概念是在UE4的动画系统里提出的feature,可以让我们设置每个Bone的blend speed。如果没有这玩意儿,正常动画Blend时,所有的Bone的Blend Speed都是相同的。

这个功能可以有效解决movement动画之间的transition造成的滑步问题,同时保证上半身动画的流畅性。

这种动画的转态,比如Idle和Moving的切换,悬空和落地的切换,都是极度需要Blend Profile功能的,我估计这个原理是让上半身的Blend Speed和下半身的BlendSpeed不同,从而保证上半身动画流畅的同时,解决下半身的滑步问题。

举个细节的例子,从动画A到动画B的Transition Time为0.5s,如果是线性Blend,那么正常Joint X在Blend时的A的权重为1,B的权重为0,那么0.5s后,A的权重会从1线性减少到0,B的权重从0线性增加到1。如果此时我把Joint Y的Blend Speed设置为X的两倍,那么Y会在0.25s内从A动画过渡到B,如下图所示:
在这里插入图片描述

UE的动画过渡里就可以使用Blend Profile,如下图所示:
在这里插入图片描述

而且Blend Space里也可以设置Blend Profile,因为Blend Space的本质就是处理动画的Blend,如下图所示是在Blend Space里的设置:
在这里插入图片描述

Blend Mask是什么

在UE的源码里,Blend Mask类型为EBlendProfileMode的一种,也就是说UE认为它是一种Blend Profile:

enum class EBlendProfileMode : uint8
{
	// The bone's transition time is a factor based on the transition time. 
	// For example 0.5 means it takes half the time of the transition.
	// Values should be between 0 and 1. They will be clamped if they go out of this range.
	// A bone value of 0 means the bone will instantly transition into the target state.
	TimeFactor = 0,// 值必须<=1

	// The bone's transition weight is multiplied by this factor.
	// For example 2.0 means the bone's blend weight is twice as high as the transition's blend weight.
	// Values should typically be equal or greater than 1.0.
	// If you want certain bones to instantly transition into the target state
	// the Time Factor based method might be a better choice.
	WeightFactor,// 值必须>=1

	// Used for blend masks. Per bone alpha
	BlendMask UMETA(Hidden),
};

感觉UE里认为,能够改变joint影响Blend时的joint的权重设置的东西,都属于Blend Profile,所以它才把Blend Mask也归类为Blend Profile的一种。Blend Mask跟其他类型的Blend Profile不同,它并不会加速blend过程中各个joint的权重值的改变,它只能决定各个joint的权重值的改变是否随着MainWeight改变,改变的程度是多少(范围在[0, 1]区间),类似于Unity的AvatarMask


Blend Mask的用法

创建和编辑的操作跟其他类型的Blend Profile是差不多的:
在这里插入图片描述
默认Blend Mask的每个Bone的BlendScale值为0,可供选择的范围在[0, 1]区间,0代表此Bone完全不受Blend Profile影响

具体使用时要通过动画蓝图的Layered Blend Per Bone节点实现,如下图所示:
在这里插入图片描述
蓝图连接逻辑如下图所示,其实这里的Blend Mask很像Unity里的Avatar Mask


Blend Profile的使用场景

Blend Mask应该是只能用在Layered Blend Per Bone里,而Weight Factor和Time Factor类型的Blend Profile会被用在这五个地方:

  • Blend Poses by bool.
  • Blend Poses by int.
  • Blend Poses by enum.
  • State Machine Transitions.
  • Animation Montages.

BlendSpace里有类似的设置,不过不是BlendProfile,而是叫Target Weight Interpolation Speed Per Sec。

这里的五种节点都会最终调用UBlendProfile::CalculateBoneWeight函数,计算每个Bone在Blend过程中的权重值,无非调用地点有所区别:

  • Blend Poses by bool,Blend Poses by int和Blend Poses by enum都继承于FAnimNode_BlendListBase类,会在里面的Update_AnyThread函数里被调用
  • State Machine Transitions则是在FAnimationActiveTransitionEntry::Update里被调用
  • Animation Montages则是通过AnimationSlot,在FAnimInstanceProxy::SlotEvaluatePoseWithBlendProfiles里被调用的,当AnimationSlotNode在其Evaluate函数里发现了处于Blend状态下的Montage,且设置了BlendProfile时,会调用SlotEvaluatePoseWithBlendProfiles函数

UE相关源码记录

这里拿动画状态机里BlendProfile的应用举例,我在动画的转态上加了个Blend Profile,源码里的CallStack顺序如下:
在这里插入图片描述
图中的展示顺序表示,最终调用的UBlendProfile::UpdateBoneWeights的caller是statieMachine对应的AnimNode(FAnimNode_StateMachine::Update_AnyThread函数),对应的状态机节点会遍历每个动画Transition的对象,然后根据是否存在BlendProfile改变骨骼权重:

void FAnimationActiveTransitionEntry::Update(const FAnimationUpdateContext& Context, int32 CurrentStateIndex, bool& bOutFinished)
{
	bOutFinished = false;

	// Advance time
	if (bActive)
	{
		ElapsedTime += Context.GetDeltaTime();
		Blend.Update(Context.GetDeltaTime());

		// If non-zero, calculate the query alpha
		float QueryAlpha = 0.0f;
		if (CrossfadeDuration > 0.0f)
		{
			QueryAlpha = ElapsedTime / CrossfadeDuration;
		}

		Alpha = FAlphaBlend::AlphaToBlendOption(QueryAlpha, Blend.GetBlendOption(), Blend.GetCustomCurve());

		if (Blend.IsComplete())
		{
			bActive = false;
			bOutFinished = true;
		}

		// Update state blend data (only when we're using per-bone)
		if (BlendProfile)
		{
			for (int32 Idx = 0 ; Idx < 2 ; ++Idx)
			{
				const bool bForwards = (Idx == 0);
				StateBlendData[Idx].TotalWeight = bForwards ? Alpha : 1.0f - Alpha;
				BlendProfile->UpdateBoneWeights(StateBlendData[Idx], Blend, 0.0f, StateBlendData[Idx].TotalWeight, !bForwards);
			}

			FBlendSampleData::NormalizeDataWeight(StateBlendData);
		}
	}
}


Montage里的Blend Profile

相关设置不在LayeredBlendPerBone或者AnimationSlotNode的蓝图Details面板上,而是在Montage资产里,如下图所示,可以选择在Blend In Montage和Blend Out Montage时使用Blend Profile:
在这里插入图片描述


三种模式下的BlendProfile

UE的枚举叫EBlendProfileMode:

/** The mode in which the blend profile should be applied. */
UENUM()
enum class EBlendProfileMode : uint8
{
	// The bone's transition time is a factor based on the transition time. 
	// For example 0.5 means it takes half the time of the transition.
	// 注意这里设置0.5并不是0.5秒的意思, 而是指的TransitionTime的一半
	// Values should be between 0 and 1. They will be clamped if they go out of this range.
	// A bone value of 0 means the bone will instantly transition into the target state.
	TimeFactor = 0,

	// The bone's transition weight is multiplied by this factor.
	// For example 2.0 means the bone's blend weight is twice as high as the transition's blend weight.
	// Values should typically be equal or greater than 1.0.
	// If you want certain bones to instantly transition into the target state
	// the Time Factor based method might be a better choice.
	WeightFactor,

	// Used for blend masks. Per bone alpha
	BlendMask UMETA(Hidden),
};

TimeFactorWeightFactor模式的区别在于权重的计算方法不同,其他基本没任何区别,WeightFactor算法其实很简单,直接根据原本的PoseWeight乘以对应的BlendProfile即可:

float UBlendProfile::CalculateBoneWeight(float BoneFactor, EBlendProfileMode Mode, const FAlphaBlend& BlendInfo, float BlendStartAlpha, float MainWeight, bool bInverse)
{
	switch (Mode)
	{
		// The per bone value is a factor of the transition time, where 0.5 means half the transition time, 0.1 means one tenth of the transition time, etc.
		case EBlendProfileMode::TimeFactor:
		{
			// Most bones will have a bone factor of 1, so let's optimize that case.
			// Basically it means it will just follow the main weight.
			if (BoneFactor >= 1.0f - ZERO_ANIMWEIGHT_THRESH)
			{
				return !bInverse ? MainWeight : 1.0f - MainWeight;
			}

			// Make sure our input values are valid, which is between 0 and 1.
			const float ClampedFactor = FMath::Clamp(BoneFactor, 0.0f, 1.0f);

			// Calculate where blend begin value is for this specific bone. So where did our blend start from?
			// Note that this isn't just the BlendInfo.GetBlendedValue() because it can be different per bone as some bones are further ahead in time.
			// We also need to sample the actual curve for this to get the real value.
			const float BeginValue = (ClampedFactor > ZERO_ANIMWEIGHT_THRESH) ? FMath::Clamp(BlendStartAlpha / ClampedFactor, 0.0f, 1.0f) : 1.0f;
			const float RealBeginValue = FAlphaBlend::AlphaToBlendOption(BeginValue, BlendInfo.GetBlendOption(), BlendInfo.GetCustomCurve());

			// Calculate the current alpha value for the bone.
			// As some bones can blend faster than others, we basically scale the current blend's alpha by the bone's factor.
			// After that we sample the curve to get the real alpha blend value.
			const float LinearAlpha = (ClampedFactor > ZERO_ANIMWEIGHT_THRESH) ? FMath::Clamp(BlendInfo.GetAlpha() / ClampedFactor, 0.0f, 1.0f) : 1.0f;
			const float RealBoneAlpha = FAlphaBlend::AlphaToBlendOption(LinearAlpha, BlendInfo.GetBlendOption(), BlendInfo.GetCustomCurve());

			// Now that we know the alpha for our blend, we can calculate the actual weight value.
			// Also make sure the bone weight is valid. Values can't be zero because this could introduce issues during normalization internally in the pipeline.
			const float BoneWeight = RealBeginValue + RealBoneAlpha * (BlendInfo.GetDesiredValue() - RealBeginValue);
			const float ClampedBoneWeight = FMath::Clamp(BoneWeight, ZERO_ANIMWEIGHT_THRESH, 1.0f);

			// Return our calculated weight, depending whether we'd like to invert it or not.
			return !bInverse ? ClampedBoneWeight : (1.0f - ClampedBoneWeight);
		}

		// The per bone value is a factor of the main blend's weight.
		case EBlendProfileMode::WeightFactor:
		{
			if (!bInverse)
			{
				return FMath::Max(MainWeight * BoneFactor, ZERO_ANIMWEIGHT_THRESH);
			}

			// We're inversing.
			const float Weight = (BoneFactor > ZERO_ANIMWEIGHT_THRESH) ? MainWeight / BoneFactor : 1.0f;
			return FMath::Max(Weight, ZERO_ANIMWEIGHT_THRESH);
		}

		// Handle unsupported modes.
		// If you reach this point you have to add another case statement for your newly added blend profile mode.
		default:
		{
			checkf(false, TEXT("The selected Blend Profile Mode is not supported (Mode=%d)"), Mode);
			break;
		}
	}

	return MainWeight;
}

这里面没有对BlendMask这种模式的处理,因为BlendMask是专门用于 Layered Blend Per Bone节点上的,

这里的TimeFactor和WeightFactor的BlendProfile的使用机制是差不多的,但UE的文档告诉我,这里weightFactor模式下的值应该>=1,而TimeFactor下的值应该<=1,如下所示:
在这里插入图片描述
这说明Blend Profile只能加速动画转态里的特定joint的Blend速度,而不能减速,顺便看了下代码里面,也是这么个逻辑。感觉也比较合理,毕竟动画转态时间外部已经给定了,所以只能加速部分joints的转态,而不能减速。


附录

Unity里实现Blend Profile

首先,既然是逐Bone Blend,那么需要有个编辑的地方,去设置每个Bone的Blend速度,可以选择在AvatarMask界面里编辑,如下图所示(注意这里的时间至少是1, otherwise there would be some problem with transition time):
在这里插入图片描述
然后在动画状态机的Transition里设置加上对应的Blend Profile借助的Avatar Mask即可:
在这里插入图片描述


Unity的BlendTree里使用Blend Profile

其实就跟UE的Blend Space里用Blend Profile是一样的,UE里Blend Space有这么个设置:
在这里插入图片描述

也是类似的,在Blend Tree的Inspector界面里添加Avatar Mask即可:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51566.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

aws eks 使用keycloak集成eks oidc访问apiserver

资料 OpenID Connect Tokens Kubernetes 与 OpenID 集成 SSO 登录测试 Introducing OIDC identity provider authentication for Amazon EKS Understanding Amazon Cognito user pool OAuth 2.0 grants 在 Kubernetes 中使用 Keycloak OIDC Provider 对用户进行身份验证 深…

Koa2基础笔记

目录 一、快速入门 二、中间件 链式调用 洋葱圈模型 异步处理 三、路由 koa-router 四、请求参数解析 处理URL参数 处理body参数 五、错误处理 原生的错误处理 使用中间件 一、快速入门 新建文件夹使用VSCode打开&#xff0c;终端运行npm init -y生成package.json…

[附源码]计算机毕业设计springboot水果管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

冬季寒冷,普通空调如何做到智能控制,增温又降耗的?

寒冷的冬日总少不得取暖的工具&#xff0c;空调成为人们的优先选择。但是&#xff0c;在办公大楼、商场等需要长时间空调供暖&#xff0c;空调又多的地方&#xff0c;空调控制让不少人感觉到了困扰。上下班时间空调多&#xff0c;检查慢&#xff0c;如果漏掉其中一个&#xff0…

C++——类和对象讲解

C——类和对象讲解总览 本篇文章的讲解将按照上述的方式&#xff0c;逐层递进&#xff0c;进一步帮你拿捏C类和对象这部分。 类和对象上&#xff08;基础篇&#xff09; 1、初识面向对象编程&#xff1a; 初步了解面向过程和面向对象的区别 举个比较感人的例子用来帮助铁子…

Qt视频监控系统一个诡异问题的解决思路(做梦都想不到)

一、前言 由于Qt版本众多&#xff0c;几百个版本之间存在不兼容的情况&#xff0c;为此如果要兼容很多版本&#xff0c;没有取巧的办法和特殊的捷径&#xff0c;必须自己亲自安装各个版本编译运行并测试&#xff0c;大问题一般不会有&#xff0c;除非缺少模块&#xff0c;小问…

健壮性测试是什么?

当大多数人开车时&#xff0c;他们不会担心刹车失灵。当他们的孩子得到一个新玩具时&#xff0c;他们也不担心因故障受伤。事实上&#xff0c;大多数人在日常生活中根本不担心系统故障。 这是因为软件开发人员或质量控制工程师已经解决了质量问题。如果目标是交付高质量、可靠…

基础:html5、html、htm、xhtml、dhtml、shtml、shtm、xml、xsl、xslt

一、理解 1.1、HTML5&#xff08; HyperText Markup Language 5 &#xff09; HTML5_百度百科 HTML5 技术结合了 HTML4.01 的相关标准并革新&#xff0c;符合现代网络发展要求&#xff0c;在 2008 年正式发布。HTML5 由不同的技术构成&#xff0c;其在互联网中得到了非常广泛…

TCP/IP五层协议栈(2)

1.传输层协议(TCP传输的机制补充) 1.1.滑动窗口 滑动窗口存在的意义就是在保证可靠性的前提下,尽量提高传输效率. 由于TCP是得保证可靠传输的,所以每一次数据发送时都需要等待对方的ACK.大部分时间都用在等ACK上,而确认应答是可靠传输的主要机制又不能不等.所以有了新的机制…

JavaScript -- 多种类型转换方法总结

文章目录类型转换1 转换为字符串2 转换为数值3 转换为布尔值4 总结类型转换 类型转换指将一种数据类型转换为其他类型 类型转换是根据当前值去创建另一个值&#xff0c;而不是将当前值直接进行转换 这里说到的转换都是显示类型转换&#xff0c;除此之外还有隐式类型转换 1 转…

MnTTS: 开源蒙古语语音合成数据集及其基线模型

本次分享内蒙古大学蒙古文信息处理重点实验室、蒙古文智能信息处理技术国家地方联合工程研究中心及语音理解与生成实验室 (S2LAB) &#xff08;https://ttslr.github.io/index_S2Group.html&#xff09;共同发布的开源蒙古语语音合成数据集及其基线模型。相关论文《MnTTS: An O…

BI业务用户商业分析新时代,如何把数据用透?

数字化转型进入实质性阶段&#xff0c;企业对于数据的需求也随之加深。然而&#xff0c;一些企业积累了大量数据&#xff0c;却难以深度释放数据价值。 近几年&#xff0c;作为数据应用的有力工具&#xff0c;BI商业智能分析平台同样进入了转折期&#xff0c;其发展趋势明显呈…

Nacos后台系统未授权添加管理员

一&#xff1a;什么是nacos Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中…

爱奇艺:基于龙蜥与 Koordinator 在离线混部的实践解析 | 龙蜥技术

在 2022 云栖大会龙蜥峰会云原生专场上&#xff0c;来自爱奇艺的基础架构研究员赵慰分享了《基于龙蜥与 Koordinator 的在离线混部实践》技术演讲&#xff0c;以下为本次演讲内容&#xff1a; 一、爱奇艺离线业务混部背景 与众多互联网公司一样&#xff0c;爱奇艺常见的负载…

Go语言学习笔记-A Tour of Go 练习笔记-Images

Exercise: Images 练习题目: Remember the picture generator you wrote earlier? Lets write another one, but this time it will return an implementation of image.Image instead of a slice of data. Define your own Image type, implement the necessary methods, …

您的推特营销选对群控了吗

作为跨境电商从业者&#xff0c;我们都知道&#xff0c;如果平台检测到违规&#xff0c;最多的是多账号关联。平台怎么判定我们的账号是否关联呢&#xff1f;一个重要的依据是浏览器的指纹信息。 一、进行站外引流的困难有哪些&#xff1a; 国内用户在推广海外社交媒体时&…

【OpenCV-Python】教程:3-10 直方图(4)直方图反向投影

OpenCV Python 直方图 反向投影 【目标】 直方图反向投影calcBackProject 【原理】 用于图像分割和查找感兴趣目标。简单的说&#xff0c;会创建一个与输入图像同样大小的图像&#xff08;单通道&#xff09;&#xff0c;每个像素对应像素属于目标的概率。更简单的说就是&am…

pdf文件丢失怎么办?别慌,详细介绍4种恢复方法

pdf文件丢失怎么找到&#xff1f;别慌&#xff0c;停止往电脑上写入新的内容&#xff0c;重要的事情说三遍&#xff01;下面&#xff0c;我们将会向您详细介绍在pdf文件丢失后的多种方法&#xff0c;请继续阅读以获得更多帮助&#xff01; 方法1.运用Windows搜索功能 很多时候…

运放参数-共模输入范围-运算放大器

运放共模输入范围 根据实际的应用我们会选择一个运算放大器&#xff08;op amp&#xff09;&#xff0c;选型过程中工程师会考虑一些参数可例如&#xff1a;电源电压、增益带宽积、输入共模范围、转换速率和输入噪声电压等等。 在本篇文章中重点介绍了运放的输入共模范围的定…

Codeforces Round #724 (Div. 2) C. Diluc and Kaeya

翻译&#xff1a; 蒙德施塔特一个酒庄帝国的大亨&#xff0c;在任何方面都无可匹敌。法佛尼乌斯骑士团中具有异域外表的思想家。 这一次&#xff0c;兄弟俩要处理的是一块刻着他们名字的奇怪木头。这块木板可以表示为一串&#x1d45b;字符。每个字符不是“D”就是“K”。您希…