ã¯ããã«
VR åã Leap Motion ã¢ã»ããã«åã³ç¥ã¢ãããã¼ãããã¾ããã
- Unity Core Assets 2.3.0 + ImageHands - Development - Leap Motion (Ultraleap) Community Forums
- https://developer.leapmotion.com/gallery/category/image-hand
ããã¤ãã¢ãããã¼ããããä¸ã§ç®çã¯ãImage Handãã¨ããæ©è½ã§ãå¾æ¥ã¯ 3D ã®ã¢ãã«ãèªèããæã®å½¢ç¶ã«åããã¦åããã¦ããã®ã«å¯¾ããã«ã¡ã©ã§åå¾ããå®éã®æã®é åãç´æ¥æç»ããã¢ã¼ãã追å ããã¾ãããå¾æ¥åæ§å½ããå¤å®ãå¹ãä¸ã«ãªã¯ã«ã¼ã¸ã§ã³ï¼VR å ã® 3D ãªãã¸ã§ã¯ãã®å¾ãã«åããããããªè¡¨ç¾ï¼ãåç¾ããã¦ãã¾ãã
æ¬ã¨ã³ããªã§ã¯ãImage Hand ã«ç¦ç¹ãå½ã¦ãªãããååï¼VR の世界に手を持ち込める Leap Motion VR の仕組みを調べてみた - 凹みTipsï¼ã¨ã®å·®åãªã©ã«ã¤ãã¦è§£èª¬ãããã¨æãã¾ãã
ç°å¢
- Windows 8.1
- OVRSDK 0.5.0.1
- Leap Motion 2.2.5+26752
- Unity 5.1.0f1
ç¾å¨ãOVRSDK 0.6.0beta ã¸å¯¾å¿ä¸ã¨ã®ãã¨ã§ãï¼åããã©ããã¯è©¦ãã¦ãªãã§ãï¼ã
ãã¢
å ¬å¼ã® 500 Blocks ãã¢ãåããã¦ã¿ã¾ããã
ãã¢ã§éã¶
å ¬å¼ã®ãã¢ãã¢ãããã¼ãããã¦ããã®ã§ãã¦ã³ãã¼ãããã°ç´æ¥éã¶ãã¨ãåºæ¥ã¾ããåç°å¢ãã¢ãããã¼ããã¦ããã¾ãããã
- VR – Ultraleap Gallery
- Widgets | Leap Motion Developers
- https://developer.leapmotion.com/gallery/image-hand-moving-demo
- https://developer.leapmotion.com/gallery/500-blocks
Unity ã§è§¦ã£ã¦ã¿ã
以ä¸ã®ãªã³ã¯ãã Unity Core Asset v2.3.0 ããã¦ã³ãã¼ãã§ãã¾ãã
LeapMotionCoreAssets_2_3_0.unitypackage
ãã¤ã³ãã¼ãããã¨ä»¥ä¸ã®æ§ãªé層ã«ãµã³ãã«ã·ã¼ã³ãå«ã¾ãã¦ãã¾ãã
å®è¡ããã¨å®éã«éã¶ãã¨ãåºæ¥ã¾ãã
ã¢ãããã¼ãå 容
詳細ã¯å¾è¿°ãã¾ãããã¢ãããã¼ãå 容ã«ã¤ãã¦ãã£ã¨è¦ã¦ã¿ã¾ãã
Enhanced passthrough experience
- Unity ã§ã®ãã¹ã¹ã«ã¼ç»ã®è¡¨ç¤ºã 2 msec åæ¸
Update()
ã¿ã¤ãã³ã°ããOnPreRender()
ã¿ã¤ãã³ã°ã«ãããã¨ï¼
- ãã¹ã¹ã«ã¼ç»è¡¨ç¤ºã¢ã»ããå©ç¨æã® Oculus Rift ã® IPD è£æ£ã®èªåå
- Leap Motion ã®ã«ã¡ã©ã¨ Oculus ã® Configuration Utility ã§è¨å®ãã IPD ã¨ã®è£æ£ãèªåã§è¡ã
Image Hands
- åè¿°ã®ããã«ãªã¢ã«ã®æãæã¡è¾¼ãã§ã¤ã³ã¿ã©ã¯ã·ã§ã³åºæ¥ãããã«ãªã£ã
Other updates and bug fixes
- æ°ãããã¹ã¹ã«ã¼ç»è¡¨ç¤ºã®ä»çµã¿
- âã®å 容ã¨ä½µãã¦å¾è¿°ãã¾ã
- ãã®ä»ãã°ãã£ãã¯ã¹ãããã
ãããã«ã¤ãã¦ãã詳ããè¦ã¦ããã¾ãã
Image Hand ã®è¡¨ç¤ºã®ä»çµã¿
ã¯ããã«ä¸çªé¢ç½ããImage Hand ã¯ã©ããã£ã¦åãã¦ããããã«ã¤ãã¦è¦ã¦ããã¾ãã
ãªã¯ã«ã¼ã¸ã§ã³è¡¨ç¾
ã¾ããªã¯ã«ã¼ã¸ã§ã³ã®è¡¨ç¾ã«ã¤ãã¦ã§ããã¯ããã«åã®éã人ã¯ããä¸æã§ä»çµã¿ãåããããããã¾ããã
ã©ãã§ãããï¼ä½ã 3D ã¢ãã«ã®è ã®ã¨ããã«ã«ã¡ã©ã§æ®ã£ãæãæ ã£ã¦ããã®ãè¦ã¦åããã¨æãã¾ããå®ã¯èæ¯ã¨è ã«ã¹ã¯ãªã¼ã³ã¹ãã¼ã¹ã§ Leap Motion ã®ã«ã¡ã©ç»ãé©ç¨ãã¦ãã¾ãï¼ã¢ãã«ã® UV æ å ±ã«é¢ä¿ãªãã¹ã¯ãªã¼ã³åº§æ¨ã§ãã¯ã¹ãã£ãè²¼ãä»ãã¦ããï¼ãããã«ãã£ã¦ã«ã¡ã©ããè¦ãã¨æã®ã¢ãã«ã¯å®å ¨ã«éãã¦è¦ãããããªå½¢ã«ãªãã¾ããããããã®æã® 3D ã¢ãã«ã ãä»ã®ãªãã¸ã§ã¯ãã¨ã®åå¾é¢ä¿ãèæ ®ããããã«ã¬ã³ããªã³ã°ãããã¨ã§ãä»ã®ãªãã¸ã§ã¯ãã®å¥¥ã«ããå ´åã¯ä»ã®ãªãã¸ã§ã¯ãããåã«ããå ´åã«ã¯ãã®ã¹ã¯ãªã¼ã³ã¹ãã¼ã¹ã®ãã¯ã¹ãã£ãè²¼ãä»ããæã表示ãããã¨ãã§ãããªã¯ã«ã¼ã¸ã§ã³ãåç¾ã§ããããã§ãã
ãã®æã®ã¢ãã«ã¯å®éã®æãããå°ãåãã«ãªã£ã¦ãã¾ãããªã®ã§ Leap Motion ã®èªèãã¡ãã£ã¨é ãã¦æãã¤ãã¦ãããããã¦ããããã¦ãã大ããªç ´ç¶»ãªããã®ã¾ã¾ã®æãéããã¦è¦ããã¨ãåºæ¥ã¾ãï¼å¾è¿°ãã¾ããæ¡ä»¶ã«ãã£ã¦ã¯ã¯ã¿åºãã¦è¦ããæãããã¾ãï¼ã
æã®ç¸ãå ã表ç¾
ã§ã¯ãã®æã®ç¸ãè¯ãæãã«å ã表ç¾ã¯ã©ããã£ã¦ããã®ã§ãããããå ã£ã¦ããç»åã¨å ã£ã¦ããªãç»åã並ã¹ã¦ã¿ã¾ãã
...ããã§ãããå¯ãã®éãããæãã®ç°è²ãããæãã®éè²ã«ã¹ã¬ãã·ã§ã«ãããã©ã¡ã¿èª¿æ´ãã¦å¤ãã¦ããã ãã§ãããªã®ã§èµ¤å¤ç·ãããåå°ããç©ä½ãå¾ãã«ããããããã¹ã¬ãã·ã§ã«ãã¨ãã¶ãè¼åº¦ããã¦ããã¨ããããéãå ã£ã¦ãã¾ãããã§ããã
åºæ¬ã¯ããã ããªã®ã§ãããããä¸ã¤ãã¤ã³ã¿ã©ã¯ã·ã§ã³ããç©ä½ã¨ã®å¢çã§ã°ãã¼ãã表ç¾ãå ãããã¦ãã¾ãã
ããã¯ã«ã¡ã©ã®æ·±åº¦ãã¯ã¹ãã£ãå©ç¨ãã¦è¡¨ç¾ãã¦ãã¾ãã詳ããã¯ã³ã¼ããè¦ã¦ã¿ã¾ãããã
ã·ã§ã¼ãã®ã³ã¼ãã§è¦ã¦ã¿ã
ç°¡æåããã³ã¼ããç°¡åãªèª¬æä»ãã§ä»¥ä¸ã«ç¤ºãã¾ãã
Shader "LeapMotion/Passthrough/ImageHandHighlight" { ... CGINCLUDE #define USE_DEPTH_TEXTURE frag_in vert(appdata v) { frag_in o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal); o.vertex.xy += TransformViewToProjection(norm.xy) * _Extrude; o.screenPos = ComputeScreenPos(o.vertex); #ifdef USE_DEPTH_TEXTURE o.projPos = o.screenPos; // COMPUTE_EYEDEPTH ã¯é ç¹ã®è¦ç¹ç©ºéããã¹ãè¨ç®ãã¦åºåãã COMPUTE_EYEDEPTH(o.projPos.z); #endif return o; } float4 trackingGlow(float4 screenPos) { // Leap ã§åå¾ããç»ã®è¼åº¦æ å ±ãåå¾ float4 leapRawColor = LeapRawColorBrightness(screenPos); // ã¹ã¬ãã·ã§ã«ãã§ã¯ãªããã³ã° clip(leapRawColor.a - _MinThreshold); // ãªãã¢ã«ãã float3 leapLinearColor = pow(leapRawColor.rgb, _LeapGammaCorrectionExponent); // ããæãã®ã°ã¬ã¼é¨åï¼= æã®é åï¼ã ãæ½åº float brightness = smoothstep(_MinThreshold, _MaxThreshold, leapRawColor.a) * _Fade; // ããæãã®ã°ã¬ã¼é¨åï¼= æã®ç¸ï¼ã ãæ½åº float glow = smoothstep(_GlowThreshold, _MinThreshold, leapRawColor.a) * brightness; // ç¸ãæå®ããè²ã§å ããã float4 linearColor = pow(_Color, _ColorSpaceGamma) * glow * _GlowPower; // æ··ãã return float4(leapLinearColor + linearColor, brightness); } #ifdef USE_DEPTH_TEXTURE float4 intersectionGlow(float4 handGlow, float4 projPos) { // ã«ã¡ã©æ·±åº¦ãã¯ã¹ãã£ãã該å½ãã¯ã»ã«ã® z 座æ¨ãåå¾ float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(projPos))); // COMPUTE_EYEDEPTH ã§åå¾ããé ç¹ã®è¦ç¹ç©ºéã§ã® z 座æ¨ãåå¾ float partZ = projPos.z; // å·®ãè¯ãæãã«ã¹ã¬ãã·ã§ã«ããè¨ãã¦ã¹ã ã¼ã¸ã³ã° // _Intersection ~ 0 ä»è¿ã®ç´°ãã段差é¨åãå ã float diff = smoothstep(_Intersection, 0, sceneZ - partZ); // ãªãã¢ã«ããã¨åæã«è¼åº¦ã®å¼·ãããã©ã¡ã¿èª¿æ´ float4 linearColor = pow(_Color, _ColorSpaceGamma) * _IntersectionEffectBrightness; return float4(lerp(handGlow.rgb, linearColor.rgb, diff), handGlow.a * (1 - diff)); } #endif // ã°ãã¼é¨åãæã float4 frag(frag_in i) : COLOR { // ç¸ãå ããã float4 handGlow = trackingGlow(i.screenPos); #ifdef USE_DEPTH_TEXTURE // 3D ãªãã¸ã§ã¯ãã¨ã®å¢çãå ããã handGlow = intersectionGlow(handGlow, i.projPos); #endif return float4(handGlow.rgb, _Fade * handGlow.a); } // Leap Motion ã®ç»ã§ã¯ãªããã³ã°ããã float4 alphaFrag(frag_in i) : COLOR { // å¾ã§ ColorMask 0 ã§åãæã float4 leapRawColor = LeapRawColorBrightness(i.screenPos); clip(leapRawColor.a - _MinThreshold); return float4(0,0,0,0); } ENDCG SubShader { Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="Transparent"} Blend SrcAlpha OneMinusSrcAlpha // 1-pass ç®ã¯ã¯ãªããã³ã°ãè¡ã Pass { ZWrite On ColorMask 0 CGPROGRAM #pragma vertex vert #pragma fragment alphaFrag ENDCG } // 2-pass ç®ã¯æã®ç»åãéãå ãã°ãã¼é¨åãæã Pass{ ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } Fallback "Unlit/Texture" }
ãã¼ããã¨ã¦ããããããã§ããã
追è¨ï¼2015/05/27ï¼
ãã¿ã¾ãããæ©ã¨ã¡ããã¦ãã¾ããã®ã§ä¸è¨èª¬æãä¿®æ£ãã¾ãããå ·ä½çã«ã¯ã1-pass ç®ã«é常ã®ç»ãæã㦠2-pass ç®ã§ã°ãã¼ãæãã¦ããã¨æã£ã¦ããã®ã§ããã1-pass ç®ã¯ã¯ãªããã³ã°ãè¡ãã2-pass ç®ã«ã°ãã¼ãå«ãæå ¨ä½ã®ç»åãæç»ãã¦ãã¾ããã
ãã¹ã¹ã«ã¼ç»ã®é«éå
æ¦è¦
次ã«ãã¹ã¹ã«ã¼ç»ã®é«éåã«ã¤ãã¦è¦ã¦ã¿ã¾ããåºæ¬çãªæ¦ç¥ã¨ãã¦ã¯ã以å㯠MonoBehaviour.Update()
ã®ã¿ã¤ãã³ã°ã§ç»ãåå¾ãã¦ããã®ã«å¯¾ããä»åãã SyncMode
ã LOW_LATENCY
ã®å ´åã¯ãæç»ããç´åã®ã¿ã¤ãã³ã°ã§å¼ã°ãã MonoBehaviour.OnPreRender()
ã®ã¿ã¤ãã³ã°ã§ç»ãåã£ã¦ããããã«ãªã£ãç¹ã ã¨èãã¦ãã¾ããLeapOVRCameraRig
ã® LeftEyeAnchor
ãRightEyeAnchor
ã«ã¢ã¿ããããã LeapImageRetriever.cs
å
ã§æ¬¡ã®ãããªã³ã¼ããè¨è¼ããã¦ãã¾ãã
public enum SYNC_MODE { SYNC_WITH_HANDS, LOW_LATENCY } public SYNC_MODE syncMode = SYNC_MODE.LOW_LATENCY; void Update() { ... if (syncMode == SYNC_MODE.SYNC_WITH_HANDS) { _imageList = frame.Images; } } void OnPreRender() { if (syncMode == SYNC_MODE.LOW_LATENCY) { _imageList = _controller.Images; } ... }
詳細
ããã«ä¼´ããè¥å¹²æç»æ¹æ³ãè¤éã«ãªã£ã¦ãã¾ãã
ã¾ããã«ã¡ã©ç»ã表示ãããã¬ã¼ã³ã¯ CenterEyeAnchor
ã® Quad
ãæ
å½ãã¾ãã
ããã«ã¯ LeapImageBasedMaterial.cs
ã¨ããã¹ã¯ãªãããã¢ã¿ããããã¦ãããã©ã¡ãã®ã«ã¡ã©ã®ç»åãå©ç¨ãããã¨ã·ã§ã¼ãã®ãã©ã¡ã¿ã®è¨å®ããã㦠Leap Motion ããç»ãåã£ã¦ãã LeapImageRetriever.cs
ã¸ç»é²ãè¡ã£ã¦ãã¾ãï¼static çµç±ï¼ã
public class LeapImageBasedMaterial : MonoBehaviour { public enum ImageMode { STEREO, LEFT_ONLY, RIGHT_ONLY } public ImageMode imageMode = ImageMode.STEREO; void Awake() { ... } void OnEnable() { LeapImageRetriever.registerImageBasedMaterial(this); ... (ã·ã§ã¼ãã®è¨å®ï¼ } void OnDisable() { LeapImageRetriever.unregisterImageBasedMaterial(this); } }
LeapImageRetriever.cs
ã¯å·¦å³ã®ã«ã¡ã©ã«åãä»ãããã¦ãã¦ããã®ï¼æã® Quad ãå
±æããããããã® OnPreRender()
ã¿ã¤ãã³ã°ã§ãã¯ã¹ãã£ãæ¸ãæãã¬ã³ããªã³ã°ãè¡ãã¾ãã
public class LeapImageRetriever : MonoBehaviour { public enum EYE { LEFT = 0, RIGHT = 1 } public EYE eye = (EYE)(-1); private static List<LeapImageBasedMaterial> _registeredImageBasedMaterials = new List<LeapImageBasedMaterial>(); public static void registerImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial) { _registeredImageBasedMaterials.Add(imageBasedMaterial); ... } public static void unregisterImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial) { _registeredImageBasedMaterials.Remove(imageBasedMaterial); } void OnPreRender() { if (syncMode == SYNC_MODE.LOW_LATENCY) { _imageList = _controller.Images; } // ããããã®ã«ã¡ã©ã«å¿ãããã¯ã¹ãã£ãè¨å® Image referenceImage = _imageList[(int)eye]; ... loadMainTexture(referenceImage); ... foreach (LeapImageBasedMaterial material in _registeredImageBasedMaterials) { if (material.imageMode == LeapImageBasedMaterial.ImageMode.STEREO || (material.imageMode == LeapImageBasedMaterial.ImageMode.LEFT_ONLY && eye == EYE.LEFT) || (material.imageMode == LeapImageBasedMaterial.ImageMode.RIGHT_ONLY && eye == EYE.RIGHT)) { updateImageBasedMaterial(material, ref referenceImage); } } } private void loadMainTexture(Image sourceImage) { Marshal.Copy(sourceImage.DataPointer(), _mainTextureData, 0, _mainTextureData.Length); _mainTexture.LoadRawTextureData(_mainTextureData); _mainTexture.Apply(); } private void updateImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial, ref Image image) { imageBasedMaterial.GetComponent<Renderer>().material.SetTexture("_LeapTexture", _mainTexture); ... } }
ãã¯ã¹ãã£ã®ã³ãã¼ã for æãåãã¦ããã®ã«å¯¾ã LoadRawTextureData()
ãå©ç¨ããããã«ãªã£ã¦ã¾ãããã±ã£ã¨è¦ã³ã¼ãã¯èªã¿ã¥ããã£ãã§ãããã£ã¦ããã¨ã¯ã¨ã¦ãåç´ãªã®ã§ãä»çµã¿ããããã°ã³ã¼ãã¯è¿½ããããã¨æãã¾ãã
IPD è£æ£ã®èªåå
IPD è£æ£ã«é¢ãã¦ã¯ãCenterEyeAnchor
ã«ã¢ã¿ããããã LeapCameraAlignment.cs
ãæ
å½ãã¦ãã¾ãã
ç°¡ç¥åããã³ã¼ãã以ä¸ã«ç¤ºãã¾ãã
public class LeapCameraAlignment : MonoBehaviour { void LateUpdate() { LeapDeviceInfo device = handController.GetDeviceInfo(); // æ¯ãã¬ã¼ã OVRSDK ã§ã»ããããã Oculus Configuration Utility // ã§è¨å®ãã IPD ã®å¤ã Unity ã®ä¸çã§ã®ã«ã¡ã©éè·é¢ã«è¨å®ããã // ï¼Execution Order ã«æ³¨æããå¿ è¦ãããï¼ var oculusIPD = rightEye.position - leftEye.position; // Leap Motion ã®ã«ã¡ã©éè·é¢ã¨ Oculus Rift ã® IPD ã®å·®ã®åå // device.baseline: ï¼ã¤ã®ã«ã¡ã©éè·é¢ // tween: Quick Switch Demo ã®ç¨ã«åçã« Leap ã®ä¸çã¨åãæ¿ããæç¨ Vector3 addIPD = 0.5f * oculusIPD.normalized * (device.baseline - oculusIPD.magnitude) * tween; // Leap Motion ã®æ®åç´ åã¾ã§ã® z æ¹åãªãã»ãã Vector3 toDevice = centerEye.forward * device.focalPlaneOffset * tween; // ã«ã¡ã©éè·é¢ã Leap ã«ä½µãã¦ã㤠z æ¹åãªãã»ããã足ã leftEye.position = leftEye.position - addIPD + toDevice; rightEye.position = rightEye.position + addIPD + toDevice; centerEye.position = 0.5f * (leftEye.position + rightEye.position); } }
èªå㧠Leap ã®ä¸çã® IPD ã«ãªã£ã¦ã¾ããLeap ã®ã«ã¡ã©éè·é¢ã¯ã¡ãã£ã¨çãæ°ãããã®ã§ã次æãã¼ã¸ã§ã³ã§ã¯ããå°ãåºããããããªãããªãã¨æã£ã¦ã¾ããã©ããªã®ã§ãããã
ãããã«
ã¾ãä¸æ®µé Leap Motion ãé²åããæ°ããã¾ããã¹ã¯ãªãã / ã·ã§ã¼ãã®éæã« RGB ç»ã¸ã®ã¢ã¯ã»ã¹ã«ã¤ãã¦ã®ã³ã¼ããä»è¾¼ã¾ãã¦ããã®ã§ãããããå ¬å¼ã§ã¯æ¬¡æãã¼ã¸ã§ã³ã§ãã Dragonfly ã®éçºãæ¢ã«å§ã¾ã£ã¦ããã®ã§ã¯ãªããã¨æ³åã§ãã¾ããä»å¾ã®å±éãã¨ã¦ã楽ãã¿ã§ããã