-
Notifications
You must be signed in to change notification settings - Fork 220
Expand file tree
/
Copy pathrender_snapshot.R
More file actions
282 lines (281 loc) · 11.5 KB
/
Copy pathrender_snapshot.R
File metadata and controls
282 lines (281 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#'@title Render Snapshot of 3D Visualization
#'
#'@description Either captures the current rgl view and displays, or saves the current view to disk.
#'
#'@param filename Filename of snapshot. If missing, will display to current device.
#'@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 `NA`. 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 title_just Default `left`. Justification of the title.
#'@param image_overlay Default `NULL`. Either a string indicating the location of a png image to overlay
#'over the image (transparency included), or a 4-layer RGBA array. This image will be resized to the
#'dimension of the image if it does not match exactly.
#'@param vignette Default `FALSE`. If `TRUE` or numeric, a camera vignetting effect will be added to the image.
#'`1` is the darkest vignetting, while `0` is no vignetting. If vignette is a length-2 vector, the second entry will
#'control the blurriness of the vignette effect.
#'@param vignette_color Default `"black"`. Color of the vignette.
#'@param vignette_radius Default `1.3`. Radius of the vignette, as a porportion of the image dimensions.
#'@param clear Default `FALSE`. If `TRUE`, the current `rgl` device will be cleared.
#'@param instant_capture Default `TRUE` if interactive, `FALSE` otherwise. If `FALSE`, a slight delay is added
#'before taking the snapshot. This can help stop prevent rendering issues when running scripts.
#'@param bring_to_front Default `FALSE`. Whether to bring the window to the front when taking the snapshot.
#'@param webshot Default `FALSE`. Set to `TRUE` to have rgl use the `webshot2` package to take images,
#'which can be used when `rgl.useNULL = TRUE`.
#'@param width Default `NULL`. Optional argument to pass to `rgl::snapshot3d()` to specify the
#'width when `software_render = TRUE`..
#'@param height Default `NULL`. Optional argument to pass to `rgl::snapshot3d()` to specify the
#'height when `software_render = TRUE`.
#'@param software_render Default `FALSE`. If `TRUE`, rayshader will use the rayvertex package to render the snapshot, which
#'is not constrained by the screen size or requires OpenGL.
#'@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 background Default `NULL`, defaults to device background. Background color when `software_render = TRUE`.
#'@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 `30`. Height of the text.
#'@param point_radius Default `0.5`. Radius of 3D points (rendered with [render_points()]). This scales the existing
#'value of size specified in [render_points()].
#'@param line_offset Default `1e-7`. Small number indicating the offset in the scene to apply to lines if using software rendering. Increase this if your lines
#'aren't showing up, or decrease it if lines are appearing through solid objects.
#'@param thick_lines Default `TRUE`. If `software_render = TRUE`, this will render path segments as thick cylinders. Otherwise, it will
#'render the lines using a single-pixel anti-aliased line algorithm.
#'@param line_radius Default `0.25`. The radius of the thick cylinders if `thick_lines = TRUE` and `software_render = TRUE`.
#'@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 text_offset Default `c(0,0,0)`. Offset to be applied to all text labels.
#'@param print_scene_info Default `FALSE`. If `TRUE`, it will print the position and lookat point of the camera.
#'@param new_page Default `TRUE`. Whether to call `grid::grid.newpage()` before plotting the image.
#'@param fsaa Default `1`. Integer specifying the amount of anti-aliasing applied `software_render = TRUE`.
#'@param rayvertex_lighting Default `FALSE`. If `TRUE` and `software_render = TRUE`, the scene will use rayvertex lighting when rendering
#'the scene, using the lights specified in `rayvertex_lights`. If no lights are specified there, they will be pulled
#'from `light` objects in the `rgl` scene.
#'@param rayvertex_lights Default `NULL`. Use `rayvertex::directional_light()` and `rayvertex::point_light()` along with the
#'`rayvertex::add_light()` function to specify lighting for your scene when `rayvertex_lighting = TRUE`.
#'@param rayvertex_shadow_map Default `FALSE`. If `TRUE` and `rayvertex_lighting = TRUE` along with `software_render = TRUE`, shadow mapping will also
#'be applied to the rendered scene.
#'@param plot Default `TRUE`. Whether to plot the snapshot.
#'@param ... Additional parameters to pass to `rayvertex::rasterize_scene()`.
#'@return Displays snapshot of current rgl plot (or saves to disk), as well as invisibly returns an RGBA `rayimg` array.
#'@export
#'@examples
#'if(run_documentation()) {
#'montereybay |>
#' sphere_shade() |>
#' plot_3d(montereybay,zscale=50,zoom=0.6,theta=-90,phi=30)
#'}
#'
#'if(run_documentation()) {
#'render_snapshot()
#'}
#'
#'#Create a title
#'if(run_documentation()) {
#'render_snapshot(title_text = "Monterey Bay, California", title_offset=c(0,20),
#' title_color = "white", title_bar_color = "black",
#' title_font = "Helvetica", title_position = "north")
#'
#'#Add a vignette effect
#'render_camera(zoom=0.8)
#'render_snapshot(title_text = "Monterey Bay, California",
#' title_color = "white", title_bar_color = "darkgreen",
#' vignette = TRUE, title_offset=c(0,20),
#' title_font = "Helvetica", title_position = "north")
#'}
#'#Use software rendering to render a scene with shadow mapping
#'if(run_documentation()) {
#'montereybay |>
#' height_shade() |>
#' plot_3d(montereybay, shadow=FALSE, solidlinecolor = NULL)
#'#No shadows
#'render_snapshot(software_render = TRUE)
#'}
#'if(run_documentation()) {
#'#Now with shadow mapped shadows, calculated in rayvertex
#'render_snapshot(rayvertex_lighting = TRUE,
#' rayvertex_lights = rayvertex::directional_light(intensity = 1.2,
#' direction = c(-1, 1, -1)),
#' rayvertex_shadow_map = TRUE, software_render = TRUE)
#'}
render_snapshot = function(
filename,
clear = FALSE,
title_text = NULL,
title_offset = c(20, 20),
title_color = "black",
title_size = 30,
title_font = "sans",
title_bar_color = NA,
title_bar_alpha = 0.5,
title_just = "left",
image_overlay = NULL,
vignette = FALSE,
vignette_color = "black",
vignette_radius = 1.3,
instant_capture = interactive(),
bring_to_front = FALSE,
webshot = FALSE,
width = NULL,
height = NULL,
software_render = FALSE,
camera_location = NULL,
camera_lookat = c(0, 0, 0),
background = NULL,
text_angle = NULL,
text_size = 30,
text_offset = c(0, 0, 0),
point_radius = 0.5,
line_offset = 1e-7,
thick_lines = TRUE,
line_radius = 0.25,
cache_scene = FALSE,
reset_scene_cache = FALSE,
new_page = TRUE,
print_scene_info = FALSE,
fsaa = 1,
rayvertex_lighting = FALSE,
rayvertex_lights = NULL,
rayvertex_shadow_map = FALSE,
plot = TRUE,
...
) {
if (rgl::rgl.useNULL()) {
software_render = TRUE
}
if (reset_scene_cache) {
assign("scene_cache", NULL, envir = ray_cache_scene_envir)
}
fsaa = as.integer(fsaa)
stopifnot(fsaa >= 1)
if (rgl::cur3d() == 0) {
stop("No rgl window currently open.")
}
if (!instant_capture) {
Sys.sleep(0.5)
}
if (!is.null(title_text)) {
has_title = TRUE
} else {
has_title = FALSE
}
if (length(title_offset) != 2) {
stop("`title_offset` needs to be length-2 vector")
}
if (!is.null(image_overlay)) {
if (inherits(image_overlay, "character")) {
image_overlay_file = image_overlay
has_overlay = TRUE
} else if (inherits(image_overlay, "array")) {
image_overlay_file = tempfile()
png::writePNG(image_overlay, image_overlay_file)
has_overlay = TRUE
}
} else {
has_overlay = FALSE
}
temp = tempfile(fileext = ".png")
if (!software_render) {
if ("webshot" %in% names(formals(rgl::snapshot3d))) {
if (!rgl::rgl.useNULL()) {
webshot = FALSE
}
if (webshot) {
rgl::snapshot3d(
filename = temp,
webshot = webshot,
width = width,
height = height
)
} else {
rgl::snapshot3d(
filename = temp,
top = bring_to_front,
webshot = webshot
)
}
} else {
rgl::snapshot3d(filename = temp, top = bring_to_front)
}
} else {
if (is.null(background)) {
bgid = rgl::ids3d("background")$id
background = rgl::rgl.attrib(bgid, "colors")
}
stopifnot(length(text_offset) == 3)
debug = render_snapshot_software(
filename = temp,
cache_scene = cache_scene,
camera_location = camera_location,
camera_lookat = camera_lookat,
background = background,
width = width,
height = height,
light_direction = NULL,
fake_shadow = TRUE,
text_angle = text_angle,
text_size = text_size,
text_offset = text_offset,
print_scene_info = print_scene_info,
point_radius = point_radius,
line_offset = -line_offset,
fsaa = fsaa,
thick_lines = thick_lines,
line_radius = line_radius,
rayvertex_lighting = rayvertex_lighting,
rayvertex_lights = rayvertex_lights,
rayvertex_shadow_map = rayvertex_shadow_map,
...
)
if (length(dim(debug)) == 2) {
return(flipud(debug))
}
}
tempmap = rayimage::ray_read_image(temp)
if (has_overlay) {
tempmap = rayimage::render_image_overlay(
tempmap,
image_overlay = image_overlay_file
)
}
if (vignette || is.numeric(vignette)) {
tempmap = rayimage::render_vignette(
tempmap,
vignette = vignette,
color = vignette_color,
radius = vignette_radius
)
}
if (has_title) {
tempmap = rayimage::render_title(
tempmap,
title_text = title_text,
title_bar_color = title_bar_color,
title_bar_alpha = title_bar_alpha,
title_offset = title_offset,
title_color = title_color,
title_just = title_just,
title_size = title_size,
title_font = title_font
)
}
if (missing(filename)) {
if (plot) {
rayimage::plot_image(tempmap, new_page = new_page)
}
} else {
rayimage::ray_write_image(tempmap, filename)
}
if (clear) {
rgl::clear3d()
}
return(invisible(tempmap))
}