Sentis
是 Unity
推出的一个用于在 Unity
环境中运行机器学习模型的框架,可以简化将机器学习模型集成到 Unity
项目中的过程。
打开 Window > Package Manager
点击 +
选择 Add package by name
输入 com.unity.sentis
安装
去 Hugging Face
选择并下载你需要的模型,或者去 ModelZoo
之类下载。
在 Hugging Face
上,模型通常以 PyTorch
或 TensorFlow
格式提供,需要转换为 ONNX
格式。
假设你使用的是 PyTorch
模型,可以使用 torch.onnx.export
方法来完成转换。
import torch
from transformers import AutoModel
# Load your Hugging Face model
model = AutoModel.from_pretrained("your-model-name")
# Set the model to evaluation mode
model.eval()
# Dummy input for model export
dummy_input = torch.randn(1, 3, 224, 224) # Adjust dimensions as needed
# Export the model to ONNX format
torch.onnx.export(model, dummy_input, "model.onnx", opset_version=11)
如果不使用 Unity
想直接在 iOS
中使用,则需要再讲 ONNX 转化为 Core Ml 格式。
using UnityEngine;
using Unity.Sentis;
using System.IO;
public class SentisExample : MonoBehaviour
{
private Model runtimeModel;
private IWorker worker;
void Start()
{
// 加载 ONNX 模型文件
var modelFilePath = Application.dataPath + "/model.onnx";
var modelData = File.ReadAllBytes(modelFilePath);
// 加载模型到 Sentis
runtimeModel = ModelLoader.Load(modelData);
worker = WorkerFactory.CreateWorker(WorkerFactory.Type.ComputePrecompiled, runtimeModel);
// 准备输入数据(示例)
// 根据模型调整维度
var inputTensor = new Tensor(1, 3, 224, 224);
// 执行推理
worker.Execute(inputTensor);
var outputTensor = worker.PeekOutput();
// 处理输出数据
Debug.Log(outputTensor);
// 清理资源
inputTensor.Dispose();
outputTensor.Dispose();
worker.Dispose();
}
}
为了在移动设备上高效运行,模型通常需要经过优化,包括:
将模型从32位浮点数表示转换为8位整数表示,可以显著减少模型的大小和计算需求。
移除模型中冗余的权重和节点,减少计算复杂度。
将复杂模型的知识转移到一个较小的模型中。
最新的iPhone
能够运行包含数百万到数千万参数的优化模型。例如,MobileNet
和 TinyBERT
等模型经过量化和其他优化技术处理后,可以在iPhone
上高效地运行。
–EOF–
]]>发一个年初做的 APP「分花」 凑一篇 Blog 吧。
大概这辈子再也不会做这种类型的软件了…
有人要内购兑换码吗:)
可以邮件: [email protected]
小红书: peakcoder
愿你有无数的鲜花与浪漫
–EOF–
]]>.gitattributes
文件在 Git 仓库中用于定义各种属性,以指定 Git 如何处理仓库中的文件。 指定文件的行尾风格,例如,强制在 Windows 和 UNIX 系统之间一致地使用 LF 或 CRLF。
*.txt text eol=lf
*.bat text eol=crlf
为特定文件或路径指定合并策略,比如使用文本方式合并或使用特定的合并驱动。
*.png merge=theirs
将大文件如视频、音频或大型二进制文件交给 Git LFS 管理。
*.mp4 filter=lfs diff=lfs merge=lfs -text
指定在使用 git archive 时应该忽略的文件。
*.log export-ignore
控制 git 命令如何统计语言使用情况,可以用于排除某些文件。
*.md linguist-documentation
指定文件的差异展示方式,如是否应该被 Git 识别为文本,并如何显示差异。
*.jpg binary
*.html diff=html
禁用对特定文件的 Git 压缩。
*.jpg -filter
为文件设置自定义的清理(clean)和还原(smudge)过滤器。
*.json filter=jsonFilter
在提交前检查文件是否符合特定的属性要求。
*.py check=python
指定文件是否应被视为文本,对行尾进行规范化,或者被视为二进制文件。
*.txt text
*.bin binary
–EOF–
]]>之前在AppStore上架过,长久不更新现已下架。
代码不多,大量的时间都在美术上消耗掉了,年少时的我是真的很爱画像素画 :)
–EOF–
]]>例如以下代码的快捷键为 CTRL + G
[MenuItem("Test/example %g")]
private static void Test()
{
Debug.Log("TEST!");
}
–EOF–
]]>这可能是以下情况导致的:
–EOF–
]]>否则需要以下操作让 Shader 在 Single Pass Instanced 模式下让渲染 compatible
1. #include "UnityCG.cginc" - near the top of the CGPROGRAM block
2. UNITY_INSTANCE_ID - should be in the the vert input data struct (ex: appdata)
3. UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); - should be in the vert function
4. UNITY_VERTEX_OUTPUT_STEREO - should be in the frag input data struct (ex: v2f)
–EOF–
]]>/path/to/unity/project/Assets/Plugins/iOS/CustomAppController.mm
注意,文件名必须是 ___AppController,前缀可自选,但不能省略;否则在 Build 项目的时候,会被移动到错误的目录中去
使用宏 IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController) 定制启动调用
#import "UnityAppController.h"
#import <UIKit/UIKit.h>
#import <FIRApp.h>
@interface CustomAppController : UnityAppController
@end
IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController)
// 如果使用 modules import,则不能用.mm混编 只能用.m
@import GoogleMobileAds;
@implementation CustomAppController
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[super application:application didFinishLaunchingWithOptions:launchOptions];
[FIRApp configure];
[[GADMobileAds sharedInstance] startWithCompletionHandler:nil];
return YES;
}
@end
–EOF–
]]>public static void RemoveInGo(GameObject g)
{
var components = g.GetComponents<Component>();
var r = 0;
for (var i = 0; i < components.Length; ++i)
{
if (components[i] != null)
continue;
var s = g.name;
var t = g.transform;
while (t.parent != null)
{
s = t.parent.name +"/"+s;
t = t.parent;
}
Debug.Log ($"{s} has a missing script at {i}", g);
var serializedObject = new SerializedObject(g);
var prop = serializedObject.FindProperty("m_Component");
prop.DeleteArrayElementAtIndex(i-r);
r++;
serializedObject.ApplyModifiedProperties();
}
foreach (Transform childT in g.transform)
{
RemoveInGo(childT.gameObject);
}
}
官方解决方案可以使用
GameObjectUtility.RemoveMonoBehavioursWithMissingScript
–EOF–
]]>基于状态机的动画控制系统,是一个面向动画应用的动画系统 动画的制作编辑(Animation)功能相对有限 重在如何从资源中提取动画,配置融合动画,应用动画,复用动画 以及为实际开发提供可视化的GUI编辑和监视功能
Unity 中的动画片段分为两种 通过剪辑 FBX 中的整段动画生成的动画切片(Clip) 以及由 Animation 窗口创建出的动画片段(Clip)
通过AnimatorEvent动画事件 我们可以在动画播放到某个特定位置时,触发脚本中的特定方法(发射子弹,生成特效,受击信息)
Controller: 状态机的配置资源文件,整合Clip资源,创建状态机状态(State),设置动画过渡与融合
Avatar: 动画节点导引替身,与动画复用(尤其是人形动画复用)有关,通过配置和应用Avatar,可以实现不同FBX模型之间的动画复用
Apply Root Motion: 是否将动画中的根节点位移,植入到Unity中的物体位移上
Update Mode: 状态机播放动画的时间模式
选中一条过渡线 Gizmo,我们可以在右侧 Inspector 窗口中配置这个过渡的相关属性
Interruption Source:
Current State 或 NextState指该过渡,允许被其它从 CurrentState 或 NextState 出发的过渡打断
CurrentStateThenNextState,指从 CurrentState 或 NextState 出发的过渡均可打断
但 CurrentState 出发的过渡打断优先于 NextState 出发的过渡
Speed:动画片段的播放速度(置为-1可倒放动画)
Multiplier:Speed的一个倍率因子,需要勾选Parameter可选定一个浮点动画参数来动态控制这个因子
Mirror:是否进行左右翻转
Cycle Offset:偏移量(和之前Clip中的偏移相同),0到1之间的浮点值,可勾选Parameter选定一个浮点动画参数来动态控制
Foot IK:脚部的IK反向力学修正,针对人物上坡,上楼梯时的踏空而行进行修正
Write Default: 主要是针对人物骨骼之外的一些节点,例如武器,跟随物,这些节点只在部分互动动作之中被用到,那些没有使用该节点的动作执行时,是要让节点处于初始状态,还是上一次互动动作末尾时的状态
是一种Controller状态机配置资源文件,它可以继承一种指定的Animator Controller,并在此基础上进行修改,从而在复用Controller的同时,做出针对性的修改
OverrideController 只能继承自 Controller,不能形成继承树
RootMotion 总共有三种状态,勾选,不勾选 Handled by Script
Hadndled by Script 状态,是由于我们在 Animator 所在物体的脚本中, 定义方法 OnAnimatorMove()
Avatar替身(人物节点导引),主要与动画的复用,尤其是人物动画的复用有关
// 这里的名称要与Animator窗口中,动画参数的名称对应
int runHash=Animator.StringToHash("Run");
// 通常对于调用频繁的动画参数我们使用哈希值进行快速访问
// 下面设置/获取动画参数均有使用String参数名称进行映射的重载和使用哈希值进行映射的重载
// 获取设置Float类型参数,通常结合Input轴线
animator.GetFloat(blendHash);animator.SetFloat(blendHash, Input.GetAxis("Horizontal"));
// 获取设置Int类型参数
animator.GetInteger(intHash);animator.SetInteger(intHash,Number);
// 获取设置Bool类型参数
animator.GetBool(boolHash);animator.SetBool(boolHash, true / false);
// 触发,取消触发Trigger的方法
animator.SetTrigger(jumpHash);animator.ResetTrigger(jumpHash);
// 我们先预设状态的哈希值
int idleHash = Animator.StringToHash("Idle");
int layerID = animator.GetLayerIndex("Base Layer");
animatorStateInfo = animator.GetCurrentAnimatorStateInfo(layerID);
// 判定当前状态是否是Idle状态
if (animatorStateInfo.shortNameHash == idleHash)
{
Debug.Log("OnState Idle");
}
// 过渡状态的 nameHash userNameHash
AnimatorTransitionInfo transitionInfo;
transitionInfo = animator.GetAnimatorTransitionInfo(layerID);
Debug.Log(transitionInfo.nameHash);
Debug.Log(transitionInfo.userNameHash);
动画分层可以让我们使用多层动画来对人物/角色不同组分分别配置动画,再使用一定的混合模式,从而实现一些复杂的效果
BlendTree的作用是将多个动画状态混合成为一个动画状态,并通过动画参数来影响状态的输出结果。
使用 BlendTree 的目的是应对状态机的高耦合导致的低扩展缺点,尤其是在人物运动相关的动作(前后左右的行走奔跑)这类繁多的动作
在状态过渡时 BlendTree 就相当于一个普通的 State,可以创建指向和指出 BlendTree 的过渡。BlendTree 的 State 配置也和普通 State 一致
–EOF–
]]>