当前位置: 首页 > article >正文

虚幻地形高度图生成及测试

虚幻地形高度图生成及测试

虚幻引擎地形系统将高度数据存储在高度图中,这是一个灰阶图像,使用黑白色值来存储地貌高程。在高度图中,纯黑色值表示最低点,纯白色值表示最高点。支持16位灰阶PNG、8位灰阶r8及16位灰阶r16格式。

本文测试使用开源的opencvcgal库将地形网格体采样插值生成栅格,并写入到16位灰阶PNG图片中,结果可支持导入虚幻UE Landscape

示例代码

#include<iostream>
#include<cmath>

#include<CGAL/Surface_mesh.h>
#include<CGAL/Surface_mesh/IO/PLY.h>

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Delaunay_triangulation_2.h>

#include <CGAL/Polygon_mesh_processing/locate.h>

#include <opencv2/opencv.hpp>

using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using Projection_traits = CGAL::Projection_traits_xy_3<Kernel>;
using Point_2 = Kernel::Point_2;
using Point_3 = Kernel::Point_3;

// Triangulated Irregular Network
using TIN = CGAL::Delaunay_triangulation_2<Projection_traits>;
using Mesh = CGAL::Surface_mesh<Point_3>;

int main(){

    Mesh mesh;
    CGAL::IO::read_PLY("test.ply",mesh);
   
    CGAL::Bbox_3 bbox = CGAL::bbox_3(mesh.points().begin(), mesh.points().end());
    std::cout << "with:" << bbox.x_span() << "\nheight:" << bbox.y_span() << std::endl;
    int width = std::ceil(bbox.x_span());
    int height = std::ceil(bbox.y_span());
    width = std::max(width, height);
    height = width;
    // 特殊处理,虚幻地形表示的范围为512单位 (可略)
  //  int max_multiple = std::ceil(bbox.zmax() / 512);
  //  int min_multiple = std::ceil(std::abs(bbox.zmin()) / 512);
  //  min_multiple = bbox.zmin() < 0 ? min_multiple : 0;
   int maxHeight = max_multiple * 512;
   int minHeight = min_multiple * 512;

    int maxGray = (1 << 16) - 1;//65535 
    
    cv::Mat image(height, width, CV_16UC1);

    TIN tin (mesh.points().begin(), mesh.points().end());
    TIN::Face_handle location;

    for (std::size_t y = 0; y < height; ++y)
        for (std::size_t x = 0; x < width; ++x)
        {
            Point_3 query(bbox.xmin() + x * (bbox.xmax() - bbox.xmin()) / double(width),
                bbox.ymin() + (height - y) * (bbox.ymax() - bbox.ymin()) / double(height),
                0); // not relevant for location in 2D
            location = tin.locate(query, location);
            // Points outside the convex hull will be colored black
            if (!tin.is_infinite(location))
            {
                std::array<double, 3> barycentric_coordinates
                    = CGAL::Polygon_mesh_processing::barycentric_coordinates
                    (Point_2(location->vertex(0)->point().x(), location->vertex(0)->point().y()),
                        Point_2(location->vertex(1)->point().x(), location->vertex(1)->point().y()),
                        Point_2(location->vertex(2)->point().x(), location->vertex(2)->point().y()),
                        Point_2(query.x(), query.y()),
                        Kernel());
                double height_at_query
                    = (barycentric_coordinates[0] * location->vertex(0)->point().z()
                        + barycentric_coordinates[1] * location->vertex(1)->point().z()
                        + barycentric_coordinates[2] * location->vertex(2)->point().z());
                // 重新映射高度值到0~65535
                double height_ratio = (height_at_query + minHeight) / (maxHeight+minHeight);
                image.at<uint16_t>(y,x)=(uint16_t)(height_ratio* maxGray);
            }
            else {
                image.at<uint16_t>(y, x) = (uint16_t)0;
            }
        }
    cv::imwrite("output.png", image);
    return 0;
}

测试运行

cmake_minimum_required(VERSION 3.1...3.23)
project(main)
# cgal
find_package(CGAL REQUIRED)
# opencv 
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )

create_single_source_cgal_program("main.cpp")
target_link_libraries(main PRIVATE ${OpenCV_LIBS})

编译&构建
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=D:\vcpkg\scripts\buildsystems\vcpkg.cmake
cmake --build build --config Debug

结果

测试地形网格
在这里插入图片描述

生成的灰度图
在这里插入图片描述

导入虚幻Landscape
在这里插入图片描述

参考

  1. https://dev.epicgames.com/documentation/zh-cn/unreal-engine/importing-and-exporting-landscape-heightmaps-in-unreal-engine
  2. https://blog.csdn.net/mrbaolong/article/details/141643001?spm=1001.2014.3001.5501

http://www.kler.cn/a/290432.html

相关文章:

  • DHCP与DNS安全管理
  • 双十一云服务器抢购后,用SD-WAN连通多云网络
  • springboot参数校验
  • vue+Leaflet.PM插件实现创建和编辑几何图形(点、线、面、圆等)
  • 数据重塑:长宽数据转换【基于tidyr】
  • 如何选择最适合的自闭症干预机构?
  • 【论文分享】MyTEE: Own the Trusted Execution Environment on Embedded Devices 23‘NDSS
  • 使用FFmpeg的AVFilter转换YUV到RGB
  • 别天天看看直播了,你知道如何用jmeter对直播间做压测吗
  • LearnOpenGL学习笔记
  • MySQL复习4
  • 构建灵活的搜索系统:Go 语言实践
  • AI驱动测试管理工具会有哪些发展前景呢?
  • 关于多线程unique_lock和guard_lock
  • 冒泡排序及qsort函数
  • P1505 [国家集训队] 旅游
  • 【C++深入学习】日期类函数从无到有实现
  • day-49 使数组中所有元素相等的最小操作数
  • glsl着色器学习(三)
  • 随时随地远程启动家里设备,极空间部署一键网络唤醒工具『UpSnap』
  • C++ 消息分发类:详细示例应用
  • Python 数据分析笔记— Numpy 基本操作(上)
  • zdppy_cache缓存框架升级,支持用户级别的缓存隔离,支持超级管理员管理普通用户的缓存
  • 【MySql】面试问答:在使用mysql时,遇到分页查询慢的情况怎么处理?
  • 观测云核心技术解密:eBPF Tracing 实现原理
  • Java项目:137 springboot基于springboot的智能家居系统