C - Count Triangles

本文介绍了一种算法,用于计算给定整数范围内满足特定条件的非退化三角形的数量。该算法通过枚举三角形两边之和并计算第三边的有效范围来实现。

Like any unknown mathematician, Yuri has favourite numbers: A, B, C, and D, where A≤B≤C≤D. Yuri also likes triangles and once he thought: how many non-degenerate triangles with integer sides x, y, and z exist, such that A≤x≤B≤y≤C≤z≤D holds?

Yuri is preparing problems for a new contest now, so he is very busy. That’s why he asked you to calculate the number of triangles with described property.

The triangle is called non-degenerate if and only if its vertices are not collinear.

Input
The first line contains four integers: A, B, C and D (1≤A≤B≤C≤D≤5⋅105) — Yuri’s favourite numbers.

Output
Print the number of non-degenerate triangles with integer sides x, y, and z such that the inequality A≤x≤B≤y≤C≤z≤D holds.

Examples
Input
1 2 3 4
Output
4
Input
1 2 2 5
Output
3
Input
500000 500000 500000 500000
Output
1
Note
In the first example Yuri can make up triangles with sides (1,3,3), (2,2,3), (2,3,3) and (2,3,4).

In the second example Yuri can make up triangles with sides (1,2,2), (2,2,2) and (2,2,3).

In the third example Yuri can make up only one equilateral triangle with sides equal to 5⋅105.

首先固定x+y为i那么z的取值范围便是c~min(d,i-1);
而对于x和y的范围,当i<=2*b时固定y为b则x=i-b(大于同理),然后减小x的值在固定i的条件下y就要增加,这样x和y的最小活动范围长度就是x和y的方案数
令x和y的方案数与z的方案数相乘便得到了x+y==i的方案数
枚举i的取值区间并加和即可得到所有方案数

#include<iostream>
using namespace std;
int main()
{
	int a,b,c,d,x,y,z;
	long long ans=0;
	cin>>a>>b>>c>>d;
	for(int i=a+b;i<=b+c;i++)
	{
		z=min(d,i-1);
		if(z<c) continue;
		z=z-c+1;
		if(i<=2*b)
		{
			x=i-b;
			ans+=1ll*(min(x-a,c-b)+1)*z;
		}
		else
		{
			y=i-b;
			ans+=1ll*(min(b-a,c-y)+1)*z;
		}
	}
	cout<<ans<<endl;
}
// Hemisphere.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; [System.Serializable] public struct RADAR { [Header("雷达参数")] public float P_t; // 发射功率(W) public float G; // 天线增益 public float σ; // 目标截面积(m²) public float S_min; // 最小可检测信号功率(W) public float f; // 频率(Hz) public float c; // 光速 public RADAR(float pT, float g, float sigma, float sMin, float frequency, float speedOfLight) { P_t = pT; G = g; σ = sigma; S_min = sMin; f = frequency; c = speedOfLight; } } public class Hemisphere : MonoBehaviour { [Header("半球设置")] [SerializeField] private int longitudeSegments = 20; [SerializeField] private int latitudeSegments = 20; public float radius; [Header("扇形设置")] [SerializeField] public float sectorAngle = Mathf.PI / 2; //扇形高度(弧度) [Range(3, 30)] [SerializeField] private int sectorSegments = 8; [SerializeField] public float rotateSpeed = 1f; [SerializeField] [Range(0.01f, 0.5f)] private float sectorThickness = 0.1f; [SerializeField] public float ScallopingAngleRadian = 1f; // 扇形宽度(弧度) [SerializeField] public float initialAngleOffsetAfter = Mathf.PI / 18f; // 第二个扇形从Z轴向左偏转的角度 public float currentRotation = 0f; private readonly float initialAngleOffset = Mathf.PI / 2f; // 从Z轴正方向开始 // 扫描完成状态标识 public bool IsSweepCompleted { get; private set; } [Header("雷达参数")] [SerializeField] public RADAR radarParams; [Header("渲染设置")] [SerializeField] private Material hemisphereMaterial; [SerializeField] private Material sectorMaterial; [SerializeField] private Material sectorMaterial2; // 第二个扇形的材质,可选 [SerializeField] private Material sideMaterial; // 侧面材质 [Header("碰撞体优化")] [SerializeField] private int collisionMeshMaxVertices = 16; // 组件引用 - 第一个扇形 private MeshFilter hemisphereMeshFilter; private MeshRenderer hemisphereMeshRenderer; private MeshCollider hemisphereCollider; private GameObject sectorObject; private MeshFilter sectorMeshFilter; private MeshRenderer sectorMeshRenderer; private MeshCollider sectorCollider; // 组件引用 - 第二个扇形 private GameObject sectorObject2; private MeshFilter sectorMeshFilter2; private MeshRenderer sectorMeshRenderer2; private MeshCollider sectorCollider2; // 组件引用 - 侧面连接 private GameObject sideObject; private MeshFilter sideMeshFilter; private MeshRenderer sideMeshRenderer; private MeshCollider sideCollider; private GameObject zxSectorObject1; private MeshFilter zxSectorMeshFilter1; private MeshRenderer zxSectorMeshRenderer1; private MeshCollider zxSectorCollider1; private GameObject zxSectorObject2; private MeshFilter zxSectorMeshFilter2; private MeshRenderer zxSectorMeshRenderer2; private MeshCollider zxSectorCollider2; [Header("Z-X平面扇形Y轴偏移")] [SerializeField] private float zxSector2YOffset = 1.0f; // Z-X平面扇形简化碰撞网格(优化性能) private Mesh simplifiedZxSectorMesh1; private Mesh simplifiedZxSectorMesh2; // 跟踪目标信息类 public class TrackedObjectInfo { public float lastDetectedAngle; // 最后检测到的角度 public Vector3 lastLocalPosition; // 最后检测到的本地位置 public bool detectedInCurrentSweep = false; // 当前扫描是否被检测到 public int missedSweeps = 0; //未检测到的扫描周期数 } // 跟踪所有目标的字典 public Dictionary<GameObject, TrackedObjectInfo> trackedObjects = new Dictionary<GameObject, TrackedObjectInfo>(); private float previousRotation = 0f; // 上一帧的旋转角度,用于判断是否完成一圈 private Mesh simplifiedSectorMesh; private Mesh simplifiedSectorMesh2; private Mesh simplifiedSideMesh; void Start() { // 初始化雷达默认参数 if (radarParams.c == 0) { radarParams = new RADAR(1.0f, 10.0f, 0.01f, 6.3e-9f, 2.4e9f, 3e8f); } // 限制参数范围 sectorSegments = Mathf.Max(3, sectorSegments); collisionMeshMaxVertices = Mathf.Max(3, collisionMeshMaxVertices); sectorAngle = Mathf.Max(0.01f, sectorAngle); initialAngleOffsetAfter = Mathf.Max(0.01f, initialAngleOffsetAfter); // 初始化组件 InitHemisphereComponents(); CreateSector(); CreateSector2(); // 创建第二个扇形 CreateSideObject(); // 创建侧面连接对象 //CreateZxSector1(); CreateZxSector2(); UpdateSector(); UpdateSector2(); // 更新第二个扇形 UpdateSideMesh(); // 更新侧面网格 // 初始化扫描状态 IsSweepCompleted = false; } void Update() { sectorAngle = Mathf.Max(0.01f, sectorAngle); initialAngleOffsetAfter = Mathf.Max(0.01f, initialAngleOffsetAfter); // 更新旋转和网格 previousRotation = currentRotation; currentRotation = currentRotation - rotateSpeed * Time.deltaTime; currentRotation = Mathf.Repeat(currentRotation, 2 * Mathf.PI); UpdateHemisphere(); UpdateSector(); UpdateSector2(); // 更新第二个扇形 UpdateSideMesh(); // 更新侧面网格 //UpdateZxSector1(); UpdateZxSector2(); // 检测是否完成一圈扫描 IsSweepCompleted = currentRotation > previousRotation; if (IsSweepCompleted) { CleanupUntrackedObjects(); } } /// <summary> /// 创建第一个Z-X平面扇形(初始化GameObject与组件) /// </summary> public void CreateZxSector1() { // 1. 创建扇形GameObject,设置父物体(跟随雷达旋转) zxSectorObject1 = new GameObject("ZxPlaneSector1"); zxSectorObject1.transform.SetParent(transform); zxSectorObject1.transform.localPosition = Vector3.zero; // 与雷达中心对齐 // 2. 添加MeshFilter(存储网格)、MeshRenderer(渲染)、MeshCollider(碰撞) zxSectorMeshFilter1 = zxSectorObject1.AddComponent<MeshFilter>(); zxSectorMeshRenderer1 = zxSectorObject1.AddComponent<MeshRenderer>(); zxSectorCollider1 = zxSectorObject1.AddComponent<MeshCollider>(); // 碰撞体设置:凸包(支持触发器)、启用触发器(用于目标检测) zxSectorCollider1.convex = true; zxSectorCollider1.isTrigger = true; // 3. 赋值材质(优先使用原扇形材质,无则提示) if (sectorMaterial != null) zxSectorMeshRenderer1.material = sectorMaterial; else Debug.LogWarning("Z-X平面扇形1:请指定sectorMaterial材质"); } /// <summary> /// 创建第二个Z-X平面扇形(逻辑与第一个一致,仅角度偏移不同) /// </summary> public void CreateZxSector2() { zxSectorObject2 = new GameObject("ZxPlaneSector2"); zxSectorObject2.transform.SetParent(transform); zxSectorObject2.transform.localPosition = Vector3.zero; zxSectorMeshFilter2 = zxSectorObject2.AddComponent<MeshFilter>(); zxSectorMeshRenderer2 = zxSectorObject2.AddComponent<MeshRenderer>(); zxSectorCollider2 = zxSectorObject2.AddComponent<MeshCollider>(); zxSectorCollider2.convex = true; zxSectorCollider2.isTrigger = true; // 材质:优先使用sectorMaterial2,无则复用sectorMaterial if (sectorMaterial2 != null) zxSectorMeshRenderer2.material = sectorMaterial2; else if (sectorMaterial != null) zxSectorMeshRenderer2.material = sectorMaterial; else Debug.LogWarning("Z-X平面扇形2:请指定sectorMaterial或sectorMaterial2材质"); } /// <summary> /// 更新第一个Z-X平面扇形(旋转、半径、碰撞体) /// </summary> private void UpdateZxSector1() { if (radius < 0.1f) return; // 关键修改:在原有旋转角度基础上,增加向右旋转的偏移(+initialAngleOffsetAfter) // 向右旋转在数学上表现为角度值增加(Y轴正方向旋转) float adjustedRotation = currentRotation + initialAngleOffset - initialAngleOffsetAfter; Mesh displayMesh = GenerateZxPlaneSectorMesh(sectorSegments, radius, adjustedRotation); zxSectorMeshFilter1.mesh = displayMesh; Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedZxSectorMesh1 = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedZxSectorMesh1) ? simplifiedZxSectorMesh1 : CreateZxFallbackCollisionMesh(radius, adjustedRotation); try { zxSectorCollider1.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"Z-X扇形1碰撞体设置失败: {e.Message},使用安全备用网格"); zxSectorCollider1.sharedMesh = CreateZxSafeFallbackMesh(radius, adjustedRotation); } } /// <summary> /// 更新第二个Z-X平面扇形(仅角度偏移不同,其余逻辑与第一个一致) /// </summary> private void UpdateZxSector2() { if (radius < 0.1f) return; float adjustedRotation = currentRotation + initialAngleOffset - initialAngleOffsetAfter; // 使用带Y轴偏移的网格生成函数 Mesh displayMesh = GenerateZxPlaneSector2Mesh(sectorSegments, radius, adjustedRotation); zxSectorMeshFilter2.mesh = displayMesh; Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedZxSectorMesh2 = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); // 使用带偏移的备用碰撞网格 Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedZxSectorMesh2) ? simplifiedZxSectorMesh2 : CreateZxSector2FallbackCollisionMesh(radius, adjustedRotation); try { zxSectorCollider2.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"带偏移的Z-X扇形2碰撞体设置失败: {e.Message},使用安全备用网格"); // 安全备用网格也需要添加Y轴偏移 zxSectorCollider2.sharedMesh = CreateZxSector2SafeFallbackMesh(radius, adjustedRotation); } } private Mesh GenerateZxPlaneSector2Mesh(int segments, float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 关键修改:使用initialAngleOffsetAfter作为扇形角度 float angleRange = initialAngleOffsetAfter; // 外边缘顶点(带Y轴偏移) for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), // Z坐标 zxSector2YOffset, // Y轴偏移 r * Mathf.Sin(angle) // X坐标 ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } int outerVertexCount = segments + 1; // 内边缘顶点(带Y轴偏移) for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), zxSector2YOffset, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 中心点(带Y轴偏移) int centerVertexIndex = outerVertexCount * 2; Vector3 centerWithOffset = new Vector3(0, zxSector2YOffset, 0); vertices.Add(RotateAroundYAxis(centerWithOffset, rotationAngle)); // 生成三角形 for (int i = 0; i < segments; i++) { triangles.AddRange(new[] { centerVertexIndex, i, i + 1 }); int innerCurrent = outerVertexCount + i; int innerNext = outerVertexCount + i + 1; triangles.AddRange(new[] { centerVertexIndex, innerNext, innerCurrent }); triangles.AddRange(new[] { i, innerCurrent, i + 1 }); triangles.AddRange(new[] { i + 1, innerCurrent, innerNext }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } // 3. 为偏移扇形创建专用备用碰撞网格 private Mesh CreateZxSector2FallbackCollisionMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); int segments = 6; // 使用initialAngleOffsetAfter作为角度范围 float angleRange = initialAngleOffsetAfter; float angleStep = angleRange / (segments - 1); // 外边缘顶点(带Y轴偏移) for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), zxSector2YOffset, // Y轴偏移 r * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 内边缘顶点(带Y轴偏移) int innerOffset = segments; for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), zxSector2YOffset, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 生成三角形 for (int i = 0; i < segments - 1; i++) { triangles.AddRange(new[] { 0, i, i + 1 }); triangles.AddRange(new[] { innerOffset, innerOffset + i + 1, innerOffset + i }); triangles.AddRange(new[] { i, innerOffset + i, i + 1 }); triangles.AddRange(new[] { i + 1, innerOffset + i, innerOffset + i + 1 }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } // 为偏移扇形创建安全备用网格 private Mesh CreateZxSector2SafeFallbackMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 带Y轴偏移的核心顶点 Vector3 centerWithOffset = new Vector3(0, zxSector2YOffset, 0); vertices.Add(RotateAroundYAxis(centerWithOffset, rotationAngle)); // 中心 vertices.Add(RotateAroundYAxis(new Vector3(r, zxSector2YOffset, 0), rotationAngle)); // 外点1 vertices.Add(RotateAroundYAxis(new Vector3( r * Mathf.Cos(sectorAngle), zxSector2YOffset, r * Mathf.Sin(sectorAngle) ), rotationAngle)); // 外点2 vertices.Add(RotateAroundYAxis(new Vector3(innerRadius, zxSector2YOffset, 0), rotationAngle)); // 内点1 vertices.Add(RotateAroundYAxis(new Vector3( innerRadius * Mathf.Cos(sectorAngle), zxSector2YOffset, innerRadius * Mathf.Sin(sectorAngle) ), rotationAngle)); // 内点2 // 三角形组合 int[] triangles = { 0, 1, 2, // 外扇面 0, 3, 4, // 内扇面 1, 3, 4, // 侧面1 1, 4, 2 // 侧面2 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } private Mesh GenerateZxPlaneSectorMesh(int segments, float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 关键修改:使用initialAngleOffsetAfter作为扇形角度,而非sectorAngle float angleRange = initialAngleOffsetAfter; // 外边缘顶点 for (int i = 0; i <= segments; i++) { // 角度范围从0到initialAngleOffsetAfter float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), // Z坐标 0, // Y坐标 r * Mathf.Sin(angle) // X坐标 ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } int outerVertexCount = segments + 1; // 内边缘顶点 for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), 0, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 中心点 int centerVertexIndex = outerVertexCount * 2; vertices.Add(RotateAroundYAxis(Vector3.zero, rotationAngle)); // 生成三角形 for (int i = 0; i < segments; i++) { triangles.AddRange(new[] { centerVertexIndex, i, i + 1 }); int innerCurrent = outerVertexCount + i; int innerNext = outerVertexCount + i + 1; triangles.AddRange(new[] { centerVertexIndex, innerNext, innerCurrent }); triangles.AddRange(new[] { i, innerCurrent, i + 1 }); triangles.AddRange(new[] { i + 1, innerCurrent, innerNext }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } /// <summary> /// Z-X平面扇形备用碰撞网格(6段简化扇形) /// </summary> private Mesh CreateZxFallbackCollisionMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); int segments = 6; // 使用initialAngleOffsetAfter作为角度范围 float angleRange = initialAngleOffsetAfter; float angleStep = angleRange / (segments - 1); // 外边缘顶点 for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), 0, r * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 内边缘顶点 int innerOffset = segments; for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), 0, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 生成三角形 for (int i = 0; i < segments - 1; i++) { triangles.AddRange(new[] { 0, i, i + 1 }); triangles.AddRange(new[] { innerOffset, innerOffset + i + 1, innerOffset + i }); triangles.AddRange(new[] { i, innerOffset + i, i + 1 }); triangles.AddRange(new[] { i + 1, innerOffset + i, innerOffset + i + 1 }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } /// <summary> /// Z-X平面扇形安全备用网格(四面体,极端容错) /// </summary> private Mesh CreateZxSafeFallbackMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 使用initialAngleOffsetAfter作为角度范围(保持之前的修改) float angleRange = initialAngleOffsetAfter; // 核心顶点(应用了向右旋转偏移) vertices.Add(RotateAroundYAxis(Vector3.zero, rotationAngle)); // 中心 // 外点1(0角度位置) vertices.Add(RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle)); // 外点2(angleRange角度位置) vertices.Add(RotateAroundYAxis(new Vector3( r * Mathf.Cos(angleRange), 0, r * Mathf.Sin(angleRange) ), rotationAngle)); // 内点1(0角度位置) vertices.Add(RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle)); // 内点2(angleRange角度位置) vertices.Add(RotateAroundYAxis(new Vector3( innerRadius * Mathf.Cos(angleRange), 0, innerRadius * Mathf.Sin(angleRange) ), rotationAngle)); // 三角形组合 int[] triangles = { 0, 1, 2, // 外扇面 0, 3, 4, // 内扇面 1, 3, 4, // 侧面1 1, 4, 2 // 侧面2 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } private Vector3 RotateAroundXAxis(Vector3 point, float angle) { float cosAngle = Mathf.Cos(angle); float sinAngle = Mathf.Sin(angle); // 应用X轴旋转矩阵 return new Vector3( point.x, // X坐标不变 point.y * cosAngle - point.z * sinAngle, // Y坐标变换 point.y * sinAngle + point.z * cosAngle // Z坐标变换 ); } private void CleanupUntrackedObjects() { List<GameObject> toRemove = new List<GameObject>(); foreach (var it in trackedObjects) { if (!it.Value.detectedInCurrentSweep) { it.Value.missedSweeps++; if (it.Value.missedSweeps >= 1) { toRemove.Add(it.Key); } } else { it.Value.missedSweeps = 0; } it.Value.detectedInCurrentSweep = false; } foreach (var obj in toRemove) { trackedObjects.Remove(obj); } } // 组件初始化 private void InitHemisphereComponents() { hemisphereMeshFilter = GetComponent<MeshFilter>() ?? gameObject.AddComponent<MeshFilter>(); hemisphereMeshRenderer = GetComponent<MeshRenderer>() ?? gameObject.AddComponent<MeshRenderer>(); hemisphereCollider = GetComponent<MeshCollider>() ?? gameObject.AddComponent<MeshCollider>(); if (hemisphereMaterial != null) hemisphereMeshRenderer.material = hemisphereMaterial; else Debug.LogWarning("请指定半球材质"); } private void CreateSector() { sectorObject = new GameObject("RotatingSector"); sectorObject.transform.SetParent(transform); sectorObject.transform.localPosition = Vector3.zero; sectorMeshFilter = sectorObject.AddComponent<MeshFilter>(); sectorMeshRenderer = sectorObject.AddComponent<MeshRenderer>(); sectorCollider = sectorObject.AddComponent<MeshCollider>(); sectorCollider.convex = true; sectorCollider.isTrigger = true; if (sectorMaterial != null) sectorMeshRenderer.material = sectorMaterial; else Debug.LogWarning("请指定扇形材质"); } // 创建第二个扇形 private void CreateSector2() { sectorObject2 = new GameObject("RotatingSector2"); sectorObject2.transform.SetParent(transform); sectorObject2.transform.localPosition = Vector3.zero; sectorMeshFilter2 = sectorObject2.AddComponent<MeshFilter>(); sectorMeshRenderer2 = sectorObject2.AddComponent<MeshRenderer>(); sectorCollider2 = sectorObject2.AddComponent<MeshCollider>(); sectorCollider2.convex = true; sectorCollider2.isTrigger = true; // 使用指定的第二个材质或默认使用第一个材质 if (sectorMaterial2 != null) sectorMeshRenderer2.material = sectorMaterial2; else if (sectorMaterial != null) sectorMeshRenderer2.material = sectorMaterial; else Debug.LogWarning("请指定扇形材质"); } // 创建侧面连接对象 private void CreateSideObject() { sideObject = new GameObject("SideConnection"); sideObject.transform.SetParent(transform); sideObject.transform.localPosition = Vector3.zero; sideMeshFilter = sideObject.AddComponent<MeshFilter>(); sideMeshRenderer = sideObject.AddComponent<MeshRenderer>(); sideCollider = sideObject.AddComponent<MeshCollider>(); sideCollider.convex = true; sideCollider.isTrigger = true; if (sideMaterial != null) sideMeshRenderer.material = sideMaterial; else if (sectorMaterial != null) sideMeshRenderer.material = sectorMaterial; else Debug.LogWarning("请指定侧面材质"); } public void UpdateHemisphere() { radius = Mathf.Max(0.1f, RadarScanningRadius(radarParams)); Mesh mesh = GenerateHemisphereMesh(longitudeSegments, latitudeSegments, radius); hemisphereCollider.sharedMesh = mesh; hemisphereMeshFilter.mesh = mesh; } public void UpdateSector() { if (radius < 0.1f) return; float adjustedRotation = currentRotation + initialAngleOffset; Mesh displayMesh = GenerateSectorMesh(sectorSegments, radius, adjustedRotation); sectorMeshFilter.mesh = displayMesh; // 处理碰撞体网格 Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedSectorMesh = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedSectorMesh) ? simplifiedSectorMesh : CreateFallbackCollisionMesh(radius, adjustedRotation); try { sectorCollider.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"碰撞体设置失败: {e.Message},使用备用网格"); sectorCollider.sharedMesh = CreateSafeFallbackMesh(radius, adjustedRotation); } } // 更新第二个扇形 private void UpdateSector2() { if (radius < 0.1f) return; // 第二个扇形从Z轴向左偏转initialAngleOffsetAfter弧度 float adjustedRotation = currentRotation + initialAngleOffset - initialAngleOffsetAfter; Mesh displayMesh = GenerateSectorMesh(sectorSegments, radius, adjustedRotation); sectorMeshFilter2.mesh = displayMesh; // 处理碰撞体网格 Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedSectorMesh2 = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedSectorMesh2) ? simplifiedSectorMesh2 : CreateFallbackCollisionMesh(radius, adjustedRotation); try { sectorCollider2.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"第二个扇形碰撞体设置失败: {e.Message},使用备用网格"); sectorCollider2.sharedMesh = CreateSafeFallbackMesh(radius, adjustedRotation); } } // 更新侧面连接网格 private void UpdateSideMesh() { if (radius < 0.1f) return; float adjustedRotation1 = currentRotation + initialAngleOffset; float adjustedRotation2 = currentRotation + initialAngleOffset - initialAngleOffsetAfter; Mesh sideMesh = GenerateSideConnectionMesh(sectorSegments, radius, adjustedRotation1, adjustedRotation2); sideMeshFilter.mesh = sideMesh; // 处理碰撞体网格 Mesh cleanedMesh = CleanMeshVertices(sideMesh); simplifiedSideMesh = SimplifySideMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedSideMesh) ? simplifiedSideMesh : CreateSideFallbackCollisionMesh(radius, adjustedRotation1, adjustedRotation2); try { sideCollider.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"侧面碰撞体设置失败: {e.Message},使用备用网格"); sideCollider.sharedMesh = CreateSideSafeFallbackMesh(radius, adjustedRotation1, adjustedRotation2); } } private Mesh GenerateHemisphereMesh(int lonSeg, int latSeg, float r) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); // 生成顶点 for (int i = 0; i <= lonSeg; i++) { float lonAngle = Mathf.Lerp(0, 2 * Mathf.PI, (float)i / lonSeg); for (int j = 0; j <= latSeg; j++) { float latAngle = Mathf.PI / 2 * (1 - (float)j / latSeg); vertices.Add(new Vector3( r * Mathf.Sin(latAngle) * Mathf.Cos(lonAngle), r * Mathf.Cos(latAngle), r * Mathf.Sin(latAngle) * Mathf.Sin(lonAngle) )); } } // 生成三角形 for (int i = 0; i < lonSeg; i++) { for (int j = 0; j < latSeg; j++) { int topLeft = i * (latSeg + 1) + j; int topRight = (i + 1) * (latSeg + 1) + j; int bottomLeft = i * (latSeg + 1) + (j + 1); int bottomRight = (i + 1) * (latSeg + 1) + (j + 1); triangles.AddRange(new[] { topLeft, bottomLeft, topRight, topRight, bottomLeft, bottomRight }); } } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } private Mesh GenerateSectorMesh(int segments, float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 外边缘顶点 for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); vertices.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), r * Mathf.Sin(angle), 0), rotationAngle)); } int outerCount = segments + 1; // 内边缘顶点 for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); vertices.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), innerRadius * Mathf.Sin(angle), 0), rotationAngle)); } // 中心点 int originIndex = outerCount * 2; vertices.Add(RotateAroundYAxis(Vector3.zero, rotationAngle)); // 生成三角形 for (int i = 0; i < segments; i++) { // 外扇形面 triangles.AddRange(new[] { originIndex, i, i + 1 }); // 内扇形面 triangles.AddRange(new[] { originIndex, outerCount + i + 1, outerCount + i }); // 侧面 triangles.AddRange(new[] { i, outerCount + i, i + 1 }); triangles.AddRange(new[] { i + 1, outerCount + i, outerCount + i + 1 }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } // 生成连接两个扇形的侧面网格 private Mesh GenerateSideConnectionMesh(int segments, float r, float rotationAngle1, float rotationAngle2) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 获取第一个扇形的顶点 List<Vector3> sector1Outer = new List<Vector3>(); List<Vector3> sector1Inner = new List<Vector3>(); for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); sector1Outer.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), r * Mathf.Sin(angle), 0), rotationAngle1)); sector1Inner.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), innerRadius * Mathf.Sin(angle), 0), rotationAngle1)); } // 获取第二个扇形的顶点 List<Vector3> sector2Outer = new List<Vector3>(); List<Vector3> sector2Inner = new List<Vector3>(); for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); sector2Outer.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), r * Mathf.Sin(angle), 0), rotationAngle2)); sector2Inner.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), innerRadius * Mathf.Sin(angle), 0), rotationAngle2)); } // 添加顶点 vertices.AddRange(sector1Outer); vertices.AddRange(sector1Inner); vertices.AddRange(sector2Outer); vertices.AddRange(sector2Inner); int sector1OuterStart = 0; int sector1InnerStart = segments + 1; int sector2OuterStart = (segments + 1) * 2; int sector2InnerStart = (segments + 1) * 3; // 生成连接两个扇形外侧的三角形 for (int i = 0; i < segments; i++) { triangles.Add(sector1OuterStart + i); triangles.Add(sector2OuterStart + i); triangles.Add(sector1OuterStart + i + 1); triangles.Add(sector1OuterStart + i + 1); triangles.Add(sector2OuterStart + i); triangles.Add(sector2OuterStart + i + 1); } // 生成连接两个扇形内侧的三角形 for (int i = 0; i < segments; i++) { triangles.Add(sector1InnerStart + i); triangles.Add(sector1InnerStart + i + 1); triangles.Add(sector2InnerStart + i); triangles.Add(sector1InnerStart + i + 1); triangles.Add(sector2InnerStart + i + 1); triangles.Add(sector2InnerStart + i); } // 生成连接两个扇形端面的三角形 triangles.Add(sector1OuterStart); triangles.Add(sector1InnerStart); triangles.Add(sector2OuterStart); triangles.Add(sector1InnerStart); triangles.Add(sector2InnerStart); triangles.Add(sector2OuterStart); triangles.Add(sector1OuterStart + segments); triangles.Add(sector2OuterStart + segments); triangles.Add(sector1InnerStart + segments); triangles.Add(sector1InnerStart + segments); triangles.Add(sector2OuterStart + segments); triangles.Add(sector2InnerStart + segments); mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } private Mesh CleanMeshVertices(Mesh originalMesh) { Mesh cleanedMesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); Dictionary<Vector3, int> vertexMap = new Dictionary<Vector3, int>(); List<int> newTriangles = new List<int>(); // 顶点去重 foreach (Vector3 v in originalMesh.vertices) { Vector3 rounded = new Vector3( Mathf.Round(v.x * 10000) / 10000, Mathf.Round(v.y * 10000) / 10000, Mathf.Round(v.z * 10000) / 10000 ); if (!vertexMap.ContainsKey(rounded)) { vertexMap[rounded] = vertices.Count; vertices.Add(rounded); } } // 过滤无效三角形 int[] originalTris = originalMesh.triangles; for (int i = 0; i < originalTris.Length; i += 3) { Vector3 p0 = originalMesh.vertices[originalTris[i]]; Vector3 p1 = originalMesh.vertices[originalTris[i + 1]]; Vector3 p2 = originalMesh.vertices[originalTris[i + 2]]; // 过滤面积过小的三角形 if (Vector3.Cross(p1 - p0, p2 - p0).magnitude < 0.0001f) continue; // 映射新顶点索引 if (TryGetMappedIndex(vertexMap, p0, out int n0) && TryGetMappedIndex(vertexMap, p1, out int n1) && TryGetMappedIndex(vertexMap, p2, out int n2)) { newTriangles.AddRange(new[] { n0, n1, n2 }); } } cleanedMesh.vertices = vertices.ToArray(); cleanedMesh.triangles = newTriangles.ToArray(); cleanedMesh.RecalculateNormals(); cleanedMesh.RecalculateBounds(); return cleanedMesh; } private Mesh SimplifyMesh(Mesh originalMesh, int maxVertices) { maxVertices = Mathf.Clamp(maxVertices, 3, 30); if (originalMesh.vertexCount <= maxVertices) return originalMesh; Mesh simplified = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float totalAngle = sectorAngle; float angleStep = totalAngle / (maxVertices - 1); int halfCount = originalMesh.vertexCount / 2; // 采样外边缘顶点 for (int i = 0; i < maxVertices / 2; i++) { int index = FindClosestVertexByAngle(originalMesh.vertices, 0, halfCount, i * angleStep, currentRotation + initialAngleOffset); vertices.Add(originalMesh.vertices[index]); int inde1x = FindClosestVertexByAngle(originalMesh.vertices, 0, halfCount, i * angleStep, currentRotation + initialAngleOffsetAfter); vertices.Add(originalMesh.vertices[inde1x]); } // 采样内边缘顶点 for (int i = 0; i < maxVertices - vertices.Count; i++) { int index = FindClosestVertexByAngle(originalMesh.vertices, halfCount, originalMesh.vertexCount, i * angleStep, currentRotation + initialAngleOffset); vertices.Add(originalMesh.vertices[index]); int inde1x = FindClosestVertexByAngle(originalMesh.vertices, halfCount, originalMesh.vertexCount, i * angleStep, currentRotation + initialAngleOffsetAfter); vertices.Add(originalMesh.vertices[inde1x]); } // 确保顶点数量不超标 if (vertices.Count > maxVertices) vertices.RemoveRange(maxVertices, vertices.Count - maxVertices); // 按角度排序 vertices.Sort((a, b) => Mathf.Atan2(a.z, a.x).CompareTo(Mathf.Atan2(b.z, b.x)) ); GenerateConvexTriangles(vertices, triangles); simplified.vertices = vertices.ToArray(); simplified.triangles = triangles.ToArray(); simplified.RecalculateNormals(); simplified.RecalculateBounds(); return simplified; } // 简化侧面网格 private Mesh SimplifySideMesh(Mesh originalMesh, int maxVertices) { maxVertices = Mathf.Clamp(maxVertices, 6, 30); if (originalMesh.vertexCount <= maxVertices) return originalMesh; // 对于侧面网格,我们只需要保留关键顶点 Mesh simplified = new Mesh(); List<Vector3> vertices = new List<Vector3>(); // 获取原始网格的边界点 Bounds bounds = originalMesh.bounds; vertices.Add(bounds.min); vertices.Add(new Vector3(bounds.min.x, bounds.min.y, bounds.max.z)); vertices.Add(new Vector3(bounds.max.x, bounds.min.y, bounds.min.z)); vertices.Add(bounds.max); vertices.Add(new Vector3(bounds.min.x, bounds.max.y, bounds.min.z)); vertices.Add(new Vector3(bounds.max.x, bounds.max.y, bounds.min.z)); vertices.Add(new Vector3(bounds.min.x, bounds.max.y, bounds.max.z)); vertices.Add(bounds.max); // 生成凸包三角形 List<int> triangles = new List<int>(); GenerateConvexTriangles(vertices, triangles); simplified.vertices = vertices.ToArray(); simplified.triangles = triangles.ToArray(); simplified.RecalculateNormals(); simplified.RecalculateBounds(); return simplified; } private bool IsValidConvexMesh(Mesh mesh) { if (!IsValidCollisionMesh(mesh)) return false; // 检查法向量一致性 Vector3[] normals = mesh.normals; if (normals.Length == 0) return false; Vector3 firstNormal = normals[0]; foreach (Vector3 normal in normals) { if (Vector3.Dot(firstNormal, normal) < 0.9f) return false; } return true; } private bool IsValidCollisionMesh(Mesh mesh) { if (mesh == null || mesh.vertexCount < 3 || mesh.triangles.Length < 3) return false; // 检查至少有3个不同顶点 HashSet<Vector3> uniqueVertices = new HashSet<Vector3>(); foreach (Vector3 v in mesh.vertices) { uniqueVertices.Add(v); if (uniqueVertices.Count >= 3) break; } return uniqueVertices.Count >= 3; } private Mesh CreateFallbackCollisionMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); int segments = 6; float VerticalAngleStep = sectorAngle / (segments - 1);//竖直角度步长 // 外边缘顶点 for (int i = 0; i < segments; i++) { float angle = i * VerticalAngleStep; vertices.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), 0, r * Mathf.Sin(angle)), rotationAngle)); } // 内边缘顶点 int innerOffset = segments; for (int i = 0; i < segments; i++) { float angle = i * VerticalAngleStep; vertices.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), 0, innerRadius * Mathf.Sin(angle)), rotationAngle)); } // 生成三角形 for (int i = 0; i < segments - 1; i++) { triangles.AddRange(new[] { 0, i, i + 1 }); // 外扇面 triangles.AddRange(new[] { innerOffset, innerOffset + i + 1, innerOffset + i }); // 内扇面 triangles.AddRange(new[] { i, innerOffset + i, i + 1 }); // 侧面1 triangles.AddRange(new[] { i + 1, innerOffset + i, innerOffset + i + 1 }); // 侧面2 } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } private Mesh CreateSafeFallbackMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 核心顶点 vertices.Add(Vector3.zero); // 中心 vertices.Add(RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle)); // 外点1 vertices.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(sectorAngle), 0, r * Mathf.Sin(sectorAngle)), rotationAngle)); // 外点2 vertices.Add(RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle)); // 内点1 vertices.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(sectorAngle), 0, innerRadius * Mathf.Sin(sectorAngle)), rotationAngle)); // 内点2 // 简单三角形组合 int[] triangles = { 0, 1, 2, // 外扇面 0, 3, 4, // 内扇面 1, 3, 4, // 侧面1 1, 4, 2 // 侧面2 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } // 创建侧面备用碰撞体网格 private Mesh CreateSideFallbackCollisionMesh(float r, float rotationAngle1, float rotationAngle2) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 获取关键点 Vector3 outer1Start = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle1); Vector3 outer1End = RotateAroundYAxis(new Vector3(r * Mathf.Cos(sectorAngle), 0, r * Mathf.Sin(sectorAngle)), rotationAngle1); Vector3 inner1Start = RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle1); Vector3 inner1End = RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(sectorAngle), 0, innerRadius * Mathf.Sin(sectorAngle)), rotationAngle1); Vector3 outer2Start = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle2); Vector3 outer2End = RotateAroundYAxis(new Vector3(r * Mathf.Cos(sectorAngle), 0, r * Mathf.Sin(sectorAngle)), rotationAngle2); Vector3 inner2Start = RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle2); Vector3 inner2End = RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(sectorAngle), 0, innerRadius * Mathf.Sin(sectorAngle)), rotationAngle2); // 添加顶点 vertices.Add(outer1Start); vertices.Add(outer1End); vertices.Add(inner1Start); vertices.Add(inner1End); vertices.Add(outer2Start); vertices.Add(outer2End); vertices.Add(inner2Start); vertices.Add(inner2End); // 生成连接三角形 triangles.AddRange(new[] { 0, 4, 1 }); // 外侧面1 triangles.AddRange(new[] { 1, 4, 5 }); // 外侧面2 triangles.AddRange(new[] { 2, 3, 6 }); // 内侧面1 triangles.AddRange(new[] { 3, 7, 6 }); // 内侧面2 triangles.AddRange(new[] { 0, 2, 4 }); // 起始端面1 triangles.AddRange(new[] { 2, 6, 4 }); // 起始端面2 triangles.AddRange(new[] { 1, 5, 3 }); // 结束端面1 triangles.AddRange(new[] { 3, 5, 7 }); // 结束端面2 mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } private Mesh CreateSideSafeFallbackMesh(float r, float rotationAngle1, float rotationAngle2) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); // 简单的四面体作为备用 Vector3 center = Vector3.zero; Vector3 outer1 = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle1); Vector3 outer2 = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle2); Vector3 top = Vector3.up * r * 0.5f; vertices.Add(center); vertices.Add(outer1); vertices.Add(outer2); vertices.Add(top); int[] triangles = { 0, 1, 2, 0, 1, 3, 0, 2, 3, 1, 2, 3 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } private float RadarScanningRadius(RADAR radar) { if (radar.f <= 0 || radar.S_min <= 0) { Debug.LogWarning("雷达参数错误,使用默认半径"); return 10f; } float wavelength = radar.c / radar.f; float numerator = radar.P_t * Mathf.Pow(radar.G, 2) * wavelength * radar.σ; float denominator = Mathf.Pow(4 * Mathf.PI, 3) * radar.S_min; if (denominator <= 0 || numerator <= 0) { Debug.LogWarning("雷达参数无效,使用默认半径"); return 10f; } return Mathf.Max(0.1f, Mathf.Pow(numerator / denominator, 1f / 4f)); } private Vector3 RotateAroundYAxis(Vector3 point, float angle) { float cos = Mathf.Cos(angle); float sin = Mathf.Sin(angle); return new Vector3( point.x * cos - point.z * sin, point.y, point.x * sin + point.z * cos ); } private int FindClosestVertexByAngle(Vector3[] vertices, int start, int end, float targetAngle, float rotation) { int closestIndex = start; float minDiff = float.MaxValue; for (int i = start; i < end; i++) { Vector3 localPos = Quaternion.Inverse(Quaternion.Euler(0, rotation * Mathf.Rad2Deg, 0)) * vertices[i]; float angle = Mathf.Repeat(Mathf.Atan2(localPos.z, localPos.x), 2 * Mathf.PI); float diff = Mathf.Abs(angle - targetAngle); if (diff < minDiff) { minDiff = diff; closestIndex = i; } } return closestIndex; } private void GenerateConvexTriangles(List<Vector3> vertices, List<int> triangles) { if (vertices.Count < 3) return; // 生成三角形扇 int center = 0; for (int i = 1; i < vertices.Count - 1; i++) { triangles.AddRange(new[] { center, i, i + 1 }); } // 闭合最后一个三角形 if (vertices.Count > 3) triangles.AddRange(new[] { center, vertices.Count - 1, 1 }); } private bool TryGetMappedIndex(Dictionary<Vector3, int> map, Vector3 original, out int index) { Vector3 rounded = new Vector3( Mathf.Round(original.x * 10000) / 10000, Mathf.Round(original.y * 10000) / 10000, Mathf.Round(original.z * 10000) / 10000 ); return map.TryGetValue(rounded, out index); } // 第一个扇形的碰撞检测 private void OnTriggerStay(Collider other) { CheckAndTrackObject(other); } // 第二个扇形的碰撞检测 private void OnTriggerStay2(Collider other) { CheckAndTrackObject(other); } // 统一的碰撞检测和目标跟踪逻辑 private void CheckAndTrackObject(Collider other) { if (other is BoxCollider) { GameObject detectedObj = other.gameObject; // 目标相对于雷达的本地坐标 Vector3 localPos = transform.InverseTransformPoint(detectedObj.transform.position); float X = Mathf.Abs(localPos.x); float Y = Mathf.Abs(localPos.y); float Z = Mathf.Abs(localPos.z); float projectionLength = Mathf.Sqrt(X * X + Z * Z); float angleWithXZPlane; if (projectionLength < Mathf.Epsilon) { angleWithXZPlane = Mathf.PI / 2; } else { angleWithXZPlane = Mathf.Atan2(Mathf.Abs(Y), projectionLength); } float targetAngle = Mathf.Atan2(localPos.z, localPos.x); // 检查是否在第一个扇形范围内 float adjustedRotation1 = currentRotation + initialAngleOffset; float sectorStartAngle1 = adjustedRotation1; float sectorEndAngle1 = adjustedRotation1 + sectorAngle; // 检查是否在第二个扇形范围内 float adjustedRotation2 = currentRotation + initialAngleOffset - initialAngleOffsetAfter; float sectorStartAngle2 = adjustedRotation2; float sectorEndAngle2 = adjustedRotation2 + sectorAngle; // 限制角度0-2PI targetAngle = Mathf.Repeat(targetAngle, 2 * Mathf.PI); sectorStartAngle1 = Mathf.Repeat(sectorStartAngle1, 2 * Mathf.PI); sectorEndAngle1 = Mathf.Repeat(sectorEndAngle1, 2 * Mathf.PI); sectorStartAngle2 = Mathf.Repeat(sectorStartAngle2, 2 * Mathf.PI); sectorEndAngle2 = Mathf.Repeat(sectorEndAngle2, 2 * Mathf.PI); // 检查是否在任何一个扇形内 bool isInSector1 = false; if (sectorStartAngle1 <= sectorEndAngle1) { isInSector1 = targetAngle >= sectorStartAngle1 && targetAngle <= sectorEndAngle1; } else { isInSector1 = targetAngle >= sectorStartAngle1 || targetAngle <= sectorEndAngle1; } bool isInSector2 = false; if (sectorStartAngle2 <= sectorEndAngle2) { isInSector2 = targetAngle >= sectorStartAngle2 && targetAngle <= sectorEndAngle2; } else { isInSector2 = targetAngle >= sectorStartAngle2 || targetAngle <= sectorEndAngle2; } bool isInSector = isInSector1 || isInSector2; bool isInRange = localPos.magnitude <= radius; bool isradian = 0 <= angleWithXZPlane && angleWithXZPlane <= sectorAngle; if (isInSector && isInRange && isradian) { if (!trackedObjects.ContainsKey(detectedObj)) { trackedObjects[detectedObj] = new TrackedObjectInfo(); } TrackedObjectInfo info = trackedObjects[detectedObj]; info.lastDetectedAngle = currentRotation; info.lastLocalPosition = localPos; // 本地坐标 info.detectedInCurrentSweep = true; info.missedSweeps = 0; } } } private void OnDestroy() { if (simplifiedSectorMesh != null) Destroy(simplifiedSectorMesh); if (simplifiedSectorMesh2 != null) Destroy(simplifiedSectorMesh2); if (sectorObject != null) Destroy(sectorObject); if (sectorObject2 != null) Destroy(sectorObject2); if (simplifiedZxSectorMesh1 != null) Destroy(simplifiedZxSectorMesh1); if (simplifiedZxSectorMesh2 != null) Destroy(simplifiedZxSectorMesh2); if (zxSectorObject1 != null) Destroy(zxSectorObject1); if (zxSectorObject2 != null) Destroy(zxSectorObject2); } // 获取当前检测到的目标的本地坐标 public Dictionary<GameObject, Vector3> GetCurrentlyDetectedLocalPositions() { return trackedObjects.Where(it => it.Value.detectedInCurrentSweep) .ToDictionary(it => it.Key, it => it.Value.lastLocalPosition); } // 获取所有跟踪目标的最后本地坐标 public Dictionary<GameObject, Vector3> GetAllTrackedLocalPositions() { return trackedObjects.ToDictionary(it => it.Key, it => it.Value.lastLocalPosition); } public float RadianTo360Degree() { return Mathf.Rad2Deg * currentRotation; } public void StartRotation() { currentRotation = 0; // 重置旋转角度 enabled = true; } }目前生成的是0-90纬度的半球,保留0-45度的部分
最新发布
09-18
<think>我们正在讨论的是Vulkan扩展VK_KHR_acceleration_structure,特别是关于VK_GEOMETRY_TYPE_TRIANGLES_KHR的使用。这个扩展用于硬件加速的光线追踪。在Unity中启用光线追踪需要一些设置步骤。注意:Unity的光线追踪支持主要是在高清渲染管线(HDRP)中,并且需要Unity2020.2或更高版本。以下是启用VK_KHR_acceleration_structure扩展并使用VK_GEOMETRY_TYPE_TRIANGLES_KHR的最佳设置代码示例:由于这个设置涉及到底层的VulkanAPI,我们通常需要编写原生插件(C++)来直接调用VulkanAPI,然后在Unity中通过C#脚本调用。步骤:1.检测设备是否支持光线追踪扩展(包括VK_KHR_acceleration_structure)。2.创建加速结构(包括顶层和底层加速结构)。3.将几何体(三角形)添加到加速结构中。下面是一个简化的示例:一、C#层(Unity脚本):这个脚本将用于触发底层Vulkan代码,并传递网格数据。```csharpusingUnityEngine;usingSystem.Collections;usingSystem.Runtime.InteropServices;publicclassRayTracingSetup:MonoBehaviour{//定义几何体类型privateconstintVK_GEOMETRY_TYPE_TRIANGLES_KHR=0;//导入原生插件函数[DllImport("VulkanRayTracingPlugin")]privatestaticexternboolInitializeVulkanRayTracing();[DllImport("VulkanRayTracingPlugin")]privatestaticexternvoidCreateBottomLevelAccelerationStructure(Vector3[]vertices,uintvertexCount,int[]indices,uintindexCount);voidStart(){//检测设备是否支持光线追踪if(SystemInfo.supportsRayTracing){Debug.Log("Raytracingissupported.");//初始化Vulkan光线追踪if(InitializeVulkanRayTracing()){Debug.Log("Vulkanraytracinginitialized.");//获取网格数据Meshmesh=GetComponent<MeshFilter>().mesh;Vector3[]vertices=mesh.vertices;int[]indices=mesh.triangles;//创建底层加速结构(BLAS)CreateBottomLevelAccelerationStructure(vertices,(uint)vertices.Length,indices,(uint)indices.Length);}}else{Debug.LogError("Raytracingnotsupported!");}}}```二、C++插件层(VulkanRayTracingPlugin.cpp):这里包含实际的VulkanAPI调用。```cpp#include<vulkan/vulkan.h>#include<vector>//函数指针用于KHR扩展PFN_vkCreateAccelerationStructureKHRvkCreateAccelerationStructureKHR=nullptr;//其他函数指针...extern"C"{//初始化函数UNITY_INTERFACE_EXPORTboolUNITY_INTERFACE_APIInitializeVulkanRayTracing(){//获取Vulkan实例和设备(此处简化,实际需要从Unity获取)//假设已经获取了VkDevicedevice//加载扩展函数vkCreateAccelerationStructureKHR=(PFN_vkCreateAccelerationStructureKHR)vkGetDeviceProcAddr(device,"vkCreateAccelerationStructureKHR");//加载其他所需函数...returntrue;}//创建底层加速结构(BLAS)UNITY_INTERFACE_EXPORTvoidUNITY_INTERFACE_APICreateBottomLevelAccelerationStructure(float*vertices,uintvertexCount,int*indices,uintindexCount){//1.创建Vulkan缓冲区来存储顶点和索引数据(此处省略缓冲区创建过程)//2.填充VkAccelerationStructureGeometryKHRVkAccelerationStructureGeometryKHRgeometry{};geometry.sType=VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;geometry.geometryType=VK_GEOMETRY_TYPE_TRIANGLES_KHR;geometry.flags=VK_GEOMETRY_OPAQUE_BIT_KHR;//根据实际情况设置VkAccelerationStructureGeometryTrianglesDataKHR&triangles=geometry.geometry.triangles;triangles.sType=VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;triangles.vertexFormat=VK_FORMAT_R32G32B32_SFLOAT;//假设顶点是Vector3triangles.vertexData.deviceAddress=...;//顶点缓冲区的设备地址triangles.vertexStride=sizeof(float)*3;triangles.maxVertex=vertexCount;triangles.indexType=VK_INDEX_TYPE_UINT32;//因为我们传入的是int[]triangles.indexData.deviceAddress=...;//索引缓冲区的设备地址//3.设置几何体数据范围VkAccelerationStructureBuildRangeInfoKHRrangeInfo{};rangeInfo.firstVertex=0;rangeInfo.primitiveCount=indexCount/3;//三角形个数rangeInfo.primitiveOffset=0;rangeInfo.transformOffset=0;//4.创建加速结构(省略了创建加速结构对象和内存分配的过程)//5.构建加速结构VkAccelerationStructureBuildGeometryInfoKHRbuildInfo{};buildInfo.sType=VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;buildInfo.type=VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;buildInfo.mode=VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;buildInfo.geometryCount=1;buildInfo.pGeometries=&geometry;//6.获取构建所需的大小VkAccelerationStructureBuildSizesInfoKHRsizeInfo{};vkGetAccelerationStructureBuildSizesKHR(device,VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,&buildInfo,&rangeInfo.primitiveCount,&sizeInfo);//7.分配加速结构内存(此处省略)//8.创建加速结构对象VkAccelerationStructureCreateInfoKHRcreateInfo{};createInfo.sType=VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;createInfo.type=buildInfo.type;createInfo.size=sizeInfo.accelerationStructureSize;//...设置其他参数VkAccelerationStructureKHRaccelerationStructure;vkCreateAccelerationStructureKHR(device,&createInfo,nullptr,&accelerationStructure);//9.构建加速结构(需要命令缓冲区)//此处省略命令缓冲区的记录和提交//10.清理(在实际应用中需要管理内存和加速结构的生命周期)}}```三、在Unity项目设置中启用光线追踪:-使用高清渲染管线(HDRP)。-在Edit>ProjectSettings>Graphics>HDRPGlobalSettings中,启用RayTracing。四、注意事项:1.**设备支持**:确保目标设备支持Vulkan光线追踪(VK_KHR_acceleration_structure扩展)。2.**内存管理**:加速结构需要大量的内存,务必仔细管理。3.**生命周期**:加速结构在场景改变时需要更新或重建。4.**性能**:构建加速结构(尤其是动态物体)可能开销较大,尽量重用或增量更新。五、最佳实践:-对于静态物体,使用VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR。-对于动态物体,使用VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR并增量更新。-三角形几何体应使用索引三角形(效率更高)。###数学原理在光线追踪中,加速结构(如BVH)的构建复杂度为$O(n\logn)$[^1],而遍历的复杂度为$O(\logn)$[^2],其中$n$为三角形数量。###性能优化-使用紧凑的数据结构以减少内存带宽。-尽可能减少加速结构的重建次数。---§§相关问题§§1.如何检测设备是否支持Vulkan光线追踪?2.在Unity中如何更新动态物体的加速结构?3.Vulkan光线追踪与传统光栅化有何性能差异?4.如何优化光线追踪中的阴影计算?</think> 以下是针对 Unity 启用 `VK_GEOMETRY_TYPE_TRIANGLES_KHR`(光线追踪加速结构中的三角形几何类型)的最佳设置代码,包含完整实现步骤和优化策略: ```csharp using UnityEngine; using UnityEngine.Rendering; #if UNITY_2020_1_OR_NEWER using UnityEngine.Experimental.Rendering; #endif using System; using System.Collections.Generic; using System.Runtime.InteropServices; public class RayTracingAcceleration : MonoBehaviour { // 1. 检测设备支持 void Start() { if (!SystemInfo.supportsRayTracing) { Debug.LogError("设备不支持光线追踪"); return; } // 检查Vulkan加速结构扩展 bool supportsAcceleration = SystemInfo.IsVulkanExtensionSupported("VK_KHR_acceleration_structure"); Debug.Log($"VK_KHR_acceleration_structure 支持状态: {supportsAcceleration}"); if (supportsAcceleration) { // 2. 初始化加速结构 InitializeAccelerationStructures(); } } // 2. 加速结构初始化 void InitializeAccelerationStructures() { // 加载原生插件 IntPtr asHandle = CreateAccelerationStructure(); // 获取场景网格数据 MeshFilter[] meshFilters = FindObjectsOfType<MeshFilter>(); List<MeshData> meshDataList = new List<MeshData>(); foreach (var mf in meshFilters) { Mesh mesh = mf.sharedMesh; Vector3[] vertices = mesh.vertices; int[] indices = mesh.triangles; // 转换到GPU缓冲区 AddGeometryToAS(asHandle, vertices, indices, (uint)indices.Length); } // 3. 构建加速结构 BuildAccelerationStructure(asHandle, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR); // 4. 渲染时使用 Camera.main.AddCommandBuffer(CameraEvent.AfterForwardOpaque, CreateRayTracingCB(asHandle)); } // 原生插件接口 [DllImport("VulkanASPlugin")] private static extern IntPtr CreateAccelerationStructure(); [DllImport("VulkanASPlugin")] private static extern void AddGeometryToAS(IntPtr asHandle, [In] Vector3[] vertices, [In] int[] indices, uint indexCount); [DllImport("VulkanASPlugin")] private static extern void BuildAccelerationStructure(IntPtr asHandle, uint buildFlags); // 创建光线追踪命令缓冲区 CommandBuffer CreateRayTracingCB(IntPtr asHandle) { CommandBuffer cb = new CommandBuffer { name = "RayTracingAcceleration" }; // 将加速结构绑定到管线 cb.IssuePluginEventAndData( GetFunctionPointer("BindAccelerationStructure"), 0, asHandle ); return cb; } // 获取原生函数指针 [DllImport("VulkanASPlugin")] private static extern IntPtr GetFunctionPointer(string functionName); } ``` ### 原生插件层 (C++) 实现 ```cpp #include <vulkan/vulkan.h> #include <vector> // 加速结构句柄 struct AccelerationStructure { VkAccelerationStructureKHR handle; VkDeviceMemory memory; }; // 函数原型 typedef void (*BindASFunc)(VkCommandBuffer, VkAccelerationStructureKHR); extern "C" { // 创建加速结构 UNITY_INTERFACE_EXPORT AccelerationStructure* UNITY_INTERFACE_API CreateAccelerationStructure() { VkAccelerationStructureCreateInfoKHR createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; createInfo.size = ...; // 根据几何体计算大小 AccelerationStructure* as = new AccelerationStructure(); vkCreateAccelerationStructureKHR(device, &createInfo, nullptr, &as->handle); return as; } // 添加三角形几何体 UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API AddGeometryToAS( AccelerationStructure* as, float* vertices, int vertexCount, int* indices, int indexCount) { VkAccelerationStructureGeometryKHR geometry{}; geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; geometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; // 三角形数据配置 geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; geometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; geometry.geometry.triangles.vertexData.deviceAddress = ...; // GPU地址 geometry.geometry.triangles.vertexStride = sizeof(float) * 3; geometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; geometry.geometry.triangles.indexData.deviceAddress = ...; // GPU地址 // 添加几何数据到加速结构 VkAccelerationStructureBuildGeometryInfoKHR buildInfo{}; buildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; buildInfo.geometryCount = 1; buildInfo.pGeometries = &geometry; VkAccelerationStructureBuildRangeInfoKHR rangeInfo{}; rangeInfo.primitiveCount = indexCount / 3; // 三角形数量 rangeInfo.primitiveOffset = 0; // 构建指令 vkCmdBuildAccelerationStructuresKHR(commandBuffer, 1, &buildInfo, &rangeInfo); } // 绑定到管线 UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API BindAccelerationStructure( VkCommandBuffer cmdBuffer, VkAccelerationStructureKHR as) { // 绑定加速结构到光线追踪管线 vkCmdBindAccelerationStructuresKHR( cmdBuffer, 0, // firstBinding 1, // accelerationStructureCount &as ); } } ``` ### 最佳设置参数 1. **几何体类型**:始终使用 `VK_GEOMETRY_TYPE_TRIANGLES_KHR` ```cpp geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; ``` 2. **顶点格式**:使用紧凑格式提升性能 ```cpp geometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; // 32位浮点 ``` 3. **构建标志**:根据场景选择优化策略 ```cpp // 静态场景:优化追踪速度 VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR // 动态场景:允许更新 VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR ``` 4. **内存管理**:使用专用内存类型 ```cpp VkMemoryAllocateInfo allocInfo{}; allocInfo.allocationSize = asSize; allocInfo.memoryTypeIndex = FindMemoryType( VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // 设备本地内存 ``` ### 数学优化原理 加速结构(BVH)的构建时间复杂度为: $$T_{\text{build}} = O(n \log n)$$ 其中 $n$ 是场景中的三角形数量。通过几何压缩技术,可将内存占用降低 30%[^1]: $$M_{\text{compressed}} = M_{\text{original}} \times (0.7 \pm 0.05)$$ ### 性能优化点 1. **实例化重用**:对重复物体使用 `VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR` 2. **增量更新**:对动态物体启用 `ALLOW_UPDATE` 标志 3. **压缩顶点**:使用 16-bit 浮点格式(`VK_FORMAT_R16G16B16_SFLOAT`) 4. **批次构建**:单次构建多个几何体减少 API 开销 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值