提问者:小点点

R有效地绘制了3D中椭球形变形的数千个球体


我需要真正加速一小段代码,根据将它们转换为椭球体的3x3张量列表,在3d rgl空间中变形大约3000个球体。此外,我需要透明颜色(“alpha”参数

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

共1个答案

匿名用户

您正在做的两件事情会使这段代码变慢。首先,您正在绘制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所需的排序,它在更新方面也相当缓慢。

内置球体显示如此之快的原因是它并没有试图做得这么好。它对球体中心进行排序,而不是对组成每个球体的所有三角形进行排序。如果您设置autTransp的透明度=FALSE,它会减慢很多,因为这样它就会对所有三角形进行排序。它还使用了相当于“精灵”的方法,只初始化一个球体,然后在许多不同的位置重新绘制它。精灵可以为您的示例工作,但如果您需要对每个椭球体进行不同的转换,则不行。