一、 为什么要用 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 地形图的全过程,可以在实际研究或生产中进行应用了。