|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": {}, |
| 6 | + "source": [ |
| 7 | + "## High dynamic range and environment maps\n", |
| 8 | + "\n", |
| 9 | + "This example shows how to:\n", |
| 10 | + " - setup camera for baking 360 degree panoramic environment map\n", |
| 11 | + " - save image file with 8/16/32 bits per sample color depth\n", |
| 12 | + " - use OpenCV to save image in HDR format\n", |
| 13 | + "\n", |
| 14 | + "\n", |
| 15 | + "\n", |
| 16 | + "Saving images to jpg format is great when you want to use them quickly, with no additional editing outside your script. Jpg uses 8 bit/sample color depth and lossy compression - such output of your work is ready to go e.g. to the web.\n", |
| 17 | + "\n", |
| 18 | + "If you plan to apply retouching in an image editing software you'll appreciate saving your renders to a lossless format, with 16 bit/sample color depth. PlotOptiX can do that, tiff (Linux/Windows) and png (Windows) formats are supported.\n", |
| 19 | + "\n", |
| 20 | + "However, the full information is preserved only if 32 bit/sample, floating point precision data, is saved to a file. Such a high dynamic range (HDR) representation keeps all the bright lights and details in shadows without clamping and rounding errors. Exposure and any tonal corrections can be re-adjusted in HDR images without quality losses. And, most importantly, HDR images are the best for lighting scenes as environment maps - that is what we are going to show in this example. HDR image are written by PlotOptiX natively in tiff format, or you can use OpenCV to save such images in the Radiance file format (.hdr)." |
| 21 | + ] |
| 22 | + }, |
| 23 | + { |
| 24 | + "cell_type": "code", |
| 25 | + "execution_count": 1, |
| 26 | + "metadata": {}, |
| 27 | + "outputs": [], |
| 28 | + "source": [ |
| 29 | + "import numpy as np\n", |
| 30 | + "from plotoptix import TkOptiX" |
| 31 | + ] |
| 32 | + }, |
| 33 | + { |
| 34 | + "cell_type": "markdown", |
| 35 | + "metadata": {}, |
| 36 | + "source": [ |
| 37 | + "Generate some data - balls distributed on a sphere:" |
| 38 | + ] |
| 39 | + }, |
| 40 | + { |
| 41 | + "cell_type": "code", |
| 42 | + "execution_count": 2, |
| 43 | + "metadata": {}, |
| 44 | + "outputs": [], |
| 45 | + "source": [ |
| 46 | + "n = 2000\n", |
| 47 | + "r0 = 0.1\n", |
| 48 | + "R = 4\n", |
| 49 | + "\n", |
| 50 | + "r = r0 + 0.3 * np.random.rand(n)\n", |
| 51 | + "\n", |
| 52 | + "x = np.random.normal(loc=0, scale=1.0, size=n)\n", |
| 53 | + "y = np.random.normal(loc=0, scale=1.0, size=n)\n", |
| 54 | + "z = np.random.normal(loc=0, scale=1.0, size=n)\n", |
| 55 | + "xyz = np.stack((x, y, z)).T\n", |
| 56 | + "for i in range(n): xyz[i] *= R / np.linalg.norm(xyz[i])" |
| 57 | + ] |
| 58 | + }, |
| 59 | + { |
| 60 | + "cell_type": "markdown", |
| 61 | + "metadata": {}, |
| 62 | + "source": [ |
| 63 | + "A simple function to signal that ray tracing has finished:" |
| 64 | + ] |
| 65 | + }, |
| 66 | + { |
| 67 | + "cell_type": "code", |
| 68 | + "execution_count": 3, |
| 69 | + "metadata": {}, |
| 70 | + "outputs": [], |
| 71 | + "source": [ |
| 72 | + "def accum_done(rt: TkOptiX) -> None:\n", |
| 73 | + " print(\"rt completed!\")" |
| 74 | + ] |
| 75 | + }, |
| 76 | + { |
| 77 | + "cell_type": "markdown", |
| 78 | + "metadata": {}, |
| 79 | + "source": [ |
| 80 | + "Setup the ray tracing parameters. **Note** that AI denoiser is NOT applied. It could result with a visible seam at the line joining vertical edges of the image, when the image is displayed as an environment map. Instead, only a gamma correction is used and you need to accumulate enough data to reduce the noise." |
| 81 | + ] |
| 82 | + }, |
| 83 | + { |
| 84 | + "cell_type": "code", |
| 85 | + "execution_count": 4, |
| 86 | + "metadata": {}, |
| 87 | + "outputs": [], |
| 88 | + "source": [ |
| 89 | + "rt = TkOptiX(on_rt_accum_done=accum_done)\n", |
| 90 | + "rt.set_param(\n", |
| 91 | + " min_accumulation_step=2,\n", |
| 92 | + " max_accumulation_frames=300\n", |
| 93 | + ")\n", |
| 94 | + "rt.set_uint(\"path_seg_range\", 4, 8) # a little more than the default (2,6) to improve the ambient occlusion impression\n", |
| 95 | + "\n", |
| 96 | + "exposure = 1; gamma = 2.2\n", |
| 97 | + "rt.set_float(\"tonemap_exposure\", exposure)\n", |
| 98 | + "rt.set_float(\"tonemap_gamma\", gamma)\n", |
| 99 | + "rt.add_postproc(\"Gamma\")" |
| 100 | + ] |
| 101 | + }, |
| 102 | + { |
| 103 | + "cell_type": "markdown", |
| 104 | + "metadata": {}, |
| 105 | + "source": [ |
| 106 | + "Setup lighting: one bright warm spherical light and some cold light from the ambient." |
| 107 | + ] |
| 108 | + }, |
| 109 | + { |
| 110 | + "cell_type": "code", |
| 111 | + "execution_count": 5, |
| 112 | + "metadata": { |
| 113 | + "scrolled": true |
| 114 | + }, |
| 115 | + "outputs": [], |
| 116 | + "source": [ |
| 117 | + "rt.setup_light(\"l1\", pos=[1.5, 0, 1.5], color=[4.5, 4.2, 3.1], radius=0.75, in_geometry=False)\n", |
| 118 | + "\n", |
| 119 | + "rt.set_ambient([0.4, 0.6, 0.8])\n", |
| 120 | + "rt.set_background(0)" |
| 121 | + ] |
| 122 | + }, |
| 123 | + { |
| 124 | + "cell_type": "markdown", |
| 125 | + "metadata": {}, |
| 126 | + "source": [ |
| 127 | + "Setup cameras: one for making the panoramic view, one to show balls from inside the sphere, and one looking from a distance." |
| 128 | + ] |
| 129 | + }, |
| 130 | + { |
| 131 | + "cell_type": "code", |
| 132 | + "execution_count": 6, |
| 133 | + "metadata": {}, |
| 134 | + "outputs": [], |
| 135 | + "source": [ |
| 136 | + "rt.setup_camera(\"cam1\", cam_type=\"Panoramic\", eye=[0, 0, 0], target=[0, 0, -1], up=[0, 1, 0])\n", |
| 137 | + "\n", |
| 138 | + "rt.setup_camera(\"cam2\", cam_type=\"DoF\",\n", |
| 139 | + " eye=[0, 0, 2], target=[0, 0, 0], up=[0, 1, 0],\n", |
| 140 | + " aperture_radius=0.2, fov=45, focal_scale=2.8)\n", |
| 141 | + "\n", |
| 142 | + "rt.setup_camera(\"cam3\", cam_type=\"DoF\",\n", |
| 143 | + " eye=[0, 0, 10], target=[0, 0, 0], up=[0, 1, 0],\n", |
| 144 | + " aperture_radius=0.07, fov=35, focal_scale=0.56)" |
| 145 | + ] |
| 146 | + }, |
| 147 | + { |
| 148 | + "cell_type": "markdown", |
| 149 | + "metadata": {}, |
| 150 | + "source": [ |
| 151 | + "Upload data points:" |
| 152 | + ] |
| 153 | + }, |
| 154 | + { |
| 155 | + "cell_type": "code", |
| 156 | + "execution_count": 7, |
| 157 | + "metadata": {}, |
| 158 | + "outputs": [], |
| 159 | + "source": [ |
| 160 | + "rt.set_data(\"points\", pos=xyz, r=r, c=0.7)" |
| 161 | + ] |
| 162 | + }, |
| 163 | + { |
| 164 | + "cell_type": "markdown", |
| 165 | + "metadata": {}, |
| 166 | + "source": [ |
| 167 | + "Open the GUI window:" |
| 168 | + ] |
| 169 | + }, |
| 170 | + { |
| 171 | + "cell_type": "code", |
| 172 | + "execution_count": 8, |
| 173 | + "metadata": { |
| 174 | + "scrolled": true |
| 175 | + }, |
| 176 | + "outputs": [], |
| 177 | + "source": [ |
| 178 | + "rt.show()" |
| 179 | + ] |
| 180 | + }, |
| 181 | + { |
| 182 | + "cell_type": "markdown", |
| 183 | + "metadata": {}, |
| 184 | + "source": [ |
| 185 | + "Switch camera views. Let the ray tracing to converge with *cam1* active, this is the image to be used in the next example." |
| 186 | + ] |
| 187 | + }, |
| 188 | + { |
| 189 | + "cell_type": "code", |
| 190 | + "execution_count": 9, |
| 191 | + "metadata": { |
| 192 | + "scrolled": true |
| 193 | + }, |
| 194 | + "outputs": [], |
| 195 | + "source": [ |
| 196 | + "rt.set_current_camera(\"cam2\")" |
| 197 | + ] |
| 198 | + }, |
| 199 | + { |
| 200 | + "cell_type": "code", |
| 201 | + "execution_count": 10, |
| 202 | + "metadata": {}, |
| 203 | + "outputs": [ |
| 204 | + { |
| 205 | + "name": "stdout", |
| 206 | + "output_type": "stream", |
| 207 | + "text": [ |
| 208 | + "rt completed!\n" |
| 209 | + ] |
| 210 | + } |
| 211 | + ], |
| 212 | + "source": [ |
| 213 | + "rt.set_current_camera(\"cam1\")" |
| 214 | + ] |
| 215 | + }, |
| 216 | + { |
| 217 | + "cell_type": "markdown", |
| 218 | + "metadata": {}, |
| 219 | + "source": [ |
| 220 | + "Save images with 8, 16 and 32 bit/sample color depths. 360 degree environment maps can be inspected with the script ``7_panorama_viewer.py`` or used by the notebook ``10_2_read_hdr_360deg_env_map.ipynb``. " |
| 221 | + ] |
| 222 | + }, |
| 223 | + { |
| 224 | + "cell_type": "code", |
| 225 | + "execution_count": 11, |
| 226 | + "metadata": {}, |
| 227 | + "outputs": [], |
| 228 | + "source": [ |
| 229 | + "rt.save_image(\"rt_output_8bps.jpg\")" |
| 230 | + ] |
| 231 | + }, |
| 232 | + { |
| 233 | + "cell_type": "code", |
| 234 | + "execution_count": 12, |
| 235 | + "metadata": {}, |
| 236 | + "outputs": [], |
| 237 | + "source": [ |
| 238 | + "rt.save_image(\"rt_output_16bps.tif\", bps=\"Bps16\")" |
| 239 | + ] |
| 240 | + }, |
| 241 | + { |
| 242 | + "cell_type": "code", |
| 243 | + "execution_count": 13, |
| 244 | + "metadata": {}, |
| 245 | + "outputs": [], |
| 246 | + "source": [ |
| 247 | + "rt.save_image(\"rt_output_32bps.tif\", bps=\"Bps32\")" |
| 248 | + ] |
| 249 | + }, |
| 250 | + { |
| 251 | + "cell_type": "markdown", |
| 252 | + "metadata": {}, |
| 253 | + "source": [ |
| 254 | + "Save the image also in Radiance file, if you have OpenCV installed." |
| 255 | + ] |
| 256 | + }, |
| 257 | + { |
| 258 | + "cell_type": "code", |
| 259 | + "execution_count": 14, |
| 260 | + "metadata": {}, |
| 261 | + "outputs": [ |
| 262 | + { |
| 263 | + "name": "stdout", |
| 264 | + "output_type": "stream", |
| 265 | + "text": [ |
| 266 | + "float32 (1084, 1924, 3) 1.2066944\n" |
| 267 | + ] |
| 268 | + }, |
| 269 | + { |
| 270 | + "data": { |
| 271 | + "text/plain": [ |
| 272 | + "True" |
| 273 | + ] |
| 274 | + }, |
| 275 | + "execution_count": 14, |
| 276 | + "metadata": {}, |
| 277 | + "output_type": "execute_result" |
| 278 | + } |
| 279 | + ], |
| 280 | + "source": [ |
| 281 | + "import cv2\n", |
| 282 | + "\n", |
| 283 | + "a = rt.get_rt_output(\"Bps32\", \"BGR\") # ray tracing output in 32bps depth and channels order required for .hdr format\n", |
| 284 | + "print(a.dtype, a.shape, np.max(a)) # note it is a floating point array, and strong lights are above 1.0 values\n", |
| 285 | + "\n", |
| 286 | + "cv2.imwrite('rt_output_32bps.hdr', a)" |
| 287 | + ] |
| 288 | + }, |
| 289 | + { |
| 290 | + "cell_type": "code", |
| 291 | + "execution_count": 15, |
| 292 | + "metadata": {}, |
| 293 | + "outputs": [], |
| 294 | + "source": [ |
| 295 | + "rt.close()" |
| 296 | + ] |
| 297 | + } |
| 298 | + ], |
| 299 | + "metadata": { |
| 300 | + "kernelspec": { |
| 301 | + "display_name": "Python 3", |
| 302 | + "language": "python", |
| 303 | + "name": "python3" |
| 304 | + }, |
| 305 | + "language_info": { |
| 306 | + "codemirror_mode": { |
| 307 | + "name": "ipython", |
| 308 | + "version": 3 |
| 309 | + }, |
| 310 | + "file_extension": ".py", |
| 311 | + "mimetype": "text/x-python", |
| 312 | + "name": "python", |
| 313 | + "nbconvert_exporter": "python", |
| 314 | + "pygments_lexer": "ipython3", |
| 315 | + "version": "3.7.4" |
| 316 | + } |
| 317 | + }, |
| 318 | + "nbformat": 4, |
| 319 | + "nbformat_minor": 2 |
| 320 | +} |
0 commit comments