首页 > 其他分享 >八叉树-Unity

八叉树-Unity

时间:2024-07-05 17:11:04浏览次数:11  
标签:center 八叉树 Vector3 Unity nodeBounds new quarter

八叉树

octree

八叉树简介

八叉树(Octree)是一种在三维空间中进行数据组织和存储的树型数据结构。它的工作原理是将一个大的三维空间递归地分割成八个相等的小空间,每个小空间又可以继续分割成八个更小的空间,以此类推,直到达到某个预定的深度或者满足特定的终止条件(例如,空间内元素数量少于一个阈值)。每个分割后的空间都可以视为一个节点,整个结构形成了一个树形层次。

在Unity游戏引擎中,八叉树主要用于场景管理,特别是对于大型环境和密集物体的高效处理。其主要作用包括:

  1. 碰撞检测:在复杂的环境中,八叉树可以快速定位可能发生的碰撞区域,减少不必要的碰撞检测计算,提高效率。
  2. 视图裁剪:在渲染过程中,八叉树可以帮助确定哪些物体位于相机视野之内,从而避免渲染视野外的物体,节省GPU资源。
  3. 物体查找:八叉树可以快速定位和检索空间中的物体,尤其是在大规模场景中,这种能力尤为重要。
  4. 动态负载平衡:在多人在线游戏中,八叉树可以辅助服务器管理玩家的位置,将玩家合理分配到不同的服务器分区,以达到负载平衡。
  5. 粒子系统管理:对于具有大量粒子的系统,八叉树可以优化粒子的更新和渲染,减少计算量。
  6. 路径寻找:在AI路径规划中,八叉树可以作为障碍物的快速查询结构,加速寻路算法的执行。
  7. LOD(Level of Detail)管理:八叉树可以辅助管理不同细节级别的模型,确保远处的模型使用较低细节,近处的模型使用较高细节,从而节省资源

八叉树的实现示例:

八叉树

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;

[System.Serializable]
public class OctreeObject
{
    public GameObject GameObject;
    public Bounds Bounds;

    public OctreeObject(GameObject go)
    {
        GameObject = go;
        Bounds = go.GetComponent<Collider>().bounds;
    }
}
[System.Serializable]
public class OctreeNode
{
    public Bounds NodeBounds;
    public List<OctreeObject> ContainedObjects;
    private float minSize;
    private Bounds[] childBounds;
    public OctreeNode[] Children=null;
    public OctreeNode(Bounds nodeBounds, float minNodeSize)
    {
        ContainedObjects = new List<OctreeObject>();
        this.NodeBounds = nodeBounds;
        this.minSize = minNodeSize;
        
        float quarter= this.NodeBounds.size.y / 4;
        float childLength = this.NodeBounds.size.y / 2;
        Vector3 childSize = new Vector3(childLength, childLength, childLength);
        childBounds = new Bounds[8];
        childBounds[0] = new Bounds(new Vector3(nodeBounds.center.x - quarter, nodeBounds.center.y + quarter, nodeBounds.center.z - quarter), childSize);
        childBounds[1] = new Bounds(new Vector3(nodeBounds.center.x + quarter, nodeBounds.center.y + quarter, nodeBounds.center.z - quarter), childSize);
        childBounds[2] = new Bounds(new Vector3(nodeBounds.center.x - quarter, nodeBounds.center.y + quarter, nodeBounds.center.z + quarter), childSize);
        childBounds[3] = new Bounds(new Vector3(nodeBounds.center.x + quarter, nodeBounds.center.y + quarter, nodeBounds.center.z + quarter), childSize);
        childBounds[4] = new Bounds(new Vector3(nodeBounds.center.x - quarter, nodeBounds.center.y - quarter, nodeBounds.center.z - quarter), childSize);
        childBounds[5] = new Bounds(new Vector3(nodeBounds.center.x + quarter, nodeBounds.center.y - quarter, nodeBounds.center.z - quarter), childSize);
        childBounds[6] = new Bounds(new Vector3(nodeBounds.center.x - quarter, nodeBounds.center.y - quarter, nodeBounds.center.z + quarter), childSize);
        childBounds[7] = new Bounds(new Vector3(nodeBounds.center.x + quarter, nodeBounds.center.y - quarter, nodeBounds.center.z + quarter), childSize);
    }

    

    public void AddObject(GameObject go)
    {
        DivideAndAdd(go);
    }

    private void DivideAndAdd(GameObject go)
    {
        OctreeObject octObj = new OctreeObject(go);
        
        if (NodeBounds.size.y <= minSize)
        {
            ContainedObjects.Add(octObj);
            return;
        }
        
        Children ??= new OctreeNode[8];
        
        var dividing=false;
        for (var i = 0; i < 8; i++)
        {
            Children[i]??= new OctreeNode(childBounds[i], minSize);
            if(childBounds[i].Intersects(octObj.Bounds))
            {
                dividing = true;
                Children[i].DivideAndAdd(go);
            }
        }

        if (dividing == false)
        {
            ContainedObjects.Add(octObj);
            Children = null;
        }
    }
    
    public void Draw()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawWireCube(NodeBounds.center, NodeBounds.size);
        
        Gizmos.color = Color.red;
        foreach (var obj in ContainedObjects)
        {
            Gizmos.DrawWireCube(obj.Bounds.center, obj.Bounds.size);
        }


        if (Children != null)
        {
            foreach (var child in Children)
            {
                if (child != null)
                    child.Draw();
            }
        }
        else if (ContainedObjects.Count != 0)
        {
            Gizmos.color = new Color(0,0,1,0.25f);
            Gizmos.DrawCube(NodeBounds.center, NodeBounds.size);
        }
    }
}

示例——寻找包围盒中最近的点

假设我们已经通过八叉树对很多点进行了分割,现在想找出一个点里这些点集最近的点,太远则视为不相交。

public Vector3? FindNearestPoint(Vector3 p)
{
    if (!Bounds.Contains(p))
        return null; // 如果点P不在八叉树有点的空间,返回null

    if (points.Count > 0)
    {
        // 如果当前节点有点,找到离p最近的唯一一个点
        Vector3 nearestPoint = points[0];
        float nearestDistance = Vector3.Distance(nearestPoint, p);

        for (int i = 1; i < points.Count; i++)
        {
            float distance = Vector3.Distance(points[i], p);
            if (distance < nearestDistance)
            {
                nearestPoint = points[i];
                nearestDistance = distance;
            }
            else if (distance == nearestDistance)
            {
                // 如果有多个点与p的距离相同,返回null
                return null;
            }
        }

        return nearestPoint;
    }
    else if (children != null)
    {
        // 如果当前节点没有点,递归查找子节点
        Vector3? nearestPoint = null;
        float nearestDistance = float.MaxValue;

        for (int i = 0; i < children.Length; i++)
        {
            if (children[i] != null)
            {
                Vector3? childNearestPoint = children[i].FindNearestPoint(p);
                if (childNearestPoint.HasValue)
                {
                    float distance = Vector3.Distance(childNearestPoint.Value, p);
                    if (distance < nearestDistance)
                    {
                        nearestPoint = childNearestPoint;
                        nearestDistance = distance;
                    }
                    else if (distance == nearestDistance)
                    {
                        // 如果有多个点与p的距离相同,返回null
                        return null;
                    }
                }
            }
        }

        return nearestPoint;
    }
    else
    {
        // 如果当前节点既没有点,也没有子节点,返回null
        return null;
    }
}

参考

BVH从Octree开始https://zhuanlan.zhihu.com/p/696725602

Unity中实现八叉树和BVHhttps://www.bilibili.com/video/BV1Ei421e7xi

八叉树等功能库插件https://prf.hn/click/camref:1011l5dHP/destination:https://assetstore.unity.com/packages/tools/integration/coding-essentials-76797

Unity资源商城https://prf.hn/click/camref:1011l5dHP

免费AI工具:https://fas.st/t/Ep3jbPVm

获得永久免费的无限 GPT 查询次数!点击链接并下载Monica插件,即可参加限时活动。

https://monica.im/invitation?c=OWBJ2ZBE

标签:center,八叉树,Vector3,Unity,nodeBounds,new,quarter
From: https://www.cnblogs.com/Firepad-magic/p/18286166

相关文章

  • 【Unity几种数据存储之间的区别】PlayerPrefs、Json、XML、二进制、SQLite数据存储之
    ......
  • Unity海面效果——4、法线贴图和高光
    Unity引擎制作海面效果  大家好,我是阿赵。  继续做海面效果,上次做完了漫反射颜色和水波动画,这次来做法线和高光效果。一、高光的计算  之前介绍过高光的光照模型做法,比较常用的是Blinn-Phong  所以我这里也稍微连线实现了一下  为了能看得更清楚......
  • 微信SDK与Unity的Addressables发生引用冲突的解决办法
    当我使用Unity的Addressables和微信的minigame-SDK时,会发生一个CS0433的报错,如下图所示: 关于CS0433错误,微软的官方文档中是这么描述的: 因此,根据报错信息,我揣测是Unity的Compat与mscorlib发生了重复,所以将mscorlib.dll文件全部删除了,但是问题没有得到解决,后面在一个大佬的帮......
  • unity canvas显示相机照射画面的方法
    1. 使用 Image 组件显示处理后的图像如果你的图像数据已经是一个 Texture2D 或 Sprite,你可以将它直接显示在Canvas上的 Image 组件中:创建 Sprite:将你的 Texture2D 数据转换为 Sprite,以便可以在 Image 组件中使用。publicSpriteCreateSpriteFromTexture(......
  • Unity的Package库在IDE里不显示API注释的解决方法
    当你在代码里使用Package库的API的时候,比如Addressable和Unity.Entities等等,以VisualStudio为例,鼠标放到API上,会发现不显示注释:然而按F12访问源代码,会发现代码里面是有注释的,而且对于Unity的包,注释会非常的详细:本质原因是Unity在编译这些Package的时候,没有生成XML注释文档,导......
  • unity 从list中获取最近的坐标 / 获取最接近的角度(数值)
    ///<summary>///从列表points中获取距离targetPoint最近的坐标///</summary>///<paramname="points"></param>///<paramname="targetPoint"></param>///<returns><......
  • 【Unity】HoloLens2 开发日记
    2022/1/12第一天!START!配置环境微软官方教程:练习-导入和配置资源-Learn|MicrosoftDocsWindows10SDK地址:WindowsSDK-Windows应用开发(microsoft.com)MRTK(混合现实工具包):DownloadMixedRealityFeatureToolfromOfficialMicrosoftDownloadCenter......
  • 【Unity】EventTrigger各事件触发时机解释
    OnSelect():当鼠标按下了一个当前未在选中状态的UI时触发。如果一个UI已经被选中(成为焦点UI),那么再点击这个UI时,则不会再一次触发OnSelect。OnDeselect():在OnSelect()已经执行过之后,又选中了另外一个物体时,原有的物体就会触发OnDeselect(),取消选择。OnUpdateSelect():在......
  • Unity写个多用对象池
    1.介绍游戏开发中我们会频繁使用到预制体来优化内存,优化性能,增强游戏表现。当要使用的预制体次数很多,创建销毁很频繁时,为了方便管理、提升性能,我们需要一个对象池。一般使用单例+一个预制体+一个存储类型就能做出一个简单的对象池。但当我们需要对很多种物体进行对象池管理时......
  • Unity 导航路线生成,小地图同步映射, 经过以后地图与小地图删除点位(点击小地图控制导航
    效果:(如下图所示)操作方法:搭建小地图UI截取图片创建地面挂载如下代码:usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.UI;[RequireComponent(typeof(MeshFilter),typeof(MeshCollider),typeof(MeshRenderer))]publicclassMap:Mo......