首页 > 其他分享 >unity四叉树地形

unity四叉树地形

时间:2023-04-05 18:45:18浏览次数:33  
标签:int private 地形 unity World 顶点 四叉树

在unity中,我们可以使用unity自带的地形系统创建一个超大的地形场景,并且可以利用地形图层,创建出富有真实感的地表材质。但是当我们需要更改地形的渲染方式的时候,比如需要风格化渲染时,使用unity自带的地形系统就会很麻烦。因此,我尝试在unity中使用mesh的方式实现了一个简易的地形系统,这样地形的渲染就和场景中其他网格物体的渲染没有什么区别了,可以很方便地实现各种效果。

以下我将分步简要描述实现思路与过程,目录如下:

1、 将world machine中创建的地形导入到unity的mesh中;

2、 处理地形数据,按照四叉树层级分块;

3、 根据摄像机位置动态地组合分块,达到动态地形LOD的效果;

以下是实现的具体过程,个人编码水平比较低,希望大家多多提出意见。

 

一、导入地形数据

在unity自带的地形系统中,我们可以通过一张高度图的方式将地形数据从地形生成软件(World Machine,Gaea等)导入到unity引擎中,这张高度图通常是原始图片数据格式(.raw)。.raw格式的图片保存了最原始、无损的信息,而我们常用的png、jpg图片采用了一些压缩算法来减小图片的大小,但是会损失一些数据,因此在程序化地形工作流中,我们常常使用.raw来保存地形信息。

但是.raw格式的图片并不方便,windows本身是无法查看这类图片的,而C#也需要配置一些环境,并且也比较复杂。因此我选择不使用.raw图片来存储地形数据,转而直接使用模型文件.obj格式。

在World Machine中,我们可以直接导出.obj文件格式,方式如下:

其中Triangulation项表示导出模型的精度,我们选择Full Resolution Mesh,创建完整的模型。第三项坐标系统,我们选择X=east Z=north,以匹配unity的坐标系统。其他导出选项可以根据需要来选择。

然后我们就获得了地形网格模型,是常见的obj文件,本质上是一个,分别用建模软件和VS code查看如下

 

       可以看到三角形网格和以字符文本形式保存的模型信息。在.obj文件中,v关键字表示顶点,后面三个值分别表示该顶点的x、y、z位置坐标。

       现在我们获得了地形的完整的模型文件,在使用它之前需要先将它按照四叉树分割成小块,具体如下图所示:

对于本例而言,地形大小为4096*4096,因此采用六级LOD,最终LOD0有1024块,所有LOD分块一共1365块,我们先从obj文件中提取顶点数据,再根据每一块的LOD级别与覆盖范围分别建立网格。

因此,我们先用C++写一个小程序来将顶点信息提取出来,存入到一个文本文件中,方便后续建立网格时使用。

观察顶点数据,发现顶点数据按照行-列的方式排列,这方便了后续的操作。因此,为了提高文件读取的效率,可以将x轴与z轴的数据抛弃,通过在第一行添加一个常数来表示x、z平面上顶点的间隔距离,并且添加第二个常数来表示地形高度的乘数,增强了我们对网格的控制。

在完善之后,这一部分的C++代码如下:

#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <string>
using namespace std;

struct vec3{
    int x;
    float y;
    int z;
};

vec3 vertices[16785409];

int main() {
    fstream newtxt;
    newtxt.open(R"(D:\WorldMachine\World Machine 4016\World Machine 4016 Professional\World Machine Documents\Desert\TerrainData.txt)", ios::out);


    fstream myObj;

    myObj.open(R"(D:\WorldMachine\World Machine 4016\World Machine 4016 Professional\World Machine Documents\Desert\height.obj)", ios::in);
    if(!myObj.is_open())
        std::cerr<<"cannot open the file";


    // 输出先导数据
    float xz_bias = 5;
    float y_bias = 1;
    newtxt << xz_bias << " " << y_bias << endl;

    // 全局

    char buffer[1024] = {0};// 读取文件行缓存
    int mesh_num = 0;       // 网格(物体)计数编号
    int count_num = 0;      // 行数计数编号

    int cnt_vert = 0;

    while (myObj.getline(buffer,sizeof(buffer))){
        count_num ++;
        stringstream ss(buffer);    // 一行的字符串流操作
        if (ss.str().empty())          // 空行
            continue;

        string flag;    // 行首关键字
        ss >> flag;

        if (flag == "#"){
            cout << "第 " << count_num << " 行: " << "这是一行注释, " << "注释的内容是:" << ss.str() << endl;
        }
        else if (flag == "mtllib"){
            continue;
        }
        else if (flag == "v"){  // 顶点
            float a, b, c;
            ss >> a >> b >> c;
            vertices[cnt_vert].x = (cnt_vert % 4097);
            vertices[cnt_vert].y = b;
            vertices[cnt_vert].z = (cnt_vert / 4097);
            newtxt << b << endl;
            cnt_vert ++;
        }
        else
            cout << ss.str() << endl;
        memset(buffer, 0, sizeof(buffer));
    }
    myObj.close();
    cout << "读取文件完成力" << endl;
    return 0;
}

 

在拿到处理后的顶点数据后,我们打开unity,新建一个脚本。

首先创建如下的成员变量:

    // 需要读取的文件路径
    public string datapath;


    private float xzbias = 0, ybias = 0;    // 格点间隔距离与高度乘数
    public int Size = 4097;    // 地形的规模(正方形边长,要求是2的幂+1)
    private int LEN;            // 常数,表示完整顶点组的长度,等于Size^2
    private int len;            // 常数,表示单片顶点组的长度,等于129*129


    private Vector3[] CompVert;   // 完整的地形顶点数据,在开始时从文本文件读取数据到该顶点组,在构建mesh时按需从该顶点组读取数据

    private Vector2[] CompUV1;    // CompVert对应的第一套UV(地表贴图纹理uv)
    private Vector2[] CompUV2;    // CompVert对应的第二套UV(遮罩图贴图splat map uv)

    private Mesh LodMesh;       // 结果的网格,在每次四叉树遍历过程中即时更新该mesh并保存至本地,因此不需要开数组

    private Vector3[] vert;         // 结果的顶点数据
    private Vector2[] uv1;
    private Vector2[] uv2;
    private int[] tri;              // 结果的三角形数据(不需要在每次遍历中更新)

    private struct quadTreeNodeInfo // 地形瓦片 四叉树节点 信息
    {
        public Vector2 begin_Pos;  // 开始顶点在完整顶点组中的第几行、第几列
        public int interval;       // 该地形瓦片的连续顶点在完整顶点组中对应顶点相差数量(LOD0对应该值为1,LOD2对应该值为4)
        public int LodLeval;       // 该地形瓦片的lod等级
        public Vector2 Center; // 该地形瓦片的xz平面几何中心位置
    }
    private quadTreeNodeInfo[] qTree;   // 四叉树

 

标签:int,private,地形,unity,World,顶点,四叉树
From: https://www.cnblogs.com/yaocenji/p/17290337.html

相关文章

  • Unity-NaughtAttributes
    SpecialAttributesAllowNestingStruct需要嵌套时使用DrawerAttributesAnimator显示下拉参数publicAnimatorsomeAnimator;[AnimatorParam("someAnimator")]publicintparamHash;Button直接执行函数[Button("ButtonText")]privatevoidMethodTwo(){}Ani......
  • Unity 字体研究
    在学习公司项目的时候看到有的字体定义了内容  和以前我看见其他人提出的问题,“热更框架之外,用到了字体文件,热更里也有使用字体文件,如何去重。”我对这个问题印象深刻,今天看见项目如此操作估计就是想解决这个问题利用字体压缩工具 https://github.com/forJrking/FontZip......
  • Unity升级后打包AssetBundle变慢
    1)Unity升级后打包AssetBundle变慢​2)打包使有些资源合成了一个资源data.unity3d,有些分开的原因3)Unreal在移动设备中无法使用Stat命令获取到GPUThread的耗时4)Unity中如何看到相机视野范围内的剔除结果这是第330篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社......
  • Unity
    找不到progrids,使用git远程安装https://stackoverflow.com/questions/67119431/unity-3ds-package-manager-is-not-showing-progrids-module......
  • re/【unity】游戏逆向首试 [BJDCTF2020]BJD hamburger competition
    本题是是一个unity游戏,而且是以c#和.net编写尝试直接用idea进行反汇编,但是没有找到运行逻辑,后来在大佬的wp上发现是利用dnspy对c#的dll文件进行返回编,进而获得结果。反汇编BJDhanburgercompetirion_Data中的Assembly-CSharp.dll即可获得如下代码段:可以看到先利用sha1进行加......
  • 世界地形图多国高清珍藏版
    世界地形  亚洲                                                欧洲                          ......
  • Unity客户端开发工程师的进阶之路
    UWA技能成长系统是UWA根据学员的职业发展目标,提供技能学习的推荐路径,再将所需学习内容按难易等多维度,设计分成多个学习阶段,可以循序渐进地进行学习。每个阶段学员完成学习任务后不但可以获得技能的提升,还将获得UWA社区相应的积分奖励(积分可兑换礼品和优惠券哦)。 进入技能成长......
  • unity3d面试题及答案
    unity3d面试题及答案1.请描述游戏动画有哪几种,以及其原理。答:主要有关节动画、单一网格模型动画(关键帧动画)、骨骼动画。    关节动画把角色分成若干独立部分,一个部分对应一个网格模型,部分的动画连接成一个整体的动画,角色比较灵活Quake2中使用了这种动画;   单一网......
  • unity学习——c#初级编程
    1.作为行为组件的脚本首先新建一个cube立方体  然后新建一个c#脚本,脚本用来实现立方体cube的三种颜色变化(按键实现)  脚本代码如下:usingUnityEngine;usingSystem.Collections;publicclasscolor:MonoBehaviour{voidUpdate(){if(Input.GetKeyD......
  • Spine在Unity 的URP模式下,描边的坑。
    在urp模式下,spine描边需要特殊处理,首先去。官网下载他们的urp拓展包,然后通过packagemanager导入到项目。打开包里outlineshadersurp场景。发现他们实现这个场景的时候,是在spine动画下面套了一个outline对象,这个outline对象有一个脚本叫做renderexistingmesh。将这个脚本......