这篇文章是补课的性质,以在云端的Linux虚拟机上跑起R语言生态中需要使用X window图形系统的R软件包。R语言的最强项之一是它的绘图能力,其生态中有1万多个软件包,其中很多使用OpenGL来绘图,因为它是跨平台的,可以运行在Linux、MacOS、Windows等各种操作系统上面。但各大厂提供的云端虚拟主机,一般是没有显示器的,它们在数据中心的某个机架上,一般通过网络用文本终端来管理,称为headless server。如果不配好X window及OpenGL环境,很多R软件包甚至都安装不了。因此,本篇通过在虚拟机上配置X window虚拟屏幕,让这些R软件包都可以跑起来,从而在云端虚拟机上解锁X window OpenGL图形系统的功能,释放R语言整个生态系统的强大能量。Rstudio在Windows上单机运行的时候,直接就支持OpenGL,不用做什么配置。
上一篇文章介绍了在Shiny中使用Echarts,提供的图形控件已经非常丰富了,为什么还要其他的绘图能力呢?一是因为还有非常多使用OpenGL的软件包,它们提供了强大而独特的功能;二是有时候需要输出高质量的绘图,比如印刷用的图片等等,比如在GIS应用中,地图及其渲染效果图就要求高像素和高质量;三是Echarts不适合处理比较大的数据量。本篇以一个用rayshader包渲染的3D地形图为例展示一下,它记录了montereybay峡湾29万多个点阵的高程数据。
一、几种渲染效果
1、echarts4r绘图,可以通过鼠标拖动图形3维转动观察。Echarts在浏览器中渲染3D图用的是WebGL,受限于浏览器沙箱及JavaScript语言环境,图(数据)的大小会有一定的限制。
library(echarts4r) # load echarts4r
data("montereybay", package = "rayshader")
bay <- as.data.frame(as.table(montereybay))
bay$Var1 <- as.numeric(bay$Var1)
bay$Var2 <- as.numeric(bay$Var2)
bay |>
e_charts(Var1, reorder = FALSE) |>
e_surface(Var2, Freq) |>
e_visual_map(Freq)
2、rayshader包绘图并渲染。
在Windows上安装rayshader包是很直接的,它会把需要的依赖包都一起装上,包括为R语言环境提供OpenGL绘图支持的rgl包。因为在单机或服务器端渲染图,可用内存足够大,图(数据)大小的限制会少很多。
library(rgl)
library(rayshader)
library(rayrender)
montshadow = ray_shade(montereybay, zscale = 50, lambert = FALSE)
montamb = ambient_shade(montereybay, zscale = 50)
montereybay %>%
sphere_shade(zscale = 10, texture = "imhof1") %>%
add_shadow(montshadow, 0.5) %>%
add_shadow(montamb,0) %>%
plot_3d(montereybay, zscale = 50, fov = 0, theta = -100, phi = 30, windowsize = c(1000, 800), zoom = 0.6,
water = TRUE, waterdepth = 0, waterlinecolor = "white", waterlinealpha = 0.5,
wateralpha = 0.5, watercolor = "lightblue")
render_label(montereybay, x = 350, y = 160, z = 1000, zscale = 50,
text = "Moss Landing", textsize = 2, linewidth = 5)
render_label(montereybay, x = 220, y = 70, z = 7000, zscale = 50,
text = "Santa Cruz", textcolor = "darkred", linecolor = "darkred",
textsize = 2, linewidth = 5)
render_label(montereybay, x = 300, y = 270, z = 4000, zscale = 50,
text = "Monterey", dashed = TRUE, textsize = 2, linewidth = 5)
render_label(montereybay, x = 50, y = 270, z = 1000, zscale = 50, textcolor = "white", linecolor = "white",
text = "Monterey Canyon", relativez = FALSE, textsize = 2, linewidth = 5)
程序会打开一个3D绘图窗口,可以通过鼠标3维转动图片寻找一个比较好的视角,然后手工再执行下面的语句截图输出上图的PNG文件。
# Sys.sleep(0.2) # 这一句是自动执行时稍微等待一下,让3D渲染完成。
render_snapshot('demo.png')
渲染输出高质量图片,直接PNG输出到当前图形设备(Rstudio Plots窗口):
render_highquality(samples=256, line_radius = 1, text_size = 18, text_offset = c(0,12,0),
clamp_value=10, clear = TRUE)
完成后,执行下面的语句关闭绘图窗口。
rgl::rgl.close()
rayshader包还有给3D图打光渲染等很多方便的功能,具体可参阅它的Github项目主页,上面有一些具体的例子。
3、Linux上运行rayshader。这是配好运行的效果,安装配置工程不小,后面再详细介绍,这是本篇的重点。渲染输出效果见下图,这是把云端虚拟主机上的虚拟屏幕输出到笔记本上本地显示,也是可以用鼠标等3维转动,选个好视角然后截图输出,程序与操作跟Windows上一样。
然后截图输出并关闭绘图窗口,Linux上用程序关闭方便一点。
render_snapshot('demo.png')
rgl::rgl.close()
二、为虚拟主机配置虚拟屏幕
我的虚拟主机用的是CentOS 7.6,其它Linux系统上的配置可能稍有不同,参阅资料。
1、修复curl工具。Anaconda安装Python包时,安装了更新的版本,与yum工具不匹配,导致yum工具不能正确运行,R编译也有问题,从源码编译重装一遍自带的7.29.0就好了,参阅资料。
(base) [root@VM-4-12-centos bin]# curl --version
curl 7.80.0 (x86_64-conda-linux-gnu) libcurl/7.80.0 OpenSSL/1.1.1n zlib/1.2.11 libssh2/1.9.0 nghttp2/1.46.0
Release-Date: 2021-11-10
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS GSS-API HSTS HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz NTLM NTLM_WB SPNEGO SSL TLS-SRP UnixSockets
(base) [root@VM-4-12-centos bin]# yum list curl*
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* centos-sclo-rh: mirrors.163.com
* centos-sclo-sclo: mirrors.163.com
已安装的软件包
curl.x86_64 7.29.0-59.el7_9.1 @updates
# wget https://curl.se/download/archeology/curl-7.29.0.tar.gz
# tar -zxvf curl-7.29.0.tar.gz
# cd curl-7.29.0
# ./configure --prefix="/usr/lib64/curl-7.29.0"
# make
# make install
然后在搜索路径的前面加入该版本,优先执行。
# export PATH=/usr/lib64/curl-7.29.0/bin:$PATH
# echo $PATH
2、安装虚拟屏幕服务。用的是Xvfb,它提供了一个虚拟的X11 Server。
# yum install xorg-x11-server-Xvfb.x86_64
这是装好后:
(base) [root@VM-4-12-centos jean]# yum list *Xvfb*
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* centos-sclo-rh: mirrors.163.com
* centos-sclo-sclo: mirrors.163.com
已安装的软件包
xorg-x11-server-Xvfb.x86_64 1.20.4-18.el7_9 @updates
可安装的软件包
python-xvfbwrapper.noarch 0.2.4-2.el7 epel
3、安装远程虚拟桌面服务x11vnc。
# yum install x11vnc.x86_64
这是装好后:
(base) [root@VM-4-12-centos jean]# yum list *x11vnc*
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* centos-sclo-rh: mirrors.163.com
* centos-sclo-sclo: mirrors.163.com
已安装的软件包
x11vnc.x86_64 0.9.13-12.el7 @epel
可安装的软件包
x11vnc-javaviewers.noarch 0.9.13-12.el7 epel
4、配置开机自动启动这两个服务。
运行下面的命令编辑开机自启动脚本:
# vi /etc/rc.d/rc.local
增加下面的几行代码。
1)Xvfb虚拟屏幕的编号是2,1280*960,24位颜色(Linux上的最大深度)。屏幕编号输出到环境变量DISPLAY中,R语言将根据该环境变量确定X window系统是否可用。
2)x11vnc远程桌面侦听的端口是5900(默认),连接的是2号屏幕,连接的口令是password。这个端口连接的密码验证经过加密,密码是安全的,但传输的内容是明文,有一定的安全风险,所以防火墙上不打开它,不直接访问。后面通过SecureCRT(或者PuTTY) SSH建立的加密隧道连接到22号端口,再转发到5900端口,这样在网络上的传输就全程加密了。
# Startup Xvfb and assign DISPLAY :2 to virtual X11 Server.
Xvfb :2 -screen 0 1280x960x24 2>/dev/null &
export DISPLAY=:2
# Startup x11vnc server for remote access
x11vnc -listen 0.0.0.0 -rfbport 5900 -noipv6 -passwd password -display :2 -forever &
5、配置R语言环境识别该虚拟屏幕,以使用X window系统。
这是所有R进程启动时都要执行的脚本,设置环境变量,R进程启动时,父进程的一些环境变量,比如前面已经输出的DISPLAY变量,并没有输入到R进程中,需要在这里设置一下,具体参阅这个帖子以及这个帖子,有些脚本只在登录时才被调用,可能R服务器启动的进程没有调用设置。
# vi /usr/lib64/R-4.1.2/lib64/R/etc/Renviron
# Added for Xvfb by Jean, 2022/10/23
DISPLAY=:2
在Rstudio IDE或R控制台中验证,可以看到DISPLAY环境变量正确设置了,X11是TRUE,说明X window图形系统可用了。
> Sys.getenv("DISPLAY")
[1] ":2"
> capabilities()
jpeg png tiff tcltk X11 aqua http/ftp sockets libxml fifo
TRUE TRUE TRUE FALSE TRUE FALSE TRUE TRUE TRUE TRUE
cledit iconv NLS Rprof profmem cairo ICU long.double libcurl
TRUE TRUE TRUE TRUE FALSE TRUE FALSE TRUE TRUE
>
6、SecureCRT配置SSH隧道连接。
我喜欢用SecureCRT,传统终端的样式,功能多,还可以SSH上FTP传文件,很方便,配置参阅资料。PuTTY配置参阅资料。SecureCRT有时会出现远程桌面颜色不对的情况,多连几次试试,还没有找到原因,PuTTY使用更简单,远程桌面的颜色也没有问题。
SecureCRT配SSH连服务器就不啰嗦了,网上一搜一大把。这里讲讲在主菜单“选项->会话选项->连接->端口转发”下增加SSH隧道定义,是“端口转发”页面下,不是它下面的“远程/X11”,不要搞错了。在“本地端口转发”下按“添加”:
设定连接的参数,端口号是前面定义的端口号5900,远程(Linux)转发的主机名是127.0.0.1或localhost,即远程SSH服务器转发到它本机的5900端口,本地(Windows)也在5900号端口提供服务。参数设定后SecureCRT要断开并重新连接并保持连接。
7、用VNC Viewer连接远程桌面。
参阅该资料在Windows上下载安装VNC Viewer,设置连接参数(IP及端口,默认5900)即可连上。因为配置了SSH加密隧道,这里连接的地址是127.0.0.1或localhost,前面已有连上后看到的远程桌面示例。
三、Linux配置OpenGL开发环境
参阅这篇帖子。
1、安装OpenGL支持库,mesa是一个OpenGL的开源实现,freeglut则提供了一些跨平台封装的工具。
# yum install mesa*
# yum install freeglut*
2、编译一个C程序测试,后面再讲升级gcc编译器,系统自带安装的是4.8.5,这里是可以的。
(base) [root@VM-4-12-centos jean]# gcc -v
使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
目标:x86_64-redhat-linux
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
线程模型:posix
gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
注意这里编译的参数与帖子中稍有不同,否则编译通不过,参考了另一个帖子。然后用"DISPLAY=:2 ./test"来运行编译好的测试程序,输出到虚拟屏幕2,它画了一个茶壶,说明OpenGL的开发运行环境可以了。
# cd /home/jean
# vi test.cc
# gcc -I/usr/include -L/usr/local/lib -L/usr/lib -lglut -lGLU -lGL -lX11 -lXext -lXmu -lm test.cc -o test
# DISPLAY=:2 ./test
源码如下:
#include <GL/gl.h>
#include <GL/glut.h>
#include <stdlib.h>
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glOrtho(-5,5,-5,5,5,15);
glMatrixMode(GL_MODELVIEW);
gluLookAt(0,0,10,0,0,0,0,1,0);
return;
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0, 0);
glutWireTeapot(3);
glFlush();
return;
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);
glutInitWindowPosition(0,0);
glutInitWindowSize(300, 300);
glutCreateWindow("OpenGL 3D View");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
四、编译安装rgl包与rayshader包
rgl包提供了R语言环境下的OpenGL实现,由rayshader包调用。R软件包大部分都是C和C++写的,Linux上从源码安装时要用gcc编译,有些包对编译器的版本就有要求,rgl包就要求支持C++ 17,所以要升级系统自带的gcc。
1、升级gcc开发环境,参阅资料。
安装能得到的最新版本,gcc-c++ 11.2.1-9。
# yum list|grep devtoolset-(base) [root@VM-4-12-centos ~]# yum list|grep devtoolset-
devtoolset-11-binutils.x86_64 2.36.1-1.el7.2 @centos-sclo-rh
devtoolset-11-gcc.x86_64 11.2.1-9.el7 @centos-sclo-rh
devtoolset-11-gcc-c++.x86_64 11.2.1-9.el7 @centos-sclo-rh
# yum install centos-release-scl
# yum install devtoolset-11-gcc*
在本次绘话中激活gcc-11:
# scl enable devtoolset-11 bash
或者:
(base) [root@VM-4-12-centos ~]# source /opt/rh/devtoolset-11/enable
(base) [root@VM-4-12-centos ~]# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/opt/rh/devtoolset-11/root/usr --mandir=/opt/rh/devtoolset-11/root/usr/share/man --infodir=/opt/rh/devtoolset-11/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --with-default-libstdcxx-abi=gcc4-compatible --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-11.2.1-20220127/obj-x86_64-redhat-linux/isl-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 11.2.1 20220127 (Red Hat 11.2.1-9) (GCC)
Rstudio从C源码安装R包,要指定C++ 14、17等相应版本的编译器,参阅资料与资料。
# cd ~
# mkdir ~/.R
# vim ~/.R/Makevars
内容如下:
CXX14FLAGS=-O3 -march=native -mtune=native -fPIC
CXX14=g++
CXX17FLAGS=-O3 -march=native -mtune=native -fPIC
CXX17=g++
现在,启动R,尝试安装rgl包,注意用"DISPLAY=:2"告诉R有X window系统可用:
# DISPLAY=:2 R
>install.packages("rgl")
编译没有问题,然而尝试加载时还是出错了,找不到符号“_ZSt28__throw_bad_array_new_lengthv”。
installing to /usr/lib64/R-4.1.2/lib64/R/library/00LOCK-rgl/00new/rgl/libs
** R
** demo
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
*** copying figures
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
libgcc_s.so.1 must be installed for pthread_cancel to work
sh: 行 1: 2266 已放弃 R_TESTS= '/usr/lib64/R-4.1.2/lib64/R/bin/R' --no-save --no-restore --no-echo 2>&1 < '/tmp/RtmptUxAmj/file3f063df2c82'
Error in dyn.load(dynlib <- getDynlib(dir)) :
无法载入共享目标对象‘/usr/lib64/R-4.1.2/lib64/R/library/00LOCK-rgl/00new/rgl/libs/rgl.so’::
/usr/lib64/R-4.1.2/lib64/R/library/00LOCK-rgl/00new/rgl/libs/rgl.so: undefined symbol: _ZSt28__throw_bad_array_new_lengthv
Warning: Loading rgl's DLL failed.
Warning: Trying without OpenGL...
Error in dyn.load(dynlib <- getDynlib(dir)) :
无法载入共享目标对象‘/usr/lib64/R-4.1.2/lib64/R/library/00LOCK-rgl/00new/rgl/useNULL/rgl.so’::
/usr/lib64/R-4.1.2/lib64/R/library/00LOCK-rgl/00new/rgl/useNULL/rgl.so: undefined symbol: _ZSt28__throw_bad_array_new_lengthv
Error: package or namespace load failed for ‘rgl’:
loadNamespace()里算'rgl'时.onLoad失败了,详细内容:
调用: fun(libname, pkgname)
错误: Loading failed.
错误: loading failed
停止执行
ERROR: loading failed
* removing ‘/usr/lib64/R-4.1.2/lib64/R/library/rgl’
经过艰苦的搜索和排查,最后看到了这个帖子,确定了原因是gcc 11.2.1-9的libstdc++.so.6.0.28还不够新,这个符号引用出现在更新的版本中。下面是从源码编译好gcc-c++ 11.3.0后系统中所有libstdc++.so.6的版本与符号链接,/usr/lib64/libstdc++.so.6原来链接到的是libstdc++.so.6.0.28。
(base) [root@VM-4-12-centos etc]# find / -name libstdc++.so.6*
/usr/lib/libstdc++.so.6
/usr/lib/libstdc++.so.6.0.19
/usr/local/qcloud/stargate/lib/libstdcxx-x86_64/libstdc++.so.6.0.28
/usr/local/qcloud/stargate/lib/libstdcxx-x86_64/libstdc++.so.6.0.20
/usr/local/qcloud/stargate/lib/libstdc++.so.6
/usr/local/qcloud/stargate/lib/libstdcxx-arm64/libstdc++.so.6.0.24
/usr/local/gcc-11.3.0/lib64/libstdc++.so.6
/usr/local/gcc-11.3.0/lib64/libstdc++.so.6.0.29
/usr/local/gcc-11.3.0/lib64/libstdc++.so.6.0.29-gdb.py
/usr/share/gdb/auto-load/usr/lib/libstdc++.so.6.0.19-gdb.py
/usr/share/gdb/auto-load/usr/lib/libstdc++.so.6.0.19-gdb.pyc
/usr/share/gdb/auto-load/usr/lib/libstdc++.so.6.0.19-gdb.pyo
/usr/share/gdb/auto-load/usr/lib64/libstdc++.so.6.0.19-gdb.py
/usr/share/gdb/auto-load/usr/lib64/libstdc++.so.6.0.19-gdb.pyc
/usr/share/gdb/auto-load/usr/lib64/libstdc++.so.6.0.19-gdb.pyo
/usr/lib64/libstdc++.so.6
/usr/lib64/libstdc++.so.6.0.19
/usr/lib64/anaconda3/pkgs/libstdcxx-ng-9.3.0-hd4cf53a_17/lib/libstdc++.so.6
/usr/lib64/anaconda3/pkgs/libstdcxx-ng-9.3.0-hd4cf53a_17/lib/libstdc++.so.6.0.28
/usr/lib64/anaconda3/pkgs/libgcc-7.2.0-h69d50b8_2/lib/libstdc++.so.6.0.21
/usr/lib64/anaconda3/lib/libstdc++.so.6
/usr/lib64/anaconda3/lib/libstdc++.so.6.0.28
/usr/lib64/anaconda3/lib/libstdc++.so.6.0.21
/usr/lib64/libstdc++.so.6.0.28
/root/gcc-releases-gcc-11.3.0/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6
/root/gcc-releases-gcc-11.3.0/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.29
/root/gcc-releases-gcc-11.3.0/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6
/root/gcc-releases-gcc-11.3.0/stage1-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.29
/root/gcc-releases-gcc-11.3.0/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6
/root/gcc-releases-gcc-11.3.0/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.29
确定libstdc++.so.6.0.28没有上面的符号引用:
(base) [root@VM-4-12-centos etc]# strings /usr/lib64/libstdc++.so.6.0.28 | grep throw_bad_array
__cxa_throw_bad_array_length
__cxa_throw_bad_array_new_length
__cxa_throw_bad_array_length
__cxa_throw_bad_array_new_length
__cxa_throw_bad_array_length
__cxa_throw_bad_array_new_length
查看它的GLIBCXX版本,到GLIBCXX_3.4.28,参看上面的帖子,GLIBCXX_3.4.29,到下一个版本应该就有了:
(base) [root@VM-4-12-centos etc]# strings /usr/lib64/libstdc++.so.6.0.28 | grep GLIBCXX
......
GLIBCXX_3.4.28
......
所以要从源码编译生成libstdc++.so.6的下一个版本,考虑到CentOS 7.6是比较老的版本,决定下载编译gcc 11.3.0,与11.2.1最接近,安装到/usr/local/gcc-11.3.0,参阅资料,只编译64位版。
# cd /root
# wget https://github.com/gcc-mirror/gcc/archive/refs/tags/releases/gcc-11.3.0.tar.gz
# tar -zxvf gcc-11.3.0.tar.gz
# cd gcc-releases-gcc-11.3.0
# ./configure --prefix=/usr/local/gcc-11.3.0 --disable-multilib
# make
# make install
等了两个半小时!编译后生成了libstdc++.so.6.0.29,所引用的符号有了:
(base) [root@VM-4-12-centos etc]# strings /usr/local/gcc-11.3.0/lib64/libstdc++.so.6.0.29 | grep _ZSt28__throw_bad_array_new_lengthv
_ZSt28__throw_bad_array_new_lengthv
_ZSt28__throw_bad_array_new_lengthv
_ZSt28__throw_bad_array_new_lengthv
GLIBCXX版本到GLIBCXX_3.4.29:
(base) [root@VM-4-12-centos etc]# strings /usr/local/gcc-11.3.0/lib64/libstdc++.so.6.0.29 | grep GLIBCXX
......
GLIBCXX_3.4.28
GLIBCXX_3.4.29
......
因为不熟悉devtoolset-11,我决定不替换整个gcc编译器,上面编译时也没错,只是运行时出错,所以只替换动态链接库为刚编译好的libstdc++.so.6.0.29,建一个新符号链接指向它就可以了。
# cd /usr/lib64
# rm libstdc++.so.6
# ln -s /usr/local/gcc-11.3.0/lib64/libstdc++.so.6.0.29 libstdc++.so.6
# ldconfig -X
(base) [root@VM-4-12-centos lib64]# ls -l libstdc++.so.6*
lrwxrwxrwx 1 root root 47 10月 23 11:46 libstdc++.so.6 -> /usr/local/gcc-11.3.0/lib64/libstdc++.so.6.0.29
-rwxr-xr-x 1 root root 995840 9月 30 2020 libstdc++.so.6.0.19
-rwxr-xr-x 1 root root 13121976 2月 27 2022 libstdc++.so.6.0.28
2、编译安装rgl包与rayshader包。
现在,启动R,重新安装rgl包,搞定。
# DISPLAY=:2 R
>install.packages("rgl")
installing to /usr/lib64/R-4.1.2/lib64/R/library/00LOCK-rgl/00new/rgl/libs
** R
** demo
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
*** copying figures
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
** checking absolute paths in shared objects and dynamic libraries
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (rgl)
Updating HTML index of packages in '.Library'
Making 'packages.html' ... done
直接安装rayshader包就可以了,它会自动安装依赖的其它包。
>install.packages("rayshader")
五、测试rgl与rayshader安装
参阅资料。
1、测试rgl安装。
Rstudio中运行下面的R程序,用VNC Viewer远程桌面连上应该看到画了3维的点阵,最后记得运行rgl.close()关闭X窗口。这里也测试了输出成PDF文件。
library(rgl)
open3d()
x <- sort(rnorm(1000))
y <- rnorm(1000)
z <- rnorm(1000) + atan2(x,y)
plot3d(x, y, z, col=rainbow(1000))
rgl.postscript("foo.pdf", fmt="pdf")
sessionInfo()
#rgl.close()
2、测试rayshader安装。
Rstudio中运行下面的R程序,用VNC Viewer远程桌面连上应该看到画了3维的地形图,最后记得运行rgl.close()关闭X窗口。这里也测试了输出成PNG文件。注意rayshader其它例子中,plot_map()函数直接输出到Rstudio Plots窗口,没有打开X窗口,所以不能用render_snapshot()函数。
library(rgl)
library(rayshader)
montereybay %>%
sphere_shade() %>%
plot_3d(montereybay,zscale=50,zoom=0.6,theta=-90,phi=30)
render_snapshot("montereybay.png")
#rgl::rgl.close()
3、重启x11vnc远程桌面服务。
如果 VNC Viewer连接服务器时闪退,重启服务器上的x11vnc远程桌面服务重连即可,长时间不用可能会出现这种情况,刚开始使用还不了解是什么原因,用过后及时关闭VNC Viewer可以避免这个问题。
(base) [root@VM-4-12-centos lib64]# ps -ef|grep x11vnc
root 14269 5338 0 11:15 pts/0 00:00:00 grep --color=auto x11vnc
root 16064 5338 0 10月24 pts/0 00:00:28 x11vnc -listen 0.0.0.0 -rfbport 5900 -noipv6 -display :2 -forever
(base) [root@VM-4-12-centos lib64]# kill -9 16064
(base) [root@VM-4-12-centos lib64]# x11vnc -listen 0.0.0.0 -rfbport 5900 -noipv6 -passwd password -display :2 -forever &
4、测试rayshader包的其他例子。
这些例子需要从tif读入GIS数据,最终要调用rgdal包,R语言上主要用rgdal处理各种GIS空间与投影数据,因此Linux要先安装gdal(>=1.11.4)与proj(>=4.8.0),具体见rgdal包的文档。
# yum install gdal*
# yum install proj*
(base) [root@VM-4-12-centos lib64]# yum list |grep gdal
gdal.x86_64 1.11.4-4.el7 @epel
gdal-devel.x86_64 1.11.4-4.el7 @epel
gdal-libs.x86_64 1.11.4-4.el7 @epel
gdal-doc.noarch 1.11.4-4.el7 epel
......
(base) [root@VM-4-12-centos lib64]# yum list|grep proj
proj.x86_64 4.8.0-4.el7 @epel
proj-devel.x86_64 4.8.0-4.el7 @epel
......
Rstudio中运行例子,记得运行rgl::rgl.close() 关闭服务器上的X窗口。我稍微改了一下颜色配置,蓝天白云、绿水青山看着舒服一点。
library(rgl)
library(rayshader)
library(rayrender)
library(ambient)
#Here, I load a map with the raster package.
loadzip = tempfile()
download.file("https://tylermw.com/data/dem_01.tif.zip", loadzip)
localtif = raster::raster(unzip(loadzip, "dem_01.tif"))
unlink(loadzip)
#And convert it to a matrix:
elmat = raster_to_matrix(localtif)
elmat %>%
sphere_shade(texture = "imhof1") %>%
add_water(detect_water(elmat), color = "darkseagreen1") %>%
add_shadow(cloud_shade(elmat,zscale = 10, start_altitude = 500, end_altitude = 700,
sun_altitude = 45, attenuation_coef = 2, offset_y = 300,
cloud_cover = 0.55, frequency = 0.01, scale_y=3, fractal_levels = 32), 0) %>%
plot_3d(elmat, zscale = 10, fov = 0, theta = 135, zoom = 0.75, phi = 45, windowsize = c(1000, 800),
background="lightskyblue")
render_camera(theta = 125, phi=22,zoom= 0.47, fov= 60 )
render_clouds(elmat, zscale = 10, start_altitude = 500, end_altitude = 700,
sun_altitude = 45, attenuation_coef = 2, offset_y = 300,
cloud_cover = 0.55, frequency = 0.01, scale_y=3, fractal_levels = 32, clear_clouds = T)
render_snapshot("demo3DWithCloud.png")
# rgl::rgl.close()
六、补充说明
1、从源码编译R。
我虚拟主机上的R是4.1.2,用"--with-pcre1"选项编译的,如果没有配置虚拟屏幕,服务器端图形设备可以用Cairo,安装Cairo包后就支持PNG等,参阅资料1与资料2。我配置了虚拟屏幕,所以在编译R时可以直接支持X11与PNG。不过Anaconda安装了新版的libpng,导致./configure时PNG支持测试没通过,要把libpng.so链接回旧的版本,参阅资料。另外也要把curl指回旧版7.29.0,以及设置环境变量DISPLAY。这里直接编译最新的R-4.2.1演示。
(base) [root@VM-4-12-centos R-4.2.1]# cd /usr/lib64
(base) [root@VM-4-12-centos lib64]# ls -l libpng*
lrwxrwxrwx 1 root root 19 2月 15 2022 libpng15.so -> libpng15.so.15.13.0
lrwxrwxrwx 1 root root 19 1月 8 2021 libpng15.so.15 -> libpng15.so.15.13.0
-rwxr-xr-x 1 root root 179328 10月 13 2020 libpng15.so.15.13.0
lrwxrwxrwx 1 root root 44 10月 22 10:53 libpng16.so -> /usr/lib64/anaconda3/lib/libpng16.so.16.37.0
lrwxrwxrwx 1 root root 44 10月 22 10:52 libpng16.so.16 -> /usr/lib64/anaconda3/lib/libpng16.so.16.37.0
lrwxrwxrwx 1 root root 22 10月 22 10:57 libpng.so -> /usr/lib64/libpng16.so
# rm libpng.so
# ln -s libpng15.so.15.13.0 libpng.so
# export PATH=/usr/lib64/curl-7.29.0/bin:$PATH
# export DISPLAY=:2
# cd /root/R
# wget https://mirrors.tuna.tsinghua.edu.cn/CRAN/src/base/R-4/R-4.2.1.tar.gz
# tar -zxvf R-4.2.1.tar.gz
# cd R-4.2.1
# ./configure --prefix=/usr/lib64/R-4.2.1 --enable-R-shlib --with-pcre1 --with-cairo --with-libpng
# make -j4
# make install
# cd /usr/lib64/R-4.2.1/bin
# DISPLAY=:2 ./R
> capabilities()
jpeg png tiff tcltk X11 aqua
TRUE TRUE TRUE FALSE TRUE FALSE
http/ftp sockets libxml fifo cledit iconv
TRUE TRUE FALSE TRUE TRUE TRUE
NLS Rprof profmem cairo ICU long.double
TRUE TRUE FALSE TRUE TRUE TRUE
libcurl
TRUE
2、修复一些链接。
如果提示一些动态库找不到,可能需要补充建立一些软链接,如此类推。
# ln -s /usr/lib64/libpng16.so /usr/lib64/libpng.so
# ln -s /usr/lib64/libbz2.so.1.0.6 /usr/lib64/libbz2.so.1.0