2025-01-03 13:42:53 +08:00

687 lines
27 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using System;
using GCSeries.Core;
namespace GCSeries.zView
{
public class GVirtualCameraAR : GVirtualCamera
{
/// <summary>
/// class for generate a AR cutout mesh
/// </summary>
public class CutoutMesh
{
public Mesh BoxMesh { get { return _boxMesh; } }
Mesh _boxMesh;
public Mesh ScreenMesh { get { return _screenMesh; } }
Mesh _screenMesh;
public Mesh SkyBoxMesh { get { return _skyBoxMesh; } }
Mesh _skyBoxMesh;
public Vector2 screenSize
{
get { return _screenSize; }
set { UpdateScreenSize(value); }
}
private Vector2 _screenSize;
public Vector3 cutoutSize
{
get { return _cutoutSize; }
set { UpdateCutoutSize(value); }
}
private Vector3 _cutoutSize;
private CutoutMesh() { }
public CutoutMesh(Vector2 screenSize, Vector3 cutoutSize)
{
_screenSize = screenSize;
_cutoutSize = cutoutSize;
_boxMesh = CreateMesh(_screenSize, _cutoutSize);
_screenMesh = CreatePlane(screenSize);
_skyBoxMesh = CreateSkyPlane(screenSize);
}
public void UpdateScreenSize(Vector2 newScreenSize)
{
if (newScreenSize == _screenSize) return;
_screenSize = newScreenSize;
Vector3[] vertices = BoxMesh.vertices;
vertices[0] = new Vector3(_screenSize.x / 2, _screenSize.y / 2f, 0);
vertices[1] = new Vector3(_screenSize.x / 2, -_screenSize.y / 2f, 0);
vertices[2] = new Vector3(-_screenSize.x / 2, -_screenSize.y / 2f, 0);
vertices[3] = new Vector3(-_screenSize.x / 2, _screenSize.y / 2f, 0);
BoxMesh.vertices = vertices;
Vector3[] ScreenVertices = ScreenMesh.vertices;
ScreenVertices[0] = new Vector3(-screenSize.x / 2, -_screenSize.y / 2f, 0);
ScreenVertices[1] = new Vector3(screenSize.x / 2, -_screenSize.y / 2f, 0);
ScreenVertices[2] = new Vector3(-screenSize.x / 2, screenSize.y / 2f, 0);
ScreenVertices[3] = new Vector3(screenSize.x / 2, screenSize.y / 2f, 0);
ScreenMesh.vertices = ScreenVertices;
}
public void UpdateCutoutSize(Vector3 newCutoutSize)
{
if (newCutoutSize == _cutoutSize) return;
_cutoutSize = newCutoutSize;
Vector3[] vertices = _boxMesh.vertices;
Vector3 halfSize = _cutoutSize * 0.5f;
vertices[4] = new Vector3(-halfSize.x, halfSize.y, 0);
vertices[5] = new Vector3(halfSize.x, halfSize.y, 0);
vertices[6] = new Vector3(halfSize.x, -halfSize.y, 0);
vertices[7] = new Vector3(-halfSize.x, -halfSize.y, 0);
vertices[8] = new Vector3(-halfSize.x, halfSize.y, -_cutoutSize.z);
vertices[9] = new Vector3(halfSize.x, halfSize.y, -_cutoutSize.z);
vertices[10] = new Vector3(halfSize.x, -halfSize.y, -_cutoutSize.z);
vertices[11] = new Vector3(-halfSize.x, -halfSize.y, -_cutoutSize.z);
_boxMesh.vertices = vertices;
}
public void UpdateSize(Vector2 newScreenSize, Vector3 newCutoutSize)
{
UpdateScreenSize(newScreenSize);
UpdateCutoutSize(newCutoutSize);
}
private Mesh CreateMesh(Vector2 screenSize, Vector3 size)
{
// Create the mesh.
Mesh temp_mesh = new Mesh();
temp_mesh.name = "BoxMask";
temp_mesh.vertices = new Vector3[12];
temp_mesh.triangles = new int[]
{
// Back Face Top:
0, 4, 5,
0, 5, 1,
// Back Face Right:
1, 5, 6,
1, 6, 2,
// Back Face Bottom:
2, 6, 7,
2, 7, 3,
// Back Face Left:
3, 7, 4,
3, 4, 0,
// Top Face:
4, 8, 9,
4, 9, 5,
// Right Face:
5, 9, 10,
5, 10, 6,
// Bottom Face:
6, 10, 11,
6, 11, 7,
// Right Face:
7, 11, 8,
7, 8, 4,
// Front Face:
8, 10, 9,
8, 11, 10,
};
Vector2 halfSize = Vector2.one * 0.5f;
Vector3[] vertices = temp_mesh.vertices;
vertices[0] = new Vector3(screenSize.x / 2, screenSize.y / 2f, 0);
vertices[1] = new Vector3(screenSize.x / 2, -screenSize.y / 2f, 0);
vertices[2] = new Vector3(-screenSize.x / 2, -screenSize.y / 2f, 0);
vertices[3] = new Vector3(-screenSize.x / 2, screenSize.y / 2f, 0);
halfSize = size * 0.5f;
vertices[4] = new Vector3(-halfSize.x, halfSize.y, 0);
vertices[5] = new Vector3(halfSize.x, halfSize.y, 0);
vertices[6] = new Vector3(halfSize.x, -halfSize.y, 0);
vertices[7] = new Vector3(-halfSize.x, -halfSize.y, 0);
vertices[8] = new Vector3(-halfSize.x, halfSize.y, -size.z);
vertices[9] = new Vector3(halfSize.x, halfSize.y, -size.z);
vertices[10] = new Vector3(halfSize.x, -halfSize.y, -size.z);
vertices[11] = new Vector3(-halfSize.x, -halfSize.y, -size.z);
temp_mesh.vertices = vertices;
return temp_mesh;
}
/// <summary>
/// 屏幕平面
/// </summary>
/// <param name="screenSize"></param>
/// <returns></returns>
private Mesh CreatePlane(Vector2 screenSize)
{
Mesh temp_mesh = new Mesh();
temp_mesh.name = "screenPlane";
temp_mesh.vertices = new Vector3[4]
{
new Vector3(-screenSize.x / 2, -screenSize.y / 2f, 0),
new Vector3(screenSize.x / 2, -screenSize.y / 2f, 0),
new Vector3(-screenSize.x / 2, screenSize.y / 2f, 0),
new Vector3(screenSize.x / 2, screenSize.y / 2f, 0)
};
temp_mesh.triangles = new int[]
{
// lower left triangle
0, 2, 1,
// upper right triangle
2, 3, 1
};
return temp_mesh;
}
/// <summary>
/// 远处天空平面
/// </summary>
/// <param name="screenSize"></param>
/// <returns></returns>
private Mesh CreateSkyPlane(Vector2 screenSize)
{
Mesh temp_mesh = new Mesh();
temp_mesh.name = "skyPlane";
temp_mesh.vertices = new Vector3[4]
{
new Vector3(-0.5f, -0.5f, 0),
new Vector3(0.5f, -0.5f, 0),
new Vector3(-0.5f, 0.5f, 0),
new Vector3(0.5f, 0.5f, 0)
};
Vector2[] uv = new Vector2[4]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(1, 1)
};
temp_mesh.triangles = new int[]
{
// lower left triangle
0, 2, 1,
// upper right triangle
2, 3, 1
};
temp_mesh.uv = uv;
return temp_mesh;
}
}
//标定位置信息
private Matrix4x4 _camPoseMatrixInDisplaySpace;
private CommandBuffer _arBuffer;
private CommandBuffer _cullBuffer;
private CutoutMesh _cutoutMesh;
private RenderTexture _depthMask;
private MeshFilter _skyMeshFilter;
private MeshFilter _boxMeshFilter;
private Camera secondCamera;
private Camera skyBoxCamera;
private RenderTexture _secondRT;
private RenderTexture _skyboxRT;
private GView _zView;
private static RenderTexture _renderTexture = null;
private LayerMask _ignoreLayer;
private LayerMask _environmentMask;
/// <summary>
/// 裁剪盒大小
/// </summary>
private Vector3 _cutoutMeshSizeScaler = Vector3.one;
/// <summary>
/// 屏幕背景色
/// </summary>
//private Color _screenMaskColor = new Color(0.572549f, 0.7019608f, 0.8588236f, 1f);
void Awake()
{
// Dynamically create a new Unity camera and disable it to allow for manual
// rendering via Camera.Render().
_arCamera = gameObject.AddComponent<Camera>();
if (_arCamera == null)
{
Debug.LogError("GVirtualCameraAR.Awake():创建相机失败!");
}
else
{
_arCamera.enabled = false;
_arCamera.nearClipPlane = 0.03f;
_arCamera.targetDisplay = 0;
_arCamera.stereoTargetEye = StereoTargetEyeMask.None;
}
}
void OnPreCull()
{
if (skyBoxCamera != null)
{
skyBoxCamera.Render();
}
if (secondCamera != null)
{
secondCamera.Render();
}
}
public override void SetUp(GView zView, IntPtr connection, GView.ModeSetupPhase phase)
{
_zView = zView;
_arCamera.enabled = true;
_zCameraRig = FindObjectOfType<ZCameraRig>();
_imageWidth = zView.imageWidth;
_imageHeight = zView.imageHeight;
if (_renderTexture == null)
_renderTexture = Resources.Load<RenderTexture>("gViewRT");
_arCamera.targetTexture = _renderTexture;
_ignoreLayer = zView.ARModeIgnoreLayers;
_environmentMask = zView.ARModeEnvironmentLayers;
_cutoutMeshSizeScaler = zView.ARModeMaskSize;
//_screenMaskColor = zView.screenMaskColor;
_cullBuffer = null;
_arBuffer = null;
ReadArCamTransform(out _camPoseMatrixInDisplaySpace);
StartCoroutine(InitCamera(SetupCommandBuffer));
}
public override void TearDown()
{
CloseCamera();
if (_cullBuffer != null)
{
_cullBuffer.Release();
_cullBuffer = null;
}
if (_arBuffer != null)
{
_arBuffer.Release();
_arBuffer = null;
}
if (secondCamera != null)
{
secondCamera.enabled = false;
}
_arCamera.RemoveAllCommandBuffers();
_arCamera.enabled = false;
if (_depthMask != null)
{
_depthMask.Release();
_depthMask = null;
}
if (_secondRT != null)
{
_secondRT.Release();
_secondRT = null;
}
if (_skyboxRT != null)
{
_skyboxRT.Release();
_skyboxRT = null;
}
if (_skyMeshFilter != null)
{
_skyMeshFilter.gameObject.SetActive(false);
}
}
public override void Render(GView zView, IntPtr connection, IntPtr receivedFrame)
{
if (_cutoutMesh != null)
{
// The camera's parent transform represents viewport center and
// is used here to align the AR camera against.
Transform cameraParentTransform = zView.ActiveZCamera?.transform.parent;
// 实时更新相机位置
// TODO:GCSeries编辑器下这个值不会随拖动编辑器窗口而修改发布后表现一致
Matrix4x4 temp_matrix = Matrix4x4.TRS(cameraParentTransform.position,
cameraParentTransform.rotation,
Vector3.one);
//Debug.Log($"GVirtualCameraAr.Render(): ViewportMatrix = {temp_matrix}");
Matrix4x4 displayToWorld = cameraParentTransform?.localToWorldMatrix ?? Matrix4x4.identity;
//displayToWorld.SetColumn(3, new Vector4(0.1257f, -0.04232f, 0.0f, 1f));//这个是返回的结果
Matrix4x4 cameraWorldMatrix = displayToWorld * _camPoseMatrixInDisplaySpace;
transform.position = cameraWorldMatrix.GetColumn(3);
transform.rotation = Quaternion.LookRotation(cameraWorldMatrix.GetColumn(2), cameraWorldMatrix.GetColumn(1));
//更新遮挡mesh
_cutoutMesh.screenSize = ZProvider.DisplayReferenceSize * _zCameraRig.ViewerScale;
_cutoutMesh.cutoutSize = _cutoutMeshSizeScaler * _zCameraRig.ViewerScale;
_boxMeshFilter.mesh.vertices = _cutoutMesh.BoxMesh.vertices;
_boxMeshFilter.gameObject.transform.position = cameraParentTransform.position;
_boxMeshFilter.gameObject.transform.rotation = cameraParentTransform.rotation;
if (_cullBuffer != null)
{
//更新绘制命令矩阵
_cullBuffer.Clear();
_cullBuffer.SetRenderTarget(_depthMask);
_cullBuffer.ClearRenderTarget(true, true, Color.black);
_cullBuffer.DrawMesh(_cutoutMesh.BoxMesh, temp_matrix, _depthRenderMat);
}
_arCamera.Render();
}
}
// public override IntPtr GetNativeTexturePtr()
// {
// Debug.LogError("GVirtualCameraAR.GetNativeTexturePtr():未使用渲染到纹理");
// if (_renderTexture == null) return IntPtr.Zero;
// return _renderTexture.GetNativeTexturePtr();
// }
public override IntPtr[] GetNativeTexturePtr(out int count)
{
count = 1;
if (_renderTexture == null) return new IntPtr[] { IntPtr.Zero };
return new IntPtr[] { _renderTexture.GetNativeTexturePtr() };
}
public override RenderTexture[] GetRenderTexture(out int count)
{
count = 1;
return new RenderTexture[] { _renderTexture };
}
Material _depthRenderMat;
private void SetupCommandBuffer(Texture webCamTexture)
{
// The camera's parent transform represents viewport center and
// is used here to align the AR camera against.
Transform cameraParentTransform = _zView.ActiveZCamera?.transform.parent;
//arCamera.depthTextureMode = DepthTextureMode.Depth;
_arCamera.renderingPath = RenderingPath.Forward;
_arCamera.clearFlags = CameraClearFlags.Color;
_arCamera.backgroundColor = new Color(0f, 0f, 0f, 0f);
_arCamera.cullingMask = _arCamera.cullingMask & ~_ignoreLayer;
_cutoutMesh = new CutoutMesh(ZProvider.DisplayReferenceSize, Vector3.one);
if (_skyMeshFilter == null)
{
GameObject skyOBJ = new GameObject();
skyOBJ.transform.parent = transform;
skyOBJ.hideFlags = HideFlags.HideAndDontSave;
skyOBJ.name = "HiddenSkyObj";
_skyMeshFilter = skyOBJ.AddComponent<MeshFilter>();
MeshRenderer boxRenderer = skyOBJ.AddComponent<MeshRenderer>();
Material boxMat = new Material(Shader.Find("GcAR/SkyPlane"));
boxRenderer.material = boxMat;
boxRenderer.material.mainTexture = _skyboxRT;
_skyMeshFilter.mesh = _cutoutMesh.SkyBoxMesh;
}
_skyMeshFilter.gameObject.SetActive(true);
_skyMeshFilter.gameObject.transform.localPosition = new Vector3(0f, 0f, 0f);
_skyMeshFilter.gameObject.transform.rotation = Quaternion.identity;
_skyMeshFilter.mesh.vertices = new Vector3[4]
{
_arCamera.ScreenToWorldPoint(new Vector3(0f, 0f, _arCamera.farClipPlane * 0.9f)),
_arCamera.ScreenToWorldPoint(new Vector3(_imageWidth, 0f, _arCamera.farClipPlane * 0.9f)),
_arCamera.ScreenToWorldPoint(new Vector3(0f, _imageHeight, _arCamera.farClipPlane * 0.9f)),
_arCamera.ScreenToWorldPoint(new Vector3(_imageWidth, _imageHeight, _arCamera.farClipPlane * 0.9f))
};
//这个buffer可以避免透明物体与环境物体混合问题
//不过环境物体需要使用本例中StandardEnvironment.shader材质
if (_boxMeshFilter == null)
{
GameObject boxOBJ = new GameObject();
boxOBJ.transform.parent = transform;
boxOBJ.hideFlags = HideFlags.HideAndDontSave;
boxOBJ.name = "HiddenBoxObj";
_boxMeshFilter = boxOBJ.AddComponent<MeshFilter>();
MeshRenderer boxRenderer = boxOBJ.AddComponent<MeshRenderer>();
Material boxMat = new Material(Shader.Find("GcAr/StencilWriter"));
boxRenderer.materials = new Material[] { boxMat };
_boxMeshFilter.mesh = _cutoutMesh.BoxMesh;
}
_boxMeshFilter.gameObject.transform.position = cameraParentTransform.position;
_boxMeshFilter.gameObject.transform.rotation = cameraParentTransform.rotation;
if (_cullBuffer == null)
{
_cullBuffer = new CommandBuffer();
if (_depthMask == null)
_depthMask = new RenderTexture(_imageWidth, _imageHeight, 24);
_cullBuffer.SetRenderTarget(_depthMask);
_cullBuffer.ClearRenderTarget(true, true, Color.black);
_cullBuffer.name = "Draw Cutout Mesh";
_depthRenderMat = new Material(Shader.Find("GcAR/DepthRenderer"));
Matrix4x4 temp_matrix = Matrix4x4.TRS(cameraParentTransform.position,
cameraParentTransform.rotation,
Vector3.one);
_cullBuffer.DrawMesh(_cutoutMesh.BoxMesh, temp_matrix, _depthRenderMat);
_arCamera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, _cullBuffer);
}
if (_arBuffer == null)
{
_arBuffer = new CommandBuffer();
Material material = new Material(Shader.Find("GcAR/DepthGrayscale"));
_arBuffer.name = "GcAr Buffer";
int customDepthID = Shader.PropertyToID("_customDepthMask");
_arBuffer.GetTemporaryRT(customDepthID, -1, -1, 0, FilterMode.Bilinear);
_arBuffer.Blit(_depthMask, customDepthID);
int depthCameraID = Shader.PropertyToID("_noneCameraDepthTexture");
_arBuffer.GetTemporaryRT(depthCameraID, -1, -1, 0, FilterMode.Bilinear);
_arBuffer.Blit(_secondRT, depthCameraID);
if (webCamTexture != null)
{
int webCamTextureID = Shader.PropertyToID("_webCamTexture");
_arBuffer.GetTemporaryRT(webCamTextureID, -1, -1, 0, FilterMode.Bilinear);
_arBuffer.Blit(webCamTexture, webCamTextureID);
}
int texID = Shader.PropertyToID("_RenderTexture");
_arBuffer.GetTemporaryRT(texID, -1, -1, 0, FilterMode.Bilinear);
_arBuffer.Blit(BuiltinRenderTextureType.CameraTarget, texID);
_arBuffer.Blit(texID, BuiltinRenderTextureType.CameraTarget, material);
_arCamera.AddCommandBuffer(CameraEvent.AfterForwardAlpha, _arBuffer);
}
}
/// <summary>
/// 读取标定位置
/// </summary>
private void ReadArCamTransform(out Matrix4x4 viewPoseMatrixInDisplaySpace)
{
viewPoseMatrixInDisplaySpace = Matrix4x4.identity;
bool readPoseSuccess = false;
try
{
Matrix4x4 mat = new Matrix4x4();
GView.FARError res = GView.xxGetARCameraPose(out mat);
if (res == GView.FARError.Ok)
{
viewPoseMatrixInDisplaySpace = mat;
readPoseSuccess = true;
UnityEngine.Debug.Log("GVirtualCameraAR.ReadArCamTransform():FAR相机Pose读取成功.");
}
else if (res == GView.FARError.Unknown)
{
UnityEngine.Debug.LogError("GVirtualCameraAR.ReadArCamTransform():未知错误!");
}
else if (res == GView.FARError.NoCalib)
{
UnityEngine.Debug.LogError("GVirtualCameraAR.ReadArCamTransform():先使用工具软件进行罗技摄像头的标定.");
}
else if (res == GView.FARError.NoLicense)
{
UnityEngine.Debug.LogError("GVirtualCameraAR.ReadArCamTransform():系统没有FAR的License.");
}
}
catch (Exception e)
{
UnityEngine.Debug.LogError("GVirtualCameraAR.ReadArCamTransform():执行xxGetARCameraPose()异常!e=" + e.Message);
}
if (!readPoseSuccess)
{
Debug.LogWarning("GVirtualCameraAR.ReadArCamTransform():FAR结果读取失败,设置一个默认值.");
//*********GCSeries的标定位置矩阵**********
viewPoseMatrixInDisplaySpace.SetColumn(0, new Vector4(0.58063f, -0.57597f, -0.57543f, 0f));
viewPoseMatrixInDisplaySpace.SetColumn(1, new Vector4(0.38956f, 0.81716f, -0.42485f, 0f));
viewPoseMatrixInDisplaySpace.SetColumn(2, new Vector4(0.71492f, 0.02251f, 0.69885f, 0f));
viewPoseMatrixInDisplaySpace.SetColumn(3, new Vector4(-0.3647f, -0.02495f, -0.3946f, 1f));
//*********************
}
}
/// <summary>
/// 相机采图
/// </summary>
private static WebCamTexture _camTex;
IEnumerator InitCamera(Action<Texture> callback = null)
{
if (!Application.isPlaying) yield break;
//获取授权
//yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
if (Application.HasUserAuthorization(UserAuthorization.WebCam))
{
var devices = WebCamTexture.devices;
string _deviceName = "";
foreach (var item in devices)
{
//因为红外追踪相机都会以F3DXXXX命名
if (!item.name.Contains("F3D"))
{
_deviceName = item.name;
_arCamera.fieldOfView = 42.3f;
if (secondCamera == null)
{
GameObject secondCameraOBJ = new GameObject();
secondCameraOBJ.hideFlags = HideFlags.HideAndDontSave;
secondCameraOBJ.name = "SecondCamera";
secondCameraOBJ.transform.parent = transform;
secondCamera = secondCameraOBJ.AddComponent<Camera>();
secondCamera.stereoTargetEye = StereoTargetEyeMask.None;
}
if (_secondRT == null)
_secondRT = new RenderTexture(_imageWidth, _imageHeight, 24);
secondCamera.enabled = false; //渲染深度图可能会慢一帧
secondCamera.CopyFrom(_arCamera);
secondCamera.cullingMask = _arCamera.cullingMask & ~(_ignoreLayer) & ~(_environmentMask);
secondCamera.SetReplacementShader(Shader.Find("GcAR/DepthReplacement"), "");
secondCamera.targetTexture = _secondRT;
if (skyBoxCamera == null)
{
GameObject skyBoxCameraOBJ = new GameObject();
skyBoxCameraOBJ.hideFlags = HideFlags.HideAndDontSave;
skyBoxCameraOBJ.name = "skyBoxCameraOBJ";
skyBoxCameraOBJ.transform.parent = transform;
skyBoxCamera = skyBoxCameraOBJ.AddComponent<Camera>();
skyBoxCamera.CopyFrom(_arCamera);
skyBoxCamera.cullingMask = 0;
skyBoxCamera.clearFlags = CameraClearFlags.Skybox;
if (_skyboxRT == null)
_skyboxRT = new RenderTexture(_imageWidth, _imageHeight, 24);
skyBoxCamera.targetTexture = _skyboxRT;
skyBoxCamera.enabled = false;
}
break;
}
}
if (string.IsNullOrEmpty(_deviceName))
{
UnityEngine.Debug.LogError("GVirtualCameraAR.InitCamera():相机启动失败,没有外接相机");
yield break;
}
else
{
if (_camTex == null)
{
_camTex = new WebCamTexture(_deviceName, 1280, 720, 30);//设置为1280x720可以减少相机延迟
_camTex.Play();
}
else
{
_camTex.Play();
}
if (callback != null)
callback.Invoke(_camTex);
UnityEngine.Debug.Log("GVirtualCameraAR.InitCamera():相机启动");
}
}
}
/// <summary>
/// 关闭罗技相机采图
/// </summary>
public void CloseCamera()
{
if (_camTex != null)
{
if (_camTex.isPlaying)
{
_camTex.Stop();
}
}
}
private void OnApplicationQuit()
{
CloseCamera();
}
private Camera _arCamera = null;
private UInt16 _imageWidth = 1920;
private UInt16 _imageHeight = 1080;
private ZCameraRig _zCameraRig;
// private readonly Vector2 _screenSize = new Vector2(0.52f, 0.2925f);
}
}