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 results; private NativeArray particles; private NativeArray commands; private NativeArray rayCaster; private NativeArray flattenedPointsInfo; private bool _isRunning; private List _renderers = new List(150000); private NativeArray _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()); // } rayCount = horizontalRaysCount * verticalRaysCount; _pointsDrawer = new PointsDrawer(); _pointsDrawer.Setup(_mesh, rayCount, 1000, _mat); results = new NativeArray(rayCount, Allocator.Persistent); commands = new NativeArray(rayCount, Allocator.Persistent); rayCaster = new NativeArray(rayCount, Allocator.Persistent); particles = new NativeArray(rayCount, Allocator.Persistent); flattenedPointsInfo = new NativeArray(rayCount, Allocator.Persistent); // 初始化存储点信息的列表,预先分配空间 this._hitCountArray = new NativeArray(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(); } } /// /// 将垂直FOV转成水平FOV /// /// 垂直FOV值 /// 屏幕分辨率比例 /// 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 rayCaster; [ReadOnly] public float3 parentPos; [ReadOnly] public quaternion parentRot; public NativeArray 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 hitResults; [ReadOnly] public NativeArray rayCaster; [ReadOnly] public float3 parentPos; [ReadOnly] public quaternion parentRot; public NativeArray frameInfo; public NativeArray 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 }