- Added RpcFunction.cs with init static functions - Extend BlendShaderController for getting needed data - Added CrpcExtensions for server api
447 lines
14 KiB
C#
447 lines
14 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;
|
|
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<string> 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<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 async void GetDistArray(TaskCompletionSource<string> 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;
|
|
}
|
|
}
|