R/render_highquality.R

Defines functions render_highquality

Documented in render_highquality

#'@title Render High Quality
#'
#'@description Renders a raytraced version of the displayed rgl scene, using the `rayrender` package. 
#'User can specify the light direction, intensity, and color, as well as specify the material of the 
#'ground and add additional scene elements.
#'
#'This function can also generate frames for an animation by passing camera animation information from 
#'either `convert_path_to_animation_coords()` or `rayrender::generate_camera_motion()` functions.
#'
#'@param filename Default `NA`. Filename of saved image. If missing, will display to current device.
#'@param samples Default `128`. The maximum number of samples for each pixel. Increase this to increase the quality of the rendering.
#'@param sample_method Default `"sobol_blue"`, unless `samples > 256`, in which it defaults to `"sobol"`.
#'The type of sampling method used to generate random numbers. 
#'The other options are `random` (worst quality but fastest), 
#'`sobol_blue` (best option for sample counts below 256), and `sobol` 
#'(slowest but best quality, better than `sobol_blue` for sample counts greater than 256).
#'@param min_variance Default `1e-6`. Minimum acceptable variance for a block of pixels for the adaptive sampler. 
#'Smaller numbers give higher quality images, at the expense of longer rendering times. 
#'If this is set to zero, the adaptive sampler will be turned off and the renderer will use the maximum number of samples everywhere.
#'@param light Default `TRUE`. Whether there should be a light in the scene. If not, the scene will be lit with a bluish sky.
#'@param lightdirection Default `315`. Position of the light angle around the scene. 
#'If this is a vector longer than one, multiple lights will be generated (using values from 
#'`lightaltitude`, `lightintensity`, and `lightcolor`)
#'@param lightaltitude Default `45`. Angle above the horizon that the light is located. 
#'If this is a vector longer than one, multiple lights will be generated (using values from 
#'`lightdirection`, `lightintensity`, and `lightcolor`)
#'@param lightsize Default `NULL`. Radius of the light(s). Automatically chosen, but can be set here by the user.
#'@param lightintensity Default `500`. Intensity of the light.
#'@param lightcolor Default `white`. The color of the light.
#'@param material Default `rayrender::diffuse()`. The material properties of the object file. Only used if `override_material = TRUE`
#'@param override_material Default `FALSE`. Whether to override the default diffuse material with that in argument `material`.
#'@param cache_scene Default `FALSE`. Whether to cache the current scene to memory so it does not have to be converted to a `raymesh` object 
#'each time `render_snapshot()` is called. If `TRUE` and a scene has been cached, it will be used when rendering.
#'@param reset_scene_cache Default `FALSE`. Resets the scene cache before rendering.
#'@param width Defaults to the width of the rgl window. Width of the rendering. 
#'@param height Defaults to the height of the rgl window. Height of the rendering. 
#'@param text_angle Default `NULL`, which forces the text always to face the camera. If a single angle (degrees),
#'will specify the absolute angle all the labels are facing. If three angles, this will specify all three orientations
#'(relative to the x,y, and z axes) of the text labels.
#'@param text_size Default `6`. Height of the text.
#'@param text_offset Default `c(0,0,0)`. Offset to be applied to all text labels.
#'@param line_radius Default `0.5`. Radius of line/path segments.
#'@param smooth_line Default `FALSE`. If `TRUE`, the line will be rendered with a continuous smooth line, rather
#'than straight segments.
#'@param use_extruded_paths Default `TRUE`. If `FALSE`, paths will be generated with the `rayrender::path()` object, instead
#'of `rayrender::extruded_path()`.
#'@param point_radius Default `0.5`. Radius of 3D points (rendered with `render_points()`.
#'@param scale_text_angle Default `NULL`. Same as `text_angle`, but for the scale bar.
#'@param scale_text_size Default `6`. Height of the scale bar text.
#'@param scale_text_offset Default `c(0,0,0)`. Offset to be applied to all scale bar text labels.
#'@param title_text Default `NULL`. Text. Adds a title to the image, using magick::image_annotate. 
#'@param title_offset Default `c(20,20)`. Distance from the top-left (default, `gravity` direction in 
#'image_annotate) corner to offset the title.
#'@param title_size Default `30`. Font size in pixels.
#'@param title_color Default `black`. Font color.
#'@param title_font Default `sans`. String with font family such as "sans", "mono", "serif", "Times", "Helvetica", 
#'"Trebuchet", "Georgia", "Palatino" or "Comic Sans".
#'@param title_bar_color Default `NULL`. If a color, this will create a colored bar under the title.
#'@param title_bar_alpha Default `0.5`. Transparency of the title bar.
#'@param ground_material Default `diffuse()`. Material defined by the rayrender material functions.
#'@param ground_size Default `100000`. The width of the plane representing the ground.
#'@param camera_location Default `NULL`. Custom position of the camera. The `FOV`, `width`, and `height` arguments will still
#'be derived from the rgl window.
#'@param camera_lookat Default `NULL`. Custom point at which the camera is directed. The `FOV`, `width`, and `height` arguments will still
#'be derived from the rgl window.
#'@param camera_interpolate Default `c(0,0)`. Maximum `1`, minimum `0`. Sets the camera at a point between the `rgl` view and the `camera_location` 
#'and `camera_lookat` vectors.
#'@param scene_elements Default `NULL`. Extra scene elements to add to the scene, created with rayrender.
#'@param clear Default `FALSE`. If `TRUE`, the current `rgl` device will be cleared.
#'@param print_scene_info Default `FALSE`. If `TRUE`, it will print the position and lookat point of the camera.
#'@param clamp_value Default `10`. See documentation for `rayrender::render_scene()`.
#'@param return_scene Default `FALSE`. If `TRUE`, this will return the rayrender scene (instead of rendering the image).
#'@param load_normals Default `TRUE`. Whether to load the vertex normals if they exist in the OBJ file.
#'@param calculate_consistent_normals Default `FALSE`. Whether to calculate consistent vertex normals to prevent energy 
#'loss at edges.
#'@param point_material Default `rayrender::diffuse`. The rayrender material function to be applied
#'to point data.
#'@param point_material_args Default empty `list()`. The function arguments to `point_material`. 
#'The argument `color` will be automatically extracted from the rgl scene, but all other arguments 
#'can be specified here. 
#'@param path_material Default `rayrender::diffuse`. The rayrender material function to be applied
#'to path data.
#'@param path_material_args Default empty `list()`. The function arguments to `path_material`. 
#'The argument `color` will be automatically extracted from the rgl scene, but all other arguments 
#'can be specified here. 
#'@param animation_camera_coords Default `NULL`. Expects camera animation output from either `convert_path_to_animation_coords()`
#'or `rayrender::generate_camera_motion()` functions.
#'@param ... Additional parameters to pass to `rayrender::render_scene`()
#'
#'@export
#'@examples
#'#Render the volcano dataset using pathtracing
#'if(run_documentation()) {
#'volcano %>%
#'  sphere_shade() %>%
#'  plot_3d(volcano,zscale = 2)
#'render_highquality(min_variance = 0, sample_method = "sobol_blue") 
#'}
#'
#'#Change position of light
#'if(run_documentation()) {
#'render_highquality(lightdirection = 45, min_variance = 0, sample_method = "sobol_blue")
#'}
#'
#'#Change vertical position of light
#'if(run_documentation()) {
#'render_highquality(lightdirection = 45, lightaltitude = 10, 
#'                   min_variance = 0, sample_method = "sobol_blue")
#'}
#'
#'#Change the ground material
#'if(run_documentation()) {
#'render_highquality(lightdirection = 45, lightaltitude=60,
#'                   ground_material = rayrender::diffuse(checkerperiod = 30, checkercolor="grey50"),
#'                   min_variance = 0, sample_method = "sobol_blue")
#'}
#'
#'#Add three different color lights and a title
#'if(run_documentation()) {
#'render_highquality(lightdirection = c(0,120,240), lightaltitude=45,
#'                   lightcolor=c("red","green","blue"), title_text = "Red, Green, Blue",
#'                   title_bar_color="white", title_bar_alpha=0.8,
#'                   min_variance = 0, sample_method = "sobol_blue")
#'}
#'
#'#Change the camera:
#'if(run_documentation()) {
#'render_camera(theta=-45,phi=60,fov=60,zoom=0.8)
#'render_highquality(lightdirection = c(0),
#'                   title_bar_color="white", title_bar_alpha=0.8,
#'                   min_variance = 0, sample_method = "sobol_blue")
#'}
#'#Add a shiny metal sphere
#'if(run_documentation()) {
#'render_camera(theta=-45,phi=60,fov=60,zoom=0.8)
#'render_highquality(lightdirection = c(0,120,240), lightaltitude=45, 
#'                   lightcolor=c("red","green","blue"),
#'                   scene_elements = rayrender::sphere(z=-60,y=0,
#'                                                      radius=20,material=rayrender::metal()),
#'                   min_variance = 0, sample_method = "sobol_blue")
#'}
#'
#'#Add a red light to the volcano and change the ambient light to dusk
#'if(run_documentation()) {
#'render_camera(theta=45,phi=45)
#'render_highquality(lightdirection = c(240), lightaltitude=30, 
#'                   lightcolor=c("#5555ff"),
#'                   scene_elements = rayrender::sphere(z=0,y=15, x=-18, radius=5,
#'                                    material=rayrender::light(color="red",intensity=10)),
#'                   min_variance = 0, sample_method = "sobol_blue")
#'}
#'#Manually change the camera location and direction
#'if(run_documentation()) {
#'render_camera(theta=45,phi=45,fov=90)
#'render_highquality(lightdirection = c(240), lightaltitude=30, lightcolor=c("#5555ff"), 
#'                   camera_location = c(50,10,10), camera_lookat = c(0,15,0),
#'                   scene_elements = rayrender::sphere(z=0,y=15, x=-18, radius=5,
#'                                    material=rayrender::light(color="red",intensity=10)),
#'                   min_variance = 0, sample_method = "sobol_blue")
#'}
render_highquality = function(filename = NA, samples = 128, 
                              sample_method = "sobol_blue", min_variance=1e-7,
                              light = TRUE, 
                              lightdirection = 315, lightaltitude = 45, lightsize=NULL,
                              lightintensity = 500, lightcolor = "white", material = rayrender::diffuse(),
                              override_material = FALSE,
                              cache_scene = FALSE, reset_scene_cache = FALSE, 
                              width = NULL, height = NULL, 
                              text_angle = NULL, text_size = 6, text_offset = c(0,0,0), 
                              line_radius=0.5, point_radius = 0.5, smooth_line = FALSE,
                              use_extruded_paths = FALSE,
                              scale_text_angle = NULL, scale_text_size = 6, scale_text_offset = c(0,0,0), 
                              title_text = NULL, title_offset = c(20,20), 
                              title_color = "black", title_size = 30, title_font = "sans",
                              title_bar_color = NULL, title_bar_alpha = 0.5,
                              ground_material = rayrender::diffuse(), ground_size=100000,
                              scene_elements=NULL, 
                              camera_location = NULL, camera_lookat = NULL, 
                              camera_interpolate=1, clear  = FALSE, return_scene = FALSE,
                              print_scene_info = FALSE, clamp_value = 10, 
                              calculate_consistent_normals = FALSE, load_normals = TRUE,
                              point_material = rayrender::diffuse, 
                              point_material_args = list(),
                              path_material = rayrender::diffuse, 
                              path_material_args = list(),
                              animation_camera_coords = NULL, ...) {
  if(rgl::cur3d() == 0) {
    stop("No rgl window currently open.")
  }
  if(samples > 256 && sample_method == "sobol_blue") {
    warning(r"{When `sample_method = "sobol_blue"`, `samples` must be less than or equal to 256. Setting `sample_method` to `"sobol"`.}")
    sample_method = "sobol"
  }
  if(reset_scene_cache) {
    assign("scene_cache", NULL, envir = ray_cache_scene_envir)
  }
  if(!is.na(filename)) {
    if(dirname(filename) != ".") {
      if(!dir.exists(dirname(filename))) {
        stop(sprintf("Error: directory '%s' does not exist.", dirname(filename)))
      }
    }
  }
  if(!(length(find.package("rayrender", quiet = TRUE)) > 0)) {
    stop("`rayrender` package required for render_highquality()")
  }
  
  #Check path/point material arguments
  if(!inherits(path_material, "function")) {
    stop("`path_material` is not a function: did you forget to remove the `()` at the end?")
  }
  arg_names = names(formals(path_material))
  not_matching = !(names(path_material_args) %in% arg_names)
  if(any(not_matching)) {
    arg_names = arg_names[arg_names != "color"]
    all_arg_names = paste(arg_names, collapse = ", ")
    all_not_matching = paste(names(path_material_args)[not_matching], collapse = ", ")
    stop(sprintf("Path material arguments `%s` not valid for the material. Valid argument names are: \n%s", 
                 all_not_matching, all_arg_names))
  }
  if(!inherits(point_material, "function")) {
    stop("`point_material` is not a function: did you forget to remove the `()` at the end?")
  }
  arg_names = names(formals(point_material))
  not_matching = !(names(point_material_args) %in% arg_names)
  if(any(not_matching)) {
    arg_names = arg_names[arg_names != "color"]
    all_arg_names = paste(arg_names, collapse = ", ")
    all_not_matching = paste(names(point_material_args)[not_matching], collapse = ", ")
    stop(sprintf("Point material arguments `%s` not valid for the material. Valid argument names are: \n%s", 
                 all_not_matching, all_arg_names))
  }
  #Set use_extruded_path to TRUE if path_material is dielectric
  path_material_raw = path_material()
  path_material_df = path_material_raw[[1]]
  if(path_material_df$type == "dielectric" && !use_extruded_paths) {
    message("dielectric material for paths selected--setting `use_extruded_paths = TRUE` for accurate rendering of material")
    use_extruded_paths = TRUE
  }

  #Get scene info
  windowrect = rgl::par3d()$windowRect
  if(!is.null(title_text)) {
    has_title = TRUE
  } else {
    has_title = FALSE
  }
  if(is.null(width)) {
    width = windowrect[3]-windowrect[1]
  }
  if(is.null(height)) {
    height = windowrect[4]-windowrect[2]
  }
  if(.Platform$OS.type == "windows") {
    sepval = "\\"
  } else {
    sepval = "/"
  }

  surfaceid = get_ids_with_labels(typeval = c("surface", "surface_tris"))
  surfacevertices = rgl.attrib(surfaceid$id[1], "vertices")
  polygonid = get_ids_with_labels(typeval = c("polygon3d"))
  baseid = get_ids_with_labels(typeval = c("base"))
  if(nrow(polygonid) > 0) {
    polyrange = c()
    polyrange_x = c()
    polyrange_z = c()
    for(i in seq_len(nrow(polygonid))) {
      tempverts = range(rgl.attrib(polygonid$id[i], "vertices")[,2])
      tempverts_x = range(rgl.attrib(polygonid$id[i], "vertices")[,1])
      tempverts_z = range(rgl.attrib(polygonid$id[i], "vertices")[,3])
      
      if(all(!is.na(tempverts))) {
        polyrange = range(c(tempverts,polyrange))
      }
      if(all(!is.na(tempverts_x))) {
        polyrange_x = range(c(tempverts_x,polyrange_x))
      }
      if(all(!is.na(tempverts_z))) {
        polyrange_z = range(c(tempverts_z,polyrange_z))
      }
    }
  }
  if(nrow(baseid) > 0) {
    baserange = c()
    for(i in seq_len(nrow(baseid))) {
      tempverts = range(rgl.attrib(baseid$id[i], "vertices")[,2])
      if(all(!is.na(tempverts))) {
        baserange = range(c(tempverts,baserange))
      }
    }
  }
  surfacerange = range(surfacevertices[,2],na.rm=TRUE)
  if(nrow(polygonid) > 0) {
    surfacerange[2] = range(c(surfacerange,polyrange))[2]
  }
  if(nrow(baseid) > 0) {
    surfacerange[2] = range(c(surfacerange,baserange))[2]
  }
  shadowid = get_ids_with_labels(typeval = "shadow")
  if(nrow(shadowid) > 0) {
    shadowvertices = rgl.attrib(shadowid$id[1], "vertices")
    shadowdepth = shadowvertices[1,2]
    has_shadow = TRUE
  } else {
    has_shadow = FALSE
  }
  camera_interpolate[camera_interpolate > 1] = 1
  camera_interpolate[camera_interpolate < 0] = 0
  fov = rgl::par3d()$FOV
  rotmat = rot_to_euler(rgl::par3d()$userMatrix)
  projmat = rgl::par3d()$projMatrix
  zoom = rgl::par3d()$zoom
  scalevals = rgl::par3d("scale")
  
  phi = rotmat[1]
  if(90 - abs(phi) < 1e-3) {
    phi = -phi
  }
  if(0.001 > abs(abs(rotmat[3]) - 180)) {
    theta = -rotmat[2] + 180
    movevec = rgl::rotationMatrix(-rotmat[2]*pi/180, 0, 1, 0) %*%
      rgl::rotationMatrix(-phi*pi/180, 1, 0, 0) %*% 
      rgl::par3d()$userMatrix[,4]
  } else {
    theta = rotmat[2]
    movevec = rgl::rotationMatrix(rotmat[3]*pi/180, 0, 0, 1) %*%
      rgl::rotationMatrix(rotmat[2]*pi/180, 0, 1, 0) %*%
      rgl::rotationMatrix(-phi*pi/180, 1, 0, 0) %*% 
      rgl::par3d()$userMatrix[,4]
  }
  movevec = movevec[1:3]
  observer_radius = rgl::par3d()$observer[3]
  lookvals = rgl::par3d()$bbox
  # lookvals[4] = surfacerange[2]
  if(fov == 0) {
    ortho_dimensions = c(2/projmat[1,1],2/projmat[2,2])
  } else {
    fov = 2 * atan(1/projmat[2,2]) * 180/pi
    ortho_dimensions = c(1,1)
  }
  bbox_center = c(mean(lookvals[1:2]),mean(lookvals[3:4]),mean(lookvals[5:6])) - movevec
  observery = sinpi(phi/180) * observer_radius
  observerx = cospi(phi/180) * sinpi(theta/180) * observer_radius
  observerz = cospi(phi/180) * cospi(theta/180) * observer_radius
  if(is.null(camera_location)) {
    lookfrom = c(observerx, observery, observerz) 
  } else {
    lookfrom = camera_location
  }
  if(length(camera_interpolate) == 1) {
    camera_interpolate = c(camera_interpolate,camera_interpolate)
  }
  if(is.null(camera_lookat)) {
    camera_lookat = c(0,0,0)
  }
  if(all(camera_interpolate != 1)) {
    if(!is.null(camera_location)) {
      lookfrom = (1-camera_interpolate[1]) * c(observerx, observery, observerz) + 
        camera_interpolate[1] * camera_location
    }
    if(!is.null(camera_lookat)) {
      camera_lookat = camera_interpolate[2] * camera_lookat
    }
  }
  if(cache_scene) {
    ray_scene = get("scene_cache", envir = ray_cache_scene_envir)
    if(is.null(ray_scene)) {
      ray_scene = convert_rgl_to_raymesh(save_shadow = FALSE)
      assign("scene_cache", ray_scene, envir = ray_cache_scene_envir)
    }
  } else {
    ray_scene = convert_rgl_to_raymesh(save_shadow = FALSE)
  }
  
  if(!override_material) {
    scene = rayrender::raymesh_model(ray_scene,
                                     x = -bbox_center[1],
                                     y = -bbox_center[2],
                                     z = -bbox_center[3],
                                     override_material = FALSE,
                                     calculate_consistent_normals = calculate_consistent_normals)
  } else {
    scene = rayrender::raymesh_model(ray_scene,
                                     x = -bbox_center[1],
                                     y = -bbox_center[2],
                                     z = -bbox_center[3],
                                     material = material,
                                     override_material = TRUE,
                                     calculate_consistent_normals = calculate_consistent_normals)
  }
  has_rayimage = TRUE
  if(!(length(find.package("rayimage", quiet = TRUE)) > 0)) {
    warning("`rayimage` package required for labels")
    has_rayimage = FALSE
  }
  labelids = get_ids_with_labels(typeval = "raytext")$id
  labels = list()
  counter = 1
  for(i in seq_len(length(labelids))) {
    if(!has_rayimage) {
      break
    }
    temp_label = rgl.attrib(labelids[i], "texts")
    temp_center = rgl.attrib(labelids[i], "centers")
    temp_color = rgl.attrib(labelids[i], "colors")
    for(j in seq_len(nrow(temp_label))) {
      if(is.null(text_angle)) {
        anglevec = c(-phi,theta,0)
      } else {
        if(length(text_angle) == 1) {
          anglevec = c(0,text_angle,0)
        } else {
          anglevec = text_angle
        }
      }
      labels[[counter]] = rayrender::text3d(label=temp_label[j,1],
                                  x=temp_center[j,1] - bbox_center[1] + text_offset[1], 
                                  y=temp_center[j,2] - bbox_center[2] + text_offset[2], 
                                  z=temp_center[j,3] - bbox_center[3] + text_offset[3],
                                  angle = anglevec,
                                  text_height = text_size,
                                  material = rayrender::diffuse(color = temp_color[j,1:3]))
      counter = counter + 1
    }
  }
  if(length(labels) > 0) {
    all_labels = do.call(rbind, labels)
    scene = rayrender::add_object(scene, all_labels)
  }
  labellineids = get_ids_with_labels(typeval = "textline")$id
  labelline = list()
  counter = 1
  for(i in seq_len(length(labellineids))) {
    if(!has_rayimage) {
      break
    }
    temp_verts = rgl.attrib(labellineids[i], "vertices")
    temp_color = rgl.attrib(labellineids[i], "colors")
    for(j in seq_len(nrow(temp_verts)/2)) {
      labelline[[counter]] = rayrender::segment(start = temp_verts[2*j-1,] - bbox_center, 
                                     end   = temp_verts[2*j,] - bbox_center,
                                     radius = line_radius,
                                     material = rayrender::diffuse(color = temp_color[j,1:3]))
      counter = counter + 1
    }
  }
  pathids = get_ids_with_labels(typeval = c("path3d","contour3d"))$id
  pathline = list()
  counter = 1
  for(i in seq_len(length(pathids))) {
    temp_verts = rgl.attrib(pathids[i], "vertices")
    temp_color = rgl.attrib(pathids[i], "colors")
    if(nrow(temp_color) == 1) {
      temp_color = matrix(temp_color[1:3], byrow = TRUE, ncol = 3, nrow = nrow(temp_verts))
    }
    matrix_center = matrix(bbox_center, byrow=TRUE,ncol=3,nrow = nrow(temp_verts))
    path_material_args$color = temp_color[1,1:3]
    if(use_extruded_paths) {
      pathline[[counter]] = rayrender::extruded_path(points = temp_verts - matrix_center , 
                                            width = line_radius * 2,
                                            smooth_normals = TRUE, 
                                            straight = !smooth_line,
                                            material = do.call("path_material", args = path_material_args))
    } else {
      pathline[[counter]] = rayrender::path(points = temp_verts - matrix_center, 
                                            width = line_radius * 2,
                                            straight = !smooth_line,
                                            material = do.call("path_material", args = path_material_args))
    }
    counter = counter + 1
  }
  pointids = get_ids_with_labels(typeval = "points3d")$id
  pointlist = list()
  counter = 1
  for(i in seq_len(length(pointids))) {
    temp_verts = rgl.attrib(pointids[i], "vertices")
    temp_color = rgl.attrib(pointids[i], "colors")
    if(nrow(temp_color) == 1) {
      temp_color = matrix(temp_color[1:3], byrow = TRUE, ncol = 3, nrow = nrow(temp_verts))
    }
    for(j in seq_len(nrow(temp_verts))) {
      point_material_args$color = temp_color[j,1:3]
      
      pointlist[[counter]] = rayrender::sphere(x = temp_verts[j,1] - bbox_center[1],
                                              y = temp_verts[j,2] - bbox_center[2],
                                              z = temp_verts[j,3] - bbox_center[3],
                                              radius = point_radius,
                                              material = do.call("point_material", args = point_material_args))
      counter = counter + 1
    }
  }
  scalelabelids = get_ids_with_labels(typeval = "text_scalebar")$id
  scalelabels = list()
  counter = 1
  for(i in seq_len(length(scalelabelids))) {
    if(!has_rayimage) {
      break
    }
    temp_label = rgl.attrib(scalelabelids[i], "texts")
    temp_center = rgl.attrib(scalelabelids[i], "centers")
    temp_color = rgl.attrib(scalelabelids[i], "colors")
    for(j in seq_len(nrow(temp_label))) {
      scalelabelfile = tempfile(fileext = ".png")
      rayimage::add_title(matrix(0,ncol = nchar(temp_label[j,1])*60, nrow=60), 
                          title_size  = 60,
                          title_offset = c(0,0),title_text = temp_label, title_color = "white",
                          title_position = "center", filename = scalelabelfile)
      if(is.null(text_angle)) {
        anglevec = c(-phi,theta,0)
      } else {
        if(length(text_angle) == 1) {
          anglevec = c(0,text_angle,0)
        } else {
          anglevec = text_angle
        }
      }
      scalelabels[[counter]] = rayrender::xy_rect(x=temp_center[j,1] - bbox_center[1] + scale_text_offset[1], 
                                  y=temp_center[j,2] - bbox_center[2] + scale_text_offset[2], 
                                  z=temp_center[j,3] - bbox_center[3] + scale_text_offset[3],
                                  angle = anglevec,
                                  xwidth = nchar(temp_label[j,1])*scale_text_size, ywidth = scale_text_size,
                                  material = rayrender::diffuse(color = temp_color[j,1:3], alpha_texture = scalelabelfile))
      counter = counter + 1
    }
  }
  if(length(labels) > 0) {
    all_labels = do.call(rbind, labels)
    scene = rayrender::add_object(scene, all_labels)
  }
  if(length(labelline) > 0) {
    all_labellines = do.call(rbind, labelline)
    scene = rayrender::add_object(scene, all_labellines)
  }
  if(length(scalelabels) > 0) {
    all_scalelabels = do.call(rbind, scalelabels)
    scene = rayrender::add_object(scene, all_scalelabels)
  }
  if(length(pathline) > 0) {
    all_pathline = do.call(rbind, pathline)
    scene = rayrender::add_object(scene, all_pathline)
  }
  if(length(pointlist) > 0) {
    all_pointlist = do.call(rbind, pointlist)
    scene = rayrender::add_object(scene, all_pointlist)
  }
  if(has_shadow) {
    scene = rayrender::add_object(scene, rayrender::xz_rect(zwidth=ground_size,xwidth=ground_size,
                                                            y=shadowdepth-bbox_center[2], material = ground_material))
  }
  if(any(round(scalevals,4) != 1)) {
    scene = rayrender::group_objects(scene, 
                                     scale = scalevals, 
                                     pivot_point = c(0,0,0))
  }
  if(light) {
    if(is.null(lightsize)) {
      lightsize = observer_radius/5
    }
    if(length(lightaltitude) >= 1 || length(lightdirection) >= 1) {
      if(length(lightaltitude) > 1 && length(lightdirection) > 1 && length(lightdirection) != length(lightaltitude)) {
        stop("lightaltitude vector ", lightaltitude, " and lightdirection vector ", lightdirection, "both greater than length 1 but not equal length" )
      }
      numberlights = ifelse(length(lightaltitude) > length(lightdirection), length(lightaltitude),length(lightdirection))
      lightaltitudetemp = lightaltitude[1]
      lightdirectiontemp = lightdirection[1]
      lightintensitytemp = lightintensity[1]
      lightcolortemp = lightcolor[1]
      lightsizetemp = lightsize[1]
      for(i in seq_len(numberlights)) {
        if(!is.na(lightaltitude[i])) {
          lightaltitudetemp = lightaltitude[i]
        }
        if(!is.na(lightdirection[i])) {
          lightdirectiontemp = lightdirection[i]
        }
        if(!is.na(lightintensity[i])) {
          lightintensitytemp = lightintensity[i]
        }
        if(!is.na(lightcolor[i])) {
          lightcolortemp = lightcolor[i]
        }
        if(!is.na(lightsize[i])) {
          lightsizetemp = lightsize[i]
        }
        scene = rayrender::add_object(scene, rayrender::sphere(x=observer_radius*5 * cospi(lightaltitudetemp/180) * sinpi(lightdirectiontemp/180),
                                         y=observer_radius*5 * sinpi(lightaltitudetemp/180),
                                         z=-observer_radius*5 * cospi(lightaltitudetemp/180) * cospi(lightdirectiontemp/180), radius=lightsizetemp,
                                         material = rayrender::light(color = lightcolortemp, intensity = lightintensitytemp)))
      }
    }
  }
  if(print_scene_info) {
    dist_val = sqrt(sum((camera_lookat - lookfrom)^2))
    print(sprintf("Camera position: c(%0.2f, %0.2f, %0.2f), Camera Lookat: c(%0.2f, %0.2f, %0.2f) Focal Distance: %0.2f Scene Offset:  c(%0.2f, %0.2f, %0.2f)",
                  lookfrom[1],lookfrom[2],lookfrom[3], camera_lookat[1], camera_lookat[2], camera_lookat[3], dist_val,
                  -bbox_center[1],-bbox_center[2],-bbox_center[3]))
  }
  if(!is.null(scene_elements)) {
    scene = rayrender::add_object(scene,scene_elements)
  }
  if(return_scene) {
    return(scene)
  }
  
  if(!is.null(animation_camera_coords)) {
    stopifnot(ncol(animation_camera_coords) == 14)
    rayrender::render_animation(scene, camera_motion = animation_camera_coords, width = width, height = height,
                                min_variance = min_variance, samples = samples, sample_method = sample_method,
                                filename = filename, clamp_value = clamp_value, ...)
    return()
  }
  
  if(has_title) {
    temp = tempfile(fileext = ".png")
    debug_return = rayrender::render_scene(scene, lookfrom = lookfrom, lookat = camera_lookat, fov = fov, filename=temp,
                                           min_variance = min_variance, samples = samples, sample_method = sample_method,
                 ortho_dimensions = ortho_dimensions, width = width, height = height,  #camera_up = camera_up,
                 clamp_value = clamp_value, ...)
    if(has_title) {
      if(is.na(filename)) {
        rayimage::add_title(temp, title_text = title_text, title_color = title_color, 
                            title_font = title_font, title_offset = title_offset, 
                            title_bar_alpha =  title_bar_alpha, title_bar_color = title_bar_color,
                            title_size = title_size, preview = TRUE)
      } else {
        rayimage::add_title(temp, title_text = title_text, title_color = title_color, 
                            title_font = title_font, title_offset = title_offset, 
                            title_bar_alpha =  title_bar_alpha, title_bar_color = title_bar_color,
                            title_size = title_size, filename = filename)
      }
    }
  } else {
    if(!is.na(filename)) {
      debug_return = rayrender::render_scene(scene, lookfrom = lookfrom, lookat = camera_lookat, fov = fov, filename=filename,
                                             min_variance = min_variance, samples = samples, sample_method = sample_method,
                   ortho_dimensions = ortho_dimensions, width = width, height = height, #camera_up = camera_up,
                   clamp_value = clamp_value, ...)
    } else {
      debug_return = rayrender::render_scene(scene, lookfrom = lookfrom, lookat = camera_lookat, fov = fov, 
                                             min_variance = min_variance, samples = samples, sample_method = sample_method,
                                             ortho_dimensions = ortho_dimensions, width = width, height = height, #camera_up = camera_up,
                                             clamp_value = clamp_value, ...)
    }
  }
  if(clear) {
    rgl::clear3d()
  }
  return(invisible(debug_return))
}

Try the rayshader package in your browser

Any scripts or data that you put into this service are public.

rayshader documentation built on May 29, 2024, 3:03 a.m.