Рисование гладкой неявной поверхности с помощью misc3d - PullRequest
0 голосов
/ 13 ноября 2018

Пакет misc3d обеспечивает отличную реализацию алгоритма движущихся кубов, позволяя строить неявные поверхности.

Например, давайте построим циклический цикл Дюпена:

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

enter image description here

Хорошо, за исключением того, что поверхность не гладкая. Документация drawScene.rgl гласит: «Объектно-ориентированные функции рендеринга, такие как сглаживание и материал, управляются установкой в ​​объектах». Я не знаю, что это значит. Как получить гладкую поверхность?

У меня есть решение, но не простое: это решение состоит в построении объекта mesh3d из выходных данных computeContour3d и включении нормалей поверхности в этот 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")

enter image description here

Хорошо, поверхность теперь гладкая.

Но есть ли более простой способ получить гладкую поверхность, без создания объекта mesh3d? Что они означают в документации: «Объектно-ориентированные функции рендеринга, такие как сглаживание и материал, управляются настройкой в ​​объектах.» ?

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

В функции makeTriangles есть опция smooth:

drawScene.rgl(makeTriangles(surf, smooth=TRUE))

Я думаю, что результат эквивалентен решению @ user2554330, но это более просто.

enter image description here

0 голосов
/ 13 ноября 2018

Я не знаю, что предлагает документация.Однако вы можете сделать это через объект-сетку немного легче, чем вы (хотя результаты не такие приятные), используя функцию addNormals() для автоматического вычисления нормалей, а не по формуле.

Вот шаги:

Вычислите поверхность как вы.

Создайте сетку без нормалей.Это в основном то, что вы сделали, но используя tmesh3d():

mesh <- tmesh3d(t(surf), matrix(1:nrow(surf), nrow=3), homogeneous = FALSE)

Вычислите, какие вершины являются дубликатами каких других:

verts <- apply(mesh$vb, 2, function(column) paste(column, collapse = " "))
firstcopy <- match(verts, verts)

Перепишите индексы, чтобы использовать первую копию.Это необходимо, поскольку функции misc3d дают набор несвязных треугольников;нам нужно выяснить, какие из них связаны.

it <- as.numeric(mesh$it)
it <- firstcopy[it]
dim(it) <- dim(mesh$it)
mesh$it <- it

На данный момент в сетке много неиспользуемых вершин;если память была проблемой, вы можете добавить шаг для их удаления.Я собираюсь пропустить это.

Добавьте нормали

mesh <- addNormals(mesh)

Вот снимки до и после.Слева нет нормалей, справа с ними.

screenshots

Это не так гладко, как ваше решение с использованием вычисленных нормалей, но не всегда легко найтите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...