misc3d
包提供了一个非常好的 marching cubes 算法实现,可以绘制隐式曲面。
例如,我们来绘制一个 Dupin 周线:
a = 0.94; mu = 0.56; c = 0.34 # cyclide parameters
f <- function(x, y, z, a, c, mu){ # implicit equation f(x,y,z)=0
b <- sqrt(a^2-c^2)
(x^2+y^2+z^2-mu^2+b^2)^2 - 4*(a*x-c*mu)^2 - 4*b^2*y^2
}
# define the "voxel"
nx <- 50; ny <- 50; nz <- 25
x <- seq(-c-mu-a, abs(mu-c)+a, length=nx)
y <- seq(-mu-a, mu+a, length=ny)
z <- seq(-mu-c, mu+c, length=nz)
g <- expand.grid(x=x, y=y, z=z)
voxel <- array(with(g, f(x,y,z,a,c,mu)), c(nx,ny,nz))
# plot the surface
library(misc3d)
surf <- computeContour3d(voxel, level=0, x=x, y=y, z=z)
drawScene.rgl(makeTriangles(surf))
很好,除了表面不光滑之外。 drawScene.rgl
的文档说:"对象特定的渲染特性,如平滑和材料,是通过设置对象来控制的。" 我不知道这是什么意思。如何获得平滑的表面?
我有一个解决方案,但不是一个直接有效的解决方案:该解决方案涉及从 computeContour3d
的输出构建一个 mesh3d
对象,并将表面法线包含在此 mesh3d
中。
由 f(x,y,z)= 0
定义的隐式表面的表面法线简单地由 f
的梯度给出。对于这个例子,推导梯度并不困难。
gradient <- function(xyz,a,c,mu){
x <- xyz[1]; y <- xyz[2]; z <- xyz[3]
b <- sqrt(a^2-c^2)
c(
2*(2*x)*(x^2+y^2+z^2-mu^2+b^2) - 8*a*(a*x-c*mu),
2*(2*y)*(x^2+y^2+z^2-mu^2+b^2) - 8*b^2*y,
2*(2*z)*(x^2+y^2+z^2-mu^2+b^2)
)
}
然后,法线的计算如下所示:
normals <- apply(surf, 1, function(xyz){
gradient(xyz,a,c,mu)
})
现在我们已经准备好创建
mesh3d
对象了:mesh <- list(vb = rbind(t(surf),1),
it = matrix(1:nrow(surf), nrow=3),
primitivetype = "triangle",
normals = rbind(-normals,1))
class(mesh) <- c("mesh3d", "shape3d")
最后使用rgl
绘制它:
library(rgl)
shade3d(mesh, color="red")
很好,表面现在很光滑。
但是有没有更简单的方法来获得平滑的表面,而不需要构建一个mesh3d
对象?文档中的这句话是什么意思:"通过设置对象中的设置,可以控制特定于对象的渲染功能,例如平滑和材料。"?
makeTriangles
语句中添加smooth=1000
,即drawScene.rgl(makeTriangles(surf, smooth=1000))
。 - G5Wrgl
渲染时,似乎smooth=TRUE
和smooth=1000
是相同的。 - Stéphane LaurentaddNormals
方法要好一些,但仍然显示出大的伪影。因此,如果您想要一个漂亮的光滑表面,您将不得不像您所做的那样计算真正的法线。 - user2554330