如何在unity中手写一个四叉树地形lod系统(二)

这篇具有很好参考价值的文章主要介绍了如何在unity中手写一个四叉树地形lod系统(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  在根据四叉树节点创建了1365个地形分块网格并保存到本地后,我们接下来要在游戏运行的过程中动态地显示所需的网格,这是最关键的一步。

  如何根据摄像机位置动态地选择地形块?这其中体现了由整体到局部,从简单到复杂的原则。

  0、 我们首先创建三个缓存列表。

  1、 我们先将索引为0的地形分块(即最高LOD等级)的分块放入BufferA;

  2、 然后遍历BufferA,判断BufferA中的每一个元素是否符合“无需更加详细”的条件,如果是,将它放入BufferFinal,否则放入BufferB;

  3、 在遍历完BufferA中的元素后,清空BufferA,将BufferB的元素全部复制到BufferA中,清空BufferB;

  4、 重复2-3步骤的操作,直到BufferA、BufferB列表均空。

  此时BufferFinal中存储的索引即是我们最终所需要的地形网格分块的索引。

  我们把以上的操作封装成函数,在游戏开始运行时调用一次。这一部分的代码如下:

  

    private void TerrainGen()   // 生成(更新)地形网格
    {
        // 使用了子物体网格的方法,而不是网格合并的方法

        // 因此需要首先清除所有子物体
        for (int i = 0; i < transform.childCount; i++)
        {
            Destroy(transform.GetChild(i).gameObject);
        }


        // 四叉树计算
        // 三个buffer计算方法
        List<int> BufferA = new List<int>();
        List<int> BufferB = new List<int>();
        List<int> FinalBuffer = new List<int>();

        Vector3 ppos = player.transform.position;

        // 迭代计算
        BufferA.Add(0);             // bufferA初始化,加入根节点
        while (BufferA.Count != 0)  // 当bufferA不为空时
        {
            while (BufferA.Count != 0)   // 遍历buffera每个值
            { 
                int i = BufferA[0];
                float dist = Mathf.Sqrt(Mathf.Pow((qTree[i].begin_Pos.x + 64 * qTree[i].interval) * 5 - ppos.z, 2) + Mathf.Pow((qTree[i].begin_Pos.y + 64 * qTree[i].interval) * 5 - ppos.x, 2));   // 计算瓦片中心距离玩家位置的水平距离
                
                BufferA.Remove(i);

                if (dist >= 0.8 * 128 * qTree[i].interval * 5)   // 1表示1倍瓦片边长;128表示瓦片行列数;5时xzbias(此处其实应该参数化)
                {
                    // 那么这张瓦片距离玩家太远,可以直接使用当前lod,不用细化
                    FinalBuffer.Add(i);
                }
                else
                {
                    // 否则这张瓦片距离玩家很近,如果有更小的lod,应该细化;
                    if(qTree[i].LodLeval == 0)  // 如果已经是最细节的瓦片了,那没办法了,直接显示
                    {
                        FinalBuffer.Add(i);
                    }
                    else        // 否则把它的四个更细节的子节点加入到待计算的b buffer
                    {
                        for(int j = 1; j <= 4; j++)
                        {
                            BufferB.Add(i * 4 + j);
                        }
                    }
                }
            }

            // 将ab buffer交换
            foreach(int i in BufferB)
            {
                BufferA.Add(i);
            }
            BufferB.Clear();
        }


        Mesh[] tiles = new Mesh[FinalBuffer.Count];

        Material mat = GetComponent<Renderer>().material;

        for (int i = 0; i < FinalBuffer.Count; i++)
        {
            tiles[i] = new Mesh();
            tiles[i].indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            string path = @"Assets\LodMeshes\LodMesh_" + FinalBuffer[i].ToString() + ".asset";
            tiles[i] = (Mesh)AssetDatabase.LoadAssetAtPath(path, typeof(Mesh));

            GameObject theTile = new GameObject();
            theTile.transform.parent = this.transform;
            theTile.name = "lod_" + i.ToString();
            theTile.AddComponent<MeshFilter>();
            theTile.AddComponent<MeshRenderer>();
            theTile.AddComponent<Renderer>();

            theTile.GetComponent<MeshFilter>().mesh = new Mesh();
            theTile.GetComponent<MeshFilter>().mesh = tiles[i];
            theTile.GetComponent<Renderer>().material = mat;
        }


    }

 

       这一部分我选择将需要的网格实例化到子对象中而没有合并,如果需要优化的话,应该将网格合并成一个,可以减少DrawCall的数量。

       同时要注意的是:网格的实例化与显示并非在每一帧进行,可以维护一个数对来表示玩家摄像机所在的区域,如果玩家摄像机离开了原本的区域进入到新的区域中,那我们便执行一此地形网格更新操作:

    void Update()
    {
        if((int)player.transform.position.x / (64 * 5) != x_area || (int)player.transform.position.z / (64 * 5) != z_area)  // 玩家区域改变
        {
            TerrainGen();
            x_area = (int)player.transform.position.x / (64 * 5);
            z_area = (int)player.transform.position.z / (64 * 5);
        }
    }

  补充一点,在此之前我们可以从本地读取,在上一节生成网格时保存的四叉树信息,这部分代码很简单,如下所示:

    private void ReadQTree()
    {
        using(StreamReader RawTerrainData = new StreamReader(@"E:\Unity\MyProjects\Desert_01\Assets\TerrainTree\MyQTree.txt"))
        {
            string line;
            for (int i = 0; i < qTree.Length; i++)   // 读取所有顶点   并且给原始uv数据赋值
            {
                line = RawTerrainData.ReadLine();
                string[] stringdata = line.Split(' ');

                // 读取并写入
                float.TryParse(stringdata[0], out qTree[i].begin_Pos.x);
                float.TryParse(stringdata[1], out qTree[i].begin_Pos.y);
                int.TryParse(stringdata[2], out qTree[i].interval);
                int.TryParse(stringdata[3], out qTree[i].LodLeval);
                float.TryParse(stringdata[4], out qTree[i].Center.x);
                float.TryParse(stringdata[5], out qTree[i].Center.y);

            }
        }
    }

  最终的效果如下:

如何在unity中手写一个四叉树地形lod系统(二)

如何在unity中手写一个四叉树地形lod系统(二)

  至此,我们大致完成了一个非常基础的一个四叉树网格地形系统,这其中还有很多问题,我大致思考了一下改进的方向:

  性能优化方面的问题问题,比如显示的网格应该合并成一个而非保持多个对象;明显超出视线范围的地形网格分块应该直接剔除掉而非继续显示等;

       代码复用性的方面的问题,有许多数据直接写死在代码里面,导致耦合度过高。在改进的时候,应该将这些数据参数化,将算法更优化,来降低耦合度,增强代码对不同大小的地形的复用能力;

       效果实现方面的问题,没有考虑不同LOD等级的地块的连接处的露缝问题,应该在后续中改进

       这里我仅仅实现了最基本的网格动态显示,没有考虑渲染,在以后的改进中,我会尝试在地形渲染方面做出更多改进。文章来源地址https://www.toymoban.com/news/detail-482221.html

到了这里,关于如何在unity中手写一个四叉树地形lod系统(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity制作一个地形,下载地形资源、绘制地形,添加树、草,草可摆动

    选择了Terrain Tools,添加至我的资源 在unity中打开 菜单栏  窗口 包管理器,找到已经获取的资源 下载并导入 全选、导入  方法二: 导入已经下载过的资源 (.unitypackage:Unity package file) 资源菜单 - 导入/项目视图 - 资源 - 右键 - 导入/直接拖拽进项目视图 - 资源 打开下载的资

    2024年01月22日
    浏览(31)
  • 数据结构——四叉树

    四叉树(Quadtree)是一种用于表示和管理二维空间的树状数据结构。它将二维空间递归地分割成四个象限,每个象限可以继续分割,以实现对空间的更精细的划分。四叉树通常用于解决空间搜索和查询问题,例如碰撞检测、图像压缩、地理信息系统等领域。 特别适合大规模的

    2024年02月07日
    浏览(37)
  • unity如何绘制地形

    unity如何绘制地形?Unity的地形是由一个特殊的组件称为Terrain来创建的。这个组件可以让你在Unity中创建非常复杂的地形,包括山脉、山谷、河流、湖泊等等。 在Unity中创建地形非常简单,只需要在场景中创建一个空对象,然后在Inspector面板中点击“Add Component”,选择“Terr

    2024年02月09日
    浏览(29)
  • 四叉树图像模糊(C代码及实现思路)

    原创文章,参考文章见末尾,仅供学习交流使用,如果对你有帮助,请一键三连~ 代码如有需要会整理上传~ 能够正确的对图像建立四叉树; 对于输入的图像,四叉树能够输出模糊的结果 对颜色相近的区域进行模糊 背景知识理解 PPM文件格式理解 PPM 是通过RGB三种颜色显现的图

    2024年02月02日
    浏览(28)
  • Unity地形系统Terrain

    1.Terrain组件介绍 2.Raise or Lower Terrain介绍         按shift+鼠标左键,将抹平降低地形。 3.Set Height简单介绍         Height设置可升高地形的最大高度。         通过Flatten all可整体抬高地形,结合 Raise or Lower Terrain降低地形,可以制作出洼地。再添加河流特效调整至洼地

    2024年02月08日
    浏览(28)
  • 空间数据结构(四叉树、八叉树、BVH树、BSP树、k-d树)

    在游戏程序中,利用空间数据结构加速计算往往是非常重要的优化思想,空间数据结构可以应用于场景管理、渲染、物理、游戏逻辑等方面。 2.1 四叉树 四叉树是很常见的一种 2D 碰撞检测方法,实现手段也五花八门。不过在具体实现中要注意优化细节,控制建树时间消耗与建

    2024年01月19日
    浏览(38)
  • 数据结构与算法大作业——四叉树自适应模糊

    能够正确的对图像建立四叉树; 对于输入的图像,四叉树能够输出模糊的结果 对颜色相近的区域进行模糊 可通过十六进制编辑器 010editor 打开查看二进制信息 官网获取 010editor 信息 含义 P6 指明PPM的编码格式 2156 2156 图像大小为2156*2156 255 RGB的每个色彩值范围为0~255 C0 91 89(

    2024年01月19日
    浏览(28)
  • 【Unity地编】地形系统搭建入门详解

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 : UI_Unity专栏 🅰️ GameObject 3D Object Terrain 1.创建邻近地形 直接点击相邻方块即可创建 2.笔刷 笔刷参数详解 Raise or Lower Terrain:提升或降低高

    2024年02月08日
    浏览(28)
  • C#几何算法:空间索引——Quadtree四叉树及应用(一)

    目录 前言 什么是四叉树? 四叉树的原理 结语         最近在CAD中开发拓扑检查和空间分析功能时发现,传统的双层递归法会极大的降低程序运行速度,就比如:图上有1000个图形,我们要求图形之间的交点,传统的作法就是遍历两次图形,在两次循环中分别对图形求交处

    2024年02月06日
    浏览(44)
  • Unity如何给地形进行“缩放处理”(改变尺寸)

    入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删。 🌳首先,我们先导入地形 点击“游戏对象”——“3D对象”——“地形” 🌳缩放误区 本来呢,在一般情况下是可以通过调节这个来实现对物体的缩放 但是地

    2024年02月11日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包