Unity——八叉树的原理与实现

news2024/9/29 7:22:12

八叉树原理

八叉树(Octree)是一种用于在三维空间中进行空间分割的数据结构。它将三维空间递归地划分为八个子空间,每个子空间对应于一个八叉树节点。这种分割方式可以有效地组织和管理场景中的对象,提高检索效率,特别是在进行空间查询时。

以下是八叉树的基本原理:

  1. 空间划分:

    • 初始状态:整个三维空间被表示为一个根节点,该节点包含所有的对象。
    • 递归划分:根节点被递归地划分为八个子节点,每个子节点对应于父节点的一个八分之一空间。这个过程会一直持续下去,直到达到预定义的停止条件,例如节点包含的对象数量小于某个阈值或达到最小节点大小。
  2. 节点结构:

    • 每个节点包含一个包围盒(Bounding Box)用于表示该节点所包含的空间范围。
    • 节点可能包含零个或多个对象,这些对象是该节点所表示空间范围内的物体。
    • 如果节点包含的对象数量达到某个限制,或者达到最小节点大小,不再继续划分,成为叶子节点。
  3. 添加对象:

    • 当需要向八叉树中添加对象时,从根节点开始递归地查找合适的叶子节点。
    • 将对象添加到叶子节点中。如果该节点包含的对象数量超过限制,可以考虑划分该节点。
  4. 查询操作:

    • 对于空间查询,从根节点开始,检查查询范围与每个节点的包围盒是否相交。
    • 如果相交,进一步检查该节点的子节点。
    • 递归地沿着相交的子节点继续查询,直到达到叶子节点。
    • 叶子节点包含在查询范围内的对象。
  5. 优点:

    • 空间局部性:八叉树有效地利用了物体在三维空间中的局部性,提高了检索效率。
    • 动态场景:适用于动态场景,因为对象的添加和删除只影响八叉树的局部结构。
  6. 应用领域:

    • 游戏开发:用于场景管理、碰撞检测等。
    • 计算机图形学:在光照、阴影等方面的优化中有广泛应用。
    • 计算机辅助设计(CAD):用于加速物体查询。

代码实现

1.创建项目

2.创建三个C#文件,命名为OctreeNode.cs、Octree.cs和CreateOctree.cs。

OctreeNode.cs具体代码

这个类实现了八叉树节点的功能,包括分割、添加游戏对象和绘制节点的包围盒等操作。

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

public class OctreeNode
{
    Bounds nodeBounds; // 节点的包围盒
    float minSize; // 最小节点大小
    Bounds[] childBounds; // 子节点的包围盒数组
    OctreeNode[] children = null; // 子节点数组

    // 构造函数,接受一个包围盒和最小节点大小作为参数
    public OctreeNode(Bounds b, float minNodeSize)
    {
        nodeBounds = b;
        minSize = minNodeSize;

        float quarter = nodeBounds.size.y / 4.0f;
        float childLength = nodeBounds.size.y / 2;
        // 计算子节点的包围盒
        Vector3 childSize = new Vector3(childLength, childLength, childLength);
        childBounds = new Bounds[8];
        // 创建子节点的包围盒
        childBounds[0] = new Bounds(nodeBounds.center + new Vector3(-quarter, quarter, -quarter), childSize);
        childBounds[1] = new Bounds(nodeBounds.center + new Vector3(quarter, quarter, -quarter), childSize);
        childBounds[2] = new Bounds(nodeBounds.center + new Vector3(-quarter, quarter, quarter), childSize);
        childBounds[3] = new Bounds(nodeBounds.center + new Vector3(quarter, quarter, quarter), childSize);
        childBounds[4] = new Bounds(nodeBounds.center + new Vector3(-quarter, -quarter, -quarter), childSize);
        childBounds[5] = new Bounds(nodeBounds.center + new Vector3(quarter, -quarter, -quarter), childSize);
        childBounds[6] = new Bounds(nodeBounds.center + new Vector3(-quarter, -quarter, quarter), childSize);
        childBounds[7] = new Bounds(nodeBounds.center + new Vector3(quarter, -quarter, quarter), childSize);
    }

    // 将游戏对象添加到节点
    public void AddObject(GameObject go)
    {
        DivideAndAdd(go);
    }

    // 分割并添加游戏对象
    public void DivideAndAdd(GameObject go)
    {
        if (nodeBounds.size.y <= minSize)
        {
            return; // 如果节点大小小于等于最小节点大小,停止分割
        }

        if (children == null)
        {
            children = new OctreeNode[8];
        }

        bool dividing = false;

        for (int i = 0; i < 8; i++)
        {
            if (children[i] == null)
            {
                children[i] = new OctreeNode(childBounds[i], minSize);
            }

            // 如果游戏对象的包围盒与子节点的包围盒相交,进行分割
            if (childBounds[i].Intersects(go.GetComponent<Collider>().bounds))
            {
                dividing = true;
                children[i].DivideAndAdd(go);
            }
        }

        // 如果没有进行分割,将子节点数组设为null
        if (dividing == false)
        {
            children = null;
        }
    }

    // 绘制节点的包围盒
    public void Draw()
    {
        Gizmos.color = new Color(0, 1, 0);
        Gizmos.DrawWireCube(nodeBounds.center, nodeBounds.size);

        // 如果子节点不为空,递归绘制子节点
        if (children != null)
        {
            for (int i = 0; i < 8; i++)
            {
                if (children[i] != null)
                {
                    children[i].Draw(); // 递归调用
                }
            }
        }
    }
}

Octree.cs具体代码

这个类实现主要包括构造函数和将游戏对象添加到八叉树中的方法。八叉树的根节点存储在 rootNode 中,用于表示整个八叉树的结构。

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

public class Octree
{
    public OctreeNode rootNode; // 八叉树的根节点 

    // 构造函数,接受世界中的游戏对象数组和最小节点大小作为参数
    public Octree(GameObject[] worldObjects, float minNodeSize)
    {
        Bounds bounds = new Bounds(); // 用于计算包围盒的 Bounds 对象

        // 遍历所有游戏对象,计算包围盒以包含它们
        foreach (GameObject go in worldObjects)
        {
            bounds.Encapsulate(go.GetComponent<Collider>().bounds);
        }

        // 计算包围盒的最大边长
        float maxSize = Mathf.Max(new float[] { bounds.size.x, bounds.size.y, bounds.size.z });
        Vector3 sizeVector = new Vector3(maxSize, maxSize, maxSize) * 0.5f;

        // 将包围盒的最小和最大点调整为形成一个正方体
        bounds.SetMinMax(bounds.center - sizeVector, bounds.center + sizeVector);

        // 创建八叉树的根节点,传入包围盒和最小节点大小
        rootNode = new OctreeNode(bounds, minNodeSize);

        // 将世界中的游戏对象添加到八叉树中
        AddObjects(worldObjects);
    }

    // 将游戏对象添加到八叉树中
    public void AddObjects(GameObject[] worldObjects)
    {
        foreach (GameObject go in worldObjects)
        {
            rootNode.AddObject(go);
        }
    }
}

CreateOctree.cs具体代码

这个类主要用于在启动时创建八叉树对象,并在运行时通过 OnDrawGizmos 方法绘制八叉树的根节点的包围盒。在 Start 方法中,创建了一个 Octree 对象(otree),并通过传入的世界游戏对象数组和最小节点大小进行初始化。在 OnDrawGizmos 方法中,如果应用程序正在运行,则调用八叉树的根节点的 Draw 方法来绘制八叉树的结构。

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

public class CreateOctree : MonoBehaviour
{
    public GameObject[] worldObjects; // 存储世界中的游戏对象数组
    public int nodeMinsize = 5; // 八叉树的最小节点大小
    Octree otree; // 八叉树对象

    // 在启动时调用,用于初始化
    void Start()
    {
        otree = new Octree(worldObjects, nodeMinsize); // 创建八叉树对象并初始化
    }

    // 在每一帧更新时调用
    void OnDrawGizmos()
    {
        if (Application.isPlaying)
        {
            otree.rootNode.Draw(); // 在运行时绘制八叉树的根节点的包围盒
        }
    }
}

3.创建一个空物体(Create empty),命名为Octree,添加CreateOctree组件。

4.创建对象,调整位置和缩放比例等。(本实验创建了4个立方体,可根据情况,自由创建其他对象,注:对象一定要有碰撞器 Collier,不如会报错:NullReferenceException: Object reference not set to an instance of an object)

5.把对象添加到 world Objects中。

6.运行项目,效果如下:

参考链接

最浅显易懂的 Unity 八叉树场景管理(保姆级)-CSDN博客

Unity 八叉树空间分割的简单实现,part1, Dividing 3D Space into an Octree_哔哩哔哩_bilibili

Unity 八叉树空间分割的简单实现,part2,Dividing 3D Space into an Octree_哔哩哔哩_bilibili

Unity 八叉树空间分割的简单实现,part3,Dividing 3D Space into an Octree_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1410371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

会计等式与会计事项

目录 一. 会计等式二. 会计事项 \quad 一. 会计等式 \quad 最后利润是归所有者权益所有, 就回到了原有等式 \quad \quad \quad 二. 会计事项 \quad 会计事项: 引起会计要素增减变化的经济业务。 会计六要素: 资产 负债 所有者权益 收入 费用 利润 任何会计事项都不会破坏会计…

C++ 数论相关题目(欧拉函数、筛法求欧拉函数)

1、欧拉函数 给定 n 个正整数 ai &#xff0c;请你求出每个数的欧拉函数。 欧拉函数的定义 1∼N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为 ϕ(N) 。 若在算数基本定理中&#xff0c;Npa11pa22…pamm &#xff0c;则&#xff1a; ϕ(N) Np1−1p1p2−1p2…pm−1p…

人工智能时代:让AIGC成为你的外部智慧源(文末送书)

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 什么是AIGC?二. AIGC如何运作&#xff1f;2.1 步骤一&#xff1a;收集数据2.…

day32WEB 攻防-通用漏洞文件上传二次渲染.htaccess变异免杀

本章节知识点&#xff1a; 1 、文件上传 - 二次渲染 2 、文件上传 - 简单免杀变异 3 、文件上传 -.htaccess 妙用 4 、文件上传 -PHP 语言特性 前置知识&#xff1a; 后门代码需要用特定格式后缀解析&#xff0c;不能以图片后缀解析脚本后门代码 ( 解析漏洞除外 ) 如&…

Aleo测试网回顾-测试网期间共释放了多少积分

上一篇我们整理了Aleo的详细项目介绍&#xff0c;Aleo项目详细介绍-一个兼顾隐私和可编程性的隐私公链-CSDN博客 接下来&#xff0c;让我们盘点下测试网期间的积分释放情况&#xff0c;测试网期间的奖励积分也将是Aleo主网上线后的抛压来源。测试网期间共计释放了4000万的积分…

2024转行程序员的请注意:均月薪在40-70k

前言 2023年&#xff0c;对大多数行业来说都是不太好过的一年。 对程序员来说也是如此&#xff0c;很多粉丝朋友都在说android工作特别难找&#xff0c;一个岗位都是几千份简历…大家心里都是特别的焦虑&#xff0c;本以为2024年就业情况会有好转&#xff0c;但实际上并非如此…

解决Windows系统本地端口被占用的问题

一、解决Windows系统本地端口被占用的问题&#xff0c;首先我们要在虚拟机上人为的占用本地端口 二、占用端口方法&#xff1a;以管理员身份运行cmd;输入net stop http;如果提示是否真的需要停止这些服务,则选择“Y”;完成后输入:sc config http startdisabled 弹出上图内容则成…

UE4运用C++和框架开发坦克大战教程笔记(十六)(第49~50集)

UE4运用C和框架开发坦克大战教程笔记&#xff08;十六&#xff09;&#xff08;第49~50集&#xff09; 49. 创建多个资源对象补全调用链并测试生成多个同种类名资源对象实现创建多个同资源名的对象实例 50. 资源加载系统测试补全调用链并测试生成多个同名资源对象测试生成 Widg…

【mongoDB】集合的创建和删除

目录 1.集合的创建 2. 查看所有集合 3.删除集合 1.集合的创建 格式&#xff1a; db.createCollection ( name ) 例如创建一个名为 bbb 的集合 还可以通过传递一个选项对象来指定集合的属性&#xff0c;例如最大文档的大小&#xff0c;索引选项等 例如 这样创建了一个名为 cc…

[极客大挑战 2019]BabySQL1

发现union select被过滤了&#xff0c;双写绕过 or、from被过滤 where被过滤 在b4bysql中找到flag

【pdf技巧】pdf无法编辑的原因是什么?如何编辑pdf?

打开PDF文件之后发现没有办法编辑PDF文件&#xff0c;都有哪些原因呢&#xff1f; 首先我们可以考虑一下&#xff0c;PDF文件中的内容是否是图片&#xff0c;如果确认是图片文件&#xff0c;那么我们想要编辑&#xff0c;就可以先使用PDF编辑器中的OCR扫描功能&#xff0c;将图…

【c语言】三子棋

前言&#xff1a; 三子棋是一种民间传统游戏&#xff0c;又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏规则是双方对战&#xff0c;双方依次在9宫格棋盘上摆放棋子&#xff0c;率先将自己的三个棋子走成一条线就视为胜利。但因棋盘太小&#xff0c;三子棋在很多时候会出现和…

springboot实现aop

目录 AOP(术语)引入依赖实现步骤测试验证感谢阅读 AOP(术语) 连接点 类里面哪些方法可以增强&#xff0c;这些点被称为连接点 切入点 实际被真正增强的方法 通知&#xff08;增强&#xff09; 实际增强的逻辑部分称为通知&#xff08;增强&#xff09; 通知&#xff08;增强&…

系统登录的时候的密码如何做到以加密的形式进行登录【java.security包下的api】工具类。

/** description: 将普通的publicKey转化得到一个RSAPublicKey* author: zkw* date: 2024/1/24 16:17* param: publicKey 普通的publicKey* return: RSAPublicKey 得到一个新的RSAPublicKey**/public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorit…

数据结构——静态链表

1.定义&#xff1a; &#xff08;1&#xff09;单链表&#xff1a;各个结点散落在内存中的各个角落&#xff0c;每个结点有指向下一个节点的指针(下一个结点在内存 中的地址); &#xff08;2&#xff09;静态链表&#xff1a;用数组的方式来描述线性表的链式存储结构: 分配一…

RUST笔记 FireDBG| Rust 代码调试器

安装https://firedbg.sea-ql.org/blog/2023-12-12-introducing-firedbg/ 更新VSCODE sudo dpkg -i code_1.85.2-1705561292_amd64.deb 安装FireDBG binaries (base) pddpdd-Dell-G15-5511:~$ curl https://raw.githubusercontent.com/SeaQL/FireDBG.for.Rust/main/install.sh …

java——逻辑控制

这里学过c语言这一节还是很简单的&#xff0c;只是我们这里利用的是java的语法知识。这里我们重点是熟练运用java 的语法&#xff0c;结合前面学的知识。 这一章更多的是利用java语法知识来编程。 &#x1f6a9;if 语句 if(布尔表达式){ // 语句 }if(布尔表达式){ // 语句1…

Unity动画桢事件

1&#xff0c;使用原因 在新项目内部审核的时候&#xff0c;说什么动画节奏不匹配&#xff0c;所以决定用动画桢事件来处理技能释放。当释放技能的时候&#xff0c;先播放技能动画&#xff0c;然后再动画桢所在的时间戳执行技能的逻辑。 2&#xff0c;具体实现 1&#xff0c;…

gin中使用限流中间件

限流又称为流量控制&#xff08;流控&#xff09;&#xff0c;通常是指限制到达系统的并发请求数&#xff0c;本文列举了常见的限流策略&#xff0c;并以gin框架为例演示了如何为项目添加限流组件。 限流 限流又称为流量控制&#xff08;流控&#xff09;&#xff0c;通常是指…

【前端基础--4】

定位属性 position 可以将元素定位到你想要放到位置&#xff0c;使用方位值来进行移动(top,left,right,bottom)。 1.相对定位 position: relative; top: 20px; left: 20px; 以自身为定点进行移动&#xff0c;不会脱离文档流。 不会影响元素本身的性质&#xff1b;块级…