using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal.Internal; using UnityEngine.UI; public class BlendShaderController : MonoBehaviour { private static int tex1Prop = Shader.PropertyToID("Camera1"); private static int tex2Prop = Shader.PropertyToID("Camera2"); private static int texResProp = Shader.PropertyToID("Result"); private static int offsetProp = Shader.PropertyToID("Offset"); private static int resXProp = Shader.PropertyToID("ResX"); private static int farClipPlaneProp = Shader.PropertyToID("FarClipPlane"); private static int monoChanelResProp = Shader.PropertyToID("MonoChannelResult"); private static int Depths1Id = Shader.PropertyToID("Depths1"); private static int Depths2Id = Shader.PropertyToID("Depths2"); private static int ResDepthsId = Shader.PropertyToID("MergedDepths"); private static string savePath = Application.streamingAssetsPath + '\\'; private const string filename = "dsitMap"; private const string ext = ".txt"; private Vector3 center { get { if (camera1 && camera2) return camera1.transform.position + camera1.transform.right * camera2.transform.localPosition.x * 0.5f; else return Vector3.zero; } } [Range(0.0f, 2.0f)] public float testDistance = 0.08f; public Shader DepthShader; public Material depthMaterial; public ComputeShader BlendShader; public Camera camera1; public Camera camera2; public int QuadResolution = 256; public float Multiplier; public RawImage Reciever; [Range(0, 1000)] public float OffsetMultiplier = 1000; public RenderTexture test; private RenderTexture depth1; private RenderTexture depth2; private RenderTexture cameraTexture1; private RenderTexture cameraTexture2; private RenderTexture Result; private int currentRes; private bool flag = false; private ComputeBuffer computeBuffer; private ComputeBuffer depthBuffer; private float[] resultArray; Thread savingThread; Task task1; Task task2; private ComputeBuffer depthsBuffer1; private ComputeBuffer depthsBuffer2; private ComputeBuffer resDepthsBuffer; private int uvOffset; // Start is called before the first frame update void Start() { InitializeTextures(); InitializeComputeBuffer(); } void InitializeComputeBuffer() { computeBuffer = new ComputeBuffer(currentRes * currentRes, 1 * sizeof(float));//2 dimensional array of 1 value type of float depthBuffer = new ComputeBuffer(currentRes * currentRes, 1 * sizeof(float)); depthMaterial = CoreUtils.CreateEngineMaterial(DepthShader); depthMaterial.SetBuffer("distBuffer", depthBuffer); depthsBuffer1 = new ComputeBuffer(currentRes * currentRes, 1 * sizeof(float)); depthsBuffer2 = new ComputeBuffer(currentRes * currentRes, 1 * sizeof(float)); resDepthsBuffer = new ComputeBuffer(currentRes * currentRes, 1 * sizeof(float)); } void InitializeTextures() { currentRes = QuadResolution; RenderTexture[] defaultTextures = CreateTextures(2, currentRes, currentRes); cameraTexture1 = defaultTextures[0]; cameraTexture2 = defaultTextures[1]; RenderTexture[] depthTextures = CreateTextures(2, currentRes, currentRes, true); depth1 = depthTextures[0]; depth2 = depthTextures[1]; Result = CreateTextures(1, currentRes, currentRes)[0]; Reciever.texture = Result; camera1.targetTexture = cameraTexture1; camera2.targetTexture = cameraTexture2; } public static RenderTexture[] CreateTextures(int count, int resX, int resY, bool depth = false) { RenderTexture[] result = new RenderTexture[count]; for (int i = 0; i < count; i++) { result[i] = new RenderTexture(resX, resY, 32, depth ? RenderTextureFormat.Depth : RenderTextureFormat.ARGBFloat); result[i].enableRandomWrite = !depth; result[i].Create(); } return result; } // Update is called once per frame void Update() { SetCameraDistance(testDistance); ComputeUVOffset(); UpdateShader(cameraTexture1, cameraTexture2); } void UpdateShader(RenderTexture tex1, RenderTexture tex2) { BlendShader.SetTexture(0, tex1Prop, tex1); BlendShader.SetTexture(0, tex2Prop, tex2); BlendShader.SetTexture(0, texResProp, Result); BlendShader.SetFloat(offsetProp, uvOffset); BlendShader.SetFloat(resXProp, Result.width); BlendShader.SetFloat(farClipPlaneProp, camera1.farClipPlane); BlendShader.SetBuffer(0, monoChanelResProp, computeBuffer); BlendShader.Dispatch(0, Result.width / 8, Result.height / 8, 1); } void UpdateShader(float[] depths1, float[] depths2) { depthsBuffer1.SetData(depths1); depthsBuffer2.SetData(depths2); BlendShader.SetFloat(offsetProp, uvOffset); BlendShader.SetFloat(resXProp, currentRes); BlendShader.SetBuffer(1, Depths1Id, depthsBuffer1); BlendShader.SetBuffer(1, Depths2Id, depthsBuffer2); BlendShader.SetBuffer(1, ResDepthsId, resDepthsBuffer); BlendShader.Dispatch(1, currentRes / 8, currentRes / 8, 1); } void ComputeUVOffset() { if (camera1 && camera2) { if (Physics.Raycast(center, camera1.transform.forward, out var hit, 1000)) { Vector3 target = hit.point; Vector3 clipPoint1 = camera1.WorldToScreenPoint(target); Vector3 clipPoint2 = camera2.WorldToScreenPoint(target); float diff = clipPoint1.x - clipPoint2.x; uvOffset = (int)(diff * 0.5f); return; } } uvOffset = (int)(camera2.transform.localPosition.x * OffsetMultiplier); } void SwapRenderTargets() { if (flag) { camera1.targetTexture = depth1; camera2.targetTexture = depth2; } else { camera1.targetTexture = cameraTexture1; camera2.targetTexture = cameraTexture2; } } public void SetFlag() { flag = !flag; SwapRenderTargets(); } public async void SaveTextureAsync(TaskCompletionSource tcs = null) { Debug.Log("Start Saving"); resultArray = new float[currentRes * currentRes]; computeBuffer.GetData(resultArray); Debug.Log("Data copied"); task1 = Task.Run(() => SerializeArray()); await task1; Debug.Log("After awaiting"); string result = task1.Result; task2 = SavingProcessAsync(result); await task2; Debug.Log("After saving"); if (tcs != null) tcs.SetResult(true); Debug.Log("Saving finished"); } public void SaveTexture() { resultArray = new float[currentRes * currentRes]; computeBuffer.GetData(resultArray); string result = SerializeArray(); Save(result); } public async void GetDistArray(TaskCompletionSource tcs) { int cameraId1 = camera1.GetInstanceID(); int cameraId2 = camera2.GetInstanceID(); float[] depths1 = await DepthRenderPassFeature.DepthRenderPass.RequestDepthDataAsync(cameraId1); float[] depths2 = await DepthRenderPassFeature.DepthRenderPass.RequestDepthDataAsync(cameraId2); UpdateShader(depths1, depths2); float[] depths = new float[currentRes * currentRes]; resDepthsBuffer.GetData(depths); byte[] bytes = new byte[depths.Length * sizeof(float)]; Buffer.BlockCopy(depths, 0, bytes, 0, bytes.Length); string base64 = Convert.ToBase64String(bytes); string result = "{\"width\":" + currentRes + ",\"height\":" + currentRes + ",\"data\":\"" + base64 + "\"}"; tcs.SetResult(result); } private async Task SavingProcessAsync(string data) { int filesCount = Directory.EnumerateFiles(savePath, filename + '*' + ext).Count(); Debug.Log("Start saving"); await File.WriteAllTextAsync(savePath + filename + filesCount.ToString() + ext, data); Debug.Log("File saved"); } private void Save(string data) { int filesCount = Directory.EnumerateFiles(savePath, filename + '*' + ext).Count(); File.WriteAllText(savePath + filename + filesCount.ToString() + ext, data); Debug.Log("File saved"); } private string SerializeArray() { return SerializeArray(resultArray); } private string SerializeArray(float[] arr) { string result = string.Empty; Debug.Log("StartSerializing"); for (int i = 0; i < resultArray.Length; i += currentRes) { result += string.Join("; ", arr[i..(i + currentRes)]) + '\n'; } Debug.Log("EndSerializing"); return result; } private float GetDistanceByDepth() { resultArray = new float[currentRes * currentRes]; computeBuffer.GetData(resultArray); int idx = (int)(currentRes * (0.5f + currentRes * 0.5f)); float zDepth = resultArray[idx]; float far = camera1.farClipPlane; return zDepth * far; } private float GetDistanceByRayCast() { Vector3 position = camera1.transform.position + camera1.transform.right * camera2.transform.localPosition.x * 0.5f; if (Physics.Raycast(position, camera1.transform.forward, out var hit, 100)) { return hit.distance; } return -1; } public double[] GetIntrinsicParameters(Camera camera) { int width = camera.targetTexture != null ? camera.targetTexture.width : camera.pixelWidth; int height = camera.targetTexture != null ? camera.targetTexture.height : camera.pixelHeight; double fovRad = camera.fieldOfView * Math.PI / 180.0; double fy = (height * 0.5) / Math.Tan(fovRad * 0.5); double aspect = (double)width / height; double fx = fy * aspect; double cx = width * 0.5; double cy = height * 0.5; return new double[] { fx, 0, cx, 0, fy, cy, 0, 0, 1 }; } public string GetIntrinsicParametersAsBase64(Camera camera) { double[] intrinsics = GetIntrinsicParameters(camera); byte[] bytes = new byte[intrinsics.Length * sizeof(double)]; Buffer.BlockCopy(intrinsics, 0, bytes, 0, bytes.Length); return Convert.ToBase64String(bytes); } public string GetLeftIntrinsicParametersAsBase64() { return GetIntrinsicParametersAsBase64(camera1); } public string GetRightIntrinsicParametersAsBase64() { return GetIntrinsicParametersAsBase64(camera2); } public double[] GetExtrinsicParameters(Camera camera) { Matrix4x4 m = camera.worldToCameraMatrix; return new double[] { m.m00, m.m01, m.m02, m.m03, m.m10, m.m11, m.m12, m.m13, m.m20, m.m21, m.m22, m.m23, m.m30, m.m31, m.m32, m.m33 }; } public string GetExtrinsicParametersAsBase64(Camera camera) { double[] extrinsics = GetExtrinsicParameters(camera); byte[] bytes = new byte[extrinsics.Length * sizeof(double)]; Buffer.BlockCopy(extrinsics, 0, bytes, 0, bytes.Length); return Convert.ToBase64String(bytes); } public string GetLeftExtrinsicParametersAsBase64() { return GetExtrinsicParametersAsBase64(camera1); } public string GetRightExtrinsicParametersAsBase64() { return GetExtrinsicParametersAsBase64(camera2); } public float GetCameraDistance() { if (camera1 == null || camera2 == null) return -1f; return camera2.transform.localPosition.x; } public void SetCameraDistance(float distance) { if (camera1 == null || camera2 == null) return; Vector3 direction = camera1.transform.right; distance = Mathf.Max(0, distance); Vector3 camera2LocPos = camera2.transform.localPosition; camera2.transform.localPosition = new Vector3(distance, camera2LocPos.y, camera2LocPos.z); camera1.transform.position = center - direction * distance * 0.5f; } public RenderTexture CombineTexturesSideBySide(RenderTexture left, RenderTexture right) { if (left == null || right == null) { Debug.LogError("Both render textures must be provided"); return null; } if (left.width != right.width || left.height != right.height) { Debug.LogError("Both render textures must have the same dimensions"); return null; } int w = left.width; int h = left.height; RenderTexture combined = new RenderTexture(w * 2, h, 0, RenderTextureFormat.ARGBFloat); combined.Create(); Graphics.CopyTexture(left, 0, 0, 0, 0, w, h, combined, 0, 0, 0, 0); Graphics.CopyTexture(right, 0, 0, 0, 0, w, h, combined, 0, 0, w, 0); return combined; } public string GetCombinedTexturesAsBase64Json() { RenderTexture combined = CombineTexturesSideBySide(cameraTexture1, cameraTexture2); if (combined == null) return null; int w = combined.width; int h = combined.height; RenderTexture activeRT = RenderTexture.active; RenderTexture.active = combined; Texture2D tex = new Texture2D(w, h, TextureFormat.RGBAFloat, false); tex.ReadPixels(new Rect(0, 0, w, h), 0, 0); tex.Apply(); RenderTexture.active = activeRT; byte[] rawData = tex.GetRawTextureData(); Destroy(tex); combined.Release(); string base64 = Convert.ToBase64String(rawData); return "{\"width\":" + w + ",\"height\":" + h + ",\"data\":\"" + base64 + "\"}"; } private void OnDisable() { computeBuffer.Release(); computeBuffer = null; depthBuffer.Release(); depthBuffer = null; depthsBuffer1.Release(); depthsBuffer1 = null; depthsBuffer2.Release(); depthsBuffer2 = null; resDepthsBuffer.Release(); resDepthsBuffer = null; Destroy(depthMaterial); depthMaterial = null; } }