首页 > 解决方案 > R有效地绘制了数千个在椭圆体中变形的具有透明度的3D球体

问题描述

我需要真正加速一小段代码,根据将它们转换为椭圆体的 3x3 张量列表,在 3d rgl 空间中变形大约 3000 个球体。此外,我需要透明颜色(“alpha”参数<1)。然后我想快速有效地绘制它们。我希望具有与 spheres3d() 相同的性能,但当然不是这样......(运行下面的代码)。下面是我的代码,其中包含一个完全可重现的示例所需的功能。你能帮我做这件事吗?提前谢谢了。保罗

library(Morpho)
library(Rvcg)
library(rgl)

rep.row<-function(x,n){  #### rep rows of a matrix
  matrix(rep(x,each=n),nrow=n)
}

traslamesh<-function(mesh,c){ #### just translate a mesh in the 3d space according a position vector
  newvb<-t(mesh$vb[-4,])+rep.row(c,nrow(t(mesh$vb[-4,])))
  newmesh<-list(vb=t(cbind(newvb,1)),it=mesh$it)
  class(newmesh)<-"mesh3d"
  newmesh
}

defosph<-function(sphere,mat,after=T){#### this deforms a sphere in an ellipspoid according to a 3x3 tensor 
  if(after==T){newvb<-t(sphere$vb[-4,])%*%t(mat)}else{newvb<-t(mat%*%sphere$vb[-4,])}
  newmesh<-list(vb=t(cbind(newvb,1)),it=sphere$it)
  class(newmesh)<-"mesh3d"
  newmesh
}
creasph<-function(radius=1,centroid=c(0,0,0),subdivision=1){  #### just a wrap of vcgSphere
  temp_sphere<-vcgSphere(subdivision = subdivision)
  temp_sphere$vb[1,]<-temp_sphere$vb[1,]+centroid[1]
  temp_sphere$vb[2,]<-temp_sphere$vb[2,]+centroid[2]
  temp_sphere$vb[3,]<-temp_sphere$vb[3,]+centroid[3]
  final_sphere<-scalemesh(temp_sphere, radius, center = "none")
  return(final_sphere)
}
positions<-matrix(rnorm(9000,2,20),ncol=3) ###### positions where we want to plot
spheres3d(positions,alpha=0.5) #### very fast to plot and reasonably fast to naviagate in the 3d rgl window

tensor1<-matrix(rnorm(9),ncol=3) #### a random tensor; let's use the same one for deforming all the 3000 spheres. In the real application each sphere will have its own tensor.

open3d()
for(i in 1:dim(positions)[1]){  #### embarrassingly slow ......
  sphi<-creasph(radius=1,subdivision=2)
  shade3d(traslamesh(scalemesh(defosph(sphi,tensor1,after=F),1,center="none"),positions[i,]),col=2,alpha=0.5)
print(i)
  }
我想根据 n 张量使椭球体变形并有效地绘制它们

标签: r3dvisualizationtensorrgl

解决方案


您正在做的两件事会使这段代码变慢。首先,您要绘制 3000 个对象,并在每个对象之后更新显示。这很容易解决:par3d(skipRedraw=TRUE)在开始和par3d(skipRedraw=FALSE)结束时调用,绘图会快得多。

你正在做的第二件事更难修复。您正在将椭球构造为网格,这使得每个椭球都是 320 个三角形的集合。因为你有 3000 个,所以你有将近一百万个三角形要绘制。 rgl可以处理这个问题,但真正缓慢的是你声明它们都是透明的。要绘制这些,它需要按照从最远到最近的顺序对所有百万个三角形进行排序以绘制它们。每次它从在一个椭圆体中绘制一个三角形切换到在另一个椭圆体中绘制一个三角形时,它都需要经历相当昂贵的上下文更改。

如果您设置alpha = 1,您将获得更快的显示,因为不需要排序。

您可以做的另一件事是将所有 3000 个椭圆体合并到一个巨大的网格中。它仍然需要对三角形进行排序,但不需要所有这些上下文更改。

下面的代码说明了这些建议。我假设您保留原始代码来设置功能。

# Skip redrawing and set alpha = 1

open3d()
par3d(skipRedraw=TRUE)
for(i in 1:dim(positions)[1]){  #### reasonably fast
  sphi<-creasph(radius=1,subdivision=2)
  shade3d(traslamesh(scalemesh(defosph(sphi,tensor1,after=F),1,center="none"),positions[i,]),col=2,alpha=1)
  print(i)
}
par3d(skipRedraw=FALSE)

# Keep alpha = 0.5, but plot just one big mesh

open3d()
for(i in 1:dim(positions)[1]){  #### pretty slow, but faster than the original
  sphi<-creasph(radius=1,subdivision=2)
  sphi <- traslamesh(scalemesh(defosph(sphi,tensor1,after=F),1,center="none"),positions[i,])
  if (i == 1) result <- sphi else result <- merge(result, sphi)
  print(i)
}
shade3d(result, col=2, alpha = 0.5)

第二种方法仍然很慢:它会在所有这些合并中进行大量分配。您可以通过使用网格内部构件来大大加快构建部件的速度。它的更新速度也很慢,因为alpha = 0.5.

内置球体显示速度如此之快的原因是它并没有试图做得这么好。它对球体中心进行排序,而不是对构成每个球体的所有三角形进行排序。如果你设置fastTransparency = FALSE,它会慢很多,因为它会对所有的三角形进行排序。它还使用“精灵”的等价物,只初始化一个球体并在许多不同的位置重新绘制它。Sprites 适用于您的示例,但如果您需要在每个椭圆体上进行不同的转换,则不会。


推荐阅读