Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1149 lines (1135 sloc)
63.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#' Diffuse Material | |
#' | |
#' @param color Default `white`. The color of the surface. Can be either | |
#' a hexadecimal code, R color string, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param checkercolor Default `NA`. If not `NA`, determines the secondary color of the checkered surface. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param checkerperiod Default `3`. The period of the checker pattern. Increasing this value makes the checker | |
#' pattern bigger, and decreasing it makes it smaller | |
#' @param noise Default `0`. If not `0`, covers the surface in a turbulent marble pattern. This value will determine | |
#' the amount of turbulence in the texture. | |
#' @param noisephase Default `0`. The phase of the noise. The noise will repeat at `360`. | |
#' @param noiseintensity Default `10`. Intensity of the noise. | |
#' @param noisecolor Default `#000000`. The secondary color of the noise pattern. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param gradient_color Default `NA`. If not `NA`, creates a secondary color for a linear gradient | |
#' between the this color and color specified in `color`. Direction is determined by `gradient_transpose`. | |
#' @param gradient_transpose Default `FALSE`. If `TRUE`, this will use the `v` coordinate texture instead | |
#' of the `u` coordinate texture to map the gradient. | |
#' @param gradient_point_start Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `color`. | |
#' @param gradient_point_end Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `gradient_color`. | |
#' @param gradient_type Default `hsv`. Colorspace to calculate the gradient. Alternative `rgb`. | |
#' @param image_texture Default `NA`. A 3-layer RGB array or filename to be used as the texture on the surface of the object. | |
#' @param image_repeat Default `1`. Number of times to repeat the image across the surface. | |
#' `u` and `v` repeat amount can be set independently if user passes in a length-2 vector. | |
#' @param alpha_texture Default `NA`. A matrix or filename (specifying a greyscale image) to | |
#' be used to specify the transparency. | |
#' @param bump_texture Default `NA`. A matrix, array, or filename (specifying a greyscale image) to | |
#' be used to specify a bump map for the surface. | |
#' @param bump_intensity Default `1`. Intensity of the bump map. High values may lead to unphysical results. | |
#' @param fog Default `FALSE`. If `TRUE`, the object will be a volumetric scatterer. | |
#' @param fogdensity Default `0.01`. The density of the fog. Higher values will produce more opaque objects. | |
#' @param sigma Default `NULL`. A number between 0 and Infinity specifying the roughness of the surface using the Oren-Nayar microfacet model. | |
#' Higher numbers indicate a roughed surface, where sigma is the standard deviation of the microfacet orientation angle. When 0, this reverts | |
#' to the default lambertian behavior. | |
#' @param importance_sample Default `FALSE`. If `TRUE`, the object will be sampled explicitly during | |
#' the rendering process. If the object is particularly important in contributing to the light paths | |
#' in the image (e.g. light sources, refracting glass ball with caustics, metal objects concentrating light), | |
#' this will help with the convergence of the image. | |
#' | |
#' @return Single row of a tibble describing the diffuse material. | |
#' @export | |
#' @importFrom grDevices col2rgb | |
#' | |
#' @examples | |
#' #Generate the cornell box and add a single white sphere to the center | |
#' scene = generate_cornell() %>% | |
#' add_object(sphere(x=555/2,y=555/2,z=555/2,radius=555/8,material=diffuse())) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' | |
#' #Add a checkered rectangular cube below | |
#' scene = scene %>% | |
#' add_object(cube(x=555/2,y=555/8,z=555/2,xwidth=555/2,ywidth=555/4,zwidth=555/2, | |
#' material = diffuse(checkercolor="purple",checkerperiod=20))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' | |
#' #Add a marbled sphere | |
#' scene = scene %>% | |
#' add_object(sphere(x=555/2+555/4,y=555/2,z=555/2,radius=555/8, | |
#' material = diffuse(noise=1/20))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' | |
#' #Add an orange volumetric (fog) cube | |
#' scene = scene %>% | |
#' add_object(cube(x=555/2-555/4,y=555/2,z=555/2,xwidth=555/4,ywidth=555/4,zwidth=555/4, | |
#' material = diffuse(fog=TRUE, fogdensity=0.05,color="orange"))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' | |
#' #' #Add an line segment with a color gradient | |
#' scene = scene %>% | |
#' add_object(segment(start = c(555,450,450),end=c(0,450,450),radius = 50, | |
#' material = diffuse(color="#1f7326", gradient_color = "#a60d0d"))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
diffuse = function(color = "#ffffff", | |
checkercolor = NA, checkerperiod = 3, | |
noise = 0, noisephase = 0, noiseintensity = 10, noisecolor = "#000000", | |
gradient_color = NA, gradient_transpose = FALSE, | |
gradient_point_start = NA, gradient_point_end = NA, gradient_type = "hsv", | |
image_texture = NA, image_repeat = 1, alpha_texture = NA, | |
bump_texture = NA, bump_intensity = 1, | |
fog = FALSE, fogdensity = 0.01, | |
sigma = NULL, importance_sample = FALSE) { | |
if(all(!is.na(checkercolor))) { | |
checkercolor = convert_color(checkercolor) | |
} else { | |
checkercolor = NA | |
} | |
if(all(!is.na(gradient_color))) { | |
gradient_color = convert_color(gradient_color) | |
} else { | |
gradient_color = NA | |
} | |
if(!is.na(gradient_point_start) && !is.na(gradient_point_end) && !is.na(gradient_color)) { | |
stopifnot(length(gradient_point_start) == 3) | |
stopifnot(length(gradient_point_end) == 3) | |
stopifnot(is.numeric(gradient_point_start)) | |
stopifnot(is.numeric(gradient_point_end)) | |
gradient_point_info = c(gradient_point_start,gradient_point_end) | |
is_world_gradient = TRUE | |
} else { | |
is_world_gradient = FALSE | |
gradient_point_info = NA | |
} | |
info = convert_color(color) | |
noisecolor = convert_color(noisecolor) | |
if(!is.array(image_texture) && !is.na(image_texture) && !is.character(image_texture)) { | |
image_texture = NA | |
warning("Texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(alpha_texture) && !is.na(alpha_texture) && !is.character(alpha_texture)) { | |
alpha_texture = NA | |
warning("Alpha texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(bump_texture) && !is.na(bump_texture) && !is.character(bump_texture)) { | |
bump_texture = NA | |
warning("Bump texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
type = "diffuse" | |
if(!is.null(sigma) && is.numeric(sigma)) { | |
if(sigma < 0) { | |
warning("sigma must be greater than 0 (input: ", sigma, ")--ignoring and using lambertian model") | |
} else { | |
if(sigma == 0) { | |
type = "diffuse" | |
} else { | |
type = "oren-nayar" | |
sigma = sigma*pi/180 | |
} | |
} | |
} else { | |
sigma = 0 | |
} | |
if(length(image_repeat) == 1) { | |
image_repeat = c(image_repeat,image_repeat) | |
} | |
stopifnot(checkerperiod != 0) | |
new_tibble_row(list(type = type, | |
properties = list(info), checkercolor=list(c(checkercolor,checkerperiod)), | |
gradient_color = list(gradient_color), gradient_transpose = gradient_transpose, | |
world_gradient = is_world_gradient,gradient_point_info = list(gradient_point_info), | |
gradient_type = gradient_type, | |
noise=noise, noisephase = noisephase, noiseintensity = noiseintensity, noisecolor = list(noisecolor), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(alpha_texture), lightintensity = NA, | |
fog=fog, fogdensity=fogdensity,implicit_sample = importance_sample, | |
sigma = sigma, glossyinfo = list(NA), bump_texture = list(bump_texture), | |
bump_intensity = bump_intensity, rough_texture = list(NA))) | |
} | |
#' Metallic Material | |
#' | |
#' @param color Default `white`. The color of the sphere. Can be either | |
#' a hexadecimal code, R color string, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param eta Default `0`. Wavelength dependent refractivity of the material (red, green, and blue channels). | |
#' If single number, will be repeated across all three channels. | |
#' @param kappa Default `0`. Wavelength dependent absorption of the material (red, green, and blue channels). | |
#' If single number, will be repeated across all three channels. | |
#' @param fuzz Default `0`. Deprecated--Use the microfacet material instead, as it is designed for rough metals. | |
#' The roughness of the metallic surface. Maximum `1`. | |
#' @param checkercolor Default `NA`. If not `NA`, determines the secondary color of the checkered surface. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param checkerperiod Default `3`. The period of the checker pattern. Increasing this value makes the checker | |
#' pattern bigger, and decreasing it makes it smaller | |
#' @param noise Default `0`. If not `0`, covers the surface in a turbulent marble pattern. This value will determine | |
#' the amount of turbulence in the texture. | |
#' @param noisephase Default `0`. The phase of the noise. The noise will repeat at `360`. | |
#' @param noiseintensity Default `10`. Intensity of the noise. | |
#' @param noisecolor Default `#000000`. The secondary color of the noise pattern. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param gradient_color Default `NA`. If not `NA`, creates a secondary color for a linear gradient | |
#' between the this color and color specified in `color`. Direction is determined by `gradient_transpose`. | |
#' @param gradient_transpose Default `FALSE`. If `TRUE`, this will use the `v` coordinate texture instead | |
#' of the `u` coordinate texture to map the gradient. | |
#' @param gradient_point_start Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `color`. | |
#' @param gradient_point_end Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `gradient_color`. | |
#' @param gradient_type Default `hsv`. Colorspace to calculate the gradient. Alternative `rgb`. | |
#' @param image_texture Default `NA`. A 3-layer RGB array or filename to be used as the texture on the surface of the object. | |
#' @param image_repeat Default `1`. Number of times to repeat the image across the surface. | |
#' `u` and `v` repeat amount can be set independently if user passes in a length-2 vector. | |
#' @param alpha_texture Default `NA`. A matrix or filename (specifying a greyscale image) to be used to specify the transparency. | |
#' @param bump_texture Default `NA`. A matrix, array, or filename (specifying a greyscale image) to | |
#' be used to specify a bump map for the surface. | |
#' @param bump_intensity Default `1`. Intensity of the bump map. High values may lead to unphysical results. | |
#' @param importance_sample Default `FALSE`. If `TRUE`, the object will be sampled explicitly during | |
#' the rendering process. If the object is particularly important in contributing to the light paths | |
#' in the image (e.g. light sources, refracting glass ball with caustics, metal objects concentrating light), | |
#' this will help with the convergence of the image. | |
#' @importFrom grDevices col2rgb | |
#' | |
#' @return Single row of a tibble describing the metallic material. | |
#' @export | |
#' | |
#' @examples | |
#' # Generate the cornell box with a single chrome sphere in the center. For other metals, | |
#' # See the website refractiveindex.info for eta and k data, use wavelengths 5 | |
#' # 80nm (R), 530nm (G), and 430nm (B). | |
#' scene = generate_cornell() %>% | |
#' add_object(sphere(x=555/2,y=555/2,z=555/2,radius=555/8, | |
#' material=metal(eta=c(3.2176,3.1029,2.1839), k = c(3.3018,3.33,3.0339)))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=50, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' #Add an aluminum rotated shiny metal block | |
#' scene = scene %>% | |
#' add_object(cube(x=380,y=150/2,z=200,xwidth=150,ywidth=150,zwidth=150, | |
#' material = metal(eta = c(1.07,0.8946,0.523), k = c(6.7144,6.188,4.95)),angle=c(0,45,0))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' #Add a copper metal cube | |
#' scene = scene %>% | |
#' add_object(cube(x=150,y=150/2,z=300,xwidth=150,ywidth=150,zwidth=150, | |
#' material = metal(eta = c(0.497,0.8231,1.338), | |
#' k = c(2.898,2.476,2.298)), | |
#' angle=c(0,-30,0))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' | |
#' #Finally, let's add a lead pipe | |
#' scene2 = scene %>% | |
#' add_object(cylinder(x=450,y=200,z=400,length=400,radius=30, | |
#' material = metal(eta = c(1.44,1.78,1.9), | |
#' k = c(3.18,3.36,3.43)), | |
#' angle=c(0,-30,0))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene2, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
metal = function(color = "#ffffff", | |
eta = 0, kappa = 0, fuzz = 0, | |
checkercolor = NA, checkerperiod = 3, | |
noise = 0, noisephase = 0, noiseintensity = 10, noisecolor = "#000000", | |
gradient_color = NA, gradient_transpose = FALSE, | |
gradient_point_start = NA, gradient_point_end = NA, gradient_type = "hsv", | |
image_texture = NA, image_repeat = 1, alpha_texture = NA, | |
bump_texture = NA, bump_intensity = 1, | |
importance_sample = FALSE) { | |
color = convert_color(color) | |
if(all(!is.na(checkercolor))) { | |
checkercolor = convert_color(checkercolor) | |
} else { | |
checkercolor = NA | |
} | |
if(all(!is.na(gradient_color))) { | |
gradient_color = convert_color(gradient_color) | |
} else { | |
gradient_color = NA | |
} | |
if(!is.na(gradient_point_start) && !is.na(gradient_point_end) && !is.na(gradient_color)) { | |
stopifnot(length(gradient_point_start) == 3) | |
stopifnot(length(gradient_point_end) == 3) | |
stopifnot(is.numeric(gradient_point_start)) | |
stopifnot(is.numeric(gradient_point_end)) | |
gradient_point_info = c(gradient_point_start,gradient_point_end) | |
is_world_gradient = TRUE | |
} else { | |
is_world_gradient = FALSE | |
gradient_point_info = NA | |
} | |
noisecolor = convert_color(noisecolor) | |
if(!is.array(image_texture) && !is.na(image_texture) && !is.character(image_texture)) { | |
image_texture = NA | |
warning("Texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(alpha_texture) && !is.na(alpha_texture) && !is.character(alpha_texture)) { | |
alpha_texture = NA | |
warning("Alpha texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(bump_texture) && !is.na(bump_texture) && !is.character(bump_texture)) { | |
bump_texture = NA | |
warning("Bump texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(length(eta) == 1) { | |
eta = c(eta,eta,eta) | |
} | |
if(length(kappa) == 1) { | |
kappa = c(kappa,kappa,kappa) | |
} | |
if(length(eta) > 3 || length(eta) == 2) { | |
stop("eta must be either single number or 3-component vector") | |
} | |
if(length(kappa) > 3 || length(kappa) == 2) { | |
stop("kappa must be either single number or 3-component vector") | |
} | |
if(length(image_repeat) == 1) { | |
image_repeat = c(image_repeat,image_repeat) | |
} | |
glossyinfo = list(c(1, 0, 0, eta, kappa)); | |
new_tibble_row(list(type = "metal", | |
properties = list(c(color,fuzz)), | |
checkercolor=list(c(checkercolor,checkerperiod)), | |
gradient_color = list(gradient_color), gradient_transpose = gradient_transpose, | |
world_gradient = is_world_gradient,gradient_point_info = list(gradient_point_info), | |
gradient_type = gradient_type, | |
noise=noise, noisephase = noisephase, | |
noiseintensity = noiseintensity, noisecolor = list(noisecolor), | |
lightinfo = list(NA), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(alpha_texture), | |
lightintensity = NA,fog=FALSE,fogdensity=0.01, | |
implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = glossyinfo, bump_texture = list(bump_texture), | |
bump_intensity = bump_intensity, rough_texture = list(NA))) | |
} | |
#' Dielectric (glass) Material | |
#' | |
#' | |
#' @param color Default `white`. The color of the surface. Can be either | |
#' a hexadecimal code, R color string, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param refraction Default `1.5`. The index of refraction. | |
#' @param attenuation Default `c(0,0,0)`. The Beer-Lambert color-channel specific exponential attenuation | |
#' through the material. Higher numbers will result in less of that color making it through the material. | |
#' Note: This assumes the object has a closed surface. | |
#' @param priority Default `0`. When two dielectric materials overlap, the one with the lower priority value | |
#' is used for intersection. NOTE: If the camera is placed inside a dielectric object, its priority value | |
#' will not be taken into account when determining hits to other objects also inside the object. | |
#' @param importance_sample Default `FALSE`. If `TRUE`, the object will be sampled explicitly during | |
#' the rendering process. If the object is particularly important in contributing to the light paths | |
#' in the image (e.g. light sources, refracting glass ball with caustics, metal objects concentrating light), | |
#' this will help with the convergence of the image. | |
#' @param bump_texture Default `NA`. A matrix, array, or filename (specifying a greyscale image) to | |
#' be used to specify a bump map for the surface. | |
#' @param bump_intensity Default `1`. Intensity of the bump map. High values may lead to unphysical results. | |
#' | |
#' @return Single row of a tibble describing the dielectric material. | |
#' @export | |
#' | |
#' @examples | |
#' #Generate a checkered ground | |
#' scene = generate_ground(depth=-0.5, material = diffuse(checkercolor="grey30",checkerperiod=2)) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene,parallel=TRUE) | |
#' } | |
#' | |
#' #Add a glass sphere | |
#' if(rayrender:::run_documentation()) { | |
#' scene %>% | |
#' add_object(sphere(x=-0.5,radius=0.5,material=dielectric())) %>% | |
#' render_scene(parallel=TRUE,samples=128) | |
#' } | |
#' | |
#' #Add a rotated colored glass cube | |
#' if(rayrender:::run_documentation()) { | |
#' scene %>% | |
#' add_object(sphere(x=-0.5,radius=0.5,material=dielectric())) %>% | |
#' add_object(cube(x=0.5,xwidth=0.5,material=dielectric(color="darkgreen"),angle=c(0,-45,0))) %>% | |
#' render_scene(parallel=TRUE,samples=128) | |
#' } | |
#' | |
#' #Add an area light behind and at an angle and turn off the ambient lighting | |
#' if(rayrender:::run_documentation()) { | |
#' scene %>% | |
#' add_object(sphere(x=-0.5,radius=0.5,material=dielectric())) %>% | |
#' add_object(cube(x=0.5,xwidth=0.5,material=dielectric(color="darkgreen"),angle=c(0,-45,0))) %>% | |
#' add_object(yz_rect(z=-3,y=1,x=0,zwidth=3,ywidth=1.5, | |
#' material=light(intensity=15), | |
#' angle=c(0,-90,45), order_rotation = c(3,2,1))) %>% | |
#' render_scene(parallel=TRUE,aperture=0, ambient_light=FALSE,samples=1000) | |
#' } | |
#' | |
#' #Color glass using Beer-Lambert attenuation, which attenuates light on a per-channel | |
#' #basis as it travels through the material. This effect is what gives some types of glass | |
#' #a green glow at the edges. We will get this effect by setting a lower attenuation value | |
#' #for the `green` (second) channel in the dielectric `attenuation` argument. | |
#' if(rayrender:::run_documentation()) { | |
#' generate_ground(depth=-0.5,material=diffuse(checkercolor="grey30",checkerperiod=2)) %>% | |
#' add_object(sphere(z=-5,x=-0.5,y=1,material=light(intensity=10))) %>% | |
#' add_object(cube(y=0.3,ywidth=0.1,xwidth=2,zwidth=2, | |
#' material=dielectric(attenuation=c(1.2,0.2,1.2)),angle=c(45,110,0))) %>% | |
#' render_scene(parallel=TRUE, samples = 1000) | |
#' } | |
#' | |
#' #If you have overlapping dielectrics, the `priority` value can help disambiguate what | |
#' #object wins. Here, I place a bubble inside a cube by setting a lower priority value and | |
#' #making the inner sphere have a index of refraction of 1. I also place spheres at the corners. | |
#' if(rayrender:::run_documentation()) { | |
#' generate_ground(depth=-0.51,material=diffuse(checkercolor="grey30",checkerperiod=2)) %>% | |
#' add_object(cube(material = dielectric(priority=2, attenuation = c(10,3,10)))) %>% | |
#' add_object(sphere(radius=0.49,material = dielectric(priority=1, refraction=1))) %>% | |
#' add_object(sphere(radius=0.25,x=0.5,z=-0.5,y=0.5, | |
#' material = dielectric(priority=0,attenuation = c(10,3,10) ))) %>% | |
#' add_object(sphere(radius=0.25,x=-0.5,z=0.5,y=0.5, | |
#' material = dielectric(priority=0,attenuation = c(10,3,10)))) %>% | |
#' render_scene(parallel=TRUE, samples = 128,lookfrom=c(5,1,5)) | |
#' } | |
#' | |
#' # We can also use this as a basic Constructive Solid Geometry interface by setting | |
#' # the index of refraction equal to empty space, 1. This will subtract out those regions. | |
#' # Here I make a concave lens by subtracting two spheres from a cube. | |
#' if(rayrender:::run_documentation()) { | |
#' generate_ground(depth=-0.51,material=diffuse(checkercolor="grey30",checkerperiod=2,sigma=90)) %>% | |
#' add_object(cube(material = dielectric(attenuation = c(3,3,1),priority=1))) %>% | |
#' add_object(sphere(radius=1,x=1.01, | |
#' material = dielectric(priority=0,refraction=1))) %>% | |
#' add_object(sphere(radius=1,x=-1.01, | |
#' material = dielectric(priority=0,refraction=1))) %>% | |
#' add_object(sphere(y=10,x=3,material=light(intensit=150))) %>% | |
#' render_scene(parallel=TRUE, samples = 128,lookfrom=c(5,3,5)) | |
#' } | |
dielectric = function(color="white", refraction = 1.5, attenuation = c(0,0,0), | |
priority = 0, importance_sample = FALSE, | |
bump_texture = NA, bump_intensity = 1) { | |
color = convert_color(color) | |
if(!is.array(bump_texture) && !is.na(bump_texture) && !is.character(bump_texture)) { | |
bump_texture = NA | |
warning("Bump texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
new_tibble_row(list(type = "dielectric", | |
properties = list(c(color, refraction, attenuation, priority)), | |
checkercolor=list(NA), | |
gradient_color = list(NA), gradient_transpose = FALSE, | |
world_gradient = FALSE,gradient_point_info = list(NA), | |
gradient_type = NA, | |
noise=0, noisephase = 0, noiseintensity = 0, noisecolor = list(c(0,0,0)), | |
image = list(NA), image_repeat = list(c(1,1)), | |
alphaimage = list(NA), lightintensity = NA, | |
fog=FALSE, fogdensity=NA, implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = list(NA), bump_texture = list(bump_texture), | |
bump_intensity = bump_intensity, rough_texture = list(NA))) | |
} | |
#' Microfacet Material | |
#' | |
#' @param color Default `white`. The color of the surface. Can be either | |
#' a hexadecimal code, R color string, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param roughness Default `0.0001`. Roughness of the surface, between `0` (smooth) and `1` (diffuse). | |
#' Can be either a single number, or two numbers indicating an anisotropic distribution of normals. `0` is a smooth surface, while | |
#' `1` is extremely rough. This can be used to create a wide-variety of materials (e.g. `0-0.01` is specular | |
#' metal, `0.02`-`0.1` is brushed metal, `0.1`-`0.3` is a rough metallic surface , `0.3`-`0.5` is diffuse, and | |
#' above that is a rough satin-like material). | |
#' Two numbers will specify the x and y roughness separately (e.g. `roughness = c(0.01, 0.001)` gives an | |
#' etched metal effect). If `0`, this defaults to the `metal()` material for faster evaluation. | |
#' @param transmission Default `FALSE`. If `TRUE`, this material will be a rough dielectric instead of a rough metallic surface. | |
#' @param eta Default `0`. Wavelength dependent refractivity of the material (red, green, and blue channels). | |
#' If single number, will be repeated across all three channels. If `transmission = TRUE`, this is a single value representing the | |
#' index of refraction of the material. | |
#' @param kappa Default `0`. Wavelength dependent absorption of the material (red, green, and blue channels). | |
#' If single number, will be repeated across all three channels. If `transmission = TRUE`, this length-3 vector specifies | |
#' the attenuation of the dielectric (analogous to the dielectric `attenuation` argument). | |
#' @param microfacet Default `tbr`. Type of microfacet distribution. Alternative option `beckmann`. | |
#' @param checkercolor Default `NA`. If not `NA`, determines the secondary color of the checkered surface. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param checkerperiod Default `3`. The period of the checker pattern. Increasing this value makes the checker | |
#' pattern bigger, and decreasing it makes it smaller | |
#' @param noise Default `0`. If not `0`, covers the surface in a turbulent marble pattern. This value will determine | |
#' the amount of turbulence in the texture. | |
#' @param noisephase Default `0`. The phase of the noise. The noise will repeat at `360`. | |
#' @param noiseintensity Default `10`. Intensity of the noise. | |
#' @param noisecolor Default `#000000`. The secondary color of the noise pattern. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param gradient_color Default `NA`. If not `NA`, creates a secondary color for a linear gradient | |
#' between the this color and color specified in `color`. Direction is determined by `gradient_transpose`. | |
#' @param gradient_transpose Default `FALSE`. If `TRUE`, this will use the `v` coordinate texture instead | |
#' of the `u` coordinate texture to map the gradient. | |
#' @param gradient_point_start Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `color`. | |
#' @param gradient_point_end Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `gradient_color`. | |
#' @param gradient_type Default `hsv`. Colorspace to calculate the gradient. Alternative `rgb`. | |
#' @param image_texture Default `NA`. A 3-layer RGB array or filename to be used as the texture on the surface of the object. | |
#' @param image_repeat Default `1`. Number of times to repeat the image across the surface. | |
#' `u` and `v` repeat amount can be set independently if user passes in a length-2 vector. | |
#' @param alpha_texture Default `NA`. A matrix or filename (specifying a greyscale image) to be used to specify the transparency. | |
#' @param bump_texture Default `NA`. A matrix, array, or filename (specifying a greyscale image) to | |
#' be used to specify a bump map for the surface. | |
#' @param bump_intensity Default `1`. Intensity of the bump map. High values may lead to unphysical results. | |
#' @param roughness_texture Default `NA`. A matrix, array, or filename (specifying a greyscale image) to | |
#' be used to specify a roughness map for the surface. | |
#' @param roughness_range Default ` c(0.0001, 0.2)`. This is a length-2 vector that specifies the range of roughness values | |
#' that the `roughness_texture` can take. | |
#' @param roughness_flip Default `FALSE`. Setting this to `TRUE` flips the roughness values specified in the `roughness_texture` | |
#' so high values are now low values and vice versa. | |
#' @param importance_sample Default `FALSE`. If `TRUE`, the object will be sampled explicitly during | |
#' the rendering process. If the object is particularly important in contributing to the light paths | |
#' in the image (e.g. light sources, refracting glass ball with caustics, metal objects concentrating light), | |
#' this will help with the convergence of the image. | |
#' | |
#' @return Single row of a tibble describing the microfacet material. | |
#' @export | |
#' | |
#' @examples | |
#' # Generate a golden egg, using eta and kappa taken from physical measurements | |
#' # See the website refractiveindex.info for eta and k data, use | |
#' # wavelengths 580nm (R), 530nm (G), and 430nm (B). | |
#' if(rayrender:::run_documentation()) { | |
#' generate_cornell() %>% | |
#' add_object(ellipsoid(x=555/2,555/2,y=150, a=100,b=150,c=100, | |
#' material=microfacet(roughness=0.1, | |
#' eta=c(0.216,0.42833,1.3184), kappa=c(3.239,2.4599,1.8661)))) %>% | |
#' render_scene(lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, parallel=TRUE,clamp_value=10) | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' #Make the roughness anisotropic (either horizontal or vertical), adding an extra light in front | |
#' #to show off the different microfacet orientations | |
#' generate_cornell() %>% | |
#' add_object(sphere(x=555/2,z=50,y=75,radius=20,material=light())) %>% | |
#' add_object(ellipsoid(x=555-150,555/2,y=150, a=100,b=150,c=100, | |
#' material=microfacet(roughness=c(0.3,0.1), | |
#' eta=c(0.216,0.42833,1.3184), kappa=c(3.239,2.4599,1.8661)))) %>% | |
#' add_object(ellipsoid(x=150,555/2,y=150, a=100,b=150,c=100, | |
#' material=microfacet(roughness=c(0.1,0.3), | |
#' eta=c(0.216,0.42833,1.3184), kappa=c(3.239,2.4599,1.8661)))) %>% | |
#' render_scene(lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, parallel=TRUE,clamp_value=10) | |
#'} | |
#' if(rayrender:::run_documentation()) { | |
#' #Render a rough silver R with a smaller golden egg in front | |
#' generate_cornell() %>% | |
#' add_object(obj_model(r_obj(),x=555/2,z=350,y=0, scale_obj = 200, angle=c(0,200,0), | |
#' material=microfacet(roughness=0.2, | |
#' eta=c(1.1583,0.9302,0.5996), kappa=c(6.9650,6.396,5.332)))) %>% | |
#' add_object(ellipsoid(x=200,z=200,y=80, a=50,b=80,c=50, | |
#' material=microfacet(roughness=0.1, | |
#' eta=c(0.216,0.42833,1.3184), kappa=c(3.239,2.4599,1.8661)))) %>% | |
#' render_scene(lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, parallel=TRUE,clamp_value=10) | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' #Increase the roughness | |
#' generate_cornell() %>% | |
#' add_object(obj_model(r_obj(),x=555/2,z=350,y=0, scale_obj = 200, angle=c(0,200,0), | |
#' material=microfacet(roughness=0.5, | |
#' eta=c(1.1583,0.9302,0.5996), kappa=c(6.9650,6.396,5.332)))) %>% | |
#' add_object(ellipsoid(x=200,z=200,y=80, a=50,b=80,c=50, | |
#' material=microfacet(roughness=0.3, | |
#' eta=c(0.216,0.42833,1.3184), kappa=c(3.239,2.4599,1.8661)))) %>% | |
#' render_scene(lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, parallel=TRUE,clamp_value=10) | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' #Use transmission for a rough dielectric | |
#' generate_cornell() %>% | |
#' add_object(obj_model(r_obj(),x=555/2,z=350,y=0, scale_obj = 200, angle=c(0,200,0), | |
#' material=microfacet(roughness=0.3, transmission=T, eta=1.6))) %>% | |
#' add_object(ellipsoid(x=200,z=200,y=80, a=50,b=80,c=50, | |
#' material=microfacet(roughness=0.3, transmission=T, eta=1.6))) %>% | |
#' render_scene(lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, parallel=TRUE,clamp_value=10, min_variance=1e-6) | |
#' } | |
microfacet = function(color="white", roughness = 0.0001, transmission = FALSE, | |
eta = 0, kappa = 0, microfacet = "tbr", | |
checkercolor = NA, checkerperiod = 3, | |
noise = 0, noisephase = 0, noiseintensity = 10, noisecolor = "#000000", | |
gradient_color = NA, gradient_transpose = FALSE, | |
gradient_point_start = NA, gradient_point_end = NA, gradient_type = "hsv", | |
image_texture = NA, image_repeat = 1, alpha_texture = NA, | |
bump_texture = NA, bump_intensity = 1, roughness_texture = NA, | |
roughness_range = c(0.0001, 0.2), roughness_flip = FALSE, | |
importance_sample = FALSE) { | |
microtype = switch(microfacet, "tbr" = 1,"beckmann" = 2, 1) | |
roughness[roughness <= 0] = 0 | |
roughness[roughness > 1] = 1 | |
if(length(roughness) == 1) { | |
alphax = roughness^2 | |
alphay = roughness^2 | |
} else { | |
alphax = roughness[1]^2 | |
alphay = roughness[2]^2 | |
} | |
if(length(roughness_range) != 2) { | |
stop("length of roughness_range must be 2") | |
} | |
if(roughness_range[1] > roughness_range[2]) { | |
roughness_range = rev(roughness_range) | |
} | |
if(roughness_range[1] == roughness_range[2] && !is.na(roughness_texture)) { | |
roughness_texture = NA | |
roughness = roughness_range[1] | |
} | |
if(length(eta) == 1) { | |
eta = c(eta,eta,eta) | |
} | |
if(length(kappa) == 1) { | |
kappa = c(kappa,kappa,kappa) | |
} | |
if(length(eta) > 3 || length(eta) == 2) { | |
stop("eta must be either single number or 3-component vector") | |
} | |
if(length(kappa) > 3 || length(kappa) == 2) { | |
stop("kappa must be either single number or 3-component vector") | |
} | |
color = convert_color(color) | |
if(all(!is.na(checkercolor))) { | |
checkercolor = convert_color(checkercolor) | |
} else { | |
checkercolor = NA | |
} | |
if(all(!is.na(gradient_color))) { | |
gradient_color = convert_color(gradient_color) | |
} else { | |
gradient_color = NA | |
} | |
if(!is.na(gradient_point_start) && !is.na(gradient_point_end) && !is.na(gradient_color)) { | |
stopifnot(length(gradient_point_start) == 3) | |
stopifnot(length(gradient_point_end) == 3) | |
stopifnot(is.numeric(gradient_point_start)) | |
stopifnot(is.numeric(gradient_point_end)) | |
gradient_point_info = c(gradient_point_start,gradient_point_end) | |
is_world_gradient = TRUE | |
} else { | |
is_world_gradient = FALSE | |
gradient_point_info = NA | |
} | |
noisecolor = convert_color(noisecolor) | |
if(!is.array(image_texture) && !is.na(image_texture) && !is.character(image_texture)) { | |
image_texture = NA | |
warning("Texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(alpha_texture) && !is.na(alpha_texture) && !is.character(alpha_texture)) { | |
alpha_texture = NA | |
warning("Alpha texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(bump_texture) && !is.na(bump_texture) && !is.character(bump_texture)) { | |
bump_texture = NA | |
warning("Bump texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(roughness_texture) && !is.na(roughness_texture) && !is.character(roughness_texture)) { | |
roughness_texture = NA | |
warning("Roughness texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(length(image_repeat) == 1) { | |
image_repeat = c(image_repeat,image_repeat) | |
} | |
roughness_flip = ifelse(roughness_flip,1,0) | |
glossyinfo = list(c(microtype, alphax, alphay, eta, kappa, roughness_range,roughness_flip)); | |
if(alphax == 0 && alphay == 0 ) { | |
if(!transmission) { | |
new_tibble_row(list(type = "metal", | |
properties = list(c(color, 0)), | |
gradient_color = list(gradient_color), gradient_transpose = FALSE, | |
world_gradient = is_world_gradient, gradient_point_info = list(gradient_point_info), | |
gradient_type = gradient_type, | |
checkercolor=list(c(checkercolor,checkerperiod)), | |
noise=noise, noisephase = noisephase, noiseintensity = noiseintensity, noisecolor = list(noisecolor), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(alpha_texture), lightintensity = NA, | |
fog=FALSE, fogdensity=NA, implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = glossyinfo, bump_texture = list(bump_texture), | |
bump_intensity = bump_intensity, rough_texture = list(NA))) | |
} else { | |
new_tibble_row(list(type = "dielectric", | |
properties = list(c(color, eta[1], c(0,0,0), 0)), | |
gradient_color = list(gradient_color), gradient_transpose = FALSE, | |
world_gradient = is_world_gradient, gradient_point_info = list(gradient_point_info), | |
gradient_type = gradient_type, | |
checkercolor=list(c(checkercolor,checkerperiod)), | |
noise=noise, noisephase = noisephase, noiseintensity = noiseintensity, noisecolor = list(noisecolor), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(alpha_texture), lightintensity = NA, | |
fog=FALSE, fogdensity=NA, implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = glossyinfo, bump_texture = list(bump_texture), | |
bump_intensity = bump_intensity, rough_texture = list(NA))) | |
} | |
} else { | |
if(transmission) { | |
typeval = "microfacet_transmission" | |
} else { | |
typeval = "microfacet" | |
} | |
new_tibble_row(list(type = typeval, | |
properties = list(c(color)), | |
gradient_color = list(gradient_color), gradient_transpose = FALSE, | |
world_gradient = is_world_gradient,gradient_point_info = list(gradient_point_info), | |
gradient_type = gradient_type, | |
checkercolor=list(c(checkercolor,checkerperiod)), | |
noise=noise, noisephase = noisephase, noiseintensity = noiseintensity, noisecolor = list(noisecolor), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(alpha_texture), lightintensity = NA, | |
fog=FALSE, fogdensity=NA, implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = glossyinfo, bump_texture = list(bump_texture), | |
bump_intensity = bump_intensity, rough_texture = list(roughness_texture))) | |
} | |
} | |
#' Light Material | |
#' | |
#' @param color Default `white`. The color of the light Can be either | |
#' a hexadecimal code, R color string, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param intensity Default `10`. If a positive value, this will turn this object into a light emitting the value specified | |
#' in `color` (ignoring other properties). Higher values will produce a brighter light. | |
#' @param importance_sample Default `TRUE`. Keeping this on for lights improves the convergence of the rendering | |
#' algorithm, in most cases. If the object is particularly important in contributing to the light paths | |
#' in the image (e.g. light sources, refracting glass ball with caustics, metal objects concentrating light), | |
#' this will help with the convergence of the image. | |
#' @param spotlight_focus Default `NA`, no spotlight. Otherwise, a length-3 numeric vector specifying | |
#' the x/y/z coordinates that the spotlight should be focused on. Only works for spheres and rectangles. | |
#' @param spotlight_width Default `30`. Angular width of the spotlight. | |
#' @param spotlight_start_falloff Default `15`. Angle at which the light starts fading in intensity. | |
#' @param invisible Default `FALSE`. If `TRUE`, the light itself will be invisible. | |
#' @param image_texture Default `NA`. A 3-layer RGB array or filename to be used as the texture on the surface of the object. | |
#' @param image_repeat Default `1`. Number of times to repeat the image across the surface. | |
#' `u` and `v` repeat amount can be set independently if user passes in a length-2 vector. | |
#' @param gradient_color Default `NA`. If not `NA`, creates a secondary color for a linear gradient | |
#' between the this color and color specified in `color`. Direction is determined by `gradient_transpose`. | |
#' @param gradient_transpose Default `FALSE`. If `TRUE`, this will use the `v` coordinate texture instead | |
#' of the `u` coordinate texture to map the gradient. | |
#' @param gradient_point_start Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `color`. | |
#' @param gradient_point_end Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `gradient_color`. | |
#' @param gradient_type Default `hsv`. Colorspace to calculate the gradient. Alternative `rgb`. | |
#' | |
#' @return Single row of a tibble describing the light material. | |
#' @export | |
#' @importFrom grDevices col2rgb | |
#' | |
#' @examples | |
#' #Generate the cornell box without a light and add a single white sphere to the center | |
#' scene = generate_cornell(light=FALSE) %>% | |
#' add_object(sphere(x=555/2,y=555/2,z=555/2,radius=555/8,material=light())) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' | |
#' #Remove the light for direct camera rays, but keep the lighting | |
#' scene = generate_cornell(light=FALSE) %>% | |
#' add_object(sphere(x=555/2,y=555/2,z=555/2,radius=555/8, | |
#' material=light(intensity=15,invisible=TRUE))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=128, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
#' | |
#' #All gather around the orb | |
#' scene = generate_ground(material = diffuse(checkercolor="grey50")) %>% | |
#' add_object(sphere(radius=0.5,material=light(intensity=5,color="red"))) %>% | |
#' add_object(obj_model(r_obj(), z=-3,x=-1.5,y=-1, angle=c(0,45,0))) %>% | |
#' add_object(pig(scale=0.3, x=1.5,z=-2,y=-1.5,angle=c(0,-135,0))) | |
#' if(rayrender:::run_documentation()) { | |
#' render_scene(scene, samples=128, parallel=TRUE, clamp_value=10) | |
#' } | |
light = function(color = "#ffffff", intensity = 10, importance_sample = TRUE, | |
spotlight_focus = NA, spotlight_width = 30, spotlight_start_falloff = 15, | |
invisible = FALSE, image_texture = NA, image_repeat = 1, | |
gradient_color = NA, gradient_transpose = FALSE, | |
gradient_point_start = NA, gradient_point_end = NA, gradient_type = "hsv") { | |
info = convert_color(color) | |
if(!is.array(image_texture) && !is.na(image_texture) && !is.character(image_texture)) { | |
image_texture = NA | |
warning("Texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(length(image_repeat) == 1) { | |
image_repeat = c(image_repeat,image_repeat) | |
} | |
if(all(!is.na(gradient_color))) { | |
gradient_color = convert_color(gradient_color) | |
} else { | |
gradient_color = NA | |
} | |
if(!is.na(gradient_point_start) && !is.na(gradient_point_end) && !is.na(gradient_color)) { | |
stopifnot(length(gradient_point_start) == 3) | |
stopifnot(length(gradient_point_end) == 3) | |
stopifnot(is.numeric(gradient_point_start)) | |
stopifnot(is.numeric(gradient_point_end)) | |
gradient_point_info = c(gradient_point_start,gradient_point_end) | |
is_world_gradient = TRUE | |
} else { | |
is_world_gradient = FALSE | |
gradient_point_info = NA | |
} | |
if(invisible) { | |
invisible = 1 | |
} else { | |
invisible = 0 | |
} | |
if(all(!is.na(spotlight_focus))) { | |
stopifnot(length(spotlight_focus) == 3) | |
spotlight_width = min(c(spotlight_width,180)) | |
spotlight_start_falloff = min(c(spotlight_start_falloff,90)) | |
info = c(info, spotlight_focus, cospi(spotlight_width/180), cospi(spotlight_start_falloff/180), invisible) | |
new_tibble_row(list(type = "spotlight", | |
properties = list(info), checkercolor=list(NA), | |
gradient_color = list(NA), gradient_transpose = FALSE, | |
world_gradient = FALSE,gradient_point_info = list(NA), | |
gradient_type = NA, | |
noise=0, noisephase = 0, noiseintensity = 0, noisecolor = list(c(0,0,0)), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(NA), lightintensity = intensity, | |
fog=FALSE, fogdensity=0.01, implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = list(NA), bump_texture = list(NA), | |
bump_intensity = 1, rough_texture = list(NA))) | |
} else { | |
info = c(info, invisible) | |
new_tibble_row(list(type = "light", | |
properties = list(info), checkercolor=list(NA), | |
gradient_color = list(gradient_color), gradient_transpose = gradient_transpose, | |
world_gradient = is_world_gradient, gradient_point_info = list(gradient_point_info), | |
gradient_type = gradient_type, | |
noise=0, noisephase = 0, noiseintensity = 0, noisecolor = list(c(0,0,0)), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(NA), lightintensity = intensity, | |
fog=FALSE, fogdensity=0.01, implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = list(NA), bump_texture = list(NA), | |
bump_intensity = 1, rough_texture = list(NA))) | |
} | |
} | |
#' Glossy Material | |
#' | |
#' @param color Default `white`. The color of the surface. Can be either | |
#' a hexadecimal code, R color string, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param gloss Default `0.8`. Gloss of the surface, between `1` (completely glossy) and `0` (rough glossy). | |
#' Can be either a single number, or two numbers indicating an anisotropic distribution of normals (as in `microfacet()`). | |
#' @param reflectance Default `0.03`. The reflectivity of the surface. `1` is a full mirror, `0` is diffuse with a glossy highlight. | |
#' @param microfacet Default `tbr`. Type of microfacet distribution. Alternative option `beckmann`. | |
#' @param checkercolor Default `NA`. If not `NA`, determines the secondary color of the checkered surface. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param checkerperiod Default `3`. The period of the checker pattern. Increasing this value makes the checker | |
#' pattern bigger, and decreasing it makes it smaller | |
#' @param noise Default `0`. If not `0`, covers the surface in a turbulent marble pattern. This value will determine | |
#' the amount of turbulence in the texture. | |
#' @param noisephase Default `0`. The phase of the noise. The noise will repeat at `360`. | |
#' @param noiseintensity Default `10`. Intensity of the noise. | |
#' @param noisecolor Default `#000000`. The secondary color of the noise pattern. | |
#' Can be either a hexadecimal code, or a numeric rgb vector listing three intensities between `0` and `1`. | |
#' @param gradient_color Default `NA`. If not `NA`, creates a secondary color for a linear gradient | |
#' between the this color and color specified in `color`. Direction is determined by `gradient_transpose`. | |
#' @param gradient_transpose Default `FALSE`. If `TRUE`, this will use the `v` coordinate texture instead | |
#' of the `u` coordinate texture to map the gradient. | |
#' @param gradient_point_start Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `color`. | |
#' @param gradient_point_end Default `NA`. If not `NA`, this changes the behavior from mapping texture coordinates to | |
#' mapping to world space coordinates. This should be a length-3 vector specifying the x,y, and z points where the gradient | |
#' begins with value `gradient_color`. | |
#' @param gradient_type Default `hsv`. Colorspace to calculate the gradient. Alternative `rgb`. | |
#' @param image_texture Default `NA`. A 3-layer RGB array or filename to be used as the texture on the surface of the object. | |
#' @param image_repeat Default `1`. Number of times to repeat the image across the surface. | |
#' `u` and `v` repeat amount can be set independently if user passes in a length-2 vector. | |
#' @param alpha_texture Default `NA`. A matrix or filename (specifying a greyscale image) to be used to specify the transparency. | |
#' @param bump_texture Default `NA`. A matrix, array, or filename (specifying a greyscale image) to | |
#' be used to specify a bump map for the surface. | |
#' @param bump_intensity Default `1`. Intensity of the bump map. High values may lead to unphysical results. | |
#' @param roughness_texture Default `NA`. A matrix, array, or filename (specifying a greyscale image) to | |
#' be used to specify a roughness map for the surface. | |
#' @param roughness_range Default ` c(0.0001, 0.2)`. This is a length-2 vector that specifies the range of roughness values | |
#' that the `roughness_texture` can take. | |
#' @param roughness_flip Default `FALSE`. Setting this to `TRUE` flips the roughness values specified in the `roughness_texture` | |
#' so high values are now low values and vice versa. | |
#' @param importance_sample Default `FALSE`. If `TRUE`, the object will be sampled explicitly during | |
#' the rendering process. If the object is particularly important in contributing to the light paths | |
#' in the image (e.g. light sources, refracting glass ball with caustics, metal objects concentrating light), | |
#' this will help with the convergence of the image. | |
#' | |
#' @return Single row of a tibble describing the glossy material. | |
#' @export | |
#' | |
#' @examples | |
#' if(rayrender:::run_documentation()) { | |
#' #Generate a glossy sphere | |
#' generate_ground(material=diffuse(sigma=90)) %>% | |
#' add_object(sphere(y=0.2,material=glossy(color="#2b6eff"))) %>% | |
#' add_object(sphere(y=2.8,material=light())) %>% | |
#' render_scene(parallel=TRUE,clamp_value=10,samples=128,sample_method="sobol_blue") | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' #Change the color of the underlying diffuse layer | |
#' generate_ground(material=diffuse(sigma=90)) %>% | |
#' add_object(sphere(y=0.2,x=-2.1,material=glossy(color="#fc3d03"))) %>% | |
#' add_object(sphere(y=0.2,material=glossy(color="#2b6eff"))) %>% | |
#' add_object(sphere(y=0.2,x=2.1,material=glossy(color="#2fed4f"))) %>% | |
#' add_object(sphere(y=8,z=-5,radius=3,material=light(intensity=20))) %>% | |
#' render_scene(parallel=TRUE,clamp_value=10,samples=128,fov=40,sample_method="sobol_blue") | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' #Change the amount of gloss | |
#' generate_ground(material=diffuse(sigma=90)) %>% | |
#' add_object(sphere(y=0.2,x=-2.1,material=glossy(gloss=1,color="#fc3d03"))) %>% | |
#' add_object(sphere(y=0.2,material=glossy(gloss=0.5,color="#2b6eff"))) %>% | |
#' add_object(sphere(y=0.2,x=2.1,material=glossy(gloss=0,color="#2fed4f"))) %>% | |
#' add_object(sphere(y=8,z=-5,radius=3,material=light(intensity=20))) %>% | |
#' render_scene(parallel=TRUE,clamp_value=10,samples=128,fov=40,sample_method="sobol_blue") | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' #Add gloss to a pattern | |
#' generate_ground(material=diffuse(sigma=90)) %>% | |
#' add_object(sphere(y=0.2,x=-2.1,material=glossy(noise=2,noisecolor="black"))) %>% | |
#' add_object(sphere(y=0.2,material=glossy(color="#ff365a",checkercolor="#2b6eff"))) %>% | |
#' add_object(sphere(y=0.2,x=2.1,material=glossy(color="blue",gradient_color="#2fed4f"))) %>% | |
#' add_object(sphere(y=8,z=-5,radius=3,material=light(intensity=20))) %>% | |
#' render_scene(parallel=TRUE,clamp_value=10,samples=128,fov=40,sample_method="sobol_blue") | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' #Add an R and a fill light (this may look familiar) | |
#' generate_ground(material=diffuse()) %>% | |
#' add_object(sphere(y=0.2,material=glossy(color="#2b6eff",reflectance=0.05))) %>% | |
#' add_object(obj_model(r_obj(),z=1,y=-0.05,scale_obj=0.45,material=diffuse())) %>% | |
#' add_object(sphere(y=6,z=1,radius=4,material=light(intensity=3))) %>% | |
#' add_object(sphere(z=15,material=light(intensity=50))) %>% | |
#' render_scene(parallel=TRUE,clamp_value=10,samples=128,sample_method="sobol_blue") | |
#' } | |
glossy = function(color="white", gloss = 1, reflectance = 0.05, microfacet = "tbr", | |
checkercolor = NA, checkerperiod = 3, | |
noise = 0, noisephase = 0, noiseintensity = 10, noisecolor = "#000000", | |
gradient_color = NA, gradient_transpose = FALSE, | |
gradient_point_start = NA, gradient_point_end = NA, gradient_type = "hsv", | |
image_texture = NA, image_repeat = 1, alpha_texture = NA, | |
bump_texture = NA, roughness_texture = NA, bump_intensity = 1, | |
roughness_range = c(0.0001, 0.2), roughness_flip = FALSE, | |
importance_sample = FALSE) { | |
microtype = switch(microfacet, "tbr" = 1,"beckmann" = 2, 1) | |
gloss[gloss <= 0] = 0 | |
gloss[gloss > 1] = 1 | |
gloss = 1 - gloss | |
gloss = gloss/2 | |
if(length(gloss) == 1) { | |
alphax = gloss^2 | |
alphay = gloss^2 | |
} else { | |
alphax = gloss[1]^2 | |
alphay = gloss[2]^2 | |
} | |
if(length(roughness_range) != 2) { | |
stop("length of roughness_range must be 2") | |
} | |
if(roughness_range[1] > roughness_range[2]) { | |
roughness_range = rev(roughness_range) | |
} | |
if(roughness_range[1] == roughness_range[2] && !is.na(roughness_texture)) { | |
roughness_texture = NA | |
gloss = 1-roughness_range[1] | |
} | |
color = convert_color(color) | |
reflectance = rep(reflectance,3) | |
if(all(!is.na(checkercolor))) { | |
checkercolor = convert_color(checkercolor) | |
} else { | |
checkercolor = NA | |
} | |
if(all(!is.na(gradient_color))) { | |
gradient_color = convert_color(gradient_color) | |
} else { | |
gradient_color = NA | |
} | |
if(!is.na(gradient_point_start) && !is.na(gradient_point_end) && !is.na(gradient_color)) { | |
stopifnot(length(gradient_point_start) == 3) | |
stopifnot(length(gradient_point_end) == 3) | |
stopifnot(is.numeric(gradient_point_start)) | |
stopifnot(is.numeric(gradient_point_end)) | |
gradient_point_info = c(gradient_point_start,gradient_point_end) | |
is_world_gradient = TRUE | |
} else { | |
is_world_gradient = FALSE | |
gradient_point_info = NA | |
} | |
noisecolor = convert_color(noisecolor) | |
if(!is.array(image_texture) && !is.na(image_texture) && !is.character(image_texture)) { | |
image_texture = NA | |
warning("Texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(alpha_texture) && !is.na(alpha_texture) && !is.character(alpha_texture)) { | |
alpha_texture = NA | |
warning("Alpha texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(bump_texture) && !is.na(bump_texture) && !is.character(bump_texture)) { | |
bump_texture = NA | |
warning("Bump texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(!is.array(roughness_texture) && !is.na(roughness_texture) && !is.character(roughness_texture)) { | |
roughness_texture = NA | |
warning("Roughness texture not in recognized format (array, matrix, or filename), ignoring.") | |
} | |
if(length(image_repeat) == 1) { | |
image_repeat = c(image_repeat,image_repeat) | |
} | |
roughness_flip = ifelse(roughness_flip,1,0) | |
glossyinfo = list(c(microtype, alphax, alphay, reflectance, c(0,0,0), roughness_range, roughness_flip)); | |
new_tibble_row(list(type = "glossy", | |
properties = list(c(color)), | |
gradient_color = list(gradient_color), gradient_transpose = FALSE, | |
world_gradient = is_world_gradient, gradient_point_info = list(gradient_point_info), | |
gradient_type = gradient_type, | |
checkercolor=list(c(checkercolor,checkerperiod)), | |
noise=noise, noisephase = noisephase, noiseintensity = noiseintensity, noisecolor = list(noisecolor), | |
image = list(image_texture), image_repeat = list(image_repeat), | |
alphaimage = list(alpha_texture), lightintensity = NA, | |
fog=FALSE, fogdensity=NA, implicit_sample = importance_sample, | |
sigma = 0, glossyinfo = glossyinfo, bump_texture = list(bump_texture), | |
bump_intensity = bump_intensity, rough_texture = list(roughness_texture))) | |
} | |
SigmaAFromConcentration = function(ce, cp) { | |
eumelaninSigmaA = c(0.419, 0.697, 1.37); | |
pheomelaninSigmaA = c(0.187, 0.4, 1.05); | |
sigma_a = ce * eumelaninSigmaA + cp * pheomelaninSigmaA | |
return(sigma_a); | |
} | |
SigmaAFromReflectance = function(c, beta_n) { | |
sigma_a = (log(c) / (5.969 - 0.215 * beta_n + 2.532 * (beta_n)^2 - | |
10.73 * (beta_n)^3 + 5.574 * (beta_n)^4 + | |
0.245 * (beta_n)^5))^2; | |
return(sigma_a); | |
} | |
#' Hair Material | |
#' | |
#' @param pigment Default `1.3`. Concentration of the eumelanin pigment in the hair. Blonde hair has concentrations around 0.3, brown around 1.3, and black around 8. | |
#' @param red_pigment Default `0`.Concentration of the pheomelanin pigment in the hair. Pheomelanin makes red hair red. | |
#' @param color Default `NA`. Approximate color. Overrides `pigment`/`redness` arguments. | |
#' @param sigma_a Default `NA`. Attenuation. Overrides `color` and `pigment`/`redness` arguments. | |
#' @param eta Default `1.55`. Index of refraction of the hair medium. | |
#' @param beta_m Default `0.3`. Longitudinal roughness of the hair. Should be between 0 and 1. This roughness controls the size and shape of the hair highlight. | |
#' @param beta_n Default `0.3`. Azimuthal roughness of the hair. Should be between 0 and 1. | |
#' @param alpha Default `2`. Angle of scales on the hair surface, in degrees. | |
#' | |
#' @return Single row of a tibble describing the hair material. | |
#' @export | |
#' @importFrom grDevices col2rgb | |
#' | |
#' @examples | |
#' #Create a hairball | |
#' if(rayrender:::run_documentation()) { | |
#' #Generate rendom points on a sphere | |
#' lengthval = 0.5 | |
#' theta = acos(2*runif(10000)-1.0); | |
#' phi = 2*pi*(runif(10000)) | |
#' bezier_list = list() | |
#' | |
#' #Grow the hairs | |
#' for(i in 1:length(phi)) { | |
#' pointval = c(sin(theta[i]) * sin(phi[i]), | |
#' cos(theta[i]), | |
#' sin(theta[i]) * cos(phi[i])) | |
#' bezier_list[[i]] = bezier_curve(width=0.01, width_end=0.008, | |
#' p1 = pointval, | |
#' p2 = (1+(lengthval*0.33))*pointval, | |
#' p3 = (1+(lengthval*0.66))*pointval, | |
#' p4 = (1+(lengthval)) * pointval, | |
#' material=hair(pigment = 0.3, red_pigment = 1.3, | |
#' beta_m = 0.3, beta_n= 0.3), | |
#' type="flat") | |
#' } | |
#' hairball = dplyr::bind_rows(bezier_list) | |
#' | |
#' generate_ground(depth=-2,material=diffuse(color="grey20")) %>% | |
#' add_object(sphere()) %>% | |
#' add_object(hairball) %>% | |
#' add_object(sphere(y=20,z=20,radius=5,material=light(color="white",intensity = 100))) %>% | |
#' render_scene(samples=64, lookfrom=c(0,3,10),clamp_value = 10, | |
#' fov=20) | |
#' } | |
#' if(rayrender:::run_documentation()) { | |
#' | |
#' #Specify the color directly and increase hair roughness | |
#' for(i in 1:length(phi)) { | |
#' pointval = c(sin(theta[i]) * sin(phi[i]), | |
#' cos(theta[i]), | |
#' sin(theta[i]) * cos(phi[i])) | |
#' bezier_list[[i]] = bezier_curve(width=0.01, width_end=0.008, | |
#' p1 = pointval, | |
#' p2 = (1+(lengthval*0.33))*pointval, | |
#' p3 = (1+(lengthval*0.66))*pointval, | |
#' p4 = (1+(lengthval)) * pointval, | |
#' material=hair(color="purple", | |
#' beta_m = 0.5, beta_n= 0.5), | |
#' type="flat") | |
#' } | |
#' hairball = dplyr::bind_rows(bezier_list) | |
#' generate_ground(depth=-2,material=diffuse(color="grey20")) %>% | |
#' add_object(sphere()) %>% | |
#' add_object(hairball) %>% | |
#' add_object(sphere(y=20,z=20,radius=5,material=light(color="white",intensity = 100))) %>% | |
#' render_scene(samples=64, lookfrom=c(0,3,10),clamp_value = 10, | |
#' fov=20) | |
#' } | |
hair = function(pigment = 1.3, red_pigment = 0, color = NA, sigma_a = NA, | |
eta = 1.55, beta_m = 0.3, beta_n = 0.3, alpha = 2) { | |
if(!is.na(sigma_a)) { | |
sigma_a = sigma_a | |
} else if(!is.na(color)) { | |
sigma_a = SigmaAFromReflectance(convert_color(color), beta_n) | |
} else { | |
stopifnot(pigment >= 0) | |
stopifnot(red_pigment >= 0) | |
sigma_a = SigmaAFromConcentration(pigment, red_pigment) | |
} | |
info = c(sigma_a, eta, beta_m, beta_n, alpha) | |
new_tibble_row(list(type = "hair", | |
properties = list(info), checkercolor=list(NA), | |
gradient_color = list(NA), gradient_transpose = NA, | |
world_gradient = FALSE, gradient_point_info = list(NA), | |
gradient_type = NA, | |
noise=0, noisephase = 0, noiseintensity = 0, noisecolor = list(c(0,0,0)), | |
image = list(NA), image_repeat = list(NA), | |
alphaimage = list(NA), lightintensity = NA, | |
fog=FALSE, fogdensity=NA, implicit_sample = FALSE, | |
sigma = NA, glossyinfo = list(NA), bump_texture = list(NA), | |
bump_intensity = NA, rough_texture = list(NA))) | |
} | |
#' Lambertian Material (deprecated) | |
#' | |
#' @param ... Arguments to pass to diffuse() function. | |
#' | |
#' @return Single row of a tibble describing the diffuse material. | |
#' @export | |
#' @importFrom grDevices col2rgb | |
#' | |
#' @examples | |
#' #Deprecated lambertian material. Will display a warning. | |
#' if(rayrender:::run_documentation()) { | |
#' scene = generate_cornell() %>% | |
#' add_object(sphere(x=555/2,y=555/2,z=555/2,radius=555/8,material=lambertian())) | |
#' render_scene(scene, lookfrom=c(278,278,-800),lookat = c(278,278,0), samples=10, | |
#' aperture=0, fov=40, ambient_light=FALSE, parallel=TRUE) | |
#' } | |
lambertian = function(...) { | |
warning("lambertian() deprecated--use diffuse() instead.") | |
diffuse(...) | |
} | |