在Rstudio Linux Server上使用X window OpenGL图形系统

  这篇文章是补课的性质,以在云端的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) 
Echarts画的montereybay峡湾3D地形图(表面)

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) 
rayshader包打光渲染输出3D GIS地图,水是透明的质感,以看到峡湾的地形。

  程序会打开一个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)
高质量渲染3D GIS图片

  完成后,执行下面的语句关闭绘图窗口。

rgl::rgl.close()

  rayshader包还有给3D图打光渲染等很多方便的功能,具体可参阅它的Github项目主页,上面有一些具体的例子。

3、Linux上运行rayshader。这是配好运行的效果,安装配置工程不小,后面再详细介绍,这是本篇的重点。渲染输出效果见下图,这是把云端虚拟主机上的虚拟屏幕输出到笔记本上本地显示,也是可以用鼠标等3维转动,选个好视角然后截图输出,程序与操作跟Windows上一样。


Windows上VNC Viewer连接操作云端虚拟主机的X window桌面

  然后截图输出并关闭绘图窗口,Linux上用程序关闭方便一点。

render_snapshot('demo.png')
rgl::rgl.close()
Rstudio Linux Server中浏览截图输出

二、为虚拟主机配置虚拟屏幕
  我的虚拟主机用的是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”,不要搞错了。在“本地端口转发”下按“添加”:

SecureCRT添加远程VNC Server SSH连接隧道

  设定连接的参数,端口号是前面定义的端口号5900,远程(Linux)转发的主机名是127.0.0.1或localhost,即远程SSH服务器转发到它本机的5900端口,本地(Windows)也在5900号端口提供服务。参数设定后SecureCRT要断开并重新连接并保持连接。
设定远程VNC Server SSH连接隧道参数

7、用VNC Viewer连接远程桌面。
  参阅该资料在Windows上下载安装VNC Viewer,设置连接参数(IP及端口,默认5900)即可连上。因为配置了SSH加密隧道,这里连接的地址是127.0.0.1或localhost,前面已有连上后看到的远程桌面示例。
设置VNC Viewer参数并连接远程桌面

三、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
OpenGL开发环境安装测试

  源码如下:

#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()

rgl包测试

rgl输出PDF

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() 

rayshader测试

rayshader输出PNG文件

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() 
rayshader读入GIS数据渲染的例子

六、补充说明
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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容