实现一种在小尺度下快速增量像素孤岛连通分量标记算法,该算法可以高效实现在多个属性混合的情况下在目标区域内得到一个不会与其他所有属性交叉的中性点。

This commit is contained in:
2026-06-08 19:35:00 +08:00
parent 0289b1855f
commit 95677e9536
3 changed files with 135 additions and 7 deletions
+36 -1
View File
@@ -1,3 +1,38 @@
# Unity 屏幕空间后处理阶段检查器-模型数据提取器
依赖 [MaterialFunctionLibrary](https://gitea.xeric.cc/LiRuochen_WorkFlow/MaterialFunctionLibrary) 插件。
依赖 [MaterialFunctionLibrary](https://gitea.xeric.cc/LiRuochen_WorkFlow/MaterialFunctionLibrary) 插件。
插件能力:
1. 快速抓取到一个物体上多个不同材质分类区域在屏幕特定区域内的占比,中心位置坐标等信息
2. 支持webgl平台
## 使用示例
``` csharp
public class GeoLayerPicker : MonoBehaviour
{
public List<MeshFilter> MeshFilters;
public void Start()
{
GeoLayerPickerRenderPassFeature.Instance?.AddGeoMeshes(MeshFilters); // 将模型设置到处理器中
}
public void LateUpdate()
{
var islands = GeoLayerPickerRenderPassFeature.Instance.GetMaterialIslands();
foreach (var island in islands)
{
Debug.Log($"材质: {island.GetMaterialName()} (ID:{island.matID})");
Debug.Log($" 像素数: {island.totalPixelCount}");
Debug.Log($" 中心世界坐标: {island.centerWorldPos}");
Debug.Log($" 中心像素坐标: {island.centerPixelPos}");
}
}
}
```
处理器中已经包含了必要的转换功能,比如 `island.GetCenterRectAnchoredPosition(rectTransform)` 就可以得到ui坐标。
@@ -73,6 +73,9 @@ namespace XericLibrary.PostProcess
public int totalPixelCount;
public Vector2 centerPixelPos;
public Vector3 centerWorldPos;
public Vector3 minWorldPos;
public Vector3 maxWorldPos;
public bool isValid => matID > 0 && segments.Count > 0;
public Material GetMaterial()
@@ -131,6 +134,58 @@ namespace XericLibrary.PostProcess
return new Vector2(anchoredX, anchoredY);
}
public Vector3 GetCenterWorldPosition()
{
return centerWorldPos;
}
public Vector2 GetScreenPosition(Camera camera = null)
{
if (camera == null)
{
camera = Camera.main;
}
if (camera == null)
return Vector2.zero;
return camera.WorldToScreenPoint(centerWorldPos);
}
public Vector2 GetViewportPosition(Camera camera = null)
{
if (camera == null)
{
camera = Camera.main;
}
if (camera == null)
return Vector2.zero;
return camera.WorldToViewportPoint(centerWorldPos);
}
public Bounds GetWorldBounds()
{
Bounds bounds = new Bounds(centerWorldPos, Vector3.zero);
bounds.Encapsulate(minWorldPos);
bounds.Encapsulate(maxWorldPos);
return bounds;
}
public float GetWorldVolume()
{
Vector3 size = maxWorldPos - minWorldPos;
return Mathf.Abs(size.x * size.y * size.z);
}
public float GetWorldArea()
{
Vector2 size = new Vector2(
Mathf.Abs(maxWorldPos.x - minWorldPos.x),
Mathf.Abs(maxWorldPos.z - minWorldPos.z)
);
return size.x * size.y;
}
}
/// <summary>
@@ -464,6 +519,8 @@ namespace XericLibrary.PostProcess
{
island.centerPixelPos = Vector2.zero;
island.centerWorldPos = Vector3.zero;
island.minWorldPos = Vector3.zero;
island.maxWorldPos = Vector3.zero;
continue;
}
@@ -476,10 +533,25 @@ namespace XericLibrary.PostProcess
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;
Dictionary<int, int> rowPixelCounts = new Dictionary<int, int>();
foreach (var seg in island.segments)
{
if (!rowPixelCounts.ContainsKey(seg.rowIndex))
{
rowPixelCounts[seg.rowIndex] = 0;
}
rowPixelCounts[seg.rowIndex] += seg.length;
}
int maxRowIndex = rowPixelCounts.OrderByDescending(kvp => kvp.Value).First().Key;
var segmentsInMaxRow = island.segments.Where(s => s.rowIndex == maxRowIndex).ToList();
MaterialSegment maxSegment = segmentsInMaxRow.OrderByDescending(s => s.length).First();
int centerX = (maxSegment.colStart + maxSegment.colEnd) / 2;
int centerY = maxRowIndex;
int centerIndex = centerY * width + centerX;
if (centerIndex >= 0 && centerIndex < m_DecodedDataArray.Length)
{
island.centerWorldPos = m_DecodedDataArray[centerIndex].worldPos;
@@ -488,6 +560,26 @@ namespace XericLibrary.PostProcess
{
island.centerWorldPos = Vector3.zero;
}
island.minWorldPos = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
island.maxWorldPos = new Vector3(float.MinValue, float.MinValue, float.MinValue);
foreach (var seg in island.segments)
{
for (int col = seg.colStart; col <= seg.colEnd; col++)
{
int index = seg.rowIndex * width + col;
if (index < 0 || index >= m_DecodedDataArray.Length)
continue;
var pixelData = m_DecodedDataArray[index];
if (pixelData.isValid && pixelData.matID == island.matID)
{
island.minWorldPos = Vector3.Min(island.minWorldPos, pixelData.worldPos);
island.maxWorldPos = Vector3.Max(island.maxWorldPos, pixelData.worldPos);
}
}
}
}
}
@@ -75,9 +75,10 @@ Shader "Hidden/XericLibrary/GeoLayerPicker/ScreenOverrideRect"
}
// 将世界坐标映射为可见颜色
float3 debugColor = frac(col.rgb * 0.1);
float matIDColor = frac(col.a * 0.05);
debugColor.r = lerp(debugColor.r, matIDColor, 0.5);
float3 debugColor = abs(sin(col.rgb * float3(0.03, 0.1, 0.05))) * 0.5;
float3 debugColor2 = abs(cos(col.rgb * float3(0.05, 0.09, 0.07))) * 0.5 + 0.5;
float matIDColor = frac(col.a * 0.07);
debugColor = lerp(debugColor, debugColor2, matIDColor);
return float4(debugColor, 0.9);
}
ENDHLSL