NxrPostRender.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. // Copyright 2016 Nibiru. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. using UnityEngine;
  15. using System.Collections.Generic;
  16. /// Performs distortion correction on the rendered stereo screen. This script
  17. /// and NxrPreRender work together to draw the whole screen in AR Mode.
  18. /// There should be exactly one of each component in any NXR-enabled scene. It
  19. /// is part of the _NxrCamera_ prefab, which is included in
  20. /// _NxrMain_. The NxrViewer script will create one at runtime if the
  21. /// scene doesn't already have it, so generally it is not necessary to manually
  22. /// add it unless you wish to edit the Camera component that it controls.
  23. ///
  24. /// In the Unity editor, this script also draws the analog of the UI layer on
  25. /// the phone (alignment marker, settings gear, etc).
  26. namespace Nxr.Internal
  27. {
  28. [RequireComponent(typeof(Camera))]
  29. [AddComponentMenu("NXR/Internal/NxrPostRender")]
  30. public class NxrPostRender : MonoBehaviour
  31. {
  32. // Convenient accessor to the camera component used through this script.
  33. public Camera cam { get; private set; }
  34. // Distortion mesh parameters.
  35. // Size of one eye's distortion mesh grid. The whole mesh is two of these grids side by side.
  36. private const int kMeshWidth = 20;
  37. private const int kMeshHeight = 20;
  38. // Whether to apply distortion in the grid coordinates or in the texture coordinates.
  39. private const bool kDistortVertices = true;
  40. private Mesh distortionMesh;
  41. private Material meshMaterial;
  42. Mesh quadMesh;
  43. private float centerWidthPx;
  44. private float buttonWidthPx;
  45. private float xScale;
  46. private float yScale;
  47. private Matrix4x4 xfm;
  48. void Reset()
  49. {
  50. #if UNITY_EDITOR
  51. // Member variable 'cam' not always initialized when this method called in Editor.
  52. // So, we'll just make a local of the same name.
  53. var cam = GetComponent<Camera>();
  54. #endif
  55. cam.clearFlags = CameraClearFlags.Depth;
  56. cam.backgroundColor = Color.black; // Should be noticeable if the clear flags change.
  57. cam.orthographic = true;
  58. cam.orthographicSize = 0.5f;
  59. cam.cullingMask = 0;
  60. cam.useOcclusionCulling = false;
  61. cam.depth = 100;
  62. if (NxrGlobal.isVR9Platform)
  63. {
  64. // ���л��Ʊ���ͼ����ʡ����Ҫ�Ļ��Ʋ���
  65. cam.clearFlags = CameraClearFlags.Nothing;
  66. }
  67. }
  68. void Awake()
  69. {
  70. cam = GetComponent<Camera>();
  71. Reset();
  72. meshMaterial = new Material(Shader.Find("NAR/UnlitTexture"));
  73. }
  74. private float aspectComparison;
  75. #if UNITY_EDITOR || UNITY_STANDALONE_WIN
  76. void OnPreCull()
  77. {
  78. // The Game window's aspect ratio may not match the fake device parameters.
  79. float realAspect = (float)Screen.width / Screen.height;
  80. float fakeAspect = NxrViewer.Instance.Profile.screen.width / NxrViewer.Instance.Profile.screen.height;
  81. aspectComparison = fakeAspect / realAspect;
  82. cam.orthographicSize = 0.5f * Mathf.Max(1, aspectComparison);
  83. }
  84. #endif
  85. bool firstDraw = true;
  86. void OnRenderObject()
  87. {
  88. if (Camera.current != cam || !NxrViewer.Instance.SplitScreenModeEnabled)
  89. return;
  90. if (!Application.isEditor && NxrGlobal.supportDtr) return;
  91. RenderTexture stereoScreen = NxrViewer.Instance.GetStereoScreen(0);
  92. if (stereoScreen == null)
  93. {
  94. return;
  95. }
  96. bool useDFT = NxrViewer.USE_DTR && !NxrGlobal.supportDtr;
  97. if ((!NxrViewer.USE_DTR || useDFT) && NxrGlobal.distortionEnabled)
  98. {
  99. if (distortionMesh == null || NxrViewer.Instance.ProfileChanged)
  100. {
  101. RebuildDistortionMesh();
  102. }
  103. meshMaterial.mainTexture = stereoScreen;
  104. meshMaterial.SetPass(0);
  105. if (!firstDraw)
  106. {
  107. if (NxrGlobal.offaxisDistortionEnabled)
  108. {
  109. int offsetx1 = NxrGlobal.offaxisOffset[0] > 0 ? NxrGlobal.offaxisOffset[0] : 60;
  110. int offsetx2 = NxrGlobal.offaxisOffset[1] > 0 ? NxrGlobal.offaxisOffset[1] : 60;
  111. int offsety1 = NxrGlobal.offaxisOffset[2] > 0 ? NxrGlobal.offaxisOffset[2] : 10;
  112. int offsety2 = NxrGlobal.offaxisOffset[3] > 0 ? NxrGlobal.offaxisOffset[3] : 170;
  113. GL.Viewport(new Rect(offsetx1, (offsety1 + offsety2) / 2, Screen.width - offsetx1 - offsetx2, Screen.height - offsety1 - offsety2));
  114. }
  115. Graphics.DrawMeshNow(distortionMesh, transform.position, transform.rotation);
  116. }
  117. else
  118. {
  119. firstDraw = false;
  120. }
  121. }
  122. }
  123. public Texture PreviewTexture;
  124. void OnGUI()
  125. {
  126. bool useDFT = NxrViewer.USE_DTR && !NxrGlobal.supportDtr;
  127. if (NxrGlobal.distortionEnabled || (!useDFT && NxrViewer.USE_DTR)) return;
  128. if (!Event.current.type.Equals(EventType.Repaint))
  129. {
  130. return;
  131. }
  132. if (PreviewTexture != null)
  133. {
  134. GUI.DrawTexture(new Rect(0, 0, Screen.width / 2, Screen.height), PreviewTexture);
  135. GUI.DrawTexture(new Rect(Screen.width / 2, 0, Screen.width / 2, Screen.height), PreviewTexture);
  136. }
  137. if ((!NxrViewer.USE_DTR || useDFT) && !NxrGlobal.distortionEnabled && !firstDraw)
  138. {
  139. RenderTexture stereoScreen = NxrViewer.Instance.GetStereoScreen(0);
  140. if (stereoScreen == null)
  141. {
  142. return;
  143. }
  144. GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), stereoScreen);
  145. }
  146. else
  147. {
  148. firstDraw = false;
  149. }
  150. }
  151. private void BuildQuadMesh()
  152. {
  153. quadMesh = new Mesh();
  154. quadMesh.vertices = new Vector3[] { new Vector3(-1, -1, 1), new Vector3(-1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, -1, 1) };
  155. quadMesh.uv = new Vector2[] { new Vector3(0, 0, 1), new Vector3(0, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 0, 1) };
  156. quadMesh.triangles = new int[]
  157. { 0, 1, 2,
  158. 0, 2, 3
  159. };
  160. quadMesh.UploadMeshData(true);
  161. }
  162. private void RebuildDistortionMesh()
  163. {
  164. distortionMesh = new Mesh();
  165. Vector3[] vertices;
  166. Vector2[] tex;
  167. int meshWidth = kMeshWidth;
  168. int meshHeight = kMeshHeight;
  169. if (NxrGlobal.offaxisDistortionEnabled)
  170. {
  171. cam.orthographicSize = 0.5649f;// 1.12:2
  172. meshMaterial = new Material(Shader.Find("NAR/UnlitTextureOffaxis"));
  173. if (NxrGlobal.meshSize != null)
  174. {
  175. meshWidth = NxrGlobal.meshSize[0];
  176. meshHeight = NxrGlobal.meshSize[1];
  177. }
  178. ComputeMeshPointsOffAsix(meshWidth, meshHeight, kDistortVertices, out vertices, out tex);
  179. }
  180. else
  181. {
  182. ComputeMeshPoints(meshWidth, meshHeight, kDistortVertices, out vertices, out tex);
  183. }
  184. int[] indices = ComputeMeshIndices(meshWidth, meshHeight, kDistortVertices);
  185. Color[] colors = ComputeMeshColors(meshWidth, meshHeight, tex, indices, kDistortVertices);
  186. distortionMesh.vertices = vertices;
  187. distortionMesh.uv = tex;
  188. distortionMesh.colors = colors;
  189. distortionMesh.triangles = indices;
  190. #if !UNITY_5_5_OR_NEWER
  191. // Optimize() is deprecated as of Unity 5.5.0p1.
  192. distortionMesh.Optimize();
  193. #endif // !UNITY_5_5_OR_NEWER
  194. distortionMesh.UploadMeshData(true);
  195. }
  196. private static Dictionary<string, float[]> configDict = new Dictionary<string, float[]>();
  197. private static void ComputeMeshPointsOffAsix(int width, int height, bool distortVertices,
  198. out Vector3[] vertices, out Vector2[] tex)
  199. {
  200. configDict.Clear();
  201. // test config
  202. string text = NxrGlobal.offaxisDistortionConfigData;
  203. if (text == null || text.Length == 0)
  204. {
  205. TextAsset taCN = Resources.Load<TextAsset>("NibiruDistortionConfig");
  206. text = taCN.text;
  207. }
  208. string[] linesCN = text.Split('\n');
  209. //eye,xi,yi=x,y,uvx,uvy
  210. foreach (string line in linesCN)
  211. {
  212. if (line == null || line.Length <= 1)
  213. {
  214. continue;
  215. }
  216. string[] keyAndValue = line.Split('=');
  217. // Debug.Log("line=" + keyAndValue[0] + "," + keyAndValue[1]);
  218. string[] values = keyAndValue[1].Split(',');
  219. float[] uvInfo = new float[values.Length];
  220. for (int i = 0; i < values.Length; i++)
  221. {
  222. uvInfo[i] = float.Parse(values[i]);
  223. }
  224. configDict.Add(keyAndValue[0], uvInfo);
  225. }
  226. // test config
  227. vertices = new Vector3[2 * width * height];
  228. tex = new Vector2[2 * width * height];
  229. for (int e = 0, vidx = 0; e < 2; e++)
  230. {
  231. for (int j = 0; j < height; j++)
  232. {
  233. for (int i = 0; i < width; i++, vidx++)
  234. {
  235. float x = -1.0f + 2.0f * i / width;
  236. float y = -1.0f + 2.0f * j / height;
  237. string key = e + "," + i + "," + j;
  238. float[] uvInfo = new float[4];
  239. configDict.TryGetValue(key, out uvInfo);
  240. float u = uvInfo[2];
  241. float v = uvInfo[3];
  242. // -0.5~0.5
  243. x = x / 2;
  244. y = y / 2;
  245. // original 0~1
  246. if (e == 0)
  247. {
  248. // left 0~0.5
  249. u = u * 0.5f;
  250. x = x - 0.5f;
  251. }
  252. else
  253. {
  254. // right
  255. u = u * 0.5f + 0.5f;
  256. x = x + 0.5f;
  257. }
  258. vertices[vidx] = new Vector3(x, y, 1);
  259. // 0~1
  260. tex[vidx] = new Vector2(u, v);
  261. // Debug.Log(e + "," + u + "," + v);
  262. }
  263. }
  264. }
  265. }
  266. private static void ComputeMeshPoints(int width, int height, bool distortVertices,
  267. out Vector3[] vertices, out Vector2[] tex)
  268. {
  269. float[] lensFrustum = new float[4];
  270. float[] noLensFrustum = new float[4];
  271. Rect viewport;
  272. NxrProfile profile = NxrViewer.Instance.Profile;
  273. profile.GetLeftEyeVisibleTanAngles(lensFrustum);
  274. profile.GetLeftEyeNoLensTanAngles(noLensFrustum);
  275. viewport = profile.GetLeftEyeVisibleScreenRect(noLensFrustum);
  276. vertices = new Vector3[2 * width * height];
  277. tex = new Vector2[2 * width * height];
  278. for (int e = 0, vidx = 0; e < 2; e++)
  279. {
  280. for (int j = 0; j < height; j++)
  281. {
  282. for (int i = 0; i < width; i++, vidx++)
  283. {
  284. float u = (float)i / (width - 1);
  285. float v = (float)j / (height - 1);
  286. float s, t; // The texture coordinates in StereoScreen to read from.
  287. if (distortVertices)
  288. {
  289. // Grid points regularly spaced in StreoScreen, and barrel distorted in the mesh.
  290. s = u;
  291. t = v;
  292. float x = Mathf.Lerp(lensFrustum[0], lensFrustum[2], u);
  293. float y = Mathf.Lerp(lensFrustum[3], lensFrustum[1], v);
  294. float d = Mathf.Sqrt(x * x + y * y);
  295. float r = profile.viewer.distortion.distortInv(d);
  296. float p = x * r / d;
  297. float q = y * r / d;
  298. u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]);
  299. v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]);
  300. }
  301. else
  302. {
  303. // Grid points regularly spaced in the mesh, and pincushion distorted in
  304. // StereoScreen.
  305. float p = Mathf.Lerp(noLensFrustum[0], noLensFrustum[2], u);
  306. float q = Mathf.Lerp(noLensFrustum[3], noLensFrustum[1], v);
  307. float r = Mathf.Sqrt(p * p + q * q);
  308. float d = profile.viewer.distortion.distort(r);
  309. float x = p * d / r;
  310. float y = q * d / r;
  311. s = Mathf.Clamp01((x - lensFrustum[0]) / (lensFrustum[2] - lensFrustum[0]));
  312. t = Mathf.Clamp01((y - lensFrustum[3]) / (lensFrustum[1] - lensFrustum[3]));
  313. }
  314. // Convert u,v to mesh screen coordinates.
  315. float aspect = profile.screen.width / profile.screen.height;
  316. u = (viewport.x + u * viewport.width - 0.5f) * aspect;
  317. v = viewport.y + v * viewport.height - 0.5f;
  318. vertices[vidx] = new Vector3(u, v, 1);
  319. // Adjust s to account for left/right split in StereoScreen.
  320. s = (s + e) / 2;
  321. tex[vidx] = new Vector2(s, t);
  322. }
  323. }
  324. float w = lensFrustum[2] - lensFrustum[0];
  325. lensFrustum[0] = -(w + lensFrustum[0]);
  326. lensFrustum[2] = w - lensFrustum[2];
  327. w = noLensFrustum[2] - noLensFrustum[0];
  328. noLensFrustum[0] = -(w + noLensFrustum[0]);
  329. noLensFrustum[2] = w - noLensFrustum[2];
  330. viewport.x = 1 - (viewport.x + viewport.width);
  331. }
  332. }
  333. private static Color[] ComputeMeshColors(int width, int height, Vector2[] tex, int[] indices,
  334. bool distortVertices)
  335. {
  336. Color[] colors = new Color[2 * width * height];
  337. for (int e = 0, vidx = 0; e < 2; e++)
  338. {
  339. for (int j = 0; j < height; j++)
  340. {
  341. for (int i = 0; i < width; i++, vidx++)
  342. {
  343. colors[vidx] = Color.white;
  344. if (distortVertices)
  345. {
  346. if (i == 0 || j == 0 || i == (width - 1) || j == (height - 1))
  347. {
  348. colors[vidx] = Color.black;
  349. }
  350. }
  351. else
  352. {
  353. Vector2 t = tex[vidx];
  354. t.x = Mathf.Abs(t.x * 2 - 1);
  355. if (t.x <= 0 || t.y <= 0 || t.x >= 1 || t.y >= 1)
  356. {
  357. colors[vidx] = Color.black;
  358. }
  359. }
  360. }
  361. }
  362. }
  363. return colors;
  364. }
  365. private static int[] ComputeMeshIndices(int width, int height, bool distortVertices)
  366. {
  367. int[] indices = new int[2 * (width - 1) * (height - 1) * 6];
  368. int halfwidth = width / 2;
  369. int halfheight = height / 2;
  370. for (int e = 0, vidx = 0, iidx = 0; e < 2; e++)
  371. {
  372. for (int j = 0; j < height; j++)
  373. {
  374. for (int i = 0; i < width; i++, vidx++)
  375. {
  376. if (i == 0 || j == 0)
  377. continue;
  378. // Build a quad. Lower right and upper left quadrants have quads with the triangle
  379. // diagonal flipped to get the vignette to interpolate correctly.
  380. if ((i <= halfwidth) == (j <= halfheight))
  381. {
  382. // Quad diagonal lower left to upper right.
  383. indices[iidx++] = vidx;
  384. indices[iidx++] = vidx - width;
  385. indices[iidx++] = vidx - width - 1;
  386. indices[iidx++] = vidx - width - 1;
  387. indices[iidx++] = vidx - 1;
  388. indices[iidx++] = vidx;
  389. }
  390. else
  391. {
  392. // Quad diagonal upper left to lower right.
  393. indices[iidx++] = vidx - 1;
  394. indices[iidx++] = vidx;
  395. indices[iidx++] = vidx - width;
  396. indices[iidx++] = vidx - width;
  397. indices[iidx++] = vidx - width - 1;
  398. indices[iidx++] = vidx - 1;
  399. }
  400. }
  401. }
  402. }
  403. return indices;
  404. }
  405. /// <summary>
  406. /// This function is called when the MonoBehaviour will be destroyed.
  407. /// </summary>
  408. private void OnDestroy()
  409. {
  410. Debug.Log("NxrPostRender.OnDestroy");
  411. }
  412. }
  413. }