2016-09-29 21 views
5

當我使用rgl::spheres3d()時,渲染的球體具有笨重的切面邊緣。如何在rgl中增加spheres3d的平滑度

spheres = data.frame(x = c(1,2,3), y = c(1,3,1), 
        color = c("#992222" , "#222299", "#229922")) 
open3d() 
spheres3d(spheres$x, spheres$y, radius = 1, color = spheres$color) 

enter image description here

設置material3d(smooth = TRUE, line_antialias = TRUE)不改善這一點。增加半徑也無濟於事。有沒有什麼辦法可以增加它們被繪製的平滑度?

回答

1

擴展在cuttlefish44's excellent answer,我發現,工作得更好參數化我的方法 - 即它在兩極無缺陷(圖像中淺色球體上的黑色僞像)。

library(rgl) 
sphere.f <- function(x0 = 0, y0 = 0, z0 = 0, r = 1, n = 101, ...){ 
    f <- function(s, t) cbind(r * cos(s) * cos(t) + x0, 
          r * sin(s) * cos(t) + y0, 
          r * sin(t) + z0) 
    persp3d(f, slim = c(0, pi), tlim = c(0, 2*pi), n = n, add = T, ...) 
} 


sphere1.f <- function(x0 = 0, y0 = 0, z0 = 0, r = 1, n = 101, ...){ 
    f <- function(s,t){ 
    cbind( r * cos(t)*cos(s) + x0, 
      r *  sin(s) + y0, 
      r * sin(t)*cos(s) + z0) 
    } 
    persp3d(f, slim = c(-pi/2,pi/2), tlim = c(0, 2*pi), n = n, add = T, ...) 
} 


sphere.f(-1.5,0, col = "lightblue") 
sphere1.f(1.5,0, col = "pink") 

的圖像:

enter image description here

+0

這是非常好的 - 很好的工作。在n = 101時,赤道附近的脊缺陷仍然存在,但速度和質量之間的折衷很好。隨着n = 201,它已經很難看到。在n = 301時,幾乎沒有明顯的並且仍然可以接受的快速。 – dww

+0

感謝您給我正確的答案投票。願意以rgl的身份獲得銅牌徽章,但以這樣的速度將需要大約10年的時間。 –

2

這並不容易;我認爲如果你想這樣做,你將不得不

sphereMesh.setGlobe(16,16); 

用更大的值調用函數(該函數在src/SphereMesh.cpp的第25行定義;參數是in_segments a nd in_sections ...)

  • 從源代碼構建/安裝包;這不僅需要標準的編譯工具,而且相關的OpenGL庫(在Debian的Linux操作系統,你可以使用sudo apt-get build-dep r-cran-rgl得到他們,我想......)

我沒有嘗試這樣做。祝你好運......或者,你可以通過materials3d或以其他方式問清楚包的維護者,使之成爲可設置的參數...

+0

我會嘗試下載源,編輯上述行*和*了'DESCRIPTION'文件(自己設定是維護),並上傳它通過ftp上傳到'win-builder.r-project.org' –

2

雖然rgl::spheres3d()不能做到這一點unless you edit and rebuild the rgl source,另一種是寫自己的功能繪製領域。這是一個函數,它將球體呈現爲以相等的經度和緯度間隔的四邊形網格。

drawSphere = function(xc=0, yc=0, zc=0, r=1, lats=50L, longs=50L, ...) { 
    #xc,yc,zc give centre of sphere, r is radius, lats/longs for resolution 
    vertices = vector(mode = "numeric", length = 12L * lats * longs) 
    vi = 1L 
    for(i in 1:lats) { 
    lat0 = pi * (-0.5 + (i - 1)/lats) 
    z0 = sin(lat0)*r 
    zr0 = cos(lat0)*r 
    lat1 = pi * (-0.5 + i/lats) 
    z1 = sin(lat1)*r 
    zr1 = cos(lat1)*r 
    for(j in 1:longs) { 
     lng1 = 2 * pi * (j - 1)/longs 
     lng2 = 2 * pi * (j)/longs 
     x1 = cos(lng1) 
     y1 = sin(lng1) 
     x2 = cos(lng2) 
     y2 = sin(lng2) 
     vertices[vi] = x1 * zr0 + xc; vi = vi + 1L 
     vertices[vi] = y1 * zr0 + yc; vi = vi + 1L 
     vertices[vi] = z0 + zc;   vi = vi + 1L 
     vertices[vi] = x1 * zr1 + xc; vi = vi + 1L 
     vertices[vi] = y1 * zr1 + yc; vi = vi + 1L 
     vertices[vi] = z1 + zc;   vi = vi + 1L 
     vertices[vi] = x2 * zr1 + xc; vi = vi + 1L 
     vertices[vi] = y2 * zr1 + yc; vi = vi + 1L 
     vertices[vi] = z1 + zc;   vi = vi + 1L 
     vertices[vi] = x2 * zr0 + xc; vi = vi + 1L 
     vertices[vi] = y2 * zr0 + yc; vi = vi + 1L 
     vertices[vi] = z0 + zc;   vi = vi + 1L 
    } 
    } 
    indices = 1:(length(vertices)/3) 
    shade3d(qmesh3d(vertices, indices, homogeneous=F), ...) 
} 

應當能夠改善這一點,例如使用icospheres(即拉伸該球體作爲拉伸二十面體)。但是如果你讓拉丁和長褲足夠高的話,這個版本已經畫出了相當不錯的球體。

在行動功能的一個例子:

spheres = data.frame(x = c(1,2,3), y = c(1,3,1), z=c(0,0,0), color = c("#992222" , "#222299", "#229922")) 
open3d() 
material3d(ambient = "black", specular = "grey60", emission = "black", shininess = 30.0) 
rgl.clear(type = "lights") 
rgl.light(theta = -30, phi = 60, viewpoint.rel = TRUE, ambient = "#FFFFFF", diffuse = "#FFFFFF", specular = "#FFFFFF", x = NULL, y = NULL, z = NULL) 
rgl.light(theta = -0, phi = 0, viewpoint.rel = TRUE, diffuse = "gray20", specular = "gray25", ambient = "gray80", x = NULL, y = NULL, z = NULL) 
sapply(1:NROW(spheres), function(i) 
    drawSphere(spheres$x[i], spheres$y[i], spheres$z[i], r=1, lats = 400, longs = 400, color=spheres$color[i])) 

enter image description here

+0

是的,繪製你自己的球體是一條路。如果你只需要一種顏色,一種可能的改進是你可以繪製一個球體,並將其重新用作一個3d精靈。這可以節省內存,這在R中可能並不重要,但如果使用'rglwidget()'導出場景,則會在文件大小方面產生顯着差異。 – user2554330

2

一個更簡單的方法是使用subdivision3d()。在這裏,depth=4並不是那麼順利,但你可以增加。

library(rgl) 
sphere <- subdivision3d(cube3d(),depth=4) 
sphere$vb[4,] <- apply(sphere$vb[1:3,], 2, function(x) sqrt(sum(x^2))) 
open3d() 
shade3d(sphere, col="red") 

enter image description here

+2

添加法線使其看起來更好。你可以通過爲一個球體添加'sphere $ normals < - sphere $ vb'或者更一般地'shape < - addNormals(shape)'來實現。 – user2554330

+1

我喜歡這種方法的簡單性。它可以通過使用'dodecahedron3d()'而不是'cube3d()'來改進。不幸的是,它不適用於高分辨率領域。如果我增加6以上的深度,我傾向於得到內存分配錯誤。也就是說,深度6處的dodecahedron3d看起來相當不錯,如果處理起來有點緩慢 – dww

+1

除了我最後的評論之外,加入@ user2554330向在深度6處細分的icosahedron3d()添加法線的建議,使得一些相當漂亮的球體 – dww

2

下面是使用persp3d.function()

sphere.f <- function(x0 = 0, y0 = 0, z0 = 0, r = 1, n = 101, ...){ 
    f <- function(s, t) cbind(r * cos(s) * cos(t) + x0, 
          r * sin(s) * cos(t) + y0, 
          r * sin(t) + z0) 
    persp3d(f, slim = c(0, pi), tlim = c(0, 2*pi), n = n, add = T, ...) 
} 

sphere.f(col = rainbow) 

enter image description here

+0

這使得一些非常漂亮的平滑球體,並且比我的基於qmesh3d的函數處理速度快得多。有一件事讓我不能接受這個答案,那就是這些球體在兩極附近有缺陷,表面存在空隙,並且在兩個半球不匹配的赤道處也有一個微小的脊。你對如何解決這些問題有任何想法嗎? – dww

+0

@dww;不幸的是它似乎不可能有一些方法可以用兩個變量來表示球體。但是以任何方式出現低密度區域或高密度區域和/或重複點。 – cuttlefish44