文档章节

体感Kinect结合Unity3D引擎开发虚拟现实AR

地瓜儿
 地瓜儿
发布于 2015/03/06 16:08
字数 1780
阅读 8506
收藏 42

        2015年的第一场源创会,由 @爱吃鱼的猫大哥 做了一篇虚拟现实—我们能做的其实很多的猪蹄,主要介绍了增强现实框架-MetaioSDK开发包,我想大家对于这个主题肯定吃的还不够过瘾,那么今天就在来点猛料,满足大家欲望!由于公司业务需要,我也用这个sdk做过Android上一个虚拟现实app小例子,毕竟收费,有一些功能不付费体验不到,在加上后面跟上需要做一个大型户外体验的虚拟项目,综合考虑选择微软体感Kinect结合Unity3D引擎开发虚拟现实AR! @红薯 是不是后面考虑让我参加一期嘉宾演讲了,哈哈!废话不多扯,直接上干货!

       首先上好猪蹄和各种佐料:

       

        大概的流程:Kinect打开之后运行,unity通过绑定的脚本会首先获取RGB流和Depth流,以及骨骼数据(主要为识别手势和人多位置做准),其次通过脚本获取各种手势,以及位置,比如SwipeRight没右手挥动一次,就会切换一个例子场景直接与实时拍摄的场景完美融合(效果图稍后呈上),包括RaiseRightHand会出现流星撞地面,感觉像世界末日的感觉。最后就是同时利用unity的GuiTexture实时显示RGB流!说了那么多先上一张效果图感受下激情摧毁办公室:

     好了,材料准备好了,该看的都看了,接下来重点来了,就是放干货一步一步看看怎么做出来:

       第一步,也是最重要的一步,就是实现3D模型显示在平面2D图片上面,这个用unity真的很方便,首先需要添加一个摄像机Camera,并在摄像机Depth属性设置为-1,在MainCamera设置为0,说明背景层是最深,其他model所在的相机的Depth是0,这样确保3D模型在背景的前面!这里面有一点需要注意的就是一定要保证MainCamera和所有的模型的layer都在Defaule层!(在这里不得不说unity真的很方便)!

      在第一步基础上,第二步实现把Kinect获得RGB流显示出来,这个如果大家不会封装可以利用Kinect结合Unity3D引擎开发体感游戏(一) 里面的卡耐基梅隆的kinectWrapper.unitypackage,我这个就是在这个基础上修改的拓展了不少新功能,目前可以支持KinectSdk1.8版本!如果你比较熟悉unity,可以这样做:

   注:kinect封装脚本代码过多,这里接贴出一些关键点,不是很清楚,可以看卡耐基梅隆包,里面实现方法都差不多

void Update () 
{
KinectManager manager = KinectManager.Instance;//RGB流封装在这个脚本里面
		if(manager && manager.IsInitialized())
		{
		        //利用GetUsersClrTex()函数获得Texture2D的RGB流!
			this.renderer.material.mainTexture = manager.GetUsersClrTex();
			
/*******************在KinectManager脚本里面函数实现*********************/
 public Texture2D GetUsersClrTex()
    { 
	return usersClrTex;
    }
/*******************************************/

到了这一步,就可以把这个脚本直接绑定到Plane对象!这个时候RGB流就显示出来了!如果你和我一样用plane对象,有一个地方需要注意,就是它的属性的Tiling的x设置为-1(这里在扯一句废话,不清楚的可以先了解下unity怎么用,在来看可能更快)。

     第三步,在第二步基础上,绑定Tornado(龙卷风)模型,绑定这个模型是为了让Tornado跟着人一起平移运动,人道哪里,Tornado就跟随着!直接看代码:

     

void Update () 
	{
		KinectManager manager = KinectManager.Instance;
		if(manager && manager.IsInitialized())
		{
			this.renderer.material.mainTexture = manager.GetUsersClrTex();
		
			int iJointIndex = (int)TrackedJoint;
			
			if(manager.IsUserDetected())
			{
		    uint userId = manager.GetPlayer1ID();		
		    if(manager.IsJointTracked(userId, iJointIndex))
			{
			//获得需要绑定骨骼点的X,Y,Z坐标,绑定Tornado
		      Vector3 posJoint = manager.GetRawSkeletonJointPos(userId, iJointIndex);
		      Vector2 posDepth = manager.GetDepthMapPosForJointPos(posJoint);	
		      Vector2 posColor = manager.GetColorMapPosForDepthPos(posDepth);
					
		      float scaleX = (float)posColor.x / KinectWrapper.Constants.ImageWidth;
		float scaleY = 1.0f - (float)posColor.y / KinectWrapper.Constants.ImageHeight;				
                      Vector3 localPos = new Vector3(scaleX * 10f - 5f, 0f, scaleY * 10f - 5f);
		      Vector3 vPosOverlay = transform.TransformPoint(localPos);
					
                                        //保证Tornado一直是水平移动
					if(OverlayObject)
					{
						double num_y = -11.9;
						double num_z = -4.52;
						vPosOverlay.y = (float)num_y;
						vPosOverlay.z = (float)num_z;
						OverlayObject.transform.position = vPosOverlay;
					}

				}
				
			}
			
		}
	}

这里在扯一句废话,这个代码编辑,有很大的提高空间!

      第四步,这个让Tornado已经随人一起移动,接下来实现RaiseRightHand和SwipeRight切换粒子场景,直接上代码

//这个就是手势判定产生动作切换场景代码
if(slideChangeWithGestures && gestureListener)
			{
				if(gestureListener.IsSwipeLeft())
				{
					m_CurrentElementIndex++;
					m_CurrentParticleIndex = 0;
					ShowParticle();
					RotateToNext();
				}
				else if(gestureListener.IsSwipeRight())
				{
					m_CurrentElementIndex++;
					m_CurrentParticleIndex = 0;
					ShowParticle();
					RotateToPrevious();
				}
				else if(gestureListener.IsRaiseRightHand())
				{
					m_CurrentElementIndex = 6;;
					m_CurrentParticleIndex = 0;
					ShowParticle();
					RotateToPrevious();
				}
				else if(gestureListener.IsRaiseLeftHand())
				{
					m_CurrentElementIndex = 7;
					m_CurrentParticleIndex = 0;
					ShowParticle();
					RotateToPrevious();
				}
	}

那么里面的ShowParticle() 函数和  RotateToPrevious()函数的作用分别是:

ShowParticle() :主要切换显示不同场景粒子效果,比如Rain、Snow、Lighting等效果。上实现代码:

#region Functions
	void ShowParticle()
	{
		if(m_CurrentElementIndex == 6 && m_CurrentElementIndex == 7)
		{
			;
		}
		else if(m_CurrentElementIndex<0)
		{
			m_CurrentElementIndex = 5;
		}
		
		if(m_CurrentElementIndex==0)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListFire;
			m_ElementName = "FIRE";
			music.PlayOneShot(theSound);
			//music.Play(); 
		}
		else if(m_CurrentElementIndex==1)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListWater;
			m_ElementName = "WATER";
			music.PlayOneShot(theSound);
			//music.Play(); 
		}
		else if(m_CurrentElementIndex==2)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListWind;
			m_ElementName = "WIND";
			music.PlayOneShot(theSound);
			//music.Play(); 
		}
		else if(m_CurrentElementIndex==6)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListEarth;
			m_ElementName = "EARTH";
			music.PlayOneShot(theSound);
			//music.Play(); 
		}
		else if(m_CurrentElementIndex==3)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListThunder;
			m_ElementName = "THUNDER";
			//music.PlayOneShot(theSound);
			music.Play(); 
		}
		else if(m_CurrentElementIndex==4)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListIce;
			m_ElementName = "ICE";
			music.PlayOneShot(theSound);
			//music.Play(); 
		}
		else if(m_CurrentElementIndex==5)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListLight;
			m_ElementName = "LIGHT";
			music.PlayOneShot(theSound);
			//music.Play(); 
		}
		else if(m_CurrentElementIndex==7)
		{
			music.Stop();
			m_CurrentElementList = m_PrefabListDarkness;
			m_ElementName = "DARKNESS";
			music.PlayOneShot(theSound);
			//music.Play(); 
		}

		if(m_CurrentElementIndex == 5 || m_CurrentElementIndex == 6 || m_CurrentElementIndex == 7)
		{
			m_CurrentElementIndex = 0;
		}
		
		if(m_CurrentParticleIndex>=m_CurrentElementList.Length)
		{
			m_CurrentParticleIndex = 0;
		}
		else if(m_CurrentParticleIndex<0)
		{
			m_CurrentParticleIndex = m_CurrentElementList.Length-1;
		}
		
		m_ParticleName = m_CurrentElementList[m_CurrentParticleIndex].name;
		
		if(m_CurrentParticle!=null)
		{
			DestroyObject(m_CurrentParticle);
		}

		m_CurrentParticle = (GameObject) Instantiate(m_CurrentElementList[m_CurrentParticleIndex]);

		music.volume = musicVolume; 
	}
	#endregion {Functions}

RotateToPrevious():主要也是切换场景,不过不是粒子,是为了切换一个立方体,以提示切换场景成功信号(主要是考虑用户体验性)!还是上代码吧!

private void RotateToNext()
	{
		tex = (tex + 1) % maxTextures;
		
		if(!isBehindUser)
		{
			side = (side + 1) % maxSides;
		}
		else
		{
			if(side <= 0)
				side = maxSides - 1;
			else
				side -= 1;
		}

		if(horizontalSides[side] && horizontalSides[side].renderer)
		{
			horizontalSides[side].renderer.material.mainTexture = slideTextures[tex];
		}
		
		float yawRotation = !isBehindUser ? 360f / maxSides : -360f / maxSides;
		Vector3 rotateDegrees = new Vector3(0f, yawRotation, 0f);
		targetRotation *= Quaternion.Euler(rotateDegrees);
		isSpinning = true;
	}

然后将脚本绑定到camera或者自己需要的对象物体上,这样就可以控制切换场景了,至于手势判定,这个只需要你获得了骨骼数据了,是很好写出来手势判定的,这里就不赘述了!

     第五步,我们就看看效果吧,直接上图:

下图为:流星撞地球,激情毁灭办公室


下图为闪电效果:


下图为龙卷风效果:

下图为下雨效果:


真正的效果,可能截图感觉不是逼真,但是在程序跑的时候,感觉会好很多,图像不是很清晰主要一是kinect的分辨率只有640*480,二来室内还没开灯,有一张闪电图片就是开灯了,就会清楚多!


后期计划:加入高清摄像头,使2D画面更加清晰;

         添加更多音效;

         加入更多手势动作判断,更多互动使其更好玩。


这篇文章主要比较简单介绍了下虚拟现实,希望可以起到一个抛砖引玉的作用,欢迎指正,谢谢!



© 著作权归作者所有

共有 人打赏支持
地瓜儿

地瓜儿

粉丝 247
博文 7
码字总数 7837
作品 0
武汉
项目经理
私信 提问
加载中

评论(45)

我的天空等待
楼主,现在开发用的是什么SDK 啊,kinectWrapper.unitypackage已经不支持kinect2.0了
huof
huof
震撼!!!
地瓜儿
地瓜儿

引用来自“Treesun”的评论

需要神人指導~~2
Treesun
Treesun
需要神人指導~~2
地瓜儿
地瓜儿

引用来自“首席安全砖家”的评论

来道闪电,把呆逼猪 轰成渣
这个可以有… @铂金小猪
首席安全砖家
首席安全砖家
来道闪电,把呆逼猪 轰成渣
地瓜儿
地瓜儿

引用来自“我是一匹来自北方的狼”的评论

地瓜 ,求带41
地瓜儿
地瓜儿

引用来自“首席撸破皮”的评论

程序员果然再改变世界啊
我是一匹来自北方的狼
我是一匹来自北方的狼
地瓜 ,求带41
我不是九爷 带了解 Unity3D与VR虚拟现实

  对于大多数人来说,可能不知道Unity3D是什么,但是却知道VR虚拟现实是什么,更不会把VR虚拟现实和Unity3D联系在一起,外行的人根本不知道这两者之间有什么关系。那么,今天来给你讲解一下...

李伟铭k
2018/07/09
0
0
Kinect结合Unity3D引擎开发体感游戏(一)如果我想换个人物模型我该怎么做。

@地瓜儿 你好,想跟你请教个问题: Kinect结合Unity3D引擎开发体感游戏(一)如果我想换个人物模型我该怎么做。谢谢您!希望您看到了能回复下。

liusu111
2015/06/27
669
1
有關Kinect结合Unity3D引擎开发体感游戏(二)

@地瓜儿 你好,想跟你请教个问题: 依照您定義的我建立kinectMoving.cs Kinect结合Unity3D引擎开发体感游戏(二) 我建立一個terrain的scene, 拉入一個Hierarchy組件(如cubes, 內包含階層cub...

Treesun
2014/08/22
538
1
关于Kinect结合Unity3D引擎开发体感游戏(一)的问题

@地瓜儿 你好,想跟你请教个问题:我按照你写的 Kinect结合Unity3D引擎开发体感游戏(一) 的作法之后运行的时候左下角出现了个这DllNotFoundException: C:\Program Files (x86)\Microsoft ...

追忆小郭
2014/03/05
1K
1
Kinect结合Unity3D引擎开发体感游戏(一)

1.卡耐基梅隆的kinectWrapper.unitypackage; 2.OpenNI官方提供的OpenNIUnityToolkit-0.9.7.4.unitypackage(现在官方不提供更新与支持了,支持的版本unity3.4,在往上版本会出现很多问题,需...

地瓜儿
2013/09/16
0
34

没有更多内容

加载失败,请刷新页面

加载更多

租房软件隐私保护如同虚设

近日,苏州市民赵先生向江苏新闻广播新闻热线025-84658888反映,他在“安居客”手机应用软件上浏览二手房信息,并且使用该软件自动生成的虚拟号码向当地一家中介公司进行咨询。可电话刚挂不久...

linux-tao
36分钟前
1
0
分布式项目(五)iot-pgsql

书接上回,在Mapping server中,我们已经把数据都整理好了,现在利用postgresql存储历史数据。 iot-pgsql 构建iot-pgsql模块,这里我们写数据库为了性能考虑不在使用mybatis,换成spring jd...

lelinked
今天
2
0
一文分析java基础面试题中易出错考点

前言 这篇文章主要针对的是笔试题中出现的通过查看代码执行结果选择正确答案题材。 正式进入题目内容: 1、(单选题)下面代码的输出结果是什么? public class Base { private Strin...

一看就喷亏的小猿
今天
1
0
cocoapods 用法

cocoapods install pod install 更新本地已经install的仓库 更新所有的仓库 pod update --verbose --no-repo-update 更新制定的仓库 pod update ** --verbose --no-repo-update...

HOrange
今天
3
0
linux下socket编程实现一个服务器连接多个客户端

使用socekt通信一般步骤 1)服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()等待客户端连接。 2)客户端:socker()建立套接字,连接(connect)服务器,连接上后...

shzwork
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部