1.开发工具及SDK
项目演示效果:
人手不够就直接在Hololens上录了一个第一视角
2.原理:
Hololens及支持ARcore的手机均有各自的空间连续定位功能,我们要做的就是这两种设备能够统一空间坐标,这样就能准确的同步各自的操作。
最早的使用两台Hololens和一台相机的第三视角方案,原理是同步两台Holoens的锚点数据,这样就知道摄像机上面的那个Hololens的位置了,再通过Calibration软件通过获取Hololens及相机拍摄照片经过算法处理,生产一个校准文件,这个校准文件校准的就是Hololens和相机的位置角度偏移值、unity camera相机的fov,这样最后合成的视频就是正确的。
整个流程复杂,涉及C++语言和图像图形技术等,需要自己编译库,安装采集卡及驱动,有一个环节出问题就会运行不起来,就白忙活了,我之前折腾时就在相机这块踩坑了,先用的佳能,最后换了尼康的采可以,在相机这块官方也没有给出具体的适配型号...
总之,这一套第三视角确实不太好用,下面我们就开始Hololens&ARcore的第三视角折腾之旅吧~
3.定位同步
3.1网络:
本案例使用的是异步SocketAsyncEventArgs,支持手机及Hololens平台
关键代码:
private Socket client;
private IPEndPoint hostEndPoint;
private List<byte> buffer;
private AutoResetEvent connetEvent;
public delegate void OnConnectResult(bool result);
public OnConnectResult ConnectResultEvent;
public delegate void OnDataReceived(byte[] data);
public OnDataReceived DataReceiveEvent;
public delegate void OnDisconnet();
public OnDisconnet DisconnectEvent;
private int PoolCapacity;
private Stack<SocketAsyncEventArgs> SendAsyncPool;
private SocketAsyncEventArgs ReceiveAsync;
public bool Connected
{
get { return client != null && client.Connected; }
}
public void ConnectServer(string serverIP,int port)
{
if (Connected) return;
hostEndPoint = new IPEndPoint(IPAddress.Parse(serverIP),port);
buffer = new List<byte>();
client = new Socket(hostEndPoint.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
connetEvent = new AutoResetEvent(false);
SendAsyncPool = new Stack<SocketAsyncEventArgs>();
SocketAsyncEventArgs connectAsync = new SocketAsyncEventArgs();
connectAsync.UserToken = client;
connectAsync.RemoteEndPoint = hostEndPoint;
connectAsync.Completed += new EventHandler<SocketAsyncEventArgs>(ConnectResult);
client.ConnectAsync(connectAsync);
connetEvent.WaitOne(5000);
ConnectResultEvent?.Invoke(Connected);
if (Connected)
{
isConnected = true;
ReceiveAsync = new SocketAsyncEventArgs();
ReceiveAsync.Completed +=new EventHandler<SocketAsyncEventArgs>(Completed);
ReceiveAsync.RemoteEndPoint = hostEndPoint;
byte[] buffer = new byte[1024];
ReceiveAsync.SetBuffer(buffer,0,buffer.Length);
if (!client.ReceiveAsync(ReceiveAsync))
{
ProcessReceive(ReceiveAsync);
}
}
}
private void ConnectResult(object sender, SocketAsyncEventArgs e)
{
connetEvent.Set();
}
public void Send(byte[] data)
{
if (data == null || !Connected ) return;
SocketAsyncEventArgs async;
if (SendAsyncPool.Count > 0)
{
lock (SendAsyncPool)
{
async = SendAsyncPool.Pop();
}
}
else
{
if (PoolCapacity >= 5) return;
async = new SocketAsyncEventArgs();
async.Completed += new EventHandler<SocketAsyncEventArgs>(Completed);
async.RemoteEndPoint = hostEndPoint;
PoolCapacity++;
}
async.SetBuffer(data, 0, data.Length);
if (!client.SendAsync(async))
{
ProcessSend(async);
}
}
private void Completed(object sender, SocketAsyncEventArgs e)
{
try
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
break;
}
}
catch (Exception ex)
{
Debug.WriteLine("Completed Error:"+ex.Message+","+ex.StackTrace);
}
}
private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
if (e != null)
{
lock (SendAsyncPool)
{
SendAsyncPool.Push(e);
}
}
}
else
{
ProcessError(e);
}
}
private void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0 )
{
byte[] data = new byte[e.BytesTransferred];
Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
lock (buffer)
{
buffer.AddRange(data);
}
do
{
byte[] lengthBytes = buffer.GetRange(0, 4).ToArray();
int dataLength = BitConverter.ToInt32(lengthBytes,0);
if (dataLength <= buffer.Count - 4)
{
byte[] rev = buffer.GetRange(4, dataLength).ToArray();
lock (buffer)
{
buffer.RemoveRange(0, dataLength + 4);
}
DataReceiveEvent?.Invoke(rev);
}
else
{
break;
}
} while (buffer.Count >= 4);
if (!client.ReceiveAsync(e))
ProcessReceive(e);
}
else
{
ProcessError(e);
}
}
private void ProcessError(SocketAsyncEventArgs e)
{
try
{
DisConnectServer();
}
catch (Exception ex)
{
Debug.WriteLine("ProcessError:"+ ex.Message+","+ ex.StackTrace);
}
}
bool isConnected;
public void DisConnectServer()
{
if (isConnected)
{
isConnected = false;
client.Dispose();
connetEvent?.Dispose();
for (int i = 0; i < SendAsyncPool.Count; i++)
{
foreach (var async in SendAsyncPool)
{
async.Completed -= Completed;
}
}
SendAsyncPool.Clear();
PoolCapacity = 0;
ReceiveAsync.Completed -= Completed;
DisconnectEvent?.Invoke();
}
}
3.2Hololens:
https://blog.csdn.net/ShanGuUncle/article/details/89398095
3.3ARcore:
https://blog.csdn.net/ShanGuUncle/article/details/89398134
视频教程:
https://edu.csdn.net/course/detail/23985/