学习在 C++ Builder 中可视化地形 3D 数据开发GIS地理信息系统软件


您想学习如何可视化地形数据吗?我们如何查看 3D 功能?我们可以像 AI 中的激活函数一样在 3D 中显示特定功能吗?我们如何在 C++ 中创建 3D 地图?

地形数据是有关表面(通常是地球表面)高程的数据集。通常使用两种这样的数据类型,第一种是数字地形数据,表示通常在地形四边形地图上找到的信息,第二种是数字地形模型 (DEM)由数据网格组成。第一种数据模型,通常在四边形地形图上找到,例如等高线、道路、河流、铁路、城镇等。为简单起见,第一类数据将被称为数字地形图数据。第二种是数字高程模型或 DEM,由数据网格组成,网格中的每个单元格代表地球上某个点的高程。

C++ Builder 很容易构建这些简单的可视化。C++ 是一种快速编程语言,您可以开发完全本机的应用程序,这些应用程序以机器的全速运行。您可以使用 OpenGL 或 Direct3D 库或其他一些 3rd 方 3D 引擎。在 C++ Builder 中, 您可以直接创建自己的 3D 对象,您可以在运行时为它们设置动画。 C++ Builder FireMonkey 项目中的Viewport3D  ( TViewportd3D ) 组件可以很好地显示许多基本的 3D 对象,如平面、立方体、球体、圆锥、平面、Ellipse3D 等。请参阅这篇关于 在现代 Windows C++ 开发中使用 3D 的文章以 创建这些 3D 对象. 您还可以使用Model3D轻松地将 3D 对象加载到 Viewport3D  ( TModel3D )。

要创建要在Viewport3D 中使用的 3D 对象,  我们需要使用 TMesh 类。  TMesh是一种自定义的 3D 形状,可以通过绘制 3D 形状进行自定义。它是一个从其祖先TCustomMesh发布一组属性的类 ,以便让您在设计时从 IDE 中通过Object Inspector设计新的 3D 形状 。使用 Data 属性指定每个点的点、法线和纹理,以及绘制生成的三角形的顺序。设计的形状填充有通过MaterialSource 属性指定的 材料。如果未指定材质,则形状填充为红色。

视口3D

Viewport3D ( TViewport3D ) 是一个 3D 组件,用于在其空间中显示 3D 对象。 TViewport3D 实现了 IViewport3D 方法来描述如何看到 3D 对象。开发一些小型 3D 游戏或在您的应用程序中添加一些 3D 功能真的很简单也很好。Viewport3D 是一个组件,用于显示相机的视口或默认视图。您可以在表单的任何位置安排它的位置、宽度和高度,也可以在客户端视图中使用它来进行全屏显示。我们可以在没有 3D 相机的情况下使用 viewport3D,它使用自己设计的相机视图。如果您想使用相机并希望从该相机的视图中查看视图,则必须从其属性或如下所示将其UseDesignCamera属性设置为 false; 


Viewport3D1->UseDesignCamera=false;

您可以使用BeginUpdate()EndUpdate()方法更新 Viewport3D 中的任何对象,如下所示;

Viewport3D1->BeginUpdate();
    // Move or set Objects, Cameras, Lights here
Viewport3D1->EndUpdate();

这将一次性更新 Viewport3D 的完整 3D 矩阵。您还可以像这样更改此 Viewport3D 组件的背景颜色

Viewport3D1->Color=claBlack;

TPlane 是做什么的?

该 TPlane 在的Viewport3D组件中使用,它是一种类实现的2D平面,可以放置一个3D FireMonkey形式,它表示可以在3D形式使用2D平面。平面支持 3D 旋转和对齐。TPlane 是一个可视对象,可以从Tool Palette添加 。

 

Plane1->BeginUpdate();
   Plane1->Width=10;
   Plane1->Height=10;
 
   Plane1->SubDivisionsHeight=10;
   Plane1->SubDivisionsWidth=10;
 
   Plane1->Position->X=0;
   Plane1->Position->Y=0;
   Plane1->Position->Z=0;
 
   Plane1->Rotation->X=0;
   Plane1->Rotation->Y=0;
   Plane1->Rotation->Z=0;
 
   Plane1->MaterialSource =TextureMaterialSource1; //MaterialSources should be defined or dragged
Plane1->EndUpdate();

我们可以使用TPlane来显示人脸的各个部分。但是使用 TMesh 设置 4 个角节点要简单得多。

TMesh 如何帮助我们进行可视化?

使用 TMesh 显示 3D 地形数据。使用 TPlane 及其 4 个坐标可以显示 3D 面、3D 函数、3D 地面形状和任何其他数据。

 TMesh 表示可以自定义的 3D 形状。该TMesh类出版了一套从它的祖先,性能 TCustomMesh,为了让您在IDE中设计在设计时新的3D图形,通过 对象观察

使用 Data 属性指定每个点的点、法线和纹理,以及绘制生成的三角形的顺序。设计的形状填充有通过MaterialSource 属性指定的 材料。如果未指定材质,则形状填充为红色。

TMesh *mesh=new TMesh(Form1->Dummy1);
mesh->Parent=Form1->Dummy1;
// ....
 
 
// when done free from the memory
mesh->Free();

 

如何在 C++ 中生成地形数据

在我们的示例中,我们将使用 TMesh 来显示表面,我们将使用数字高程模型 (DEM)。我们将使用 x 和 y 坐标及其 z 高程创建我们自己的数据。让我们先定义我们的 100×100 网格数据和一个 TMesh 对象。

#define GRIDSX 100
#define GRIDSY 100
 
double data[GRIDSX][GRIDSY];
TMesh *mesh;

现在让我们生成一个数据。我们可以生成如下所示的随机数据,或者我们可以使用如下所示的 3D 函数。

void generate_topographic_data()
{
 srand(time(0));
 
 float ev= 1.0;
 
 for(int j=0; j<GRIDSY; j++)
 for(int i=0; i<GRIDSX; i++)
 {
 data[i][j] = 0.005*(pow(i-GRIDSX*0.5,2)+pow(j-GRIDSY*0.5,2));
 }
 
/*	for(int j=0; j<GRIDSY; j++)
 for(int i=0; i<GRIDSX; i++)
 {
 if(rand()%5==1) data[i][j]+= (50-rand()%100)/100.0;
 }
 */
 
}

从数据生成地形 3D 网格

我们可以从网格数据中生成 4 个点的网格的每个平面,每个点都有 x、y、z,我们可以缩放它以获得更好的可视化。下面看看它是如何工作的,

void generate_topographic_mesh()
{
 TPoint3D P0,P1,P2,P3;
 int NP=0, NI=0;
 float scale =2, scaleZ=2.0;
 
 float OX = GRIDSX*scale/2;
 float OY = GRIDSY*scale/2;
 
 if(mesh!=NULL) mesh->Free();
 
 mesh=new TMesh(Form1->Dummy1);
 mesh->Parent=Form1->Dummy1;
 
 mesh->Width=1;
 mesh->Height=1;
 mesh->Depth=1;
 
 mesh->Position->X=0;
 mesh->Position->Y=0;
 mesh->Position->Z=0;
 
 mesh->Scale->X=1;
 mesh->Scale->Y=1;
 mesh->Scale->Z=1;
 
 mesh->RotationAngle->X=0;
 mesh->RotationAngle->Y=0;
 mesh->RotationAngle->Z=0;
 
 mesh->RotationCenter->X=0;
 mesh->RotationCenter->Y=0;
 mesh->RotationCenter->Z=0;
 
 mesh->WrapMode=TMeshWrapMode::Original;
 mesh->TwoSide=true;
 mesh->Visible=true;
 mesh->Opacity=1.0;
 
 mesh->Data->Clear();
 mesh->Data->VertexBuffer->Length=4*GRIDSX*GRIDSY;
 mesh->Data->IndexBuffer->Length= 6*GRIDSY*GRIDSY;
 
 
 for(int j=0; j<GRIDSY-1; j++)
 for(int i=0; i<GRIDSX-1; i++)
 {
    P0.X = i*scale-OX;
    P0.Y = data[i][j]*scaleZ;
    P0.Z = j*scale-OY;
 
    P1.X = (i+1)*scale-OX;
    P1.Y = data[i+1][j]*scaleZ;
    P1.Z = j*scale-OY;
 
    P2.X = (i+1)*scale-OX;
    P2.Y = data[i+1][j+1]*scaleZ;
    P2.Z = (j+1)*scale-OY;
 
    P3.X = i*scale-OX;
    P3.Y = data[i][j+1]*scaleZ;
    P3.Z = (j+1)*scale-OY;
 
    mesh->Data->VertexBuffer->Vertices[NP+0] = P0;
    mesh->Data->VertexBuffer->Vertices[NP+1] = P1;
    mesh->Data->VertexBuffer->Vertices[NP+2] = P2;
    mesh->Data->VertexBuffer->Vertices[NP+3] = P3;
 
    mesh->Data->VertexBuffer->TexCoord0[NP+0] =PointF(0, (P0.Y+35)/45);
    mesh->Data->VertexBuffer->TexCoord0[NP+1] =PointF(0, (P0.Y+35)/45);
    mesh->Data->VertexBuffer->TexCoord0[NP+2] =PointF(0, (P0.Y+35)/45);
    mesh->Data->VertexBuffer->TexCoord0[NP+3] =PointF(0, (P0.Y+35)/45);
 
    //mesh->Data->Normals=".....";
 
    mesh->Data->IndexBuffer->Indices[NI+0] =NP+0;
    mesh->Data->IndexBuffer->Indices[NI+1] =NP+2;
    mesh->Data->IndexBuffer->Indices[NI+2] =NP+1;
 
    mesh->Data->IndexBuffer->Indices[NI+3] =NP+0;
    mesh->Data->IndexBuffer->Indices[NI+4] =NP+3;
    mesh->Data->IndexBuffer->Indices[NI+5] =NP+2;
 
    NP+=4;
    NI+=6;
 }
 
 mesh->MaterialSource=Form1->LightMaterialSource1;
 mesh->Data->CalcFaceNormals(true);
 mesh->Repaint();
}

地形 3D 网格的完整示例

这是完全最大化的 C++ Builder FMX 示例的屏幕截图。这里我们有 100×100 的网格和一个功能高度。

学习在 C++ Builder 中可视化地形 3D 数据 - 3D 网格的完整示例

 

如果我们使用上面的generate_topographic_data()的remmed行随机生成数据,我们可以看到这一点。

 

下面列出了这个例子的全部代码,

 

//---------------------------------------------------------------------------
 
#include <fmx.h>
#include <time.h>
 
#pragma hdrstop
 
#include "DataVisualization3D_TopographicData_Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
 
#define GRIDSX 100
#define GRIDSY 100
 
double data[100][100];
TMesh *mesh;
float LX,LY;
bool rotation=false;
 
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)  : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void generate_topographic_data()
{
 srand(time(0));
 
 float ev= 1.0;
 
 for(int j=0; j<GRIDSY; j++)
 for(int i=0; i<GRIDSX; i++)
 {
 data[i][j] = 0.005*(pow(i-GRIDSX*0.5,2)+pow(j-GRIDSY*0.5,2));
 }
 
 //for(int ev=0; ev<5; ev++)
 
/*	for(int j=0; j<GRIDSY; j++)
 for(int i=0; i<GRIDSX; i++)
 {
 if(rand()%5==1) data[i][j]+= (50-rand()%100)/100.0;
 }
 */
 
}
//---------------------------------------------------------------------------
void generate_topographic_mesh()
{
 TPoint3D P0,P1,P2,P3;
 int NP=0, NI=0;
 float scale =2, scaleZ=2.0;
 
 float OX = GRIDSX*scale/2;
 float OY = GRIDSY*scale/2;
 
 if(mesh!=NULL) mesh->Free();
 
 mesh=new TMesh(Form1->Dummy1);
 mesh->Parent=Form1->Dummy1;
 
 mesh->Width=1;
 mesh->Height=1;
 mesh->Depth=1;
 
 mesh->Position->X=0;
 mesh->Position->Y=0;
 mesh->Position->Z=0;
 
 mesh->Scale->X=1;
 mesh->Scale->Y=1;
 mesh->Scale->Z=1;
 
 mesh->RotationAngle->X=0;
 mesh->RotationAngle->Y=0;
 mesh->RotationAngle->Z=0;
 
 mesh->RotationCenter->X=0;
 mesh->RotationCenter->Y=0;
 mesh->RotationCenter->Z=0;
 
 mesh->WrapMode=TMeshWrapMode::Original;
 mesh->TwoSide=true;
 mesh->Visible=true;
 mesh->Opacity=1.0;
 
 mesh->Data->Clear();
 mesh->Data->VertexBuffer->Length=4*GRIDSX*GRIDSY;
 mesh->Data->IndexBuffer->Length= 6*GRIDSY*GRIDSY;
 
 
 for(int j=0; j<GRIDSY-1; j++)
 for(int i=0; i<GRIDSX-1; i++)
 {
    P0.X = i*scale-OX;
    P0.Y = data[i][j]*scaleZ;
    P0.Z = j*scale-OY;
 
    P1.X = (i+1)*scale-OX;
    P1.Y = data[i+1][j]*scaleZ;
    P1.Z = j*scale-OY;
 
    P2.X = (i+1)*scale-OX;
    P2.Y = data[i+1][j+1]*scaleZ;
    P2.Z = (j+1)*scale-OY;
 
    P3.X = i*scale-OX;
    P3.Y = data[i][j+1]*scaleZ;
    P3.Z = (j+1)*scale-OY;
 
    mesh->Data->VertexBuffer->Vertices[NP+0] = P0;
    mesh->Data->VertexBuffer->Vertices[NP+1] = P1;
    mesh->Data->VertexBuffer->Vertices[NP+2] = P2;
    mesh->Data->VertexBuffer->Vertices[NP+3] = P3;
 
    mesh->Data->VertexBuffer->TexCoord0[NP+0] =PointF(0, (P0.Y+35)/45);
    mesh->Data->VertexBuffer->TexCoord0[NP+1] =PointF(0, (P0.Y+35)/45);
    mesh->Data->VertexBuffer->TexCoord0[NP+2] =PointF(0, (P0.Y+35)/45);
    mesh->Data->VertexBuffer->TexCoord0[NP+3] =PointF(0, (P0.Y+35)/45);
 
    //mesh->Data->Normals=".....";
 
    mesh->Data->IndexBuffer->Indices[NI+0] =NP+0;
    mesh->Data->IndexBuffer->Indices[NI+1] =NP+2;
    mesh->Data->IndexBuffer->Indices[NI+2] =NP+1;
 
    mesh->Data->IndexBuffer->Indices[NI+3] =NP+0;
    mesh->Data->IndexBuffer->Indices[NI+4] =NP+3;
    mesh->Data->IndexBuffer->Indices[NI+5] =NP+2;
 
    NP+=4;
    NI+=6;
 }
 
 mesh->MaterialSource=Form1->LightMaterialSource1;
 mesh->Data->CalcFaceNormals(true);
 mesh->Repaint();
}
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 generate_topographic_data();
 generate_topographic_mesh();
 Viewport3D1->Repaint();
}
void __fastcall TForm1::Viewport3D1MouseDown(TObject *Sender, TMouseButton Button,   TShiftState Shift, float X, float Y)
{
 LX=X; LY=Y; rotation=true;
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::Viewport3D1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift,   float X, float Y)
{
 rotation=false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Viewport3D1MouseMove(TObject *Sender, TShiftState Shift, float X,  float Y)
{
 if(rotation)
 {
 Dummy1->RotationAngle->X=(Y-LY)*0.4;
 Dummy1->RotationAngle->Y=(LX-X)*0.4;
 }
}
//---------------------------------------------------------------------------

正如这里给出的,您可以生成自己的 3D 地形,您可以在此网格上添加纹理,颈椎枕还可以为河流和山脉等创建不同的纹理网格。您可以创建颜色纹理,您可以用颜色显示高程。您可以使用此方法来显示您的函数,即人工智能中的激活函数。您可以开发具有随机或精心设计的地形图的 3D 游戏,如上例所示。请注意,互联网上有很多地形数据。例如,您可以从https://opentopography.org/获取此类数据

C++ Builder真的很棒,您的梦想没有限制!只是梦想和编码!

 


 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值