diff --git a/README.md b/README.md index f3392be..890d2f0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Starlight is a Godot addon that renders 100 000 stars in realtime, with low performance cost. It's an alternative to using a skybox, and also may be relevant to anyone making a space game. -Check out the demo in your web browser: https://tiffnix.com/starlight-demo/ +Check out the demo in your web browser: https://tiffnix.com/starlight-demo # Features @@ -86,7 +86,7 @@ The following visual properties are exposed: of 3 or 4 is a good balance. - `billboard_size_deg` - This controls how much of the screen the PSF texture takes up, in degrees. For the default JWST PSF I recommend a - value of around 90. + value of around 45. - `meters_per_lightyear` - This is a scaling setting, you'll need to set it depending on how far away you want your stars to be. - `luminosity_cap` - This is the maximum brightness a star can have. The @@ -97,6 +97,9 @@ The following visual properties are exposed: is the PSF from the James Webb Space Telescope, because it looks cool. There are a few others in the `psf-textures` folder which can be used instead. +- `texture_emission_tint` - A tint value applied to the PSF texture. The + provided PSF textures need a tint value to look correct, there is a + table in psf-textures/README.md. - `clamp_output` - Clamps the output from 0 to 1 when enabled. Can be useful depending on how your HDR is setup. @@ -119,7 +122,7 @@ behavior, the shader has these properties: max_luminosity until it's just below that point. - `scaling_gamma` - Diffraction spikes usually fall in brightness according to distance^2 from the center of the texture, which means a - value of 0.5 is ideal. You may need to use other values depending on + value of ~0.5 is ideal. You may need to use other values depending on your PSF texture. For a perfect airy disk in particular, the falloff is faster than quadratic. - `debug_show_rects` - This can be useful while tweaking any of these @@ -142,13 +145,13 @@ from the scene. Code is released under [MIT license](./LICENSE.md). -The default PSF texture, `jwst.exr`, is based on FITS data [obtained -from here][5]. Code for cropping, downscaling, and converting to OpenEXR -is located in `docs/fits2exr.py`. +The default PSF texture, `jwst.png`, is based on FITS data [obtained +from here][5]. Code for cropping, downscaling, and packing into PNG +is located in `docs/jwst2png.py`. -The alternative PSF textures `hst.exr`, `hex_aperture.exr`, and -`airy_disk.exr` were created using [Poppy][6] based on examples in the -documentation. Code is located in `docs/poppy psfs.ipynb`. +The alternative PSF textures `hst.png`, `hex_aperture.png`, and +`airy_disk.png` were created using [Poppy][6] based on examples in the +documentation. Code is located in `docs/poppy_psfs.ipynb`. [5]: https://www.stsci.edu/jwst/science-planning/proposal-planning-toolbox/simulated-data [6]: https://poppy-optics.readthedocs.io/en/latest/ diff --git a/docs/fits2exr.py b/docs/jwst2png.py similarity index 51% rename from docs/fits2exr.py rename to docs/jwst2png.py index f220168..2b78e12 100644 --- a/docs/fits2exr.py +++ b/docs/jwst2png.py @@ -1,7 +1,12 @@ #!/usr/bin/env python3 -# Modified by Tiffany for the purposes of creating the JWST EXR file. -# Original description: +# Converts the JWST simulated PSF to the packed PNG format expected by +# the godot-starlight addon. The source PSF images must be obtained in +# FITS format from here: +# + +# This script was adapted by me (Tiffany) from a FITS-to-EXR conversion +# script, the original description follows. # Presented by Min-Su Shin # (2015 - , KASI, Republic of Korea) @@ -19,10 +24,10 @@ # check the output EXR files. from astropy.io import fits -import OpenEXR import numpy from img_scale import cbrt from skimage.measure import block_reduce +from PIL import Image # read FITS images # red channel @@ -64,33 +69,51 @@ def crop_center(img,cropx,cropy): starty = y//2 - cropy//2 return img[starty:starty+cropy, startx:startx+cropx] -width = 4096 -height = 4096 +width = 2048 +height = 2048 r_img_data = crop_center(r_img_data, width, height) g_img_data = crop_center(g_img_data, width, height) b_img_data = crop_center(b_img_data, width, height) -downscale = 4 - -r_img_data = block_reduce(r_img_data, block_size=(downscale,downscale), func=numpy.sum, cval=numpy.sum(r_img_data)) -g_img_data = block_reduce(g_img_data, block_size=(downscale,downscale), func=numpy.sum, cval=numpy.sum(r_img_data)) -b_img_data = block_reduce(b_img_data, block_size=(downscale,downscale), func=numpy.sum, cval=numpy.sum(r_img_data)) -width = width // downscale -height = height // downscale - -# write an EXR file -exr_fn = "jwst_psf.exr" -r_img_data = numpy.asarray(r_img_data, dtype=numpy.float32) -r_img_data = r_img_data.tobytes() -g_img_data = numpy.asarray(g_img_data, dtype=numpy.float32) -g_img_data = g_img_data.tobytes() -b_img_data = numpy.asarray(b_img_data, dtype=numpy.float32) -b_img_data = b_img_data.tobytes() -header = OpenEXR.Header(width, height) -out_exr = OpenEXR.OutputFile(exr_fn, header) -out_exr.writePixels({ - 'R': r_img_data, - 'G': g_img_data, - 'B': b_img_data, -}) -print("write the EXR file ",exr_fn," done...") +downscale = 1 + +if downscale != 1: + r_img_data = block_reduce(r_img_data, block_size=(downscale,downscale), func=numpy.sum, cval=numpy.sum(r_img_data)) + g_img_data = block_reduce(g_img_data, block_size=(downscale,downscale), func=numpy.sum, cval=numpy.sum(r_img_data)) + b_img_data = block_reduce(b_img_data, block_size=(downscale,downscale), func=numpy.sum, cval=numpy.sum(r_img_data)) + width = width // downscale + height = height // downscale + +coords = numpy.mgrid[0:width, 0:height] +xcoord = coords[0] - (width / 2) +ycoord = coords[1] - (height / 2) +dist_sq = xcoord * xcoord + ycoord * ycoord + 0 + +r_img_data = r_img_data * dist_sq +g_img_data = g_img_data * dist_sq +b_img_data = b_img_data * dist_sq + +r_max = numpy.max(r_img_data) +g_max = numpy.max(g_img_data) +b_max = numpy.max(b_img_data) + +r_img_data = r_img_data / r_max +g_img_data = g_img_data / g_max +b_img_data = b_img_data / b_max + +b = 1000 +log_b = numpy.log(b) +r_img_data = numpy.log(1.0 + r_img_data * (b - 1.0)) / log_b +g_img_data = numpy.log(1.0 + g_img_data * (b - 1.0)) / log_b +b_img_data = numpy.log(1.0 + b_img_data * (b - 1.0)) / log_b + +print("max values: ", r_max, g_max, b_max) + +r_img_data = numpy.asarray(r_img_data * 255.0, dtype=numpy.uint8) +g_img_data = numpy.asarray(g_img_data * 255.0, dtype=numpy.uint8) +b_img_data = numpy.asarray(b_img_data * 255.0, dtype=numpy.uint8) + +rgb = numpy.dstack((r_img_data, g_img_data, b_img_data)) + +image = Image.fromarray(rgb) +image.save("jwst.png") diff --git a/docs/poppy psfs.ipynb b/docs/poppy_psfs.ipynb similarity index 100% rename from docs/poppy psfs.ipynb rename to docs/poppy_psfs.ipynb