UnityLaparoscopicSceneSimul.../Assets/Scripts/BlendShaderController.cs
Max Barashev f863b8b6ca Fixed Main Thread freezing on heavy tasks
- Main thread unfreezing solution-TaskCompletionSource added in RpcTest.cs
 - Updated BlendShaderController to hold TaskCompletionSource
2026-05-20 14:31:23 +03:00

316 lines
9.4 KiB
C#

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.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 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 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 float[] resultArray;
Thread savingThread;
Task<string> task1;
Task task2;
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
}
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();
if (Input.GetKeyDown(KeyCode.F))
{
SetFlag();
}
if (flag)
{
GenerateDepth();
}
UpdateShader(cameraTexture1, cameraTexture2);
if(Input.GetKeyDown(KeyCode.S))
{
SaveTextureAsync();
}
if (Input.GetKeyDown(KeyCode.A))
{
SaveTexture();
}
}
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 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 GenerateDepth()
{
//camera1.RenderWithShader(DepthShader, "RenderType");
//camera2.RenderWithShader(DepthShader, "RenderType");
Graphics.Blit(depth1, cameraTexture1); //Depth generated due to format of render texture
Graphics.Blit(depth2, cameraTexture2);
}
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<bool> 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 void GetDistArray()
{
}
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()
{
string result = string.Empty;
Debug.Log("StartSerializing");
for (int i = 0; i < resultArray.Length; i += currentRes)
{
result += string.Join(", ", resultArray[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 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;
}
private void OnDisable()
{
computeBuffer.Release();
computeBuffer = null;
}
}