简易的“虚拟头盔”实现
头盔原理很简单,大致就是使用两个透镜,缩短眼睛的焦距,将两个眼睛看到的东西合二为一从而产生双屏3D效果。
于是开始设想:既然有了3D效果,为了达到更加沉浸的体验,为何不将头部的运动也映射到虚拟世界中去呢?抱着这个想法,我们做了如下尝试:
把头部数据(如旋转角、俯仰角等等)用陀螺仪接收传回电脑,为了简单起见快速出效果,直接手机,然后在手机端写了个app,负责使用wifi发送陀螺仪数据到pc,pc接收到数据之后,我写了个dll,共享内存的,不断更新那一块内存,宿主程序使用的时候,只需要call那块数据即可,方法非常简单。
安卓客户端我不懂,所以是我同学写的。我只说c++ dll的代码,为了方便起见我是用了一个简单sender来模拟陀螺仪发回的数据。
//sender #include <iostream> #include <Windows.h> #include <tchar.h> #include <ctime> using namespace std; struct DataStruct { float x; float y; float z; }; #define BUF_SIZE 1024 TCHAR szName[]=TEXT("Global\\MyFileMappingObject"); int main() { HANDLE hMapFile; float* pBuf; hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUF_SIZE, szName); if (hMapFile == NULL) { _tprintf(TEXT("Could not create file mapping object (%d).\n"), GetLastError()); return 1; } pBuf = (float*) MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE); if (pBuf == NULL) { _tprintf(TEXT("Could not map view of file (%d).\n"), GetLastError()); CloseHandle(hMapFile); return 1; } while(1) { float s[3] = {1.0f,33.2f,12.3f}; size_t a = sizeof(s); srand(clock()); *s = rand() / (double)(3.2); *(s+1) = rand() / (double)(3.2); *(s+2) = rand() / (double)(3.2); cout<<s[0]<<" "<<s[1]<<" "<<s[2]<<endl; memcpy((PVOID)pBuf, (void*)s, sizeof(s)); } }
接收dll代码
#include <windows.h> #include <string.h> #include <string> #include <iostream> #include <tchar.h> #include "recevedate.h" using namespace std; #define BUF_SIZE 1024 TCHAR szName[]=TEXT("Global\\MyFileMappingObject"); HANDLE hMapFile; float* pBuf; int dll_data_init() { hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE, // read/write access 0, // maximum object size (high-order DWORD) BUF_SIZE, // maximum object size (low-order DWORD) szName); // name of mapping object if (hMapFile == NULL) { _tprintf(TEXT("Could not create file mapping object (%d).\n"), GetLastError()); return -1; } pBuf = (float*) MapViewOfFile(hMapFile, // handle to map object FILE_MAP_ALL_ACCESS, // read/write permission 0, 0, BUF_SIZE); if (pBuf == NULL) { _tprintf(TEXT("Could not map view of file (%d).\n"), GetLastError()); CloseHandle(hMapFile); return -1; } return 0; } float get_x() { return pBuf[0]; } float get_y() { return pBuf[1]; } float get_z() { return pBuf[2]; } float get_dx() { return pBuf[3]; } float get_dy() { return pBuf[4]; } float get_dz() { return pBuf[5]; } void dll_data_ter() { UnmapViewOfFile(pBuf); CloseHandle(hMapFile); }
初始化调用int dll_data_init()函数,退出时调用void dll_data_ter(),中间直接获取指定的数据即可。
using UnityEngine; using System.Collections; using System; using System.Runtime.InteropServices; public class dll : MonoBehaviour { [DllImport("daterecive.dll")] public static extern int dll_data_init(); [DllImport("daterecive.dll")] public static extern void dll_data_ter (); [DllImport("daterecive.dll")] public static extern float get_x (); [DllImport("daterecive.dll")] public static extern float get_y (); [DllImport("daterecive.dll")] public static extern float get_z (); // Use this for initialization void Start () { dll_data_init (); } // Update is called once per frame void Update () { //getdata (); } void OnApplicationQuit() { dll_data_ter (); } }
初始化后这些函数都是可以调用的,修改一下摄像机代码或者其他代码即可。
在unity中处理了一个场景,做了一下实验:
左上角就是传回的数据,由于是模拟数据,所以不是很真实。
有了这个dll,这个系统可以轻松的拓展到很多游戏引擎中去,我在UE4中也做了相似的尝试。
至于显示,只要场景是双屏即可:
最后,整个装置就成型了:
比较简陋不要在意那些细节,看上去效果还是很不错的,但是不知道怎么展示……