DotCloud/Assets/TestRay.cs

290 lines
10 KiB
C#
Raw Normal View History

2024-12-09 16:51:45 +08:00

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Profiling;
using Debug = UnityEngine.Debug;
namespace ZC
{
public class TestRay : MonoBehaviour
{
[SerializeField] int frameRate = 5;
[SerializeField] int _currentFrameCount;
[SerializeField] int rayCount;
[SerializeField] [Header("需要检测的层级")] private LayerMask toHitMask;
[SerializeField] [Header("水平射线数")] private int horizontalRaysCount = 400;
[SerializeField] [Header("垂直射线数")] private int verticalRaysCount = 375;
[SerializeField] private Mesh _mesh;
[SerializeField] private Material _mat;
[SerializeField] private Camera _camera;
public ParticleSystem _particleSystem;
private PointsDrawer _pointsDrawer;
private NativeArray<RaycastHit> results;
private NativeArray<ParticleSystem.Particle> particles;
private NativeArray<RaycastCommand> commands;
private NativeArray<TransData> rayCaster;
private NativeArray<ZPointInfo> flattenedPointsInfo;
private bool _isRunning;
private List<Renderer> _renderers = new List<Renderer>(150000);
private NativeArray<int> _hitCountArray;
public void AddRenderer(Renderer renderer)
{
this._renderers.Add(renderer);
}
private void Start()
{
// var o = new GameObject();
// for (var i = 0; i < 10000; i++)
// {
// var insideUnitSphere = UnityEngine.Random.insideUnitSphere * 100;
// var primitive = UnityEngine.GameObject.CreatePrimitive(PrimitiveType.Cube);
// primitive.transform.position = insideUnitSphere;
// primitive.transform.parent = o.transform;
// _renderers.Add(primitive.GetComponent<Renderer>());
// }
rayCount = horizontalRaysCount * verticalRaysCount;
_pointsDrawer = new PointsDrawer();
_pointsDrawer.Setup(_mesh, rayCount, 1000, _mat);
results = new NativeArray<RaycastHit>(rayCount, Allocator.Persistent);
commands = new NativeArray<RaycastCommand>(rayCount, Allocator.Persistent);
rayCaster = new NativeArray<TransData>(rayCount, Allocator.Persistent);
particles = new NativeArray<ParticleSystem.Particle>(rayCount, Allocator.Persistent);
flattenedPointsInfo = new NativeArray<ZPointInfo>(rayCount, Allocator.Persistent); // 初始化存储点信息的列表,预先分配空间
this._hitCountArray = new NativeArray<int>(1, Allocator.Persistent);
StartCoroutine(InitRays());
}
private void OnDestroy()
{
_pointsDrawer.OnDispose();
results.Dispose();
commands.Dispose();
rayCaster.Dispose();
particles.Dispose();
flattenedPointsInfo.Dispose();
_hitCountArray.Dispose();
StopAllCoroutines();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
Debug.Log("Start");
this._isRunning = true;
}
if (Input.GetKeyDown(KeyCode.D))
{
Debug.Log("Stop");
this._isRunning = false;
}
if (Input.GetKeyDown(KeyCode.K))
{
foreach (var renderer1 in this._renderers)
{
renderer1.enabled = false;
}
}
if (Input.GetKeyDown(KeyCode.L))
{
foreach (var renderer1 in this._renderers)
{
renderer1.enabled = true;
}
}
if (this._isRunning)
{
RayTest();
}
}
/// <summary>
/// 将垂直FOV转成水平FOV
/// </summary>
/// <param name="verFOV">垂直FOV值</param>
/// <param name="aspect">屏幕分辨率比例</param>
/// <returns></returns>
public static float ConvertVerticalFOVToHorizontally(float verFOV, float aspect)
{
// 垂直fov角度转成弧度
float verFovRadian = verFOV * Mathf.Deg2Rad;
// 算出视野高度的一半
float camHalfHeight = Mathf.Tan(verFovRadian / 2);
// 算出水平视野的弧度
float horFOVRadian = Mathf.Atan(camHalfHeight * aspect) * 2;
// 将水平视野弧度转成角度
float horFOV = horFOVRadian * Mathf.Rad2Deg;
return horFOV;
}
IEnumerator InitRays()
{
var horizontalFov = ConvertVerticalFOVToHorizontally(this._camera.fieldOfView, this._camera.aspect);
var verticalFov = this._camera.fieldOfView;
float halfXAngle = horizontalFov / 2.0f;
float halfYAngle = verticalFov / 2.0f;
int index = 0;
for (int i = 0; i < this.horizontalRaysCount; i++)
{
float angleDeltaX = Mathf.Lerp(-halfXAngle, halfXAngle, i * 1f / horizontalRaysCount);
for (int j = 0; j < this.verticalRaysCount; j++)
{
float angleDeltaY = Mathf.Lerp(-halfYAngle, halfYAngle, j * 1f / verticalRaysCount);
rayCaster[index] = new TransData()
{
localPosition = Vector3.zero,
localRotation = Quaternion.Euler(-angleDeltaY, angleDeltaX, 0),
};
index++;
}
}
yield return null;
}
void RayTest()
{
Profiler.BeginSample("ExecuteJobs");
float3 transformPosition = this._camera.transform.position;
quaternion transformRotation = this._camera.transform.rotation;
BuildRaycastCommandJob rJob = new BuildRaycastCommandJob
{
rayCaster = this.rayCaster,
commands = this.commands,
queryParameters = new QueryParameters(this.toHitMask, false, QueryTriggerInteraction.Ignore, false),
parentPos = transformPosition,
parentRot = transformRotation
};
var handle1 = rJob.Schedule(this.rayCount, 1);
JobHandle handle2 = RaycastCommand.ScheduleBatch(rJob.commands, results, 100, dependsOn: handle1); //调度批量射线投射命令
_hitCountArray[0] = 0;
HandleHitResultJob jJob = new HandleHitResultJob
{
rayCaster = this.rayCaster,
hitResults = this.results,
frameInfo = this.flattenedPointsInfo,
parentRot = transformRotation,
parentPos = transformPosition,
hitCount = this._hitCountArray
};
var handle3 = jJob.Schedule(dependsOn: handle2);
handle3.Complete();
var hitCount = _hitCountArray[0];
Profiler.EndSample();
Profiler.BeginSample("RenderPoints");
var bounds = new Bounds(transformPosition, Vector3.one*1000);
// _pointsDrawer.Render(flattenedPointsInfo,hitCount,bounds, transformPosition);
PointsPublisher.pointPublisher.PushData(flattenedPointsInfo,hitCount,transformPosition,transformRotation ,bounds);
Profiler.EndSample();
}
}
[BurstCompile]
struct BuildRaycastCommandJob : IJobParallelFor
{
[ReadOnly] public QueryParameters queryParameters;
[ReadOnly] public NativeArray<TransData> rayCaster;
[ReadOnly] public float3 parentPos;
[ReadOnly] public quaternion parentRot;
public NativeArray<RaycastCommand> commands;
public void Execute(int index)
{
var transData = rayCaster[index];
// var position = math.rotate(parentRot, transData.localPosition) + parentPos;
var position = this.parentPos;
var rotation = math.mul(parentRot, transData.localRotation);
var forward = math.mul(rotation, math.forward());
commands[index] = new RaycastCommand(position, forward, this.queryParameters, 1000); // 准备 RaycastCommand
}
}
public struct TransData
{
public float3 localPosition;
public quaternion localRotation;
}
[BurstCompile]
struct HandleHitResultJob : IJob
{
[ReadOnly] public NativeArray<RaycastHit> hitResults;
[ReadOnly] public NativeArray<TransData> rayCaster;
[ReadOnly] public float3 parentPos;
[ReadOnly] public quaternion parentRot;
public NativeArray<ZPointInfo> frameInfo;
public NativeArray<int> hitCount;
public void Execute()
{
for (var index = 0; index < this.hitResults.Length; index++)
{
var hit = this.hitResults[index];
var isHit = hit.colliderInstanceID != 0;
if (!isHit)
{
continue; // 若未命中碰撞体,则跳过
}
var transData = this.rayCaster[index];
var quaternion = math.mul(parentRot, transData.localRotation);
var forward = math.mul(quaternion, math.forward());
var dir = math.abs(math.dot(hit.normal, forward));
this.frameInfo[this.hitCount[0]++] = new ZPointInfo(hit.point, dir);
}
}
}
#region mD
[StructLayout(LayoutKind.Sequential)]
public struct ZPointInfo
{
private float3 _point;
private float _dir;
public ZPointInfo(float3 point, float dir)
{
this._point = point;
this._dir = dir;
}
public float3 point
{
get => this._point;
set => this._point = value;
}
}
#endregion
}