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

2024-01-25 23:36
文章标签 实现 原理 unity 八叉树

本文主要是介绍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);}}// 如果没有进行分割,将子节点数组设为nullif (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

这篇关于Unity——八叉树的原理与实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/644988

相关文章

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核