从 DEM 到 3D 渲染:R 语言 rayshader 地形可视化全指南

一、 为什么要用 rayshader?

当我们打开一张普通的地图时,看到的往往是平面的线条和色块。虽然我们可以通过等高线去想象山脉的起伏,或者通过蓝色的深浅去猜测湖泊的深浅,但这始终缺乏一种身临其境的震撼感。rayshader 的出现,就是为了打破二维平面与三维世界之间的那堵墙。 它不仅仅是一个绘图工具,更像是一位精通光影艺术的数字雕刻家。它能将那些枯燥的地理高程数据,转变为栩栩如生的微缩景观。

rayshader 最大的价值在于化繁为简,它依托于 R 语言,将极其复杂的 3D 渲染流程封装成了极简的指令。你不需要懂得如何计算光线的折射角度,也不需要手动去构建每一个山峰的模型,你只需要告诉它:"这里是数据,给我加上早晨 8 点的阳光",它就能自动帮你完成剩下所有的任务。而且,rayshader 最迷人的地方在于它对真实感的追求。普通的 3D 地图往往看起来像是由塑料堆砌而成的玩具,缺乏质感。而 rayshader 引入了光线追踪与环境光遮蔽技术,它能精确模拟阳光是如何洒在山脊上的,云层的阴影是如何投射在深谷中的,甚至能模拟出大气层的雾气感和水面的波光粼粼。这种对光影的细腻处理,能让一张普通的地形图瞬间拥有艺术摄影般的质感。因此,其特别适合在GIS相关工作中应用。

二、 数据读取与转换

首先打开RStudio,从开始菜单中或桌面快捷方式打开都可以:

图1 找到RStudio并打开

我们以示例数据集为例,将附件的数据集名字重命名为satellite_image.csv,然后将其粘贴到R的工作目录内。由于目前我们并不知道工作目录在哪,因此需要现在R studio中输入代码进行寻找:

R 复制代码
getwd()

输入后,便能够得到返回结果,找到当前的工作目录:

图2 返回的工作目录

将获得的地址粘贴到此电脑的地址栏中,回车进入:

图3 粘贴到地址栏回车进入目录

将示例数据集改名并粘贴到这一目录:

图4 改名并粘贴

rayshader 并不直接分析 TIF 文件,它需要矩阵数据。因此,我们需要在 RStudio 中复制并粘贴运行以下代码:

R 复制代码
# 0. 安装工具包

install.packages(c("rayshader", "terra"))



# 1. 加载必要的工具包

library(rayshader)

library(terra)



# 2. 读取您的本地数据文件

# 这里我们使用 terra 包的 rast() 函数,就像把文件从硬盘里"拿"出来

# 注意:请确保 'satellite_image.tif' 文件在您的当前工作目录下,或者填写完整路径

tif_data <- rast("satellite_image.tif")



# 3. 关键步骤:将地理文件转换为 rayshader 能识别的"矩阵"

# rayshader 提供了一个贴心的函数 raster_to_matrix,专门负责这个翻译工作

elmat <- raster_to_matrix(tif_data)



# 4. 检查一下是否成功

# 我们看看这个"表格"的大小

dim(elmat)

运行代码后,可以看到已经输出了结果:

图5 输出的检查结果

三、 第一次 3D 渲染

数据准备好了,现在我们开始生成模型。rayshader制作 3D 地形就像是搭建积木,但我们不需要一块一块去搭,只需要两步简单的指令:先给它上色,再把它拉起。在 R 语言中,我们经常使用一个像管道一样的符号 %>%。可以把它想象成工厂里的传送带:数据从左边进去,经过加工后,传给下一个环节。这样写代码,就像在读句子一样自然。复制并粘贴运行以下代码:

R 复制代码
# 这里的 elmat 是我们在上一节生成的矩阵数据



elmat %>%

  height_shade() %>%  # 第一步:根据高度生成基础颜色(皮肤)

  plot_3d(            # 第二步:根据矩阵数值构建 3D 模型(骨架)

    elmat,            # 告诉它用哪个数据来决定山的高度

    zscale = 30,      # 【关键参数】海拔缩放比例(详见下文解读)

    fov = 0,          # 摄像机视野,0 代表正交视图(像地图),70 代表透视(像人眼)

    theta = 45,       # 摄像机的水平旋转角度(0-360度)

    phi = 45,         # 摄像机的俯视角度(0-90度,90是垂直俯视,0是平视)

    windowsize = c(1000, 800), # 弹出的 3D 窗口大小

    zoom = 0.7        # 初始缩放级别

  )

代码中,height_shade() 代表根据高度给地图上色。低的地方涂上代表水的蓝色或平原的绿色,高的地方涂上代表山峰的白色或褐色。这相当于给模型穿了一层基础的皮肤。plot_3d() 则是 rayshader 的重要函数。它会根据我们之前的 elmat 高度矩阵,把平面的颜色"顶"起来,形成 3D 实体。

生成的结果如下图:

图6 生成的3D结果

按住鼠标左键拖动可以旋转模型,从东南西北各个角度观察山脉。按住鼠标右键拖动可以拉近或推远,像无人机一样俯冲进山谷,或者飞到高空概览全貌。按住鼠标中键拖动则是平移视图。虽然现在的地形看起来还比较简单,只有颜色过渡,但它已经是一个真正的三维实体了。接下来我们通过叠加图层和光影处理,让它变得像照片一样真实。

四、 叠加图层:给地形穿衣服

如果说 plot_3d 是构建骨架,那么这一步就是给骨架穿上衣服。在专业的地图绘制中,我们很少只用一种颜色。通常,我们会把"光照质感"和"海拔颜色"混合在一起,甚至如果有卫星照片,也可以直接贴在模型表面。rayshader 强大的地方在于它允许我们像 Photoshop 一样,一层一层地叠加图片。

在上一节我们用了 height_shade(按高度上色)。现在我们使用更高级的函数:sphere_shade()。它会模拟光源照射在起伏地面上的效果,生成一张带有明暗阴影的"浮雕图"。这会让地形即使在平面状态下,看起来也有立体的质感。这个函数的搭配按照以下方式进行:

1.底图:使用 sphere_shade 生成的黑白浮雕,提供纹理细节。

2.覆盖层:使用 height_shade 生成的彩色高度图,提供直观的海拔信息。

3.混合:使用 add_overlay 将两者融合,类似给照片上色。

复制并粘贴运行以下代码:

R 复制代码
install.packages("magick")



# 1. 加载必要的库

library(rayshader)

library(terra)

library(magick)  # 【关键】加载图像处理包,解决报错问题



# 2. 制作"内衣":基于光照的黑白浮雕层

base_layer <- sphere_shade(elmat, texture = "bw", sunangle = 45)



# 3. 制作"外套":基于高度的彩色层

color_layer <- height_shade(elmat, texture = terrain.colors(256))



# 4. 穿衣并渲染

elmat %>%

  sphere_shade(texture = "bw", sunangle = 45) %>%

  add_overlay(color_layer, alphalayer = 0.6) %>%  # 现在有了 magick,这一步就能成功了

  plot_3d(

    elmat,

    zscale = 30,

    fov = 0,

    theta = 45,

    phi = 45,

    windowsize = c(1000, 800),

    zoom = 0.7

  )

可以看到,结果已经被自动生成:

图7 生成的结果

现在,3D 地形图既有丰富的颜色,又有清晰的山体脉络。接下来,我们用阴影让它变得更加深邃。

五、 添加 阴影层

在现实世界中,光线不仅仅是直射的。在一个深邃的山谷底部,即使阳光没有直接照进去,也会因为周围山壁的遮挡而显得比较暗;而在开阔的山顶,光线则非常充足。这种"因为周围物体遮挡而产生的阴影",在计算机图形学中叫做环境光遮蔽。

如果不加这一层,生成的山脉就像是用塑料做的,每个角落都很亮;加上这一层,山脉的皱褶感、深邃感会得以显现,就像给素描画加上了重重的阴影线条。

我们需要用到两个生成阴影的函数:

1.ray_shade():模拟太阳光产生的直接投影(比如山峰挡住了背后的光)。

2.ambient_shade():模拟环境光遮蔽(比如山谷缝隙里的暗部)。

接下来,我们将底色 + 太阳阴影 + 环境阴影叠在一起。复制并粘贴运行以下代码:

R 复制代码
# 1. 计算太阳光阴影(Ray Shade)

# 模拟太阳从西北方向(sunangle = 315)照射产生的硬阴影

# zscale = 30 必须与 plot_3d 中的一致,否则阴影位置会跑偏

ray_shadow <- ray_shade(elmat, sunangle = 315, zscale = 30)



# 2. 计算环境光遮蔽(Ambient Shade)

# 模拟山谷缝隙里的软阴影,这会让地形看起来更深邃

amb_shadow <- ambient_shade(elmat, zscale = 30)



# 3. 混合所有图层并渲染

elmat %>%

  sphere_shade(texture = "bw", sunangle = 315) %>% # 底层:黑白光照

  add_overlay(color_layer, alphalayer = 0.6) %>%   # 中层:彩色高度(上一节做的)

  add_shadow(ray_shadow, max_darken = 0.5) %>%     # 顶层1:叠加太阳阴影

  add_shadow(amb_shadow, max_darken = 0.5) %>%     # 顶层2:叠加环境阴影

  plot_3d(

    elmat,

    zscale = 30,

    fov = 0,

    theta = 45,

    phi = 45,

    windowsize = c(1000, 800),

    zoom = 0.7

运行这段代码后,能够发现山谷的深处和山脊的背光面质感变得厚重了。这种充满电影质感的画面,就是光影魔法的魅力。接下来,我们进一步开启光线追踪,模拟真实的大气和光线反弹。

六、 开启光线追踪

到目前为止,我们在 RGL 弹出窗口中看到的 3D 地形,只是一个实时预览版。为了保证拖动鼠标时画面流畅,电脑省略了很多复杂的光影计算。现在,我们要使用 render_highquality() 函数,启动光线追踪技术,计算每一束光线在山谷间的反射、折射和散射。

运行这段代码前,必须保持之前的预览 3D 窗口(RGL窗口)处于打开状态,并调整好最佳角度。运行代码后,R 会开始计算,可能需要几十秒,具体取决于电脑性能。复制并粘贴运行以下代码:

R 复制代码
#这里的参数不再是控制模型,而是控制"摄影棚"的灯光和相机

render_highquality(

  lightdirection = 315, # 阳光照射的方向(西北方)

  lightaltitude = 45,   # 阳光的高度(45度角,类似上午9点的太阳)

  lightintensity = 800, # 阳光的强度

  samples = 200,        # 【关键】采样数:数值越高,噪点越少,画质越细腻,但速度越慢

  clear = TRUE          # 渲染完成后,自动关闭那个 3D 预览窗口以释放内存

)

生成的结果如下所示:

图8 完成渲染的图出现在右下角

观察结果可以发现,阴影不再是完全黑暗,而是带有柔和的过渡(软阴影);原本生硬的棱角,现在被光线包裹得更加自然。这一步生成的图片,已经具备了作为壁纸或海报的潜质。但如果想让它看起来更像微距摄影作品,我们还可以加上最后一道滤镜------景深与虚化。

七、 景深与虚化

在 rayshader 中,实现景深与虚化效果不需要昂贵的相机镜头,只需要调整一个参数:光圈。光圈越大,背景虚化越强烈。在 render_highquality() 函数中,我们只需要加入 aperture 参数:

1.aperture = 0:默认值。全图清晰,没有虚化(像手机拍风景)。

2.aperture = 10 到 30:适合地形图。会让远处和近处的山峰模糊,焦点集中在画面中心,产生强烈的微缩模型感。

复制并运行以下代码:

R 复制代码
# --- 第一步:重新构建 3D 窗口---

# 这一步和之前一样,只是为了确保有一个东西可以被拍摄

elmat %>%

  sphere_shade(texture = "bw", sunangle = 315) %>%

  add_overlay(color_layer, alphalayer = 0.6) %>%

  add_shadow(ray_shadow, max_darken = 0.5) %>%

  add_shadow(amb_shadow, max_darken = 0.5) %>%

  plot_3d(

    elmat,

    zscale = 30,

    fov = 0,

    theta = 45,

    phi = 45,

    windowsize = c(1000, 800),

    zoom = 0.7

  )



# --- 第二步:带景深的"微缩摄影" ---

render_highquality(

  lightdirection = 315,

  lightaltitude = 45,

  lightintensity = 800,

  samples = 200,

  

  # 【新增核心参数】

  aperture = 30,      # 光圈大小。数值越大,虚化越强。建议尝试 10-50。

  

  # 焦点位置:默认情况下,rayshader 会自动对焦在模型中心。

  

  clear = TRUE        # 渲染完关闭窗口

)

完成渲染后的效果如图所示:

图9加入景深与虚化后的效果

观察图片可以发现,画面中的山峰具有了清晰锐利的效果,画面边缘则呈现出了朦胧的模糊感。这种虚化效果能有效消除计算机生成的生硬感,让地形图看起来就像是专业插图。现在,我们已经完成了一幅作品,最后一步就是把它保存下来。

八、 导出与分享

rayshader 提供了专门的函数,能以原始分辨率保存作品,确保每一个像素都清晰可见。如果刚刚运行完光线追踪渲染,图片显示在 Plots 面板里,则需要在 RStudio 右下角的 Plots 面板上方,点击右键,然后选择"Save Image as":

图10 右键并保存

随后,选择保存目录,点击 Save 即可完成保存:

图11 选择保存目录

另外,我们还可以生成一段航拍视角的旋转视频。复制并粘贴运行以下代码:

R 复制代码
install.packages("av")



library(av)

library(rayshader)



# 1. 确保 3D 窗口是打开的

# (如果之前关闭了,请重新运行一遍 plot_3d 的代码块)



# 2. 生成旋转动画

# theta: 摄像机旋转的角度范围(0到360度)

# phi: 摄像机的俯视角度(保持30度俯视)

render_movie(filename = "mountain_flyover.mp4", type = "oscillate",

             frames = 360,  # 总帧数,帧数越多视频越流畅

             phi = 30,

             zoom = 0.6,

             title_text = "3D Terrain Flyover")

完成渲染后,视频的保存位置会显示在输出结果中:

图12 视频的保存位置

播放的效果如图所示:

图13 视频的播放效果

至此,我们完整走完了从一张 DEM 数据,到生成 3D 地形图的全过程,可以在实际研究或生产中进行应用了。

相关推荐
2501_941322032 小时前
【医疗AI】基于Mask R-CNN的支气管镜内窥镜目标检测系统实现
人工智能·r语言·cnn
换日线°2 小时前
前端3D炫酷展开效果
前端·3d
BYSJMG3 小时前
Python毕业设计选题推荐:基于大数据的美食数据分析与可视化系统实战
大数据·vue.js·后端·python·数据分析·课程设计·美食
Funny_AI_LAB4 小时前
RAD基准重新定义多视角异常检测,传统2D方法为何战胜前沿3D与VLM?
人工智能·目标检测·3d·ai
YangYang9YangYan4 小时前
2026高职大数据专业数据分析学习必要性
大数据·学习·数据分析
地球资源数据云5 小时前
R语言网络分析与路径规划——线数据应用实战:规划散步路线
数据分析·r语言
新启航光学频率梳5 小时前
储能电池极柱深孔孔深光学3D轮廓测量-激光频率梳3D轮廓技术
科技·3d·制造
LOnghas12116 小时前
文化遗产物品识别与分类——基于Mask R-CNN的改进模型详解
分类·r语言·cnn
斯摩尔德6 小时前
重测序及群体遗传分析
r语言