skymodelr generates physically‑plausible sky domes and night skies
as high‑dynamic‑range EXR images/arrays directly from R. It provides the
Hosek–Wilkie analytic sky model and (optionally) the 2021–22 Prague
spectral sky model (below‑horizon sun, altitude, and wide‑spectrum
support). It also includes options to add a physically-accurate moon
that models phase correctly, adjusts orientation based on the viewers
latitude and longitude, accounts for changes in brightness due to
distance variation, opposition surge, and atmospheric attenuation.
skymodelr also includes the option to add an accurate visible star
field aligned to observer location/time. Outputs are lat‑long
environment maps (2:1 equirectangular) that you can feed into renderers
(such as rayrender).
generate_sky_latlong() composes a full sky environment using the
functions generate_sky(), generate_moon_latlong(), and
generate_stars() for a specific latitude, longitude, and time. By
default generate_sky_latlong() only includes the sun’s contribution,
but you can also include stars and the moon by setting moon = TRUE and
stars = TRUE.
# Latest version from GitHub
remotes::install_github("tylermorganwall/skymodelr")If you plan to use the Prague spectral model, download its coefficient
dataset(s) once (see download_sky_data() below).
-
generate_sky()— Write/return an EXR sky dome using either the Hosek–Wilkie (default) or Prague model.filename = NAto return the HDR array in‑memory (otherwise a.exris written).albedo = 0.1ground reflectance (0–1).turbidity = 3atmospheric turbidity (1.7–10; Hosek only).elevation = 10,azimuth = 90solar position (degrees).altitude = 0observer altitude in meters (Prague only).resolution = 2048image height (width is2 * resolution).number_cores = 1threads.hosek = TRUEsetFALSEto use the Prague model; Prague optionswide_spectrum,visibility.render_mode = "all"for atmosphere + solar disk; use"atmosphere"to omit the disk or"sun"for disk only.
-
generate_sky_latlong()— Produce a complete equirectangular sky array/EXR. Accepts date/time and observer location, and (optionally) adds stars and a moon‑lit atmosphere.- Core args:
filename = NA,datetime,lat,lon,albedo,turbidity,resolution,number_cores. - Model selection:
hosek = TRUE(Hosek–Wilkie) or sethosek = FALSEto use the Prague spectral model; Prague extras:wide_spectrum,visibility,altitude. - Composition:
stars = FALSE,star_width = 1,moon = FALSE.
- Core args:
-
generate_moon_latlong()— Produce a moon‑lit atmosphere by scaling a sky dome to the moon’s luminance (phase + opposition surge). Computes the moon’s position from time/location, and exposes moon-specific controls such asmoon_extinction_kV,earthshine_albedo, andsolar_irradiance_w_m2. -
generate_stars()— Generate a star‑field EXR aligned with the sky dome:filename = NA,resolution = 2048.lon,latobserver longitude/latitude (deg) anddatetime(used for local sidereal time).- Optional extinction/appearance controls:
turbidity,ozone_du,altitude,star_width,atmosphere_effects,upper_hemisphere_only,number_cores.
-
calculate_sky_values()— Sample radiance from the Prague model for given sky directions (phi,theta) and conditions (elevation,altitude,visibility,albedo,azimuth). -
download_sky_data(sea_level = TRUE, wide_spectrum = FALSE)— Download Prague model coefficient data:- Sea‑level, standard spectrum:
SkyModelDatasetGround.dat(~107 MB) - Sea‑level, wide spectrum:
PragueSkyModelDatasetGroundInfra.dat(~574 MB) - Full‑altitude dataset:
SkyModelDataset.dat(~2.4 GB)
- Sea‑level, standard spectrum:
We’ll generate the sun on the morning (right after sunrise) in Washington DC on March 21st. On this day the sun is rising directly east (90 degrees azimuth), which we can see as the sun appearing a quarter of the way from the left side of the image.
library(skymodelr)
library(rayimage)
env = generate_sky_latlong(
datetime = as.POSIXct("2025-03-21 06:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
resolution = 800,
hosek = FALSE,
verbose = TRUE
)
rayimage::render_exposure(env, exposure=-2) |>
rayimage::plot_image()Afternoon in DC:
env = generate_sky_latlong(
datetime = as.POSIXct("2025-03-21 12:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
resolution = 800,
hosek = FALSE
)
rayimage::render_exposure(env, exposure=-5) |>
rayimage::plot_image()Evening in DC:
env = generate_sky_latlong(
datetime = as.POSIXct("2025-03-21 18:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
resolution = 800,
hosek = FALSE
)
rayimage::render_exposure(env, exposure=-2) |>
rayimage::plot_image()Evening in DC (Hosek model), note the unphysical yellowish tint:
env = generate_sky_latlong(
datetime = as.POSIXct("2025-03-21 18:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
resolution = 800,
hosek = TRUE
)
rayimage::render_exposure(env, exposure=-4) |>
rayimage::plot_image()The Prague model supports solar elevations below the horizon:
env = generate_sky_latlong(
datetime = as.POSIXct("2025-03-21 18:37:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
verbose=TRUE,
resolution = 800,
hosek = FALSE
)## Sun: -4.1 elevation, 274.1 azimuth
rayimage::render_exposure(env, exposure=3) |>
rayimage::plot_image()The Prague model also supports altitudes up to 15,000m.
Note that now we’re above a good portion of the atmosphere, so we start seeing scattered light below us.
env = generate_sky_latlong(
datetime = as.POSIXct("2025-03-21 12:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
altitude = 2000,
resolution = 800,
hosek = FALSE
)
rayimage::render_exposure(env, exposure=-5) |>
rayimage::plot_image()High enough altitudes have the majority of the scattered light coming from below the viewer.
env = generate_sky_latlong(
filename = NA,
datetime = as.POSIXct("2025-03-21 12:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
altitude = 7500,
resolution = 800,
hosek = FALSE
)
rayimage::render_exposure(env, exposure=-5) |>
rayimage::plot_image()At high altitudes, the resulting render has almost all the scattered light coming from below, resulting in a space-like appearance.
env = generate_sky_latlong(
filename = NA,
datetime = as.POSIXct("2025-03-21 12:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
altitude = 15000,
resolution = 800,
hosek = FALSE
)
rayimage::render_exposure(env, exposure=-5) |>
rayimage::plot_image()Note the shadow of the Earth in the atmosphere opposite the sun when the sun is below the horizon.
env = generate_sky_latlong(
filename = NA,
datetime = as.POSIXct("2025-03-21 18:30:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
altitude = 15000,
resolution = 800,
hosek = FALSE
)
rayimage::plot_image(env)Full sun + moon + stars (with increased exposure for artistic effect):
env = generate_sky_latlong(
filename = NA,
datetime = as.POSIXct("2025-03-21 18:37:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
resolution = 800,
hosek = FALSE,
stars = TRUE,
moon = TRUE,
stars_exposure = 12
)
rayimage::render_exposure(env, exposure = 2) |>
rayimage::plot_image()skymodelr allows you to turn off the direct sun contribution to only
included scattered light.
sky = generate_sky(
albedo = 0,
elevation = 25,
azimuth = 135,
resolution = 800,
number_cores = 2,
hosek = FALSE,
render_mode = "atmosphere"
)
rayimage::render_exposure(sky, exposure=-6) |>
rayimage::plot_image()moon_sky = generate_moon_latlong(
filename = NA,
datetime = as.POSIXct("2025-03-21 02:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
albedo = 0.2,
turbidity = 3,
resolution = 6000,
number_cores = 8,
moon_atmosphere = TRUE,
verbose = TRUE
)
#Increase exposure
moon_sky |>
rayimage::render_exposure(10) |>
rayimage::plot_image()This generates and places a radiance-accurate moon into the scene,
taking into account the moon’s phase, orientation with respect to the
viewer’s latitude and longitude, surface albedo, non-lambertian
reflectivity, distance to earth, earthshine, the “opposition surge”
effect, and attenuation due to the atmosphere at varying lunar
elevations. The moon is rendered in rayvertex and resampled so it is
the correct size on the environment map. Here are some of the pre-scaled
renders.
moon_image1 = skymodelr:::generate_moon_image_latlong(datetime = as.POSIXct("2025-03-21 02:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369)
rayimage::render_exposure(moon_image1$moon_luminance_array, 2, preview = TRUE)#Change latitude and our orientation changes
moon_image2 = skymodelr:::generate_moon_image_latlong(datetime = as.POSIXct("2025-03-10 02:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369)
rayimage::render_exposure(moon_image2$moon_luminance_array, 2, preview = TRUE)moon_image3 = skymodelr:::generate_moon_image_latlong(datetime = as.POSIXct("2025-03-10 02:15:00",tz="EST"),
lat = 0,
lon = -77.0369)
rayimage::render_exposure(moon_image3$moon_luminance_array, 2, preview = TRUE)moon_image4 = skymodelr:::generate_moon_image_latlong(datetime = as.POSIXct("2025-03-10 02:15:00",tz="EST"),
lat = -38.9072,
lon = -77.0369)
rayimage::render_exposure(moon_image4$moon_luminance_array, 2, preview = TRUE)Stars are rendered using color values derived from the star’s temperature.
stars = generate_stars(
datetime = as.POSIXct("2025-03-21 02:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
resolution = 800,
atmosphere_effects = TRUE,
upper_hemisphere_only = TRUE
)
stars |>
rayimage::render_exposure(16) |>
rayimage::plot_image()Now render the entire sphere:
stars_full = generate_stars(
datetime = as.POSIXct("2025-03-21 02:15:00",tz="EST"),
lat = 38.9072,
lon = -77.0369,
resolution = 800,
atmosphere_effects = FALSE,
upper_hemisphere_only = FALSE
)
stars_full |>
rayimage::render_exposure(16) |>
rayimage::plot_image()# Download once (choose variant via args):
coef_path = download_sky_data(sea_level = TRUE, wide_spectrum = FALSE)
# Render with the Prague model:
sky_prague = generate_sky(
albedo = 0.3,
elevation = 10,
azimuth = 90,
altitude = 0,
hosek = FALSE,
wide_spectrum = FALSE,
visibility = 50,
resolution = 2048,
number_cores = 4
)
plot_image(sky_prague)You can pass these EXR images to rayrender. Here’s a daytime sky:
library(rayrender)
day_exr = tempfile(fileext=".exr")
generate_sky(
filename=day_exr,
elevation = 20,
azimuth = 135,
resolution = 1000,
hosek = FALSE
)
generate_ground(depth = -0.5, material=diffuse(color="grey20", checkercolor = "grey50"),spheresize = 10000) |>
add_object(obj_model(r_obj())) |>
render_scene(environment_light = day_exr, iso=10,
clamp_value = 1000, fov = 80, lookfrom = c(0,0,-1), aperture = 0.05)And a sunset sky, with the sun coming from due North, which is +Z in
rayrender. If you are not trying to match a real place, you make
adjustments to an existing environment map’s orientation using the
rotate_env argument in rayrender.
sunset_exr = tempfile(fileext=".exr")
generate_sky(
filename=sunset_exr,
elevation = 4,
azimuth = 0,
resolution = 2000,
hosek = FALSE
)
#This has the light coming from north
generate_ground(depth = -0.4, material=diffuse(color="grey20", checkercolor = "grey50"),spheresize = 10000) |>
add_object(obj_model(r_obj())) |>
render_scene(environment_light = sunset_exr, iso=10,
clamp_value = 1000, fov = 80, lookfrom = c(0,0,-1), aperture = 0.05)#Rotate the existing env 225 degrees to come from the southwest.
generate_ground(depth = -0.4, material=diffuse(color="grey20", checkercolor = "grey50"),spheresize = 10000) |>
add_object(obj_model(r_obj())) |>
render_scene(environment_light = sunset_exr, iso=20,
clamp_value = 10000, fov = 40, lookfrom = c(0,1,-4), aperture = 0.05,
rotate_env = 225)- Hosek–Wilkie sky model (analytic daylight model).
- Prague 2021–22 spectral sky model (wide‑spectrum, below‑horizon sun, altitude).
- Star positions from the Yale Bright Star Catalog.






















