《Head of the Order*》施法于感知计算





0/5 (0投票)
当英特尔发起一项号召,征集能够改变我们与传统 PC 交互方式的应用时,Unicorn Forest Games 的创始人 Jacob 和 Melissa Pennock 提交了他们的施法游戏《Head of the Order*》,参与了 Intel® Perceptual Computing Challenge。他们最终获得了最高奖。
引言
当英特尔发起一项号召,征集能够改变我们与传统 PC 交互方式的应用时,Unicorn Forest Games 的创始人 Jacob 和 Melissa Pennock,提交了他们的施法游戏《Head of the Order*》,参与了 Intel® Perceptual Computing Challenge,并利用了 Intel® Developer Zone 的技术资源。他们最终获得了最高奖。
其理念是通过零身体接触来控制应用。《Head of the Order》(图 1)让玩家沉浸在一个锦标赛风格的游戏中,在精美的室内和室外环境中,仅凭手势施展魔法。在演示游戏的关卡设计阶段,主要目标之一是创造一种深刻的手势体验,允许玩家组合护盾、火焰和冰霜等魔法。“虽然释放第一个火球感觉很棒,但我们真正希望的是,当玩家第一次看到对手连续施展一系列手势并将其彻底击败时,能让他们感受到那种惊叹,”Jacob 说。他想象这就像第一次看到经典格斗游戏中的高水平玩家一样,并希望设置一个学习曲线,让玩家在成功时感到成就感。Jacob 的目标是让它不同于用实体控制器精通游戏。“通过使用手势,我们能够唤起人们在学习武术时产生的同样感觉;这些动作感觉更具个人色彩,”他说。
创意团队
虽然 Jacob 构思了游戏的初始概念,但他和 Melissa 都负责创意元素,并且他们共同打磨游戏设计。Jacob 表示,Melissa 的心理学学士学位为她提供了宝贵的用户体验设计见解,而她的艺术专业辅修则造就了《Head of the Order》令人印象深刻的视觉效果。“她的专长是绘画,尤其是细致的线条描绘,”Jacob 说。Melissa 在传统艺术和二维艺术方面也有深厚的功底,并创作了所有二维 UI 资源,包括抬头显示器、主菜单和角色选择屏幕。他们一起设置了渲染和灯光,完成了大部分的纹理绘制,并根据他们能利用的资源和特效设计了关卡。“Melissa 提供了广泛的艺术指导,但 3D 建模并非我们的功劳,其中大部分是从 Unity Asset Store 购买的,”Jacob 说。Melissa 还创建了交互式法术书,玩家可以在演示中用手势翻页(参见图 2)。
Jacob 在东卡罗莱纳大学学习,在那里他开发了自己的手势识别系统,并获得了数学和计算机科学学士学位,重点是人机交互。他的工作包括一项单笔手势识别算法,该算法提高了当时已建立算法的准确性。该算法最初于 2010 年创建,用 C++ 编写,Jacob 已将其 HyperGlyph* 库授权给大型游戏工作室。“这是我研究生期间的论文工作,我已经在几个项目中使用了它,”他说。“它的准确率很高,而且是为游戏设计的。”虽然该库在将输入与手势集中的内容匹配方面做得很好,但 Jacob 还补充说,它在拒绝误报和模板集之外的输入方面也做得很好,这是许多其他笔画式手势系统的一个大问题,可能导致“……玩家可以随意涂鸦,然后发生强力攻击。”该库识别器的严格性和其他因素都可以配置。
他的教育背景在处理传入摄像头数据的复杂性方面也证明了其价值;这是他已经掌握的内容。“我在对输入流进行归一化和重采样以进行查看方面积累了大量经验,”Jacob 说。在大学期间,他开发了形式语言理论的一个扩展,该理论描述了涉及多个并发流的输入。
利用三维空间
Unicorn Forest Games 使用的感知摄像头是 Creative* Senz3D。当与 Intel® Perceptual Computing SDK 配对时,可以创建应用以执行手部和手指跟踪、面部特征跟踪,以及一些基于语音和手势的手势识别。Jacob 表示他更喜欢这款摄像头,因为它具有近距离感应功能;英特尔的摄像头设计用于跟踪几英寸到几英尺远的双手和面部。“它更轻巧,速度更快,而且感觉我的代码和硬件之间的抽象层更少,”他说。
为了处理来自摄像头的 P 数据,Jacob 为他想要跟踪的每只手的一部分设置了一个 P 流程。然后,P 应用通过 SDK 请求 P 信息流,并 P 轮询 P 流以 P 检测 P 变化。这些 P 数据 P 流 P 成为 P 三维 P 空间的 P 深度 P 图。SDK P 会 P 将 P 手部 P 从 P 背景 P 中 P 分割 P 出来,P 然后 P 基于 P 此 P 创建 P 一个 P 低 P 分辨率 P 的 P 图像。“我们 P 还 P 请求 P 手部 P 跟踪 P 的 P 信息,P 该 P 信息 P 以 P 标签 P 的 P 形式 P 提供,P 这些 P 标签 P 被 P 映射 P 到 P 具有 P 位置 P 和 P 旋转 P 的 P 变换 P 上,P 这些 P 变换 P 被 P 映射 P 在 P 三维 P 空间 P 中,”他 P 补充道。P Intel P Perceptual P Computing P SDK P 提供了 P 左手 P、P 右手 P、P 单独 P 的 P 手指 P 和 P 手掌 P 的 P 标签 P。
Jacob 提到 Intel® Perceptual Computing SDK 中一个小的怪癖是,标签有时会混淆;例如,用户的右手可能会出现在数据中,而标签却是左手的,反之亦然。Jacob 通过使系统无论使用哪只手都能完全相同地行为来解决这个问题。“事实上,[Intel®] Perceptual Computing SDK 经常会报告左右颠倒,但游戏都能很好地处理,”他说。1
抛开标签的映射问题,《Head of the Order》实现了一个手部跟踪类,该类初始化 P 流程并 P 处理 P 与 P Intel® Perceptual Computing SDK P 的 P 所有 P 交互。“这个 P 类 P 存储 P 和 P 更新 P 了 P 所有 P 的 P 摄像头 P 信息,”Jacob 说。“它 P 还 P 对 P 检测 P 到 P 或 P 丢失 P 的 P 手 P、P 速度 P 的 P 变化 P、P 手 P 的 P 打开 P 和 P 关闭 P,P 以及 P 基于 P 姿势 P 的 P 手势 P 等 P 执行 P 了 P 各 P 种 P 处理 P,P 所有 P 这些 P 都会 P 生成 P 事件 P,P 子 P 系统 P 可以 P 订阅 P 并 P 按 P 需要 P 使用 P。”
抓空气
尽管《Head of the Order》是专为 Intel Perceptual Computing Challenge 而开发的,但 Jacob 在 2006 年听了一场关于多点触控的 TED 演讲时萌生了这个 P 想法,那时多点触控 P 尚未 P 普及。这是他 P 第一次 P 看到 P 多点触控 P,Jacob P 受到 P 启发 P,P 超越 P 了 P 鼠标 P 和 P 键盘 P 的 P 限制 P,P 意识到 P 未来 P 将 P 充满 P 触控 P 和 P 感知 P 界面。他 P 开始 P 构想 P 它们 P 的 P 内容;P 不幸 P 的 P 是 P,P 非 P 触控 P 输入 P 的 P 技术 P 尚未 P 发明 P。
快进 P 几年 P,P 随着 P 手部 P 跟踪 P 技术 P 的 P 可用 P,Jacob P 和 P Melissa P 得以 P 将 P 他们 P 的 P 想法 P 付诸 P 实践 P,P 但 P 在 P Jacob P 实际 P 参与 P 了 P 感知 P 计算 P 技术 P 本身 P 的 P 开发 P 之前 P 并非 P 如此。P 在 P 研究生 P 期间 P,P Jacob P 专注于 P 实现 P 有效 P 的 P 笔画式 P 手势 P 识别 P。P 然而 P,P 《Head of the Order》中 P 形状 P 的 P 实际 P 识别 P 是 P 基于 P 触控 P 的 P,P 这 P 导致 P 了 P Jacob P 在 P 构建 P 感知 P 界面 P 时 P 面 P 临 P 的 P 最大 P 挑战 P 之一 P:P 在 P 空 P 中 P 发生的 P 输入 P 没有 P 明确 P 的 P 开始 P 和 P 停止 P 事件。P 如 Jacob P 所 P 解释 P 的 P,“你 P 没有 P 触摸 P 动作 P 和 P 抬起 P 动作 P,P 就像 P 你 P 在 P 触摸屏 P 上 P 有 P 的 P 那样 P,P 甚至 P 鼠标 P 也 P 没有 P。”P 对于 P《Head of the Order》,P Jacob P 必须 P 开发 P 一种 P 能够 P 模仿 P 这些 P 事件 P 的 P 东西 P。
手部渲染系统
Jacob 创建了一个手部渲染系统,该系统可以重新 P 采样 P Intel® Perceptual Computing SDK P 提供的 P 低 P 分辨率 P 手部 P 图像 P,P 并 P 通过 P 自定义 P 后 P 处理 P 将 P 其 P 添加 P 到 P 游戏的 P 渲染 P 堆栈 P 中 P,P 并在 P 多个 P 深度 P 上 P 进行 P。P “这 P 允许 P 我 P 向 P 用户 P 显示 P 实时 P 图像 P 流 P,P 并 P 带有 P 各种 P 视觉 P 上 P 有吸引力 P 的 P 效果。”P 图 P 3.1 P 到 P 3.3 P—P 以及 P 下面 P 的 P 代码 P 示例 P—P 说明 P 了 P 手部 P 渲染 P 系统 P 的 P 工作 P 方式。(P 代码 P 示例 P 展示了 P 渲染器 P 和 P 其 P 所 P 依赖 P 的 P 手部 P 跟踪 P 类的 P 片段 P。)
代码示例 1。 HandRenderer 脚本实现了一个简单的透明漫反射的后处理着色器。此处仅显示了脚本的初始化和关键渲染部分。
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public class RealSenseCameraConnection: MonoBehaviour
{
private PXCUPipeline pp;
[HideInInspector]
public Texture2D handImage;
/*Each Label of the Intel PXCUPipeline Label map get one of these colors.
In practice I've found indexes to be
0 the background
1 primary hand
2 secondary hand
There can be more labels but what they correspond to is not listed in the Intel Docs. */
public Color32[] colors;
public PXCMGesture.GeoNode[][] handData;
public List<HandRenderer> Renderers;
private static RealSenseCameraConnection instance;
public static RealSenseCameraConnection Instance
{
get
{
if (instance == null)
{
instance = (RealSenseCameraConnection)FindObjectOfType(typeof(RealSenseCameraConnection));
if (instance == null)
instance = new GameObject ("RealSenseCameraConnection").AddComponent<RealSenseCameraConnection> ();
}
return instance;
}
}
void Awake()
{
if(Instance != null)
{
if(Instance != this)
{
Destroy(this);
}
else
{
DontDestroyOnLoad(gameObject);
}
}
}
void Start()
{
pp=new PXCUPipeline();
int[] size=new int[2]{320,240};
if (pp.Init(PXCUPipeline.Mode.GESTURE))
{
print("Conected to Camera");
}
else
{
print("No camera Detected");
}
handImage=new Texture2D(size[0],size[1],TextureFormat.ARGB32,false);
ZeroImage(handImage);
handData=new PXCMGesture.GeoNode[2][];
handData[0]=new PXCMGesture.GeoNode[9];
handData[1]=new PXCMGesture.GeoNode[9];
}
void OnDisable()
{
CloseCameraConnection();
}
void OnApplicationQuit()
{
CloseCameraConnection();
}
void CloseCameraConnection()
{
if (pp!=null)
{
pp.Close();
pp.Dispose();
}
}
void Update()
{
if (pp!=null)
{
if (pp.AcquireFrame(false))
{
ProcessHandImage();
pp.ReleaseFrame();
}
}
}
void ProcessHandImage()
{
if (QueryLabelMapAsColoredImage(handImage,colors))
{
handImage.Apply();
}
foreach(HandRenderer renderer in Renderers)
{
renderer.SetImage(handImage);
}
}
public bool QueryLabelMapAsColoredImage(Texture2D text2d, Color32[] colors)
{
if (text2d==null) return false;
byte[] labelmap=new byte[text2d.width*text2d.height];
int[] labels=new int[colors.Length];
if (!pp.QueryLabelMap(labelmap,labels)) return false;
Color32[] pixels=text2d.GetPixels32(0);
for (int i=0;i<text2d.width*text2d.height;i++)
{
bool colorSet = false;
for(int j = 0; j < colors.Length; j++)
{
if(labelmap[i] == labels[j])
{
pixels[i]=colors[j];
colorSet = true;
break;
}
}
if(!colorSet)
pixels[i]=new Color32(0,0,0,0);
}
text2d.SetPixels32 (pixels, 0);
return true;
}
public Vector3 PXCMWorldTOUnityWorld(PXCMPoint3DF32 v)
{
return new Vector3(-v.x,v.z,-v.y);
}
public Vector3 MapCoordinates(PXCMPoint3DF32 pos1)
{
Camera cam = Renderers[0].gameObject.GetComponent<Camera>();
return MapCoordinates(pos1,cam,Vector3.zero);
}
public Vector3 MapCoordinates(PXCMPoint3DF32 pos1, Camera cam, Vector3 offset)
{
Vector3 pos2=cam.ViewportToWorldPoint(new Vector3((float)(handImage.width-1-pos1.x)/handImage.width,
(float)(handImage.height-1-pos1.y)/handImage.height,0));
pos2.z=pos1.z;
pos2 += offset;
return pos2;
}
public Vector2 GetRawPoint(PXCMPoint3DF32 pos)
{
return new Vector2(pos.x,pos.y);
}
public Vector3 GetRawPoint3d(PXCMPoint3DF32 pos)
{
return new Vector3(pos.x,pos.y,pos.z);
}
private void ZeroImage(Texture2D image)
{
Color32[] pixels=image.GetPixels32(0);
for (int x=0;x<image.width*image.height;x++) pixels[x]=new Color32(0,0,0,0);
image.SetPixels32(pixels, 0);
image.Apply();
}
public static void AddRenderer(HandRenderer handrenderer)
{
Debug.Log("Added Hand Renderer");
Instance.Renderers.Add(handrenderer);
}
public static void RemoveRenderer(HandRenderer handrenderer)
{
if(Instance != null)
{
if(!Instance.Renderers.Remove(handrenderer))
{
Debug.LogError("A request to remove a handrender was called but the render was not found");
}
}
}
}
代码示例 2。此组件附着到摄像头上(需要 Unity Pro*)。HandRender 脚本使渲染出的手在各种分辨率下都保持锐利,并在游戏中大部分地方使用。
using UnityEngine;
using System.Collections;
public class HandRenderer : MonoBehaviour
{
public bool blur = true;
[HideInInspector]
public Texture2D handImage;
public Material HandMaterial;
public int iterations = 3;
public float blurSpread = 0.6f;
public Shader blurShader = null;
static Material m_Material = null;
public Vector2 textureScale = new Vector2(-1,-1);
public Vector2 textureOffset= new Vector2(1,1);
protected Material blurmaterial
{
get
{
if (m_Material == null)
{
m_Material = new Material(blurShader);
m_Material.hideFlags = HideFlags.DontSave;
}
return m_Material;
}
}
public void SetImage(Texture2D image)
{
handImage = image;
}
void OnDisable()
{
RealSenseCameraConnection.RemoveRenderer(this);
if(m_Material)
{
DestroyImmediate( m_Material );
}
}
void OnEnable()
{
RealSenseCameraConnection.AddRenderer(this);
}
private void ZeroImage(Texture2D image)
{
Color32[] pixels=image.GetPixels32(0);
for (int x=0;x<image.width*image.height;x++) pixels[x]=new Color32(255,255,255,128);
image.SetPixels32(pixels, 0);
image.Apply();
}
public void FourTapCone (RenderTexture source, RenderTexture dest, int iteration)
{
float off = 0.5f + iteration*blurSpread;
Graphics.BlitMultiTap (source, dest, blurmaterial,
new Vector2(-off, -off),
new Vector2(-off, off),
new Vector2( off, off),
new Vector2( off, -off));
}
void OnRenderImage (RenderTexture source, RenderTexture destination)
{
if(handImage != null)
{
HandMaterial.SetTextureScale("_MainTex", textureScale);
HandMaterial.SetTextureOffset("_MainTex", textureOffset);
if(blur)
{
RenderTexture buffer = RenderTexture.GetTemporary(handImage.width, handImage.height, 0);
RenderTexture buffer2 = RenderTexture.GetTemporary(handImage.width, handImage.height, 0);
Graphics.Blit(handImage, buffer,blurmaterial);
bool oddEven = true;
for(int i = 0; i < iterations; i++)
{
if( oddEven )
FourTapCone (buffer, buffer2, i);
else
FourTapCone (buffer2, buffer, i);
oddEven = !oddEven;
}
if( oddEven )
Graphics.Blit(buffer, destination,HandMaterial);
else
Graphics.Blit(buffer2, destination,HandMaterial);
RenderTexture.ReleaseTemporary(buffer);
RenderTexture.ReleaseTemporary(buffer2);
}
else
Graphics.Blit(handImage, destination,HandMaterial);
}
}
}
一旦手被识别为输入设备,它就会在屏幕上显示出来,一系列的手部动作现在控制着游戏。抬起任一手的一根手指开始输入,张开整个手停止输入,滑动手指绘制形状,特定的形状对应特定的法术。然而,Jacob 解释说,这些动作导致了另一个挑战。“我们很难让人们意识到需要三个步骤才能创建一个法术。”因此 Jacob 修改了该 P 应用 P,P 使 P 其 P 能够 P 考虑 P 手指 P 的 P 速度。“在 P 新 P 的 P 版本 P 中 P,P 如果 P 你 P 的 P[手指] P 移动 P 得 P 足够 P 快 P,P 就会 P 产生 P 轨迹 P;P 如果 P 你 P 移动 P 得 P 慢 P,P 就 P 没有 P 轨迹 P。”P 通过 P 这种 P 新 P 的 P、P 更 P 直观 P 的 P 方法(P 图 P 4),P 玩家 P 可以在 P 没有 P 指导 P 的 P 情况 P 下 P 完成 P 演示 P。
手势序列
除了他开发 P 的 P 感知 P 界面 P,P Jacob P 认为 P 《Head of the Order》最 P 大 P 的 P 创新 P 是 P 其 P 手势 P 序列 P,P 该 P 序列 P 允许 P 玩家 P 将 P 多个 P 法术 P 组合 P 成 P 超级 P 法术 P(P 在 P 游戏中 P 称为 P“P 连击 P 法术 P”P;P 参见 P 图 P 5)。“我 P 还没有 P 见过 P 很多 P 类似的 P 系统,”他说。“P 例如 P,P 大 P 部分 P 当前 P 的 P 传统 P 手势 P 都是 P 基于 P 姿势 P 的 P,P 它们 P 是 P 临时的 P 且 P 单一的。”P 换 P 句话 P 说 P,P 其他 P 人 P 编写 P 一个 P 手势 P,P 该 P 手势 P 由 P 一个 P、P 两个 P 手指 P 的 P 姿势 P 调用。“P 那 P 被 P 映射 P 到 P 一个 P 单一 P 的 P 动作 P;P 而 P 在 P《Head of the Order》中 P,P 将 P 手势 P 串联 P 起来 P 可以 P 具有 P 序列 P 的 P 意义,”Jacob P 补充道。P 例如 P,P 一个 P《Head of the Order》玩家 P 可以 P 创建 P 并 P 施放 P 一个 P 单个 P 火球 P(P 图 P 6),P 或者 P 将 P 其 P 与 P 第二个 P 火球 P 组合 P 以 P 制造 P 一个 P 更大 P 的 P 火球 P,P 或者 P 可能 P 是 P 一个 P 岩浆 P 球 P。
开发 P 组合 P 法术 P 的 P 能力 P 涉及 P 构想 P 法术 P 组合 P 的 P 可能 P 的 P 场景 P 并 P 为 P 每个 P 场景 P 单独 P 编程。“P 主要 P 的 P 游戏 P 交互 P 有一个 P 法术 P 施放 P 组件 P,它 P‘P 监听 P’P 所有 P 的 P 手部 P 跟踪 P 事件 P,P 并 P 执行 P 所有 P 的 P 更高 P 层 P、P 笔画式 P 手势 P 识别 P 和 P 通用 P 法术 P 施放 P 交互,”Jacob P 解释道。P 他 P 将 P 其 P 功能 P 比作 P 语言 P 解释器 P。“P 它 P 的 P 工作 P 方式 P 类似于 P 处理 P 编程 P 语言 P,P 但 P 它 P 不 P 查看 P 标记 P 或 P 计算机 P 语言 P 单词 P,P 而是 P 查看 P 单独 P 的 P 手势 P,P 破译 P 它们 P 的 P 语法 P,P 并 P 确定 P 它 P 是否 P 在 P 该 P 情况 P 下 P 被 P 允许 P。”
这 P 允许 P 许多 P 复杂 P 的 P 交互 P,P 并且 P 如果 P 所有 P 这些 P 场景 P 都 P 被 P 正确 P 处理 P,P 实际上 P 可以 P 工作。P 令人 P 惊讶 P 的 P 是 P,P 这 P 允许 P 玩家 P 将 P 手势 P 串联 P 起来 P,P 然后 P 系统 P 识别 P 它们 P 并 P 创建 P 新 P 的 P 手势 P。“P 一旦 P 你 P 能够 P 施放 P 一些 P 更长 P 的 P 法术 P,P 你 P 就会 P 感觉 P 像是 P 能够 P 施展 P 魔法 P。P 这 P 就是 P 我们 P 追求 P 的 P 感觉 P。”
创新与未来
Jacob 认为,鉴于他对感知计算的巨大贡献,感知计算有一天会像计算机本身一样普遍——甚至可能更甚。感知计算将有许多实际应用。“手势在‘脏手’场景中有多种显而易见的 P 应用。例如,当 P 人 P 在 P 烹饪 P,P 或 P 医生 P 在 P 进行 P 手术 P,P 或 P 任何 P 用户 P 需要 P 与 P 设备 P 交互 P 但 P 无法 P 实际 P 触摸 P 的 P 情况 P 下 P。”
在 Jacob 看来,未来将不像科幻小说,而更像巫术。“虽然我们确实认为手势是未来,但我厌倦了每个人都将未来视为《少数派报告》或《星际迷航》的观点,”Jacob 哀叹道。他希望在三到五年内,挥手就能控制家用电器。Jacob 认为,手势互动感觉上会更像《哈利·波特》而不是《钢铁侠》。“PlayStation* 或 Wii* 控制器本质上就像一根魔杖,我认为如果操作系统支持手势交互,将有可能带来有趣的用户体验。例如,能够用手势切换应用程序将是很棒的。我希望能够挥挥手就能控制一切。这就是梦想。这就是我们希望看到的未来。”
Intel® Perceptual Computing Technology 和 Intel® RealSense™ Technology
2013 年,《Head of the Order》是使用 Intel® Perceptual Computing 技术实现的、全新的、直观的自然用户界面开发的。在 2014 年的 CES 展会上,英特尔宣布推出 Intel® RealSense™ 技术,这是 Intel Perceptual Computing 的新名称和品牌。2014 年,您可以期待看到新的 Intel® RealSense™ SDK 以及配备嵌入式 Intel® RealSense™ 3D 摄像头的 Ultrabook™ 设备。
资源
在创建《Head of the Order》时,Jacob 和 Melissa 使用了 Unity、Editor Auto Save*、PlayModePersist* v2.1、Spline Editor*、TCParticles* v.1.0.9、HOTween* v1.1.780、2D Toolkit* v2.10、Ultimate FPS Camera* v1.4.3 和 nHyperGlyph* v1.5(Unicorn Forrest 的自定义笔画式手势识别器)。
1 这是 Intel® Perceptual Computing SDK 2013 版的一个已知问题。2014 版发布时将区分左右手。
Intel® Developer Zone 提供跨平台应用开发工具和操作指南、平台和技术信息、代码示例以及同行专业知识,以帮助开发者创新和取得成功。加入我们的社区,了解 物联网、Android*、Intel® RealSense™ Technology 和 Windows*,下载工具,访问开发套件,与志同道合的开发者交流想法,并参与黑客松、竞赛、路演和本地活动。