在本文中,我讲述了我最近一直在探索的在 ArcGIS Pro 中设计 3D 模型的过程。 我的目标是尽可能避免与其他软件交互(即使是专门用于 3D 建模的软件),并利用 Pro 可以提供的可能性。
这个短暂的旅程分为三个不同的阶段:准备、组装和照明。 我们必须使用一些布局可能性以及一些地理处理工具来准备某些图层,然后继续设计局部场景。
我不是 3D 专家,也不是 Blender 或其他类似软件的熟练用户。 我只是在探索新的方法并与你分享,期望得到一些积极的反馈和改进建议。
如果你想继续我的项目(或跳过整篇文章并采取捷径),我已将其作为地图包上传到我的 ArcGIS Online 帐户上。 你可以从此处下载并在CC BY-NC-SA 4.0 许可证下使用它。
NSDT工具推荐 : Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割
1、准备
首先,我创建了一个 ArcGIS Pro 项目,将世界影像加载为底图,在地图属性中定义了一个坐标系,并在项目的默认地理数据库中创建了一个要素数据集,我将在其中存储我将创建的图层,如图1所示:
图1:准备底图、坐标系和地理数据库
为了让一切正常运转,我从一开始就必须定义感兴趣的领域。 也许这是最重要的一步,因为这建立了其他所有事情都将发生的框架。
为此,我创建了一个新布局,插入了一个地图框,然后将焦点放在感兴趣的区域上,如图 2 所示。
这里有四件事非常重要:
- 地图框的形状,在我的示例中只是一个正方形,
- 地图框的大小,在这个简单的形状中,它由正方形边的大小控制,
- 地图的比例,应与地图框的大小明智地结合起来,而且还应与用作地面高程图层的 DEM 的分辨率结合起来
- 地图的范围,由地图框四个角的坐标定义。
出于本文的目的,我创建了一个方形地图框,其边长为 20 厘米 x 20 厘米,地图比例为 1:10,000,并且通过平移到我感兴趣的区域来定义地图范围。 布局视图将帮助我生成一些图层。
首先,我需要创建一个新的折线要素图层,它将沿着地图框形状的边缘。 由于地图框是一个正方形,我必须复制其范围的坐标,这将帮助我创建折线要素图层。
为此,我在内容窗格中右键单击地图框,然后选择属性以在元素窗格中打开其属性(图 2):
图2:定义感兴趣区域的形状、大小、比例和范围,然后复制其范围坐标
在"元素"窗格的"显示选项"中,单击"范围"按钮以打开"地图框范围"弹出窗口。 从那里,我可以复制地图框范围的顶部、左侧、右侧和底部坐标(图 2)。
我必须保存这些坐标以便稍后使用,因此我打开文本编辑器或记事本(我使用 Visual Studio Code)并将它们粘贴到那里,如图 3 所示:
图3:保存感兴趣区域的范围坐标以供以后使用
如你所见,我已将坐标及其对应名称粘贴到文本编辑器中,但我还将它们转换为数组,我将在下一步中使用该数组。
在继续之前,我将以 TIFF 格式导出布局视图,如图 4 所示。这会将世界图像导出为本地 GeoTIFF 文件,剪裁在感兴趣的区域中,稍后我将使用它作为 3D 模型的纹理 。 我将其命名为 Aerial.tif。
如果我不希望导出的图像具有下面的服务层致谢信息,我可以按照John Nelson 的说明随时删除它们!
图 4:导出 GeoTIFF 中的布局视图并将当前范围保存为书签
将当前范围保存为书签也很重要,这样,如果我不小心移动了地图,就可以很容易地回到这里(图 4)。
现在,回到地图视图!
如前所述,我需要创建一个与地图框范围的形状相匹配的折线要素图层。 执行此操作的简单方法是遵循编辑过程,其中我必须在项目数据库中创建一个空的折线要素类,然后使用在前面的步骤中保存的坐标编辑和创建新要素。 当我创建特征时,绝对 X、Y、Z 将帮助我精确输入这些坐标。
但是,我更喜欢在 Python 窗口中使用简单的 ArcPy 脚本,它将为我创建所需的折线要素图层,如下图 5 所示。
图 5:Python 窗口中的简单 ArcPy 脚本,用于创建范围折线要素
该脚本显示在文本编辑器和图 5 的 Python 窗口中,如下所示:
import arcpy
from arcpy import env
env.workspace = "C:/Dropbox/SG_BLOG/3D_Model/3D_Model.gdb/Data_3857"
extent = [
-1609813.6168723013,
-19007708.81018684,
-19005708.81018684,
-1611813.6168723013
]
Top = extent[0]
Left = extent[1]
Right = extent[2]
Bottom = extent[3]
pnt1 = arcpy.Point(Left,Bottom)
pnt2 = arcpy.Point(Left,Top)
pnt3 = arcpy.Point(Right,Top)
pnt4 = arcpy.Point(Right,Bottom)
array = arcpy.Array()
array.add(pnt1)
array.add(pnt2)
array.add(pnt3)
array.add(pnt4)
array.add(pnt1)
shape = arcpy.Polyline(array)
arcpy.CopyFeatures_management(shape,"extent_polyline")
如果你想使用我的 ArcPy 脚本,只需将第三行的路径替换为计算机上你想要保存生成的要素图层的路径,并替换最后一行的名称(如果您不这样做) 我不想将其命名为"extent_polyline",就像我一样)。 当然,将范围数组中的范围坐标替换为自己的范围坐标。
如果你不熟悉 ArcPy,只需遵循上述绝对 X、Y、Z 的经典编辑过程即可。
无论如何,我最终都必须得到一个方形折线要素图层,其角点具有地图框范围的坐标,如图 6 所示。
图 6:代表我感兴趣区域的折线要素图层
我将折线要素图层命名为"extent_polyline",并用粗红色笔划对其进行符号化,因此它清晰可见。 这将在以下步骤中达到某些目的,但首先它将用于创建地面高程图层。
接下来,我需要一些海拔数据!
如果我已经下载了与我感兴趣的区域相对应的 DEM 图块,我会将它们添加到地图中,如图 7 所示。
图7:在地图上添加高程数据图块
光栅分辨率是关键。 为了本文的目的,我下载了单元大小为 1m 的图块,这非常适合我即将使用的比例 (1:10,000)。 我使用了与 Shadowplay 文章中使用的相同的高程数据源。
如果我要使用中等比例,例如 1:50,000 或 1:100,000,那么 1m 的像元大小就太大了,因此我应该重新采样或获取另一个具有更大像元大小的来源。
现在我必须对这些图块进行马赛克,为此我使用"栅格函数"窗格中的"马赛克栅格"函数,如下图 8 所示。 马赛克栅格功能将从四个图块中生成一个名为"马赛克栅格"的新栅格图层,并将其添加到内容窗格中。
图8:马赛克高程数据图块
然后,我将使用"栅格函数"窗格中的"剪辑"函数来剪辑"extent_polyline"图层(感兴趣的区域)内的马赛克栅格图层。
如图 9 所示,我打开"剪辑"功能,在其中选择"马赛克栅格"图层作为"栅格"字段的输入,选择"外部"作为"剪辑类型",为"剪辑几何/栅格"字段选择"extent_polyline"图层,然后单击 在创建新图层按钮上。 另一个新图层被创建并添加到地图上; 它的名称是"Clip_Mosaic Rasters",它被剪切在感兴趣区域的范围内:
图9:将马赛克高程图层剪切到感兴趣区域的范围
Clip_Mosaic Rasters 图层将是地面高程图层,稍后将在组装阶段的局部场景视图中使用。
设置地面图层后,现在是时候创建我将用来构建模型侧面的图层了。 为此,我必须以某种方式加密"extent_polyline"层的顶点,因为目前它只有四个顶点,即其正方形的四个角。
最简单的方法是在地图视图上选择"extent_polyline"图层,然后在"编辑"选项卡上,从"工具"组中选择"概化"工具,如图 10 所示。"修改要素"窗格将打开,其中包含概化选项 所选要素的,其中我选择 Densify 作为 Method,并选择 1m 作为 Distance 值(图 10):
图 10:使用 Generalize 编辑工具致密"extent_polyline"要素图层
然后我单击下方的 Generalize 按钮,沿着"extent_polyline"层每 1m 添加一个新顶点。 由于这是一个编辑工具,我必须单击"编辑"选项卡功能区上的"保存编辑"按钮才能使这些新顶点永久保存。
值得注意的是,我每 1m 添加一次顶点,因为我有一个像元大小为 1m 的高程源。 如果我有另一个分辨率更高的高程源,那么我应该选择顶点之间的等效距离。 例如,如果我的高程源的像元大小为 5m 或 30m,则新折点的距离应分别为 5m 或 30m。
添加折点的另一种方法是使用致密(编辑)地理处理工具,前提是你拥有标准或高级许可证。
接下来,我必须以某种方式在"extent_polyline"图层的每个顶点上添加高程信息。 这可以使用插值形状地理处理工具来完成,该工具可通过 3D Analyst 许可证或 Spatial Analyst 许可证获得。
图 11:使用插值形状地理处理工具创建新的 3D 矢量要素图层
如图 11 所示,在插值形状工具中,我在输入表面字段中选择马赛克栅格,在输入要素字段中选择"extent_polyline",然后选择保存输出要素类的位置,我将其命名为"sides_3d_vector" "。
当我单击"运行"按钮时,新创建的"sides_3d_vector"要素图层将添加到内容窗格和地图上。 这是一个 3D 矢量特征层,我将在模型的侧面使用它。
到目前为止,我还没有找到一种在没有上述许可证的情况下通过从表面插值 z 值来生成 3D 特征的替代方法。
如果你没有这些,那么另一种方法是使用 QGIS 中的 Drape(从栅格设置 Z 值)工具来执行此步骤。 事实上,这就是我在生产线上的做法,因为我只拥有基本的 ArcGIS Pro 许可证,没有扩展。
准备阶段的最后一步是清理内容窗格。 如图 12 所示,我删除了所有不必要的图层,并添加了在上一步中创建的 Aerial.tif 图像。
在内容窗格中我必须最终得到:
- "sides_3d_vector"3D 特征层,将用于构建模型的侧面,
- "extent_polyline"2D 要素图层,将用于创建模型的基础,
- Aerial.tif 图像层,将用作模型的纹理,
- Clip_Mosaic Rasters 图层,将用作模型的地面高程图层。
图 12:3D 模型所需的图层/将地图转换为局部场景
2、组装
组装阶段的开始就像将地图转换为局部场景一样简单。 如上图 12 所示,在"视图"选项卡中,我单击"视图"组中的"转换"按钮,然后从下拉列表中选择"到本地场景"选项。
地图正在转换为局部场景,如下图 13 所示。 请注意,默认情况下,场景将 WorldElevation3D/Terrain3D 作为地面图层,Pro 会自动识别内容窗格中的 3D 图层并将其与 2D 图层分开。
图13:局部场景
由于我已经生成了自己的高程图层,因此我从内容窗格的高程表面组中删除了 WorldElevation3D/Terrain3D 图层,并在其中添加了 Clip_Mosaic Rasters 图层(通过拖放),如下图 14 所示。 现在它被用作地面高程图层。
接下来,我在内容窗格的高程表面上选择地面组,然后将出现高程表面图层上下文选项卡。 在那里,我将"垂直夸张"更改为 2,将"表面颜色"更改为"无颜色",然后通过选中前面的复选框来选择"相对于光位置的阴影"(图 14):
图14:设置高程表面层
我还使用"探索"工具来导航场景并更改其视角,因此我将不再从顶视图看到它(图 14)。
现在是时候建造侧面了!
在内容窗格的 3D 图层组中,我选择"sides_3d_vector",然后右键单击打开其图层属性窗口,如下图 15 所示。 在图层属性窗口中,我选择"高程"选项卡,在"要素是"选项的下拉列表中选择"绝对高度",在"附加要素高程使用"选项中选择"几何 z 值"单选按钮,并写入 2 垂直夸张(图15):
图 15:设置"sides_3d_vector"图层的高程图层属性
这里需要注意两件事。 第一,"sides_3d_vector"具有 z 值,即每个顶点的高程信息,因为它是 3D 要素图层。 我们在上一步中使用插值形状地理处理工具创建了它。 二、垂直夸张必须与高程表层设置的完全一致(见图14)。 在我的示例中,我将两者都设置为 2。
现在,我关闭"图层属性"窗口并选择"sides_3d_vector"图层,然后转到"要素图层"上下文选项卡,在"挤出"组中单击"类型"按钮以打开"要素挤出类型"列表,如图 16 所示, 以下。 我从列表中选择"最小高度"选项,这会将拉伸添加到每个特征的最小高度。 正如您在图 16 中看到的,挤压已经构建了模型的侧面!
图 16:"sides_3d_vector"层上的拉伸正在构建模型的侧面
选择"最小高度"选项作为"sides_3d_vector"图层的特征拉伸类型后,我还可以通过单击表达式按钮打开"表达式生成器"窗口来添加额外的拉伸,如下图 17 所示。 这将控制我想要侧面的高度。 在此示例中,我输入 -50,这会将边拉伸至模型基础高度以下 50m(实际上是"sides_3d_vector"图层的最小 z 值)。
图 17:对"sides_3d_vector"层的最小 z 值进行额外挤压,以使边更高
最终结果如图 18 所示。如果我想要更高的边,我可以再次打开表达式生成器并输入另一个数字,例如 -100 或 -200。 我希望两侧的高度取决于模型的大小和比例。
图 18:3D 模型的最终侧面
除了侧面之外,我还想要一个模型的底座。 通过合适的符号系统,"extent_polyline"图层可以作为基础。 因此,首先,我将其从内容窗格中的 2D 图层组拖动到 3D 图层组,如图 19 所示。
然后,我右键单击"extent_polyline"图层以打开其图层属性窗口。 在图层属性窗口中,我选择"高程"选项卡,在"要素是"选项的下拉列表中选择"绝对高度",并在"附加要素高程使用"选项中选择"A 字段"单选按钮。 我单击表达式按钮打开"表达式生成器"窗口,在其中输入其绝对高度。 尽管一些尝试和错误会起作用,但我发现一个简单的公式可以给出所需的结果。 这个公式是:
Elevation Surface Layer's minimum height + ("sides_3d_vector" layer's additional extrusion / 2)
要找到高程表面层的最小高度,我可以返回到地图视图并查看 Clip_Mosaic Rasters 图层的最小值,在我的示例中为 20.5389(参见图 19)。 "sides_3d_vector"层的额外挤压是 -50(在上面的几个步骤中定义),因此它的一半是 -50 / 2 = -25。
因此,"extent_polyline"层的绝对高度将是简单数学方程 20.5389 -- 25 的结果,如图 19 所示。这实际上是将"extent_polyline"层精确地推到模型的底部,以服务 作为它的基础。
图19:将"extent_polyline"层推到模型底部,作为其基础
现在我必须更改它的符号系统,使其看起来像一个基地!
因此,我打开符号系统属性,并在结构选项卡中删除默认的描边符号图层,并添加具有两种符号效果(封闭多边形效果和偏移效果)的填充符号图层,如图 20 所示。
封闭多边形效果将从"extent_polyline"图层的空间范围创建动态多边形,偏移效果将以指定距离偏移此动态多边形。 符号效果的顺序很重要。 我首先需要定义封闭多边形,然后定义其偏移量。
图 20:填充符号图层上的封闭多边形和偏移效果
在符号系统窗格的图层选项卡上,我选择闭合路径作为封闭多边形效果的方法,然后在使用斜角作为方法的偏移效果处输入 10 作为偏移,如下图 21 所示。 我点击"应用",瞧! 模型的底座已经准备好了!
图 21:模型的基础,在填充符号图层上使用符号效果创建
最好给底座留一些高度。 为此,我必须将其拉伸到指定的高度,因此我在"内容"窗格中选择"extent_polyline"图层,并从"要素图层"上下文选项卡的"拉伸"组中单击"类型"按钮以打开"要素拉伸类型" 列表。 我从列表中选择"绝对高度"选项,如图 22 所示。
图22:选择模型底座的挤压类型
然后,我单击表达式按钮打开表达式生成器,在其中输入 -25,如下图 23 所示。 这意味着底座的厚度为25m。 如果我想要更厚的底座,那么我应该输入更小的数字,例如 -50 或 -100。 底座的厚度是个人喜好的问题,但它应该与侧面的高度和模型的尺寸相匹配。
图 23:定义拉伸值,该值将控制模型底座的厚度
最后一步是改变一些颜色以产生更美观的构图。 我右键单击内容窗格中的 Map_3D,打开地图属性,在常规选项卡中将场景的背景颜色更改为 #777777,如图 24 所示。
图 24:更改场景的背景颜色
然后在"sides_3d_vector"图层的符号系统窗格中的外观组中,我为颜色键入 #555555,如图 25 所示。
图 25:更改"sides_3d_vector"图层的颜色
我对"extent_polyline"图层重复相同的过程,同时在颜色中输入 #555555。
最后我到达了组装阶段的末尾。 我创建了场景,在其中放置了由地面层和航拍图像组成的模型,构建了其侧面和底座,并调整了颜色。 但为了让它看起来更真实,我还需要调整照明。
3、照明
是时候点亮一切了! 在"视图"选项卡的"场景"组中,我单击"照明"按钮旁边的箭头,如下图 26 所示。 从下拉列表中,我单击"照明设置"按钮。
图26:打开场景的照明设置
将打开"地图属性"窗口,并选择"照明"选项卡,我可以在其中配置场景的照明设置,如图 27 所示。
图 27:配置场景的照明设置
在"照明设置"中,我选中"阴影"组中"以 3D 方式显示阴影"选项之前的复选框,并在"照明"组中输入"光贡献"30(图 27)。
启用的"在 3D 中显示阴影"选项将不仅在模型上而且在模型周围创建投射阴影。 注意落在模型底部的阴影。 请记住,如果没有底座,模型周围就不会有阴影。
光贡献值也很重要。 值越低,场景就越引人注目,但也越暗,反之亦然。 因此,对此进行一些尝试和错误将表明合适的值。
接下来,我在按组定义的照明中有许多不同的选项,我选择使用用方位角和高度定义的绝对太阳位置选项来照亮我的场景(图 27)。
如图27所示,我为方位角选择了45的值,这意味着光线来自东北,而高度的值为30,意味着光线来自低角度,因此阴影会更长。 最终结果如下图 28 所示。
图28:光照调整后的模型
我发现最好遵循航空图像的光照条件,尤其是太阳方向(方位角)。 如果我回到图 4 并仔细查看 Aerial.tif 图像(源自世界图像),我可以了解到在捕获该图像时,太阳很可能来自东北方向。 因此,为了使模型看起来更真实,最好在同一角度定义照明方位角。
如果我使用卫星捕获的图像,例如 Landsat 或 Sentinel,我可以准确地知道捕获日期和时间。 因此,在这些情况下,我可以转到 SunCalc 应用程序并高精度地获取方位角和高度值。
最后一步是调整 Aerial.tif 图像的外观。 如图 29 所示,我打开符号系统窗格,在其中为拉伸类型选择最小最大,然后调整其红色、绿色和蓝色波段的直方图,直到使其更亮、更暖。
选择 Aerial.tif 图像后,在"栅格图层"上下文选项卡的"增强"组中,我还可以调整"亮度"、"对比度"和"伽玛值"(图 29)。
图 29:调整 Aerial.tif 图像的外
最后,我可以扩大模型底座的面积,为投射阴影以及我稍后可能想要添加的其他元素留出更多空间。 为此,我打开"extent_polyline"图层的符号系统窗格,并将偏移量更改为较大的数字。 在我的示例中,我给出的值为 500pt,如下图 30 所示。
根据计算机的系统,并且由于我已经加密了"extent_polyline"层,符号效果处的较大偏移量可能会降低性能。 因此,在这种情况下,最好复制该层,但使用最少的顶点数(对于正方形,只有四个,每个角一个)。 这可以通过许多不同的方式来实现,包括概括工具的逆过程,其中我使用简化选项而不是致密选项,或者通过在地理数据库中创建新要素类并数字化以创建新要素。
图 30:通过增加其偏移符号效果来扩展模型的基础
在"视图"选项卡的"导航"组中打开"相机"属性也非常有用,如图 31 所示,我可以通过调整"俯仰"和"航向"以及其他有用的参数来完全控制模型的视图。
图 31:调整相机属性并导出模型
当我对模型的外观、角度、透视、照明和航拍图像感到满意时,我会转到导出窗格以方便的格式导出它。
4、结束语
读到本文的结尾,我要感谢你的阅读,并希望你喜欢它! 我愿意接受反馈和建议,以改进我的方法。
你始终可以从此处下载该项目作为地图包,并在 CC BY-NC-SA 4.0 许可下使用它。