You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何为SkinnedMesh对象创建3D bounding box?

Unity SkinnedMeshRenderer 3D包围盒生成方案

针对带蒙皮网格渲染器的对象,直接读取组件自带的bounds属性是预计算的静态值,骨骼动画播放时顶点位移超出初始范围就会出现包围盒不准、甚至模型被视锥体错误剔除的问题,必须基于实时蒙皮后的顶点数据计算才准确。

关键实现步骤

  • 缓存目标对象的SkinnedMeshRenderer组件引用,新建临时Mesh实例用于存储烘焙后的蒙皮网格,禁止直接修改项目内的原始Mesh资源,避免资源污染。
  • 在LateUpdate(骨骼动画更新完成的生命周期节点)调用BakeMesh接口,将当前帧所有骨骼动画、BlendShape形变计算完成后的网格数据写入临时Mesh,这一步是拿到准确蒙皮顶点的核心。
  • 提取临时Mesh的所有顶点数据,根据需求选择局部/世界坐标系,遍历计算三个轴向的顶点坐标最小、最大值。
  • 基于轴向极值生成Bounds结构,即为当前帧准确的3D轴对齐包围盒;如果需要定向包围盒(OBB),额外基于顶点协方差矩阵计算特征向量得到包围盒朝向即可。
  • 计算得到的顶点列表、包围盒中心、尺寸、极值点可以直接存入自定义数据结构,后续屏幕打印、JSON导出逻辑可直接读取该部分数据。

核心功能代码片段

using UnityEngine;
using System.Collections.Generic;

[RequireComponent(typeof(SkinnedMeshRenderer))]
public class SkinnedMeshBBoxCalculator : MonoBehaviour
{
    private SkinnedMeshRenderer _skinnedRenderer;
    private Mesh _tempBakedMesh;
    // 对外暴露计算结果
    public Bounds worldSpaceAABB;
    public List<Vector3> bakedLocalVertices = new List<Vector3>();

    void Awake()
    {
        _skinnedRenderer = GetComponent<SkinnedMeshRenderer>();
        _tempBakedMesh = new Mesh();
    }

    void LateUpdate()
    {
        // 烘焙当前帧蒙皮结果到临时网格
        _skinnedRenderer.BakeMesh(_tempBakedMesh);
        bakedLocalVertices.Clear();
        _tempBakedMesh.GetVertices(bakedLocalVertices);

        if (bakedLocalVertices.Count == 0) return;
        
        // 初始化世界空间极值
        Vector3 firstVertWorld = transform.TransformPoint(bakedLocalVertices[0]);
        Vector3 minPos = firstVertWorld;
        Vector3 maxPos = firstVertWorld;

        // 遍历所有顶点计算AABB极值
        for (int i = 1; i < bakedLocalVertices.Count; i++)
        {
            Vector3 vertWorld = transform.TransformPoint(bakedLocalVertices[i]);
            minPos = Vector3.Min(minPos, vertWorld);
            maxPos = Vector3.Max(maxPos, vertWorld);
        }

        // 生成最终包围盒
        worldSpaceAABB.SetMinMax(minPos, maxPos);
    }

    // 场景视图下绘制包围盒调试
    void OnDrawGizmosSelected()
    {
        if (worldSpaceAABB.size == Vector3.zero) return;
        Gizmos.color = Color.cyan;
        Gizmos.DrawWireCube(worldSpaceAABB.center, worldSpaceAABB.size);
    }

    void OnDestroy()
    {
        // 销毁临时网格避免内存泄漏
        if (_tempBakedMesh != null) Destroy(_tempBakedMesh);
    }
}

性能提示:如果不需要逐帧跟随动画更新包围盒(比如仅计算T-Pose下的固定包围盒),把LateUpdate内的计算逻辑挪到Start中执行一次即可,能省掉每帧的网格烘焙和顶点遍历开销。
顶点数据说明:bakedLocalVertices存储的是蒙皮后模型局部空间的顶点坐标,如果需要世界空间顶点列表,遍历顶点时调用transform.TransformPoint转换后单独存储即可。

内容的提问来源于stack exchange,提问作者BrianAI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:42:28