When I make a checkered surface by setting smooth=FALSE in the rgl package in R, it appears correctly in my computer's rgl window, but not in knitted R Markdown documents nor in the RStudio viewer, where it appears as the default smooth=TRUE.
This appears as intended in an XQuartz window:
library(rgl)
# Create x, y, and z points
set.seed(123)
x <- sort(rnorm(10))
y <- sort(rnorm(10))
f <- function(x,y) {r <- x+y}
z <- outer(x,y,f)
# Create a grid on the x and y axes
x.grid <- seq(min(x), max(x), length.out =10)
y.grid <- seq(min(y), max(y), length.out =10)
# Create a checkered surface
persp3d(x = x.grid,
y = y.grid,
z = z,
xlab="x", ylab="y", zlab="z",
col=rainbow(9),
lit = FALSE,
smooth = FALSE) # This works
However, when try to recreate this in an RMarkdown file, the checkering blurs. It appears that
smooth=FALSE
is not recognized:
```{r echo=F}
library(rgl)
# Create x, y, and z points
set.seed(123)
x <- sort(rnorm(10))
y <- sort(rnorm(10))
f <- function(x,y) {r <- x+y}
z <- outer(x,y,f)
# Create a grid on the x and y axes
x.grid <- seq(min(x), max(x), length.out =10)
y.grid <- seq(min(y), max(y), length.out =10)
# Create a checkered surface
persp3d(x = x.grid,
y = y.grid,
z = z,
xlab="x", ylab="y", zlab="z",
col=rainbow(9),
lit = F,
smooth = F) # Now this does not work
rglwidget()
```
Is this a bug in rgl? How can I display this interactive checkered surface in R Markdown?
It's a bug, or at least a limitation of the WebGL display. A workaround has been added to the latest release (1.3.14), but I'll leave the rest of this answer here for users of older versions.
When you choose smooth = FALSE
in the built-in display, old OpenGL 1.2 semantics are used. They allow the colour at one vertex of a triangle or quad to be used for the whole polygon.
However, in an Rmarkdown document, you're using WebGL, which is based on a more modern OpenGL version that doesn't support that mode. To display a triangle or quad with all one colour, you would need to set all vertices of the triangle to that colour.
You can fake this yourself by drawing the facets separately.
There's a way to do it semi-automatically by drawing the surface, converting it to a mesh, then fiddling with the mesh to change how colours are applied. You want the mesh to end up with meshColor = "faces"
, but you also need to change the colour vector to give one colour per face.
Here's a version of your code that does that:
library(rgl)
# Create x, y, and z points
set.seed(123)
x <- sort(rnorm(10))
y <- sort(rnorm(10))
f <- function(x,y) {r <- x+y}
z <- outer(x,y,f)
# Create a grid on the x and y axes
x.grid <- seq(min(x), max(x), length.out =10)
y.grid <- seq(min(y), max(y), length.out =10)
# Create a checkered surface
open3d()
ids <- persp3d(x = x.grid,
y = y.grid,
z = z,
xlab="x", ylab="y", zlab="z",
col=rainbow(9),
lit = FALSE,
smooth = FALSE)
m <- as.mesh3d(rglId(ids["surface"]))
m$meshColor <- "faces"
oldcolor <- m$material$color
indices <- setdiff(1:90, (0:8)*10 + 1)
m$material$color <- oldcolor[indices]
open3d()
plot3d(m)
It would be possible to make this completely automatic. If you want to do that, you should submit it as a PR on the rgl
Github site.