实现效果:
Kinect1和Kinect2的关键骨骼位置信息:
Unity版本:5.3.5
项目包下载地址:点击打开链接
关键原理:
1.利用得到的Kinect的Body信息得到所有骨骼信息
2.通过骨骼信息得到对应点骨骼的旋转信息
3.将旋转信息用到本地的模型骨骼上
4.每个骨骼点的方向向量的计算
主要知识点:
要理解坐标系中Vector3和Quaternion之间的关系,世界坐标和相对坐标的相互转换
主要脚本:
KinectBodyControl用于获得Kinect获得的身体信息并创建模型
KinectBodyControlLogic用于控制模型骨骼的更新
CamerColorView用于UGUI显示Kinect摄像头内容
using UnityEngine;
using System.Collections;
using Windows.Kinect;
using System.Collections.Generic;
///
/// 体感游戏对象控制器
///
public class KinectBodyControl : MonoBehaviour
{
//Kinect对象
private KinectSensor _kinectSensor;
//身体信息读取流
private BodyFrameReader _bodyReader;
//从Kinect得到的身体信息 每帧获取
private Body[] _bodyDatas = null;
//场景中的体感游戏对象
private Dictionary _bodies = new Dictionary();
//当前追踪到的游戏对象
private List _nowTrackedIDs = new List();
//用于缓存已知的追踪到的游戏对象
private List _nowCreatedBodyID = new List();
void Start()
{
//得到kinect设备对象
_kinectSensor = KinectSensor.GetDefault();
if (_kinectSensor != null)
{
//得到身体数据流
_bodyReader = _kinectSensor.BodyFrameSource.OpenReader();
//设备是否开启
if (!_kinectSensor.IsOpen)
_kinectSensor.Open();
}
}
void Update()
{
//通过Kinect得到身体信息
GetBodyInfos();
//得到身体信息后 更新场景中的身体对象
UpdateBodyObject();
}
///
/// 得到身体信息
///
private void GetBodyInfos()
{
if (_bodyReader != null)
{
BodyFrame frame = _bodyReader.AcquireLatestFrame();
if (frame != null)
{
//初始化身体数组信息
if (_bodyDatas == null)
_bodyDatas = new Body[_kinectSensor.BodyFrameSource.BodyCount];
//得到该帧的身体信息
frame.GetAndRefreshBodyData(_bodyDatas);
//清楚该帧
frame.Dispose();
frame = null;
}
}
}
///
/// 根据身体信息更新场景上的对象信息 创建 骨骼位置更新等
///
private void UpdateBodyObject()
{
//判断是否得到了身体信息
if (_bodyDatas == null)
return;
_nowTrackedIDs.Clear();
//记录当前追踪到的对象
foreach (Body body in _bodyDatas)
{
if (body == null)
continue;
if (body.IsTracked)
_nowTrackedIDs.Add(body.TrackingId);//trackingid是追踪到对象的唯一ID
}
//记录当前已经有的身体对象
_nowCreatedBodyID = new List(_bodies.Keys);
//遍历场景中已经创建的体感游戏对象 看看 是否当前已经不再被追踪了 动态的删除它
foreach (ulong trackingID in _nowCreatedBodyID)
{
//当前kinect已经没有再扫描到对应对象了 那么在场景上删除它 并清楚记录
if (!_nowTrackedIDs.Contains(trackingID))
{
Destroy(_bodies[trackingID].gameObject);
_bodies.Remove(trackingID);
}
}
//现在开始更新游戏对象信息 或者动态创建游戏对象
foreach (Body body in _bodyDatas)
{
if (body == null)
continue;
//身体信息是否被追踪
if (body.IsTracked)
{
//场景上没有对应游戏对象 创建对象
if (!_bodies.ContainsKey(body.TrackingId))
CreateBodyObject(body.TrackingId);
//更新对象的位置信息
_bodies[body.TrackingId].RefreshBodyInfo(body);
}
}
}
///
/// 动态 在场景上创建对象
///
///
private void CreateBodyObject(ulong id)
{
GameObject obj = Instantiate(Resources.Load("RoleObject/GUJIA")) as GameObject;
obj.name = "Body:" + id;
KinectBodyControlLogic info = obj.GetComponent();
_bodies.Add(id, info);
}
}
using UnityEngine;
using System.Collections;
using Windows.Kinect;
using System.Collections.Generic;
///
/// 身体控制逻辑
///
public class KinectBodyControlLogic : MonoBehaviour
{
///
/// 根骨骼
///
public GameObject SpineBase;
///
/// 腰部
///
public GameObject SpineMid;
///
/// 脖子
///
public GameObject Neck;
///
/// 头
///
public GameObject Head;
///
/// 左侧肩膀
///
public GameObject ShoulderLeft;
///
/// 左侧手肘
///
public GameObject ElbowLeft;
///
/// 左侧手腕
///
public GameObject WristLeft;
///
/// 左侧手掌
///
public GameObject HandLeft;
///
/// 右侧肩膀
///
public GameObject ShoulderRight;
///
/// 右侧手肘
///
public GameObject ElbowRight;
///
/// 右侧手腕
///
public GameObject WristRight;
///
/// 右侧手掌
///
public GameObject HandRight;
///
/// 左侧胯部
///
public GameObject HipLeft;
///
/// 左侧膝盖
///
public GameObject KneeLeft;
///
/// 左侧脚腕
///
public GameObject AnkleLeft;
///
/// 左侧脚掌
///
public GameObject FootLeft;
///
/// 右侧胯部
///
public GameObject HipRight;
///
/// 右侧膝盖
///
public GameObject KneeRight;
///
/// 右侧脚踝
///
public GameObject AnkleRight;
///
/// 右侧脚掌
///
public GameObject FootRight;
///
/// 肩部中央位置
///
public GameObject SpineShoulder;
///
/// 左侧指尖
///
public GameObject HandTipLeft;
///
/// 左侧拇指尖
///
public GameObject ThumbLeft;
///
/// 右侧指尖
///
public GameObject HandTipRight;
///
/// 右侧 侧拇指尖
///
public GameObject ThumbRight;
///
/// 是否是用Kinect镜像移动 为true 就像照镜子 为false就像背面控制模型
///
public bool IsMirror = true;
//方向向量配对
///
/// 根骨骼
///
public JointType SpineBaseDir = JointType.SpineMid;
///
/// 腰部
///
public JointType SpineMidDir = JointType.SpineShoulder;
///
/// 脖子
///
public JointType NeckDir = JointType.Head;
///
/// 头
///
public JointType HeadDir = JointType.Head;
///
/// 左侧肩膀
///
public JointType ShoulderLeftDir = JointType.ElbowLeft;
///
/// 左侧手肘
///
public JointType ElbowLeftDir = JointType.WristLeft;
///
/// 左侧手腕
///
public JointType WristLeftDir = JointType.HandLeft;
///
/// 左侧手掌
///
public JointType HandLeftDir = JointType.HandTipLeft;
///
/// 右侧肩膀
///
public JointType ShoulderRightDir = JointType.ElbowRight;
///
/// 右侧手肘
///
public JointType ElbowRightDir = JointType.WristRight;
///
/// 右侧手腕
///
public JointType WristRightDir = JointType.HandRight;
///
/// 右侧手掌
///
public JointType HandRightDir = JointType.HandTipRight;
///
/// 左侧胯部
///
public JointType HipLeftDir = JointType.KneeLeft;
///
/// 左侧膝盖
///
public JointType KneeLeftDir = JointType.AnkleLeft;
///
/// 左侧脚腕
///
public JointType AnkleLeftDir = JointType.FootLeft;
///
/// 左侧脚掌
///
public JointType FootLeftDir = JointType.FootLeft;
///
/// 右侧胯部
///
public JointType HipRightDir = JointType.KneeRight;
///
/// 右侧膝盖
///
public JointType KneeRightDir = JointType.AnkleRight;
///
/// 右侧脚踝
///
public JointType AnkleRightDir = JointType.FootRight;
///
/// 右侧脚掌
///
public JointType FootRightDir = JointType.FootRight;
///
/// 肩部中央位置
///
public JointType SpineShoulderDir = JointType.Neck;
///
/// 左侧指尖
///
public JointType HandTipLeftDir = JointType.HandTipLeft;
///
/// 左侧拇指尖
///
public JointType ThumbLeftDir = JointType.ThumbLeft;
///
/// 右侧指尖
///
public JointType HandTipRightDir = JointType.HandTipRight;
///
/// 右侧 侧拇指尖
///
public JointType ThumbRightDir = JointType.ThumbRight;
///
/// 骨骼信息字典 对应Kinect的骨骼类型
///
private Dictionary _boneObjects;
///
/// 骨骼方向向量计算表示 用于骨骼方向向量计算
///
private Dictionary _boneDirTypes;
//模型骨骼默认方向向量
private Dictionary _bonesDir = new Dictionary();
//模型骨骼默认旋转四元数信息
private Dictionary _bonesRotation = new Dictionary();
//根骨骼的方向向量 之后用于判断 人物朝向
private Vector3 _rootDir;
//上半身腰部骨骼的方向向量 用于之后判断 上半身朝向
private Vector3 _upperBodyDir;
//是否已经初始化
private bool _isInit = false;
void Awake()
{
//初始化骨骼信息
InitBoneInfo();
//初始化方向
InitVector();
}
///
/// 初始化骨骼字典信息
///
private void InitBoneInfo()
{
//初始化骨骼对象字典
if(IsMirror)
{
_boneObjects = new Dictionary()
{
{ JointType.SpineBase, SpineBase},
{ JointType.SpineMid, SpineMid},
{ JointType.Neck, Neck},
{ JointType.Head, Head},
{ JointType.ShoulderLeft, ShoulderRight},
{ JointType.ElbowLeft, ElbowRight},
{ JointType.WristLeft, WristRight},
{ JointType.HandLeft, HandRight},
{ JointType.ShoulderRight, ShoulderLeft},
{ JointType.ElbowRight, ElbowLeft},
{ JointType.WristRight, WristLeft},
{ JointType.HandRight, HandLeft},
{ JointType.HipLeft, HipRight},
{ JointType.KneeLeft, KneeRight},
{ JointType.AnkleLeft, AnkleRight},
{ JointType.FootLeft, FootRight},
{ JointType.HipRight, HipLeft},
{ JointType.KneeRight, KneeLeft},
{ JointType.AnkleRight, AnkleLeft},
{ JointType.FootRight, FootLeft},
{ JointType.SpineShoulder, SpineShoulder},
{ JointType.HandTipLeft, HandTipRight},
{ JointType.ThumbLeft, ThumbRight},
{ JointType.HandTipRight, HandTipLeft},
{ JointType.ThumbRight, ThumbLeft},
};
}
else
{
_boneObjects = new Dictionary()
{
{ JointType.SpineBase, SpineBase},
{ JointType.SpineMid, SpineMid},
{ JointType.Neck, Neck},
{ JointType.Head, Head},
{ JointType.ShoulderLeft, ShoulderLeft},
{ JointType.ElbowLeft, ElbowLeft},
{ JointType.WristLeft, WristLeft},
{ JointType.HandLeft, HandLeft},
{ JointType.ShoulderRight, ShoulderRight},
{ JointType.ElbowRight, ElbowRight},
{ JointType.WristRight, WristRight},
{ JointType.HandRight, HandRight},
{ JointType.HipLeft, HipLeft},
{ JointType.KneeLeft, KneeLeft},
{ JointType.AnkleLeft, AnkleLeft},
{ JointType.FootLeft, FootLeft},
{ JointType.HipRight, HipRight},
{ JointType.KneeRight, KneeRight},
{ JointType.AnkleRight, AnkleRight},
{ JointType.FootRight, FootRight},
{ JointType.SpineShoulder, SpineShoulder},
{ JointType.HandTipLeft, HandTipLeft},
{ JointType.ThumbLeft, ThumbLeft},
{ JointType.HandTipRight, HandTipRight},
{ JointType.ThumbRight, ThumbRight},
};
}
//初始化 各骨骼的默认方向向量
_boneDirTypes = new Dictionary()
{
{ JointType.SpineBase, SpineBaseDir},
{ JointType.SpineMid, SpineMidDir},
{ JointType.Neck, NeckDir},
{ JointType.Head, HeadDir},
{ JointType.ShoulderLeft, ShoulderLeftDir},
{ JointType.ElbowLeft, ElbowLeftDir},
{ JointType.WristLeft, WristLeftDir},
{ JointType.HandLeft, HandLeftDir},
{ JointType.ShoulderRight, ShoulderRightDir},
{ JointType.ElbowRight, ElbowRightDir},
{ JointType.WristRight, WristRightDir},
{ JointType.HandRight, HandRightDir},
{ JointType.HipLeft, HipLeftDir},
{ JointType.KneeLeft, KneeLeftDir},
{ JointType.AnkleLeft, AnkleLeftDir},
{ JointType.FootLeft, FootLeftDir},
{ JointType.HipRight, HipRightDir},
{ JointType.KneeRight, KneeRightDir},
{ JointType.AnkleRight, AnkleRightDir},
{ JointType.FootRight, FootRightDir},
{ JointType.SpineShoulder, SpineShoulderDir},
{ JointType.HandTipLeft, HandTipLeftDir},
{ JointType.ThumbLeft, ThumbLeftDir},
{ JointType.HandTipRight, HandTipRightDir},
{ JointType.ThumbRight, ThumbRightDir},
};
}
///
/// 初始化方向向量等信息
///
private void InitVector()
{
//这六个骨骼用于控制模型朝向 和 上半身朝向
if( _boneObjects[JointType.HipRight] == null ||
_boneObjects[JointType.HipLeft] == null ||
_boneObjects[JointType.SpineBase] == null ||
_boneObjects[JointType.ShoulderRight] == null ||
_boneObjects[JointType.ShoulderLeft] == null ||
_boneObjects[JointType.SpineMid] == null)
{
Debug.LogError("关键骨骼为空");
}
//根骨骼的方向向量 为 右侧胯骨减去左侧胯骨
_rootDir = _boneObjects[JointType.HipRight].transform.position - _boneObjects[JointType.HipLeft].transform.position;
//转换为相对于根节点的相对坐标
_rootDir = _boneObjects[JointType.SpineBase].transform.InverseTransformDirection(_rootDir);
//上半身骨骼的方向向量 为 右侧肩部减去左侧肩部
_upperBodyDir = _boneObjects[JointType.ShoulderRight].transform.position - _boneObjects[JointType.ShoulderLeft].transform.position;
//转换为相对于腰部点的相对坐标
_upperBodyDir = _boneObjects[JointType.SpineMid].transform.InverseTransformDirection(_upperBodyDir);
GameObject boneObj = null;
foreach( JointType type in _boneObjects.Keys )
{
boneObj = _boneObjects[type];
if (boneObj == null)
continue;
//初始旋转信息
_bonesRotation.Add( type, boneObj.transform.localRotation );
//初始方向向量信息
_bonesDir.Add(type, _boneObjects[_boneDirTypes[type]].transform.position - _boneObjects[type].transform.position);
//世界坐标转换为局部坐标
_bonesDir[type] = boneObj.transform.InverseTransformDirection(_bonesDir[type]);
}
_isInit = true;
}
///
/// 跟新身体信息
///
///
public void RefreshBodyInfo(Body body)
{
if (!_isInit)
return;
//Kinect得到的Body骨骼信息
Windows.Kinect.Joint sourceJoint;
GameObject boneObj;
Vector3 boneDir;
Vector3 targetDir;
//更新模型的所有骨骼信息
foreach ( JointType jt in _boneObjects.Keys )
{
boneObj = _boneObjects[jt];
if (boneObj == null)
continue;
//得到Kinect的body信息中对应骨骼点信息
sourceJoint = body.Joints[jt];
//初始化角度
boneObj.transform.localRotation = _bonesRotation[jt];
//初始的方向向量
boneDir = _bonesDir[jt];
//当前kinect中对应骨骼的方向向量
targetDir = GetVector3FromJoint(body.Joints[_boneDirTypes[jt]]) - GetVector3FromJoint(sourceJoint);
//Kinect坐标转U3D世界坐标
targetDir = transform.TransformDirection(targetDir);
//从U3D世界坐标转相对坐标
targetDir = boneObj.transform.InverseTransformDirection(targetDir);
//得到相对角度变换
Quaternion nowQuat = Quaternion.FromToRotation(boneDir, targetDir);
//根骨骼的方向变化
if(jt == JointType.SpineBase)
{
//之前得到的胯骨平行方向向量
boneDir = _rootDir;
//得到当前kinect的肩部骨骼方向向量位置
targetDir = GetVector3FromJoint(body.Joints[JointType.HipRight]) - GetVector3FromJoint(body.Joints[JointType.HipLeft]);
//坐标转换 因为不希望在x轴上旋转 让角色平稳的站在地上 所以 让其在x轴上取投影
targetDir = Vector3.Project(targetDir, transform.right);
targetDir = transform.TransformDirection(targetDir);
targetDir = boneObj.transform.InverseTransformDirection(targetDir);
nowQuat *= Quaternion.FromToRotation(boneDir, targetDir);
}
//上半身骨骼的方向变化
else if (jt == JointType.SpineMid)
{
//上半身平行位置
boneDir = _upperBodyDir;
//得到当前kinect的肩部骨骼方向向量位置
targetDir = GetVector3FromJoint(body.Joints[JointType.ShoulderRight]) - GetVector3FromJoint(body.Joints[JointType.ShoulderLeft]);
//坐标转换
targetDir = transform.TransformDirection(targetDir);
targetDir = boneObj.transform.InverseTransformDirection(targetDir);
nowQuat *= Quaternion.FromToRotation(boneDir, targetDir);
}
//开始转
nowQuat = Quaternion.Lerp(Quaternion.identity, nowQuat, 1);
boneObj.transform.localRotation = boneObj.transform.localRotation * nowQuat;
//更新人物位置
if (jt == JointType.SpineBase)
this.transform.position = GetVector3FromJoint(sourceJoint);
}
}
///
/// 把Kinect中的骨骼关节点 转换为U3D中的Vector3
///
///
///
private Vector3 GetVector3FromJoint(Windows.Kinect.Joint joint)
{
return new Vector3(joint.Position.X, joint.Position.Y, IsMirror ? joint.Position.Z : -joint.Position.Z);
}
}
using UnityEngine;
using System.Collections;
using Windows.Kinect;
using UnityEngine.UI;
///
/// 摄像机信息 主要原理就是通过Kinect得到图像RGB数据 然后在Texture上绘制
/// 基本逻辑和获取身体信息相同
///
public class CamerColorView : MonoBehaviour
{
//Kinect对象
private KinectSensor _kinectSensor;
//颜色信息读取流
private ColorFrameReader _colorReader;
private Texture2D _textureInfo;
private byte[] _colorData;
private RawImage _cameraMaterial;
// Use this for initialization
void Start ()
{
_cameraMaterial = this.gameObject.GetComponent();
_cameraMaterial.uvRect = new Rect(0, 0, 1, -1);
_kinectSensor = KinectSensor.GetDefault();
if (_kinectSensor == null)
return;
_colorReader = _kinectSensor.ColorFrameSource.OpenReader();
//初始化图片信息
FrameDescription color = _kinectSensor.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Rgba);
_textureInfo = new Texture2D(color.Width, color.Height, TextureFormat.RGBA32, false);
_colorData = new byte[color.BytesPerPixel * color.LengthInPixels];
if (!_kinectSensor.IsOpen)
_kinectSensor.Open();
}
// Update is called once per frame
void Update ()
{
if (_colorReader == null)
return;
ColorFrame frame = _colorReader.AcquireLatestFrame();
if (frame == null)
return;
//存储rgb信息
frame.CopyConvertedFrameDataToArray(_colorData, ColorImageFormat.Rgba);
//texture加载信息
_textureInfo.LoadRawTextureData(_colorData);
//应用
_textureInfo.Apply();
frame.Dispose();
frame = null;
//设置显示信息
_cameraMaterial.texture = _textureInfo;
}
}