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

八叉树原理

八叉树(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.mfbz.cn/a/350754.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu 相关内容

ubuntu 优盘安装&#xff1a; 台式机安装纯ubuntu系统的操作步骤-CSDN博客https://blog.csdn.net/youngwah292/article/details/127032009?ops_request_misc%257B%2522request%255Fid%2522%253A%2522170583039216800213099577%2522%252C%2522scm%2522%253A%252220140713.1301…

【USTC】verilog 习题练习 36-40

36 条件运算符 题目描述 Verilog中有一个跟C语言中类似的三目条件运算符&#xff08; ? : &#xff09;,其语法格式为&#xff1a; (condition ? if_true : if_false) 该表达式可以用于为其它信号赋值&#xff0c;例如&#xff1a;signal condition ? if_true : if_fals…

455. 分发饼干 - 力扣(LeetCode)

题目描述 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且每块饼干 j&#xff0c;都有一个尺…

区块空间----流动性铭文

铭文正在改变加密世界&#xff0c;越来越多的人开始关注铭文&#xff0c;并参与进来&#xff01;铭文赛道的未来是非常具有潜力和想象力的&#xff0c;甚至能够达到加密货币的普及水平。当然&#xff0c;这需要更多的基础设施更多的用例支持&#xff0c;但是一切都才刚刚开始。…

@JsonIgnore的使用及相关问题的解决

目录 1 前言 2 对比及其使用方法 3 遇到的相关问题及解决方法 1 前言 在我们编写的后端项目中&#xff0c;有时候可能需要将某个实体类以JSON格式传送给前端&#xff0c;但是其中可能有部分内容我们并不想传送&#xff0c;这时候我们选择将这部分内容变成Null&#xff0c;这…

网络安全01--负载均衡

目录 一、环境准备 1.1三台虚拟机 二、开始搭建负载均衡&#xff1a; 2.1准备一下源 2.2正式安装 2.3Nginx安装情况 三、修改配置文件&#xff0c;反向代理达到负载均衡效果 3.1在 http 部分添加如下负载均衡配置&#xff1a; 3.2简单解释一下server端&#xff1a; 四…

Unity2020.3打包ARFoundation问题记录

文章目录 前言一、打包成功后再打包失败&#xff08;重启工程后&#xff09;二、URP管线总结 前言 在Unity 2020.3版本中使用ARFoundation进行打包时&#xff0c;遇到一些小问题。本文简要记录了其中一些问题及解决方法&#xff0c;以备不时之需。 一、打包成功后再打包失败&a…

【第一天】蓝桥杯备战

题 1、 门牌号2、卡片3、分数 1、 门牌号 https://www.lanqiao.cn/problems/592/learning/ 解法一&#xff1a;暴力遍历 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner sca…

鸿蒙开发(Harmonyos兼容与Harmonyos适配)

布局的实现 Layout_ability_main.xml布局&#xff1a; <?xml version"1.0" encoding"utf-8"?> <DirectionalLayoutxmlns:ohos"http://schemas.huawei.com/res/ohos"ohos:height"match_parent"ohos:width"match_pare…

AI引爆算力需求,思腾推出支持大规模深度学习训练的高性能AI服务器

近日人工智能研究公司OpenAI公布了其大型语言模型的最新版本——GPT-4&#xff0c;可10秒钟做出一个网站&#xff0c;60秒做出一个游戏&#xff0c;参加了多种基准考试测试&#xff0c;它的得分高于88%的应试者&#xff1b;随后百度CEO李彦宏宣布正式推出大语言模型“文心一言”…

zuul网关

zuul网关 zuul自定义过滤器hystrix和ribbon时间RibbonAutoConfiguration自动配置FeignAutoConfiguration自动配置RibbonEurekaAutoConfigurationSendErrorFilter过滤器EnableZuulServerHasFeatures EnableZuulProxy zuul自定义过滤器 继承ZuulFilter类&#xff0c;实现其方法f…

Java 设计者模式以及与Spring关系(五) 策略和观察者模式

目录 简介: 23设计者模式以及重点模式 策略模式&#xff08;Strategy Pattern&#xff09; 示例 spring中应用 观察者模式&#xff08;Observer&#xff09; 示例 spring中应用 简介: 本文是个系列一次会出两个设计者模式作用&#xff0c;如果有关联就三个&#xff0c;…

Objective-C方法的声明实现及调用

1.无参数的方法 1)声明 a.位置&#xff1a;在interface括弧的外面 b.语法&#xff1a; - (返回值类型)方法名称; interface Person : NSObject -(void) run; end 2)实现 a.位置&#xff1a;在implementation中实现 b.语法&#xff1a;加大括弧将方法实现的代码写在大括孤之中 …

【C++】filesystem

文章目录 1. 基本配置1.1. VS2019修改C标准1.2. filesystem的引入 2. 日常使用2.1. 认识2.2. 控制台输入路径并对路径进行基本操作 <filesystem>是C 17标准引入的标准库&#xff0c;主要用于处理文件系统的目录和文件操作&#xff0c;接下来总结该库的基本配置和日常使用…

python基础2

1.注释 每行注释大可不必&#xff0c;不是翻译代码&#xff0c;注释的位置一般在代码的上方书写用#表示这是注释 一个#只是单行注释 一般不写在后边&#xff0c;右边&#xff0c;多行注释’’’ ‘’’是一对配套的 eg:’’’333’’’ python爬虫里面复数不用&#xff0c…

纯血鸿蒙来了,企业开发者应该关注什么

2024年对于鸿蒙来说是一个里程碑&#xff0c;鸿蒙将正式对外发布 HarmonyOS NEXT 5.0&#xff0c;而此前传言的系统将不再对开发者层面兼容Android等消息将成为定论。 为什么鸿蒙能有这个底气&#xff1f;因为研究机构TechInsights发布预测报告称&#xff0c;华为HarmonyOS将在…

根据tree结构数据自动生成多层级勾选自定义tree-table

一、需求 需要根据tree嵌套结构数据渲染出 多层级带勾选的table&#xff0c;想要的对应结构table 二、思路 手撸原生table的demo&#xff0c;然后根据多层级嵌套的遍历&#xff0c;写成html拼接&#xff0c; 由于有几层的遍历逻辑和 tr只能包裹td元素&#xff0c;html拼接后…

MS7256C:L1 频段卫星导航射频前端低噪声放大器芯片

1、描述 MS7256C 是一款具有高增益、低噪声系数的低噪声放 大器&#xff08;LNA&#xff09;芯片&#xff0c;支持 L1 频段多模式全球卫星定位&#xff0c;可 以应用于 GPS、北斗二代、伽利略、Glonass 等 GNSS 导航 接收机中。芯片采用先进工艺制造&#xff0c;封装采用 1…

Hive3.1.3基础

参考B站尚硅谷 目录 什么是HiveHive架构原理 Hive安装Hive安装地址Hive安装部署安装Hive启动并使用Hive MySQL安装安装MySQL配置MySQL 配置Hive元数据存储到MySQL配置元数据到MySQL Hive服务部署hiveserver2服务metastore服务编写Hive服务启动脚本&#xff08;了解&#xff09…

【C++中STL】list链表

List链表 基本概念构造函数赋值和交换大小操作插入和删除数据存取反转和排序 基本概念 将数据进行链式存储 链表list是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的&#xff0c;链表是由一系列结点组成&#xff0c;结点的组…
最新文章