Files
ScreenDetectionLayerPicker/Runtime/MeshDetection_URP/GeoLayerPickerRenderPassFeature.cs
T
2026-06-08 16:23:20 +08:00

895 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using UnityEngine.Experimental.Rendering;
using XericLibrary.Runtime.MacroLibrary;
#if UNITY_2022_1_OR_NEWER
namespace XericLibrary.PostProcess
{
/// <summary>
/// 用于存储展平后的 SubMesh 渲染数据
/// </summary>
[System.Serializable]
public struct SubMeshDrawData
{
public Mesh mesh;
public Matrix4x4 matrix;
public int subMeshIndex;
public int uniqueMatID;
public Material material;
}
/// <summary>
/// 解码后的地质层拾取数据
/// </summary>
[System.Serializable]
public struct PickedGeoData
{
public int matID;
public Vector3 worldPos;
public bool isValid;
public Material GetMaterial()
{
if (GeoLayerPickerRenderPassFeature.Instance != null)
{
return GeoLayerPickerRenderPassFeature.Instance.GetMaterialByID(matID);
}
return null;
}
public string GetMaterialName()
{
var mat = GetMaterial();
return mat != null ? mat.name : "Unknown";
}
}
/// <summary>
/// 单行上的材质段信息
/// </summary>
public struct MaterialSegment
{
public int rowIndex;
public int colStart;
public int colEnd;
public int matID;
public int length => colEnd - colStart + 1;
}
/// <summary>
/// 材质孤岛数据结构
/// 表示一组相连的同材质像素区域
/// </summary>
public class MaterialIsland
{
public int matID;
public List<MaterialSegment> segments = new List<MaterialSegment>();
public int totalPixelCount;
public Vector2 centerPixelPos;
public Vector3 centerWorldPos;
public bool isValid => matID > 0 && segments.Count > 0;
public Material GetMaterial()
{
if (GeoLayerPickerRenderPassFeature.Instance != null)
{
return GeoLayerPickerRenderPassFeature.Instance.GetMaterialByID(matID);
}
return null;
}
public string GetMaterialName()
{
var mat = GetMaterial();
return mat != null ? mat.name : "Unknown";
}
}
/// <summary>
/// 地质层拾取 RenderFeature。
/// </summary>
public class GeoLayerPickerRenderPassFeature : ScriptableRendererFeature
{
#region 静态单例与外部管理 API
public static GeoLayerPickerRenderPassFeature Instance { get; private set; }
private readonly List<MeshFilter> m_GeoMeshes = new List<MeshFilter>();
private readonly List<SubMeshDrawData> m_DrawList = new List<SubMeshDrawData>();
private readonly Dictionary<Material, int> m_MaterialToID = new Dictionary<Material, int>();
private readonly Dictionary<int, Material> m_IDToMaterial = new Dictionary<int, Material>();
private bool m_IsDirty = true;
public IReadOnlyDictionary<Material, int> MaterialToID => m_MaterialToID;
public IReadOnlyDictionary<int, Material> IDToMaterial => m_IDToMaterial;
public Material GetMaterialByID(int matID)
{
if (m_IDToMaterial.TryGetValue(matID, out var material))
{
return material;
}
return null;
}
public int GetIDByMaterial(Material material)
{
if (material != null && m_MaterialToID.TryGetValue(material, out var id))
{
return id;
}
return -1;
}
public void AddGeoMesh(MeshFilter meshFilter)
{
if (meshFilter != null && !m_GeoMeshes.Contains(meshFilter))
{
m_GeoMeshes.Add(meshFilter);
m_IsDirty = true;
}
}
public void AddGeoMeshes(IEnumerable<MeshFilter> meshFilters)
{
if (meshFilters == null) return;
foreach (var mf in meshFilters)
{
if (mf != null && !m_GeoMeshes.Contains(mf))
{
m_GeoMeshes.Add(mf);
m_IsDirty = true;
}
}
}
public void RemoveGeoMesh(MeshFilter meshFilter)
{
if (m_GeoMeshes.Remove(meshFilter)) m_IsDirty = true;
}
public void ClearGeoMeshes()
{
if (m_GeoMeshes.Count > 0)
{
m_GeoMeshes.Clear();
m_IsDirty = true;
}
}
public IReadOnlyList<MeshFilter> GetGeoMeshes() => m_GeoMeshes;
#endregion
#region 数据访问 API
private PickedGeoData[] m_DecodedDataArray;
private int m_ValidDataCount = 0;
public IReadOnlyList<PickedGeoData> GetDecodedData() => m_DecodedDataArray;
public void GetColumnData(int column, List<PickedGeoData> listData)
{
listData.Clear();
if (m_DecodedDataArray == null) return;
column = MacroMath.ClampToInt(column, 0, pickRTSize.x);
for (int i = 0; i < pickRTSize.y; i++)
{
listData.Add(m_DecodedDataArray[pickRTSize.x * i + column]);
}
}
public bool TryGetPickedData(Vector2 normalizedScreenPos, out PickedGeoData data)
{
data = default;
if (m_DecodedDataArray == null || m_DecodedDataArray.Length == 0) return false;
float localX = (normalizedScreenPos.x - sampleRect.x) / sampleRect.width;
float localY = (normalizedScreenPos.y - sampleRect.y) / sampleRect.height;
if (localX < 0f || localX > 1f || localY < 0f || localY > 1f) return false;
int pixelX = Mathf.Clamp(Mathf.FloorToInt(localX * pickRTSize.x), 0, pickRTSize.x - 1);
int pixelY = Mathf.Clamp(Mathf.FloorToInt(localY * pickRTSize.y), 0, pickRTSize.y - 1);
int index = pixelY * pickRTSize.x + pixelX;
if (index < 0 || index >= m_DecodedDataArray.Length) return false;
data = m_DecodedDataArray[index];
return true;
}
public void ForceReadbackSync()
{
// 修复:使用 m_ReadbackBufferHandle 进行同步等待
if (m_ReadbackBufferHandle != null)
{
AsyncGPUReadback.WaitAllRequests();
}
}
#endregion
#region 材质孤岛统计 API
private List<MaterialIsland> m_MaterialIslands = new List<MaterialIsland>();
private bool m_IslandsDirty = true;
public IReadOnlyList<MaterialIsland> GetMaterialIslands()
{
UpdateMaterialIslands();
return m_MaterialIslands;
}
public List<MaterialIsland> GetMaterialIslandsByMatID(int matID)
{
UpdateMaterialIslands();
return m_MaterialIslands.Where(island => island.matID == matID).ToList();
}
public int GetIslandCount()
{
UpdateMaterialIslands();
return m_MaterialIslands.Count;
}
public void MarkIslandsDirty()
{
m_IslandsDirty = true;
}
private void UpdateMaterialIslands()
{
if (!m_IslandsDirty && m_MaterialIslands.Count > 0) return;
if (m_DecodedDataArray == null || m_DecodedDataArray.Length == 0)
{
m_MaterialIslands.Clear();
m_IslandsDirty = false;
return;
}
FindMaterialIslands();
m_IslandsDirty = false;
if (enableDebugLog)
{
Debug.Log($"[GeoLayerPicker] 材质孤岛统计完成,共 {m_MaterialIslands.Count} 个孤岛");
foreach (var island in m_MaterialIslands)
{
Debug.Log($" MatID {island.matID} ({island.GetMaterialName()}): {island.totalPixelCount} 像素, 中心: {island.centerWorldPos}");
}
}
}
private void FindMaterialIslands()
{
m_MaterialIslands.Clear();
int width = pickRTSize.x;
int height = pickRTSize.y;
List<List<MaterialSegment>> rowSegments = new List<List<MaterialSegment>>();
for (int y = 0; y < height; y++)
{
var segments = ExtractRowSegments(y, width);
rowSegments.Add(segments);
}
List<List<MaterialIsland>> islandGroups = new List<List<MaterialIsland>>();
for (int matID = 1; matID <= m_IDToMaterial.Count; matID++)
{
var islands = BuildIslandsForMaterial(matID, rowSegments, width, height);
islandGroups.Add(islands);
}
foreach (var islands in islandGroups)
{
m_MaterialIslands.AddRange(islands);
}
}
private List<MaterialSegment> ExtractRowSegments(int rowIndex, int width)
{
List<MaterialSegment> segments = new List<MaterialSegment>();
if (m_DecodedDataArray == null) return segments;
int col = 0;
while (col < width)
{
int index = rowIndex * width + col;
if (index >= m_DecodedDataArray.Length) break;
PickedGeoData pixel = m_DecodedDataArray[index];
if (!pixel.isValid || pixel.matID <= 0)
{
col++;
continue;
}
int startCol = col;
int matID = pixel.matID;
while (col < width)
{
int checkIndex = rowIndex * width + col;
if (checkIndex >= m_DecodedDataArray.Length) break;
PickedGeoData checkPixel = m_DecodedDataArray[checkIndex];
if (!checkPixel.isValid || checkPixel.matID != matID)
{
break;
}
col++;
}
segments.Add(new MaterialSegment
{
rowIndex = rowIndex,
colStart = startCol,
colEnd = col - 1,
matID = matID
});
}
return segments;
}
private List<MaterialIsland> BuildIslandsForMaterial(int targetMatID, List<List<MaterialSegment>> rowSegments, int width, int height)
{
List<MaterialIsland> islands = new List<MaterialIsland>();
for (int y = 0; y < height; y++)
{
if (y >= rowSegments.Count) break;
foreach (var currentSegment in rowSegments[y].Where(s => s.matID == targetMatID))
{
List<MaterialIsland> connectedIslands = new List<MaterialIsland>();
if (y > 0 && y - 1 < rowSegments.Count)
{
foreach (var island in islands)
{
foreach (var existingSegment in island.segments)
{
if (existingSegment.rowIndex == y - 1)
{
if (SegmentsOverlap(currentSegment, existingSegment))
{
connectedIslands.Add(island);
break;
}
}
}
}
}
if (connectedIslands.Count == 0)
{
MaterialIsland newIsland = new MaterialIsland
{
matID = targetMatID
};
newIsland.segments.Add(currentSegment);
islands.Add(newIsland);
}
else if (connectedIslands.Count == 1)
{
connectedIslands[0].segments.Add(currentSegment);
}
else
{
MaterialIsland mergedIsland = new MaterialIsland
{
matID = targetMatID
};
foreach (var islandToMerge in connectedIslands)
{
mergedIsland.segments.AddRange(islandToMerge.segments);
islands.Remove(islandToMerge);
}
mergedIsland.segments.Add(currentSegment);
islands.Add(mergedIsland);
}
}
}
CalculateIslandProperties(islands, width, height);
return islands;
}
private bool SegmentsOverlap(MaterialSegment a, MaterialSegment b)
{
return !(a.colEnd < b.colStart - 1 || a.colStart > b.colEnd + 1);
}
private void CalculateIslandProperties(List<MaterialIsland> islands, int width, int height)
{
foreach (var island in islands)
{
island.totalPixelCount = island.segments.Sum(s => s.length);
if (island.segments.Count == 0)
{
island.centerPixelPos = Vector2.zero;
island.centerWorldPos = Vector3.zero;
continue;
}
int minRow = island.segments.Min(s => s.rowIndex);
int maxRow = island.segments.Max(s => s.rowIndex);
float avgRow = (minRow + maxRow) * 0.5f;
MaterialSegment longestSegment = island.segments.OrderByDescending(s => s.length).First();
float avgCol = (longestSegment.colStart + longestSegment.colEnd) * 0.5f;
island.centerPixelPos = new Vector2(avgCol, avgRow);
int centerX = Mathf.Clamp(Mathf.RoundToInt(avgCol), 0, width - 1);
int centerY = Mathf.Clamp(Mathf.RoundToInt(avgRow), 0, height - 1);
int centerIndex = centerY * width + centerX;
if (centerIndex >= 0 && centerIndex < m_DecodedDataArray.Length)
{
island.centerWorldPos = m_DecodedDataArray[centerIndex].worldPos;
}
else
{
island.centerWorldPos = Vector3.zero;
}
}
}
#endregion
#region Inspector 配置参数
[Header("拾取材质与尺寸")] public Material pickMaterial;
public Vector2Int pickRTSize = new Vector2Int(64, 64);
[Header("采样区域设置")] public Rect sampleRect = new Rect(0f, 0f, 1f, 1f);
[Header("读取频率控制")] [Range(0.01f, 1.0f)]
public float readbackInterval = 0.1f;
[Header("调试设置")] public bool enableDebugLog = false;
#endregion
// 核心修改:全面使用 RTHandle 替代 RenderTexture
private RTHandle m_PickRTHandle;
private RTHandle m_ReadbackBufferHandle;
private float m_TimeSinceLastReadback = 0f;
private bool m_IsReadbackInProgress = false;
private int m_ReadbackFrameCounter = 0;
// 调试覆盖图相关
private Material m_DebugOverlayMaterial;
private static readonly int DebugRectProp = Shader.PropertyToID("_DebugRect");
private static readonly int DebugMainTex = Shader.PropertyToID("_MainTex");
class CustomRenderPass : ScriptableRenderPass
{
public Material passMaterial;
public RTHandle passRTHandle;
public RTHandle passReadbackBuffer;
public List<SubMeshDrawData> passDrawList;
public Rect passSampleRect;
public bool requestReadback;
// 屏幕调试
public bool drawDebugOverlay;
public Material debugOverlayMaterial;
public RTHandle cameraColorTarget; // 用于获取屏幕渲染目标
private int m_MatIDProperty = Shader.PropertyToID("_MatID");
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (passMaterial == null || passRTHandle == null || passDrawList == null || passDrawList.Count == 0)
return;
CommandBuffer cmd = CommandBufferPool.Get("GeoLayerPickBuffer");
// 手动设置渲染目标并清理,避免 URP 隐式状态干扰
cmd.SetRenderTarget(passRTHandle);
cmd.ClearRenderTarget(true, true, new Color(0, 0, 0, 0));
if (drawDebugOverlay)
{
Debug.Log($"[GeoLayerPicker] 开始绘制 {passDrawList.Count} 个SubMesh");
Debug.Log($"[GeoLayerPicker] 渲染目标: {passRTHandle.rt.name}, 尺寸: {passRTHandle.rt.width}x{passRTHandle.rt.height}");
}
Camera cam = renderingData.cameraData.camera;
Matrix4x4 viewMatrix = cam.worldToCameraMatrix;
Matrix4x4 projMatrix = cam.projectionMatrix;
float ndcXMin = passSampleRect.x * 2f - 1f;
float ndcXMax = (passSampleRect.x + passSampleRect.width) * 2f - 1f;
float ndcYMin = passSampleRect.y * 2f - 1f;
float ndcYMax = (passSampleRect.y + passSampleRect.height) * 2f - 1f;
float scaleX = 2f / (ndcXMax - ndcXMin);
float scaleY = 2f / (ndcYMax - ndcYMin);
float offsetX = -1f - ndcXMin * scaleX;
float offsetY = -1f - ndcYMin * scaleY;
projMatrix.m00 *= scaleX;
projMatrix.m11 *= scaleY;
projMatrix.m02 += offsetX;
projMatrix.m12 += offsetY;
projMatrix = GL.GetGPUProjectionMatrix(projMatrix, true);
cmd.SetViewProjectionMatrices(viewMatrix, projMatrix);
cmd.SetViewport(new Rect(0, 0, passRTHandle.rt.width, passRTHandle.rt.height));
MaterialPropertyBlock drawMpb = new MaterialPropertyBlock();
for (int i = 0; i < passDrawList.Count; i++)
{
var data = passDrawList[i];
drawMpb.SetInt(m_MatIDProperty, data.uniqueMatID);
cmd.DrawMesh(data.mesh, data.matrix, passMaterial, data.subMeshIndex, 0, drawMpb);
if (GeoLayerPickerRenderPassFeature.Instance != null &&
GeoLayerPickerRenderPassFeature.Instance.enableDebugLog &&
(i == 0 || i == passDrawList.Count - 1))
{
Debug.Log($"[GeoLayerPicker] 绘制第{i}个SubMesh, MatID={data.uniqueMatID}, 材质={data.material?.name ?? "null"}");
}
}
cmd.SetViewProjectionMatrices(viewMatrix, renderingData.cameraData.GetProjectionMatrix());
// 如果本帧需要回读,将数据安全地 Blit 到隔离缓冲区
if (requestReadback && passReadbackBuffer != null)
{
cmd.Blit(passRTHandle, passReadbackBuffer);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
if (requestReadback && passReadbackBuffer != null)
{
AsyncGPUReadback.Request(passReadbackBuffer.rt, 0, TextureFormat.RGBAFloat,
GeoLayerPickerRenderPassFeature.Instance.OnReadbackComplete);
}
// --- 绘制全屏调试图 ---
if (drawDebugOverlay && debugOverlayMaterial != null && passRTHandle != null)
{
var feature = GeoLayerPickerRenderPassFeature.Instance;
if (feature == null) return;
RTHandle currentColorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle;
CommandBuffer debugCmd = CommandBufferPool.Get("GeoPickerDebugOverlay");
// 【核心 1】:使用 MaterialPropertyBlock 隔离参数
MaterialPropertyBlock debugMpb = new MaterialPropertyBlock();
// 【修正 1】:直接使用字符串名称和 RTHandle 对象本身(RTHandle 隐式转为 Texture)
// 彻底避开 nameID (RenderTargetIdentifier) 的类型转换坑!
debugMpb.SetTexture(DebugMainTex, passRTHandle);
debugMpb.SetVector(DebugRectProp, new Vector4(
feature.sampleRect.x,
feature.sampleRect.y,
feature.sampleRect.width,
feature.sampleRect.height
));
// 设置渲染目标为当前屏幕
CoreUtils.SetRenderTarget(debugCmd, currentColorTarget);
// 【修正 2】:DrawProcedural 必须显式传入 instanceCount (1) 作为第 6 个参数!
debugCmd.DrawProcedural(
Matrix4x4.identity, // 1. matrix
debugOverlayMaterial, // 2. material
0, // 3. shaderPass
MeshTopology.Triangles, // 4. topology
3, // 5. vertexCount (全屏三角形)
1, // 6. instanceCount (必须显式写 1!)
debugMpb // 7. MaterialPropertyBlock
);
context.ExecuteCommandBuffer(debugCmd);
CommandBufferPool.Release(debugCmd);
}
}
}
private CustomRenderPass m_ScriptablePass;
public override void Create()
{
Instance = this;
m_IsDirty = true;
m_ScriptablePass = new CustomRenderPass
{
renderPassEvent = RenderPassEvent.AfterRenderingOpaques
};
// 释放旧的 RTHandle
if (m_PickRTHandle != null)
{
m_PickRTHandle.Release();
m_PickRTHandle = null;
}
if (m_ReadbackBufferHandle != null)
{
m_ReadbackBufferHandle.Release();
m_ReadbackBufferHandle = null;
}
// 分配新的 RTHandle
m_PickRTHandle = RTHandles.Alloc(
pickRTSize.x, pickRTSize.y,
colorFormat: GraphicsFormat.R32G32B32A32_SFloat,
filterMode: FilterMode.Point,
name: "GeoLayerPickRT"
);
m_ReadbackBufferHandle = RTHandles.Alloc(
pickRTSize.x, pickRTSize.y,
colorFormat: GraphicsFormat.R32G32B32A32_SFloat,
filterMode: FilterMode.Point,
name: "GeoLayerReadbackBuffer"
);
int requiredLength = pickRTSize.x * pickRTSize.y;
if (m_DecodedDataArray == null || m_DecodedDataArray.Length != requiredLength)
{
m_DecodedDataArray = new PickedGeoData[requiredLength];
if (enableDebugLog) Debug.Log($"[GeoLayerPicker] 数组重新初始化,长度: {requiredLength}");
}
// 初始化调试材质
if (m_DebugOverlayMaterial == null)
{
Shader overlayShader = Shader.Find("Hidden/XericLibrary/GeoLayerPicker/ScreenOverrideRect");
if (overlayShader != null)
{
m_DebugOverlayMaterial = CoreUtils.CreateEngineMaterial(overlayShader);
}
else
{
Debug.LogError("[GeoLayerPicker] 找不到调试 Shader: Hidden/XericLibrary/GeoLayerPicker/ScreenOverrideRect");
}
}
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (m_ScriptablePass == null || pickMaterial == null) return;
if (m_IsDirty)
{
RebuildDrawList();
m_IsDirty = false;
if (enableDebugLog) Debug.Log($"[GeoLayerPicker] DrawList 重建完成,包含 {m_DrawList.Count} 个 SubMesh。");
}
m_ReadbackFrameCounter++;
if (m_IsReadbackInProgress && m_ReadbackFrameCounter > 60)
{
Debug.LogWarning("[GeoLayerPicker] AsyncGPUReadback 疑似卡死,强制重置并清理队列。");
AsyncGPUReadback.WaitAllRequests();
m_IsReadbackInProgress = false;
m_ReadbackFrameCounter = 0;
}
m_TimeSinceLastReadback += Time.deltaTime;
bool shouldRead = false;
if (m_TimeSinceLastReadback >= readbackInterval && !m_IsReadbackInProgress)
{
m_TimeSinceLastReadback = 0f;
m_IsReadbackInProgress = true;
m_ReadbackFrameCounter = 0;
shouldRead = true;
}
// 后处理参数
m_ScriptablePass.passMaterial = pickMaterial;
m_ScriptablePass.passRTHandle = m_PickRTHandle;
m_ScriptablePass.passReadbackBuffer = m_ReadbackBufferHandle;
m_ScriptablePass.passDrawList = m_DrawList;
m_ScriptablePass.passSampleRect = sampleRect;
m_ScriptablePass.requestReadback = shouldRead;
// 配置调试覆盖图参数
m_ScriptablePass.drawDebugOverlay = enableDebugLog; // 由 Inspector 开关控制
m_ScriptablePass.debugOverlayMaterial = m_DebugOverlayMaterial;
// m_ScriptablePass.cameraColorTarget = renderer.cameraColorTargetHandle; // 当前是注册阶段无法获取目标
renderer.EnqueuePass(m_ScriptablePass);
}
private void OnReadbackComplete(AsyncGPUReadbackRequest request)
{
m_IsReadbackInProgress = false;
if (request.hasError || m_DecodedDataArray == null)
{
Debug.LogError("[GeoLayerPicker] Readback 发生错误或数组为空!");
return;
}
var rawData = request.GetData<Color>();
if (rawData.Length == 0) return;
// 修复:使用 m_ReadbackBufferHandle.rt 获取宽高
int width = m_ReadbackBufferHandle.rt.width;
int height = m_ReadbackBufferHandle.rt.height;
int count = m_DecodedDataArray.Length;
bool flipY = SystemInfo.graphicsUVStartsAtTop;
m_ValidDataCount = 0;
for (int i = 0; i < count; i++)
{
int x = i % width;
int y = i / width;
int sourceY = flipY ? (height - 1 - y) : y;
int sourceIndex = sourceY * width + x;
if (sourceIndex < 0 || sourceIndex >= rawData.Length)
{
m_DecodedDataArray[i] = new PickedGeoData { isValid = false };
continue;
}
Color pixel = rawData[sourceIndex];
if (pixel.a > 0.5f)
{
int decodedMatID = Mathf.RoundToInt(pixel.a);
if (enableDebugLog && i == 0)
{
Debug.Log($"[GeoLayerPicker] 像素({x},{y}), alpha={pixel.a:F4}, 解码MatID={decodedMatID}");
}
m_DecodedDataArray[i] = new PickedGeoData
{
isValid = true,
matID = decodedMatID,
worldPos = new Vector3(pixel.r, pixel.g, pixel.b)
};
m_ValidDataCount++;
}
else
{
m_DecodedDataArray[i] = new PickedGeoData { isValid = false, matID = 0 };
}
}
if (enableDebugLog)
{
Debug.Log($"[GeoLayerPicker] 回读完成!总像素: {count}, 有效拾取像素: {m_ValidDataCount}");
Dictionary<int, int> matIDCounts = new Dictionary<int, int>();
foreach (var data in m_DecodedDataArray)
{
if (data.isValid)
{
if (!matIDCounts.ContainsKey(data.matID))
{
matIDCounts[data.matID] = 0;
}
matIDCounts[data.matID]++;
}
}
Debug.Log("[GeoLayerPicker] 材质ID分布:");
foreach (var kvp in matIDCounts)
{
var mat = GetMaterialByID(kvp.Key);
Debug.Log($" MatID={kvp.Key} ({mat?.name ?? "未知"}): {kvp.Value} 像素");
}
}
MarkIslandsDirty();
}
/// <summary>
/// 重建模型绘制列表
/// </summary>
private void RebuildDrawList()
{
m_DrawList.Clear();
m_MaterialToID.Clear();
m_IDToMaterial.Clear();
int globalIDCounter = 1;
foreach (var mf in m_GeoMeshes)
{
if (mf == null || mf.sharedMesh == null) continue;
var renderer = mf.GetComponent<MeshRenderer>();
Material[] materials = renderer != null ? renderer.sharedMaterials : null;
for (int sub = 0; sub < mf.sharedMesh.subMeshCount; sub++)
{
Material material = null;
if (materials != null && sub < materials.Length)
{
material = materials[sub];
}
if (material == null)
{
continue;
}
int matID;
if (!m_MaterialToID.TryGetValue(material, out matID))
{
matID = globalIDCounter++;
m_MaterialToID[material] = matID;
m_IDToMaterial[matID] = material;
}
m_DrawList.Add(new SubMeshDrawData
{
mesh = mf.sharedMesh,
matrix = mf.transform.localToWorldMatrix,
subMeshIndex = sub,
uniqueMatID = matID,
material = material
});
}
}
if (enableDebugLog)
{
Debug.Log($"[GeoLayerPicker] 材质映射重建完成,共 {m_MaterialToID.Count} 个材质");
foreach (var kvp in m_MaterialToID)
{
Debug.Log($" ID {kvp.Value}: {kvp.Key.name}");
}
}
}
protected override void Dispose(bool disposing)
{
// 修复:正确释放 RTHandle 并置空
if (m_PickRTHandle != null)
{
m_PickRTHandle.Release();
m_PickRTHandle = null;
}
if (m_ReadbackBufferHandle != null)
{
m_ReadbackBufferHandle.Release();
m_ReadbackBufferHandle = null;
}
// 释放调试材质
if (m_DebugOverlayMaterial != null)
{
CoreUtils.Destroy(m_DebugOverlayMaterial);
m_DebugOverlayMaterial = null;
}
if (Instance == this) Instance = null;
}
}
}
#endif