diff --git a/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/.ipynb_checkpoints/Untitled-checkpoint.ipynb new file mode 100644 index 00000000..363fcab7 --- /dev/null +++ b/.ipynb_checkpoints/Untitled-checkpoint.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/README.md b/README.md index f044c821..2bb88229 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,104 @@ CUDA Denoiser For CUDA Path Tracer ================================== -**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 4** +**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 3** -* (TODO) YOUR NAME HERE -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Zhangkaiwen Chu + * [LinkedIn](https://www.linkedin.com/in/zhangkaiwen-chu-b53060225/) +* Tested on: Windows 10, R7-5800H @ 3.20GHz 16GB, RTX 3070 Laptop GPU 16310MB (Personal Laptop) -### (TODO: Your README) +This project implement a pathtracing denoiser that uses geometry buffers to guide a smoothing filter, which is based on the paper ["Edge-Avoiding A-Trous Wavelet Transform for fast Global Illumination Filtering"](https://jo.dreggn.org/home/2010_atrous.pdf). This paper use wavelet to approximmate Gaussion filter. By guiding the filter with edge-stopping function, the denoiser will not make the whole image blurry. -*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. +| raw pathtraced image | simple blur | blur guided by G-buffers | +|---|---|---| +|![](img/origin.png)|![](img/blur.png)|![](img/denoise.png) +| SSIM = 0.5651 | SSIM = 0.8352 | SSIM = 0.9704 | +The G-buffer contains position and surface normal informations. +| scene | position | surface normal | +|---|---|---| +|![](img/scene.png)|![](img/position.png)|![](img/normal.png)| + +## Performance Analysis + +To measure the performance, a ground truth image is used to compare with denoised image. The ground truth image is generated by the pathtracer with 5000 iterations, and the metric is Structural Similarity Index (SSIM). + + +### Runtime Analysis + +**Runtime with Different Resolutions** + +![](img/plot2.png) + + +The denoiser is using a 80*80 filter size. As expected, the runtime of both the pathtracer and the denoiser scale linearly with the number of pixels when the number of pixels is not too small. When the number of pixels is small, other overheads may dominate the runtime. + +**Runtime with Different Filter Size** +![](img/plot3.png) + +The number of A-Trous iterations determines the actual filter size. The runtime scales linearly with the number of A-Trous iterations. This is because each iteration the work load of the A-Trous algorithm is the same. + + +### Image Quality Analysis + +**Denoising with Different # of Iterations** + +| 1 iteration | position | surface normal | +|---|---|---| +|![](img/10itorigin.png)|![](img/1it.png)|![](img/10it.png)| +| SSIM = 0.4597 | SSIM = 0.9668 | SSIM = 0.9704 | + +![](img/plot1.png) + +With a denoiser, the pathtracer can reach 0.97 SSIM with only 7 iterations, which needs about 1500 iterations without denoiser, so the denoiser can greatly reduce the number of iterations needed to get an acceptly smooth result. + +**Denoising with Different Filter Size** + +| 0 A-Trous iteration | 1 A-Trous iteration | 2 A-Trous iteration | +|---|---|---| +|![](img/0.png)|![](img/1.png)|![](img/2.png)| +| SSIM = 0.4597 | SSIM = 0.7744 | SSIM = 0.9261 | + +| 3 A-Trous iteration | 4 A-Trous iteration | 5 A-Trous iteration | +|---|---|---| +|![](img/3.png)|![](img/4.png)|![](img/5.png)| +| SSIM = 0.9653 | SSIM = 0.9704 | SSIM = 0.9704 | + +The number of A-Trous iterations determines the actual filter size. The image quality didn't scale uniformly with the filter size. This is because when the filter size is small, increase the filter size will increase the number of pixels having similar color, position and normal greatly, while when the filter size is large, continue increasing the filter size will only add pixels far from the center to the filter, and these newly added pixels are likely to have different position, color anr surface norm, thus contribute little to the result. + +**Denoising with Different Scene** + +| cornell box with ceiling light | cornell box | +|---|---| +|![](img/denoise.png)|![](img/cornell_denoise.png) + +The denoising performance also greatly depend on scene. The result of denoising the original cornell box is apperantly worse than denoising the cornell box with ceiling light. With a smaller light, fewer number of rays can finally pass the light, so the image is much noisier with the same number of iterations. + +**Denoising with Different Material** + +| Denoised Result| +|---| +|![](img/denoise.png) + +The denoiser is effective on difussive materials, and less effective on reflective materials, especially on reflective sphere. On difussive materials, the color is likely to be uniform, so the denoiser can work well. But on reflective materials, the color likely vary a lot among pixels. Also, the surface normal changes a lot among the pixels on sphere, so the weight on neighboring pixels will be low, thus the denoising will be less effective. + +### Compare with Gaussian Filter + +The generating kernel is used to approximate gaussian filters. By comparing the A-Trous filter and gaussian filter, we can know whether the approximation is successful. + +| A-Trous Filter | Gaussian Filter | +|---|---| +|![](img/denoise.png)|![](img/gaussian.png) +| SSIM = 0.9704 | SSIM = 0.9559 | + +However, the gaussian filter have a worse performance with the same filter size as the A-Trous filter. Gaussian filter blurred the reflective sphere. A possible reason is the A-Trous filter have several iterations, and it uses the edge-stopping function at each iteration, so the edge-stopping function actually works for multiple times, and thus the weighted average is more accurate. Another hypothesis is there are still better parameters for Gaussian filter. I choose variance = 30, sigma_c = 5.41, sigma_n = 0.30, sigma_x = 4.94. By tuning variance and weights, we may achieve a better result. However, the runtime of Gaussian filter is not acceptable. With filtersize = 65, the runtime of Gaussian filter is 71ms, while the runtime of A-Trous filter is only 6.15ms. This is because the A-Trous filter's time complexity is O(nlog(k)), while the Gaussian filter's time complexity is O(nk^2), where n is the pixel count and k is the filter size. + +### G-Buffer Optimization + +Given the depth, the camera position and the pixel index, we can calculate the position of the object. Thus, we only need to store the depth information in the G-buffer instead of the whole position. This [paper](http://jcgt.org/published/0003/02/01/paper.pdf) also provides a method to encode a 3-d normal vector with oct. With this two method, we can half the size of the G-buff. Here is the runtime + +| | no optimization | optimize normal | optimize position | optimize both | +|--------------|-----------------|-----------------|-------------------|---------------| +| time elapsed (ms) | 6.15 | 6.258 | 6.104 | 6.517 | + +It shows that use depth to store the position information can reduce the runtime slightly, while oct-encode the normal will enlarge the runtime. This may because we didn't futher encode the oct-encoded normal to a single float. Also, the computation for oct-encoding and decoding is a little complex, so the increased computation time is larger than the saved I/O time. \ No newline at end of file diff --git a/Untitled.ipynb b/Untitled.ipynb new file mode 100644 index 00000000..e9bad9b1 --- /dev/null +++ b/Untitled.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "a63a4781-85ee-425c-a7bb-d9ddc0bed379", + "metadata": {}, + "outputs": [], + "source": [ + "613.33" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e33a3ba-cba3-4bab-bd08-ad8a9365668e", + "metadata": {}, + "outputs": [], + "source": [ + "1.00 36.5 74.9 111 148 186\n", + "0 1 2 3 4 5" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "d1bbc242-bb22-4ac8-a5f9-e9e9c9dd3a1b", + "metadata": {}, + "outputs": [], + "source": [ + "from sewar.full_ref import msssim,ssim,rmse,uqi" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "57d94353-2465-4cfc-8707-916325d5c4d5", + "metadata": {}, + "outputs": [], + "source": [ + "from PIL import Image" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "3a7a30c9-5c3c-4b48-afc8-3f46918c522e", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "d79f2ca6-8db1-4be2-b9bf-5d2052a8888b", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "8f6d8438-4a21-4c0e-a595-82a1904cc76e", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'str' object has no attribute 'dtype'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_1212/2738552631.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmsssim\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mr'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\scene.png'\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;34mr'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\data\\0.png'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python39\\lib\\site-packages\\sewar\\full_ref.py\u001b[0m in \u001b[0;36mmsssim\u001b[1;34m(GT, P, weights, ws, K1, K2, MAX)\u001b[0m\n\u001b[0;32m 289\u001b[0m \t\"\"\"\n\u001b[0;32m 290\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mMAX\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 291\u001b[1;33m \u001b[0mMAX\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0miinfo\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGT\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdtype\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmax\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 292\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 293\u001b[0m \u001b[0mGT\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mP\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0m_initial_check\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGT\u001b[0m\u001b[1;33m,\u001b[0m\u001b[0mP\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mAttributeError\u001b[0m: 'str' object has no attribute 'dtype'" + ] + } + ], + "source": [ + "msssim(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\scene.png',r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\data\\0.png')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ec7d75a3-d4d9-4c2a-8333-1ed04a00c1b5", + "metadata": {}, + "outputs": [], + "source": [ + "image = Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\scene.png')" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "6db2f38f-d558-4331-86eb-f887b7db8d51", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.9703717774329762, 0.9719030877572862)" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ssim(np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\scene.png')),np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\denoise.png')))" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "442b67b3-78c7-438e-a4ef-14d89c263f2f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.9704269793875967, 0.9723324665068347)" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ssim(np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\scene.png')),np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\10it.png')))" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "a6239584-a631-4faf-a261-87170341e550", + "metadata": {}, + "outputs": [], + "source": [ + "score = [ssim(np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\scene.png')),np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\data\\cornell.2022-10-21_10-32-13z.'+str(i+1)+'samp.png'))) for i in range(100)]" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "e0a97102-881a-4916-ac38-494ddd47f2a1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(list(range(1,101)),[s[0] for s in score])\n", + "plt.xlabel('# of iterations')\n", + "plt.ylabel('SSIM')\n", + "plt.savefig('plot1.png',dpi=1200)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "15ea6a23-2636-4c86-945d-153414a6876b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.953756155962373, 0.955935875702111)" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ssim(np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\scene.png')),np.array(Image.open(r'C:\\Users\\chuzh\\Study\\CIS565\\Project4-CUDA-Denoiser\\img\\gaussian.png')))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f57e48d-422c-4a04-8f3b-b93f72758baa", + "metadata": {}, + "outputs": [], + "source": [ + "[26.24,53.57,161.03,608.30,2391]\n", + "[8.387,18.63,59.94,219.98,889.5]" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "2f04c5c1-b8c7-45ed-a5f1-47fa3a1923d1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.loglog([100**2,200**2,400**2,800**2,1600**2,3200**2],[0.8173,1.0651,1.9423,5.509,18.502,70.84],c='r',label='pathtrace')\n", + "plt.loglog([100**2,200**2,400**2,800**2,1600**2,3200**2],[2.42,3.5257,5.6791,6.359,22.024,95.77],c='b',label='denoise')\n", + "plt.xlabel('pixelcount')\n", + "plt.ylabel('time elapsed(ms)')\n", + "plt.legend()\n", + "plt.savefig('plot2.png',dpi=1200)" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "077538fb-43c3-449d-a5c2-adf45366cab8", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot([1.96,2.74,4.16,4.45,5.33,5.98],c='b')\n", + "plt.xlabel('# of A-Trous Iterations')\n", + "plt.ylabel('time elapsed(ms)')\n", + "plt.savefig('plot3.png',dpi=1200)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11deab71-105c-4225-9d56-d97931308aed", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/data/0.png b/data/0.png new file mode 100644 index 00000000..2200f578 Binary files /dev/null and b/data/0.png differ diff --git a/data/1.png b/data/1.png new file mode 100644 index 00000000..1dee02f8 Binary files /dev/null and b/data/1.png differ diff --git a/data/2.png b/data/2.png new file mode 100644 index 00000000..635201d2 Binary files /dev/null and b/data/2.png differ diff --git a/data/3.png b/data/3.png new file mode 100644 index 00000000..1a99a334 Binary files /dev/null and b/data/3.png differ diff --git a/data/4.png b/data/4.png new file mode 100644 index 00000000..04be6f19 Binary files /dev/null and b/data/4.png differ diff --git a/data/5.png b/data/5.png new file mode 100644 index 00000000..58d95cee Binary files /dev/null and b/data/5.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.100samp.png b/data/cornell.2022-10-21_10-32-13z.100samp.png new file mode 100644 index 00000000..9d76d23f Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.100samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.10samp.png b/data/cornell.2022-10-21_10-32-13z.10samp.png new file mode 100644 index 00000000..58d95cee Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.10samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.11samp.png b/data/cornell.2022-10-21_10-32-13z.11samp.png new file mode 100644 index 00000000..51ec2444 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.11samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.12samp.png b/data/cornell.2022-10-21_10-32-13z.12samp.png new file mode 100644 index 00000000..021dbed0 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.12samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.13samp.png b/data/cornell.2022-10-21_10-32-13z.13samp.png new file mode 100644 index 00000000..e0e69810 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.13samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.14samp.png b/data/cornell.2022-10-21_10-32-13z.14samp.png new file mode 100644 index 00000000..027ddc1c Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.14samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.15samp.png b/data/cornell.2022-10-21_10-32-13z.15samp.png new file mode 100644 index 00000000..a1bbc105 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.15samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.16samp.png b/data/cornell.2022-10-21_10-32-13z.16samp.png new file mode 100644 index 00000000..a78e9ca9 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.16samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.17samp.png b/data/cornell.2022-10-21_10-32-13z.17samp.png new file mode 100644 index 00000000..d0c53fb3 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.17samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.18samp.png b/data/cornell.2022-10-21_10-32-13z.18samp.png new file mode 100644 index 00000000..950dd242 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.18samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.19samp.png b/data/cornell.2022-10-21_10-32-13z.19samp.png new file mode 100644 index 00000000..c1715ab6 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.19samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.1samp.png b/data/cornell.2022-10-21_10-32-13z.1samp.png new file mode 100644 index 00000000..84be5793 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.1samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.20samp.png b/data/cornell.2022-10-21_10-32-13z.20samp.png new file mode 100644 index 00000000..cca8fda2 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.20samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.21samp.png b/data/cornell.2022-10-21_10-32-13z.21samp.png new file mode 100644 index 00000000..7c6dbf9b Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.21samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.22samp.png b/data/cornell.2022-10-21_10-32-13z.22samp.png new file mode 100644 index 00000000..ce948c84 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.22samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.23samp.png b/data/cornell.2022-10-21_10-32-13z.23samp.png new file mode 100644 index 00000000..8b9d2c33 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.23samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.24samp.png b/data/cornell.2022-10-21_10-32-13z.24samp.png new file mode 100644 index 00000000..dbdab831 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.24samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.25samp.png b/data/cornell.2022-10-21_10-32-13z.25samp.png new file mode 100644 index 00000000..df696fb3 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.25samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.26samp.png b/data/cornell.2022-10-21_10-32-13z.26samp.png new file mode 100644 index 00000000..228aeb8b Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.26samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.27samp.png b/data/cornell.2022-10-21_10-32-13z.27samp.png new file mode 100644 index 00000000..1a723962 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.27samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.28samp.png b/data/cornell.2022-10-21_10-32-13z.28samp.png new file mode 100644 index 00000000..7e1a54cc Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.28samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.29samp.png b/data/cornell.2022-10-21_10-32-13z.29samp.png new file mode 100644 index 00000000..d9206fde Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.29samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.2samp.png b/data/cornell.2022-10-21_10-32-13z.2samp.png new file mode 100644 index 00000000..af5f5cf3 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.2samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.30samp.png b/data/cornell.2022-10-21_10-32-13z.30samp.png new file mode 100644 index 00000000..6fa9c717 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.30samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.31samp.png b/data/cornell.2022-10-21_10-32-13z.31samp.png new file mode 100644 index 00000000..88ff785e Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.31samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.32samp.png b/data/cornell.2022-10-21_10-32-13z.32samp.png new file mode 100644 index 00000000..73b1f482 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.32samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.33samp.png b/data/cornell.2022-10-21_10-32-13z.33samp.png new file mode 100644 index 00000000..fa2c0055 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.33samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.34samp.png b/data/cornell.2022-10-21_10-32-13z.34samp.png new file mode 100644 index 00000000..c1675b4d Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.34samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.35samp.png b/data/cornell.2022-10-21_10-32-13z.35samp.png new file mode 100644 index 00000000..08ad0ad1 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.35samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.36samp.png b/data/cornell.2022-10-21_10-32-13z.36samp.png new file mode 100644 index 00000000..ca6982c7 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.36samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.37samp.png b/data/cornell.2022-10-21_10-32-13z.37samp.png new file mode 100644 index 00000000..4ff75fc9 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.37samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.38samp.png b/data/cornell.2022-10-21_10-32-13z.38samp.png new file mode 100644 index 00000000..7808bf44 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.38samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.39samp.png b/data/cornell.2022-10-21_10-32-13z.39samp.png new file mode 100644 index 00000000..a66d9c22 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.39samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.3samp.png b/data/cornell.2022-10-21_10-32-13z.3samp.png new file mode 100644 index 00000000..39938ef7 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.3samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.40samp.png b/data/cornell.2022-10-21_10-32-13z.40samp.png new file mode 100644 index 00000000..0233dc74 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.40samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.41samp.png b/data/cornell.2022-10-21_10-32-13z.41samp.png new file mode 100644 index 00000000..7931676f Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.41samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.42samp.png b/data/cornell.2022-10-21_10-32-13z.42samp.png new file mode 100644 index 00000000..8d7913de Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.42samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.43samp.png b/data/cornell.2022-10-21_10-32-13z.43samp.png new file mode 100644 index 00000000..536fdf1f Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.43samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.44samp.png b/data/cornell.2022-10-21_10-32-13z.44samp.png new file mode 100644 index 00000000..e62b2e3e Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.44samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.45samp.png b/data/cornell.2022-10-21_10-32-13z.45samp.png new file mode 100644 index 00000000..481b5715 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.45samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.46samp.png b/data/cornell.2022-10-21_10-32-13z.46samp.png new file mode 100644 index 00000000..2c5d0e57 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.46samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.47samp.png b/data/cornell.2022-10-21_10-32-13z.47samp.png new file mode 100644 index 00000000..dc89eab3 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.47samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.48samp.png b/data/cornell.2022-10-21_10-32-13z.48samp.png new file mode 100644 index 00000000..19b848f5 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.48samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.49samp.png b/data/cornell.2022-10-21_10-32-13z.49samp.png new file mode 100644 index 00000000..91df072c Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.49samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.4samp.png b/data/cornell.2022-10-21_10-32-13z.4samp.png new file mode 100644 index 00000000..ee213657 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.4samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.50samp.png b/data/cornell.2022-10-21_10-32-13z.50samp.png new file mode 100644 index 00000000..f9f34286 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.50samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.51samp.png b/data/cornell.2022-10-21_10-32-13z.51samp.png new file mode 100644 index 00000000..d0fb2122 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.51samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.52samp.png b/data/cornell.2022-10-21_10-32-13z.52samp.png new file mode 100644 index 00000000..e4a2bfad Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.52samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.53samp.png b/data/cornell.2022-10-21_10-32-13z.53samp.png new file mode 100644 index 00000000..4ecf18f8 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.53samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.54samp.png b/data/cornell.2022-10-21_10-32-13z.54samp.png new file mode 100644 index 00000000..b4765535 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.54samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.55samp.png b/data/cornell.2022-10-21_10-32-13z.55samp.png new file mode 100644 index 00000000..99b1df63 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.55samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.56samp.png b/data/cornell.2022-10-21_10-32-13z.56samp.png new file mode 100644 index 00000000..4d94a1cb Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.56samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.57samp.png b/data/cornell.2022-10-21_10-32-13z.57samp.png new file mode 100644 index 00000000..732e6287 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.57samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.58samp.png b/data/cornell.2022-10-21_10-32-13z.58samp.png new file mode 100644 index 00000000..8c2b514b Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.58samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.59samp.png b/data/cornell.2022-10-21_10-32-13z.59samp.png new file mode 100644 index 00000000..20c4e304 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.59samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.5samp.png b/data/cornell.2022-10-21_10-32-13z.5samp.png new file mode 100644 index 00000000..fd8ec85f Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.5samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.60samp.png b/data/cornell.2022-10-21_10-32-13z.60samp.png new file mode 100644 index 00000000..f6498a91 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.60samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.61samp.png b/data/cornell.2022-10-21_10-32-13z.61samp.png new file mode 100644 index 00000000..40920f28 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.61samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.62samp.png b/data/cornell.2022-10-21_10-32-13z.62samp.png new file mode 100644 index 00000000..15ab503c Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.62samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.63samp.png b/data/cornell.2022-10-21_10-32-13z.63samp.png new file mode 100644 index 00000000..6ca1eb45 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.63samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.64samp.png b/data/cornell.2022-10-21_10-32-13z.64samp.png new file mode 100644 index 00000000..b775d969 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.64samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.65samp.png b/data/cornell.2022-10-21_10-32-13z.65samp.png new file mode 100644 index 00000000..562f147c Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.65samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.66samp.png b/data/cornell.2022-10-21_10-32-13z.66samp.png new file mode 100644 index 00000000..ffe2d09a Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.66samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.67samp.png b/data/cornell.2022-10-21_10-32-13z.67samp.png new file mode 100644 index 00000000..049f5852 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.67samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.68samp.png b/data/cornell.2022-10-21_10-32-13z.68samp.png new file mode 100644 index 00000000..faf48bf4 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.68samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.69samp.png b/data/cornell.2022-10-21_10-32-13z.69samp.png new file mode 100644 index 00000000..c1a48178 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.69samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.6samp.png b/data/cornell.2022-10-21_10-32-13z.6samp.png new file mode 100644 index 00000000..6404ab3e Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.6samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.70samp.png b/data/cornell.2022-10-21_10-32-13z.70samp.png new file mode 100644 index 00000000..6cddd124 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.70samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.71samp.png b/data/cornell.2022-10-21_10-32-13z.71samp.png new file mode 100644 index 00000000..34d3c66a Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.71samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.72samp.png b/data/cornell.2022-10-21_10-32-13z.72samp.png new file mode 100644 index 00000000..2fdcedf9 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.72samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.73samp.png b/data/cornell.2022-10-21_10-32-13z.73samp.png new file mode 100644 index 00000000..9b97b6c6 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.73samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.74samp.png b/data/cornell.2022-10-21_10-32-13z.74samp.png new file mode 100644 index 00000000..d7f72fe5 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.74samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.75samp.png b/data/cornell.2022-10-21_10-32-13z.75samp.png new file mode 100644 index 00000000..b8af369a Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.75samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.76samp.png b/data/cornell.2022-10-21_10-32-13z.76samp.png new file mode 100644 index 00000000..48d3a577 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.76samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.77samp.png b/data/cornell.2022-10-21_10-32-13z.77samp.png new file mode 100644 index 00000000..0a48b1b4 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.77samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.78samp.png b/data/cornell.2022-10-21_10-32-13z.78samp.png new file mode 100644 index 00000000..4bf4c63e Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.78samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.79samp.png b/data/cornell.2022-10-21_10-32-13z.79samp.png new file mode 100644 index 00000000..7fe44e17 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.79samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.7samp.png b/data/cornell.2022-10-21_10-32-13z.7samp.png new file mode 100644 index 00000000..23a8856a Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.7samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.80samp.png b/data/cornell.2022-10-21_10-32-13z.80samp.png new file mode 100644 index 00000000..4dc755d3 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.80samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.81samp.png b/data/cornell.2022-10-21_10-32-13z.81samp.png new file mode 100644 index 00000000..8a1d8a47 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.81samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.82samp.png b/data/cornell.2022-10-21_10-32-13z.82samp.png new file mode 100644 index 00000000..d60ce888 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.82samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.83samp.png b/data/cornell.2022-10-21_10-32-13z.83samp.png new file mode 100644 index 00000000..152c91be Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.83samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.84samp.png b/data/cornell.2022-10-21_10-32-13z.84samp.png new file mode 100644 index 00000000..a98897b3 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.84samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.85samp.png b/data/cornell.2022-10-21_10-32-13z.85samp.png new file mode 100644 index 00000000..b9b0459b Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.85samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.86samp.png b/data/cornell.2022-10-21_10-32-13z.86samp.png new file mode 100644 index 00000000..e129a0dc Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.86samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.87samp.png b/data/cornell.2022-10-21_10-32-13z.87samp.png new file mode 100644 index 00000000..9aa17f46 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.87samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.88samp.png b/data/cornell.2022-10-21_10-32-13z.88samp.png new file mode 100644 index 00000000..f3190a6f Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.88samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.89samp.png b/data/cornell.2022-10-21_10-32-13z.89samp.png new file mode 100644 index 00000000..97058460 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.89samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.8samp.png b/data/cornell.2022-10-21_10-32-13z.8samp.png new file mode 100644 index 00000000..0ff2a311 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.8samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.90samp.png b/data/cornell.2022-10-21_10-32-13z.90samp.png new file mode 100644 index 00000000..f7033b51 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.90samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.91samp.png b/data/cornell.2022-10-21_10-32-13z.91samp.png new file mode 100644 index 00000000..19a8acb8 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.91samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.92samp.png b/data/cornell.2022-10-21_10-32-13z.92samp.png new file mode 100644 index 00000000..0938ac65 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.92samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.93samp.png b/data/cornell.2022-10-21_10-32-13z.93samp.png new file mode 100644 index 00000000..fa8b8ff1 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.93samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.94samp.png b/data/cornell.2022-10-21_10-32-13z.94samp.png new file mode 100644 index 00000000..2bde52d2 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.94samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.95samp.png b/data/cornell.2022-10-21_10-32-13z.95samp.png new file mode 100644 index 00000000..e32d3e68 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.95samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.96samp.png b/data/cornell.2022-10-21_10-32-13z.96samp.png new file mode 100644 index 00000000..5beaad33 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.96samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.97samp.png b/data/cornell.2022-10-21_10-32-13z.97samp.png new file mode 100644 index 00000000..a79aac20 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.97samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.98samp.png b/data/cornell.2022-10-21_10-32-13z.98samp.png new file mode 100644 index 00000000..e8fcb06b Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.98samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.99samp.png b/data/cornell.2022-10-21_10-32-13z.99samp.png new file mode 100644 index 00000000..8768e557 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.99samp.png differ diff --git a/data/cornell.2022-10-21_10-32-13z.9samp.png b/data/cornell.2022-10-21_10-32-13z.9samp.png new file mode 100644 index 00000000..e4b71ae6 Binary files /dev/null and b/data/cornell.2022-10-21_10-32-13z.9samp.png differ diff --git a/dummy b/dummy new file mode 100644 index 00000000..e69de29b diff --git a/img/0.png b/img/0.png new file mode 100644 index 00000000..2200f578 Binary files /dev/null and b/img/0.png differ diff --git a/img/1.png b/img/1.png new file mode 100644 index 00000000..1dee02f8 Binary files /dev/null and b/img/1.png differ diff --git a/img/10it.png b/img/10it.png new file mode 100644 index 00000000..58d95cee Binary files /dev/null and b/img/10it.png differ diff --git a/img/10itorigin.png b/img/10itorigin.png new file mode 100644 index 00000000..2200f578 Binary files /dev/null and b/img/10itorigin.png differ diff --git a/img/1it.png b/img/1it.png new file mode 100644 index 00000000..84be5793 Binary files /dev/null and b/img/1it.png differ diff --git a/img/2.png b/img/2.png new file mode 100644 index 00000000..635201d2 Binary files /dev/null and b/img/2.png differ diff --git a/img/3.png b/img/3.png new file mode 100644 index 00000000..1a99a334 Binary files /dev/null and b/img/3.png differ diff --git a/img/4.png b/img/4.png new file mode 100644 index 00000000..04be6f19 Binary files /dev/null and b/img/4.png differ diff --git a/img/5.png b/img/5.png new file mode 100644 index 00000000..58d95cee Binary files /dev/null and b/img/5.png differ diff --git a/img/blooper.png b/img/blooper.png new file mode 100644 index 00000000..1475a3f5 Binary files /dev/null and b/img/blooper.png differ diff --git a/img/blur.png b/img/blur.png new file mode 100644 index 00000000..4180b0fb Binary files /dev/null and b/img/blur.png differ diff --git a/img/cornell_denoise.png b/img/cornell_denoise.png new file mode 100644 index 00000000..71b9d3ff Binary files /dev/null and b/img/cornell_denoise.png differ diff --git a/img/denoise.png b/img/denoise.png new file mode 100644 index 00000000..66f7b9f0 Binary files /dev/null and b/img/denoise.png differ diff --git a/img/denoise2.png b/img/denoise2.png new file mode 100644 index 00000000..a2f6cb77 Binary files /dev/null and b/img/denoise2.png differ diff --git a/img/gaussian.png b/img/gaussian.png new file mode 100644 index 00000000..5854de63 Binary files /dev/null and b/img/gaussian.png differ diff --git a/img/normal.png b/img/normal.png new file mode 100644 index 00000000..7a99dc66 Binary files /dev/null and b/img/normal.png differ diff --git a/img/origin.png b/img/origin.png new file mode 100644 index 00000000..42967c98 Binary files /dev/null and b/img/origin.png differ diff --git a/img/plot1.png b/img/plot1.png new file mode 100644 index 00000000..4ee0daaf Binary files /dev/null and b/img/plot1.png differ diff --git a/img/plot2.png b/img/plot2.png new file mode 100644 index 00000000..558e92db Binary files /dev/null and b/img/plot2.png differ diff --git a/img/plot3.png b/img/plot3.png new file mode 100644 index 00000000..b51e1b1a Binary files /dev/null and b/img/plot3.png differ diff --git a/img/position.png b/img/position.png new file mode 100644 index 00000000..b82d2bc0 Binary files /dev/null and b/img/position.png differ diff --git a/img/scene.png b/img/scene.png new file mode 100644 index 00000000..1e992575 Binary files /dev/null and b/img/scene.png differ diff --git a/scenes/cornell.txt b/scenes/cornell.txt index 83ff8202..77ad5512 100644 --- a/scenes/cornell.txt +++ b/scenes/cornell.txt @@ -52,7 +52,7 @@ EMITTANCE 0 CAMERA RES 800 800 FOVY 45 -ITERATIONS 5000 +ITERATIONS 10 DEPTH 8 FILE cornell EYE 0.0 5 10.5 diff --git a/scenes/cornell_ceiling_light.txt b/scenes/cornell_ceiling_light.txt index 15af5f19..59b42eeb 100644 --- a/scenes/cornell_ceiling_light.txt +++ b/scenes/cornell_ceiling_light.txt @@ -52,7 +52,7 @@ EMITTANCE 0 CAMERA RES 800 800 FOVY 45 -ITERATIONS 10 +ITERATIONS 30 DEPTH 8 FILE cornell EYE 0.0 5 10.5 diff --git a/src/main.cpp b/src/main.cpp index 4092ae4a..9e8eb55c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,14 @@ int iteration; int width; int height; +cudaEvent_t start; +cudaEvent_t stop; + +float t; +float t_denoise = 0; +float t_pathtrace = 0; +float count_denoise = 0; + //------------------------------- //-------------MAIN-------------- //------------------------------- @@ -91,6 +99,9 @@ int main(int argc, char** argv) { // Initialize CUDA and GL components init(); + cudaEventCreate(&start); + cudaEventCreate(&stop); + // GLFW main loop mainLoop(); @@ -102,6 +113,8 @@ void saveImage() { // output image file image img(width, height); + update(ui_denoise); + for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int index = x + (y * width); @@ -150,6 +163,7 @@ void runCuda() { // No data is moved (Win & Linux). When mapped to CUDA, OpenGL should not use this buffer if (iteration == 0) { + t_pathtrace = 0.f; pathtraceFree(); pathtraceInit(scene); } @@ -162,12 +176,41 @@ void runCuda() { // execute the kernel int frame = 0; + + cudaEventRecord(start); + pathtrace(frame, iteration); + + + + cudaEventRecord(stop); + cudaEventSynchronize(stop); + cudaEventElapsedTime(&t, start, stop); + t_pathtrace += t; + if (iteration == ui_iterations){ + printf("pathtrace time:%f\n", t_pathtrace / iteration); + } } - if (ui_showGbuffer) { + if (ui_denoise) { + cudaEventRecord(start); + + denoise(ui_colorWeight, ui_normalWeight, ui_positionWeight, ui_filterSize); + + cudaEventRecord(stop); + cudaEventSynchronize(stop); + cudaEventElapsedTime(&t, start, stop); + t_denoise += t; + count_denoise++; + + showDenoisedImage(pbo_dptr, iteration); + } else if (ui_showGbuffer) { + count_denoise = 0.f; + t_denoise = 0.f; showGBuffer(pbo_dptr); } else { + count_denoise = 0.f; + t_denoise = 0.f; showImage(pbo_dptr, iteration); } @@ -192,6 +235,9 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods case GLFW_KEY_S: saveImage(); break; + case GLFW_KEY_I: + printf("denoise count:%f,denoise time:%f\n", count_denoise,t_denoise / count_denoise); + break; case GLFW_KEY_SPACE: camchanged = true; renderState = &scene->state; @@ -203,6 +249,7 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods } void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { + return; if (ImGui::GetIO().WantCaptureMouse) return; leftMousePressed = (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS); rightMousePressed = (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS); @@ -210,6 +257,7 @@ void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { } void mousePositionCallback(GLFWwindow* window, double xpos, double ypos) { + return; if (xpos == lastX || ypos == lastY) return; // otherwise, clicking back into window causes re-start if (leftMousePressed) { // compute new camera parameters diff --git a/src/pathtrace.cu b/src/pathtrace.cu index 23e5f909..876bad7f 100644 --- a/src/pathtrace.cu +++ b/src/pathtrace.cu @@ -16,6 +16,10 @@ #define ERRORCHECK 1 +#define GAUSSIAN 0 + +#define SHARE 0 + #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) void checkCUDAErrorFn(const char *msg, const char *file, int line) { @@ -73,12 +77,14 @@ __global__ void gbufferToPBO(uchar4* pbo, glm::ivec2 resolution, GBufferPixel* g if (x < resolution.x && y < resolution.y) { int index = x + (y * resolution.x); - float timeToIntersect = gBuffer[index].t * 256.0; - +#if OPTIMAL_X + return; +#else pbo[index].w = 0; - pbo[index].x = timeToIntersect; - pbo[index].y = timeToIntersect; - pbo[index].z = timeToIntersect; + pbo[index].x = (gBuffer[index].x.x / 15.f) * 255.f; + pbo[index].y = (gBuffer[index].x.y / 15.f) * 255.f; + pbo[index].z = (gBuffer[index].x.z / 15.f) * 255.f; +#endif } } @@ -91,6 +97,8 @@ static ShadeableIntersection * dev_intersections = NULL; static GBufferPixel* dev_gBuffer = NULL; // TODO: static variables for device memory, any extra info you need, etc // ... +static glm::vec3* dev_denoised_image1 = NULL; +static glm::vec3* dev_denoised_image2 = NULL; void pathtraceInit(Scene *scene) { hst_scene = scene; @@ -114,6 +122,11 @@ void pathtraceInit(Scene *scene) { cudaMalloc(&dev_gBuffer, pixelcount * sizeof(GBufferPixel)); // TODO: initialize any extra device memeory you need + cudaMalloc(&dev_denoised_image1, pixelcount * sizeof(glm::vec3)); + cudaMemset(dev_denoised_image1, 0, pixelcount * sizeof(glm::vec3)); + + cudaMalloc(&dev_denoised_image2, pixelcount * sizeof(glm::vec3)); + cudaMemset(dev_denoised_image2, 0, pixelcount * sizeof(glm::vec3)); checkCUDAError("pathtraceInit"); } @@ -126,6 +139,8 @@ void pathtraceFree() { cudaFree(dev_intersections); cudaFree(dev_gBuffer); // TODO: clean up any extra device memory you created + cudaFree(dev_denoised_image1); + cudaFree(dev_denoised_image2); checkCUDAError("pathtraceFree"); } @@ -273,30 +288,89 @@ __global__ void shadeSimpleMaterials ( } } -__global__ void generateGBuffer ( - int num_paths, - ShadeableIntersection* shadeableIntersections, - PathSegment* pathSegments, - GBufferPixel* gBuffer) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < num_paths) - { - gBuffer[idx].t = shadeableIntersections[idx].t; - } +__host__ __device__ +glm::vec2 signNotZero(glm::vec2 v) { + return glm::vec2((v.x >= 0.f) ? 1.f : -1.f, (v.y >= 0.f) ? 1.f : -1.f); +} + +__host__ __device__ +glm::vec2 f2o(glm::vec3 v) { + glm::vec2 p = glm::vec2(v.x, v.y) * (1.f / (abs(v.x) + abs(v.y) + abs(v.z))); + return (v.z <= 0.f) ? ((1.f - abs(glm::vec2(p.y, p.x))) * signNotZero(p)) : p; +} + +__host__ __device__ +glm::vec3 o2f(glm::vec2 e) { + glm::vec3 v = glm::vec3(e.x, e.y, 1.f - abs(e.x) - abs(e.y)); + if (v.z < 0) { + glm::vec2 vxy = (1.f - glm::vec2(v.y, v.x)) * signNotZero(glm::vec2(v.x, v.y)); + v.x = vxy.x; + v.y = vxy.y; + } + v = glm::normalize(v); + if (isnan(v.x)) v.x = 0.f; + if (isnan(v.y)) v.y = 0.f; + if (isnan(v.z)) v.z = 0.f; + return v; +} + + +__host__ __device__ +glm::vec3 f2g(Camera cam, float x, float y, float z) { + if (z == PI) { + return glm::vec3(0.f, 0.f, 0.f); + } + glm::vec3 direction = glm::normalize(cam.view + - cam.right * cam.pixelLength.x * ((float)x - (float)cam.resolution.x * 0.5f) + - cam.up * cam.pixelLength.y * ((float)y - (float)cam.resolution.y * 0.5f)); + return cam.position + (z-cam.position.z) * direction; +} + + +__global__ void generateGBuffer( + int num_paths, + ShadeableIntersection* shadeableIntersections, + PathSegment* pathSegments, + GBufferPixel* gBuffer) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < num_paths) + { +#if OPTIMAL_N + gBuffer[idx].n = f2o(glm::normalize(shadeableIntersections[idx].surfaceNormal)); +#else + gBuffer[idx].n = shadeableIntersections[idx].surfaceNormal; +#endif +#if OPTIMAL_X + if (shadeableIntersections[idx].t > 0.0f) { + gBuffer[idx].x = (pathSegments[idx].ray.origin + shadeableIntersections[idx].t * glm::normalize(pathSegments[idx].ray.direction)).z; + } + else { + gBuffer[idx].x = PI; + } +#else + if (shadeableIntersections[idx].t > 0.0f) { + gBuffer[idx].x = pathSegments[idx].ray.origin + shadeableIntersections[idx].t * glm::normalize(pathSegments[idx].ray.direction); + } + else { + gBuffer[idx].x = glm::vec3(0.f, 0.f, 0.f); + } +#endif + } } // Add the current iteration's output to the overall image -__global__ void finalGather(int nPaths, glm::vec3 * image, PathSegment * iterationPaths) +__global__ void finalGather(int nPaths, glm::vec3* image, PathSegment* iterationPaths) { - int index = (blockIdx.x * blockDim.x) + threadIdx.x; + int index = (blockIdx.x * blockDim.x) + threadIdx.x; - if (index < nPaths) - { - PathSegment iterationPath = iterationPaths[index]; - image[iterationPath.pixelIndex] += iterationPath.color; - } + if (index < nPaths) + { + PathSegment iterationPath = iterationPaths[index]; + image[iterationPath.pixelIndex] += iterationPath.color; + } } + /** * Wrapper for the __global__ call that sets up the kernel calls and does a ton * of memory management @@ -357,45 +431,45 @@ void pathtrace(int frame, int iter) { // Shoot ray into scene, bounce between objects, push shading chunks // Empty gbuffer - cudaMemset(dev_gBuffer, 0, pixelcount * sizeof(GBufferPixel)); + cudaMemset(dev_gBuffer, 0, pixelcount * sizeof(GBufferPixel)); // clean shading chunks cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection)); - bool iterationComplete = false; - while (!iterationComplete) { + bool iterationComplete = false; + while (!iterationComplete) { // tracing - dim3 numblocksPathSegmentTracing = (num_paths + blockSize1d - 1) / blockSize1d; - computeIntersections <<>> ( - depth - , num_paths - , dev_paths - , dev_geoms - , hst_scene->geoms.size() - , dev_intersections + dim3 numblocksPathSegmentTracing = (num_paths + blockSize1d - 1) / blockSize1d; + computeIntersections <<>> ( + depth + , num_paths + , dev_paths + , dev_geoms + , hst_scene->geoms.size() + , dev_intersections ); - checkCUDAError("trace one bounce"); - cudaDeviceSynchronize(); - - if (depth == 0) { - generateGBuffer<<>>(num_paths, dev_intersections, dev_paths, dev_gBuffer); - } - - depth++; - - shadeSimpleMaterials<<>> ( - iter, - num_paths, - dev_intersections, - dev_paths, - dev_materials - ); - iterationComplete = depth == traceDepth; - } + checkCUDAError("trace one bounce"); + cudaDeviceSynchronize(); + + if (depth == 0) { + generateGBuffer<<>>(num_paths, dev_intersections, dev_paths, dev_gBuffer); + } + + depth++; + + shadeSimpleMaterials<<>> ( + iter, + num_paths, + dev_intersections, + dev_paths, + dev_materials + ); + iterationComplete = depth == traceDepth; + } // Assemble this iteration and apply it to the image - dim3 numBlocksPixels = (pixelcount + blockSize1d - 1) / blockSize1d; + dim3 numBlocksPixels = (pixelcount + blockSize1d - 1) / blockSize1d; finalGather<<>>(num_paths, dev_image, dev_paths); /////////////////////////////////////////////////////////////////////////// @@ -409,6 +483,111 @@ void pathtrace(int frame, int iter) { checkCUDAError("pathtrace"); } +__global__ void ATrousFilter( + int stride, float sigma_c, float sigma_n, float sigma_x, glm::ivec2 resolution, GBufferPixel* gBuffer, glm::vec3* in, glm::vec3* out, const Camera cam) { + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + if ((x < resolution.x) && (y < resolution.y)){ + int index = x + (y * resolution.x); + float k = 0; + glm::vec3 sum = glm::vec3(0.f,0.f,0.f); + float filter[3] = { 3.f / 8.f,1.f / 4.f,1.f / 16.f }; + + for (int i = -2; i <= 2; i++) { + for (int j = -2; j <= 2; j++) { + if (((x + i * stride) <= (resolution.x - 1)) && ((x + i * stride) >= 0) && ((y + j * stride <= resolution.y - 1)) && ((y + j * stride >= 0))) { + + int neighbor = x + i * stride + (y + j * stride) * resolution.x; + //int neighbor = glm::clamp(x + i * stride, 0, resolution.x - 1) + glm::clamp(y + j * stride, 0, resolution.y - 1) * resolution.x; + + + + float wrt = exp(-glm::length(in[index] - in[neighbor]) / sigma_c / sigma_c); +#if OPTIMAL_N + float wn = exp(-glm::length(o2f(gBuffer[index].n) - o2f(gBuffer[neighbor].n)) / sigma_n / sigma_n); +#else + float wn = exp(-glm::length(gBuffer[index].n - gBuffer[neighbor].n) / sigma_n); +#endif +#if OPTIMAL_X + float wx = exp(-glm::length(f2g(cam, x , y , gBuffer[index].x) - f2g(cam, x + i * stride, y + j * stride, gBuffer[neighbor].x)) / sigma_x / sigma_x); +#else + float wx = exp(-glm::length(gBuffer[index].x - gBuffer[neighbor].x) / sigma_x / sigma_x); +#endif + + sum += in[neighbor] * filter[max(abs(i), abs(j))] * wrt * wn * wx; + k += filter[max(abs(i), abs(j))] * wrt * wn * wx; + } + } + } + out[index] = sum / k; + } +} + + + + +__global__ void GaussianFilter( + int filterSize, float sigma_c, float sigma_n, float sigma_x, glm::ivec2 resolution, GBufferPixel* gBuffer, glm::vec3* in, glm::vec3* out) { + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + if ((x < resolution.x) && (y < resolution.y)) { + int index = x + (y * resolution.x); + float k = 0; + glm::vec3 sum = glm::vec3(0.f, 0.f, 0.f); + + for (int i = -filterSize; i <= filterSize; i++) { + for (int j = -filterSize; j <= filterSize; j++) { + if ((x + i > resolution.x - 1) || (x + i < 0) || (y + j > resolution.y - 1) || (y + j < 0)) { + continue; + } + int neighbor = x + i + (y + j) * resolution.x; + float wrt = exp(-glm::length(in[index] - in[neighbor]) / sigma_c / sigma_c); + float wn = exp(-glm::length(gBuffer[index].n - gBuffer[neighbor].n) / sigma_n / sigma_n); + float wx = exp(-glm::length(gBuffer[index].x - gBuffer[neighbor].x) / sigma_x / sigma_x); + + float h = exp(-(i * i + j * j) / (2.f * 30 * 30)); + + sum += in[neighbor] * h * wrt * wn * wx; + k += h * wrt * wn * wx; + } + } + out[index] = sum / k; + } +} + +void denoise(float sigma_c, float sigma_n, float sigma_x, int filterSize) { + const Camera& cam = hst_scene->state.camera; + const dim3 blockSize2d(8, 8); + const dim3 blocksPerGrid2d( + (cam.resolution.x + blockSize2d.x - 1) / blockSize2d.x, + (cam.resolution.y + blockSize2d.y - 1) / blockSize2d.y); + + cudaMemcpy(dev_denoised_image1, dev_image, cam.resolution.x * cam.resolution.y * sizeof(glm::vec3), cudaMemcpyDeviceToDevice); + +#if GAUSSIAN + GaussianFilter <<>> (filterSize / 2, sigma_c, sigma_n, sigma_x, cam.resolution, dev_gBuffer, dev_denoised_image1, dev_denoised_image2); + std::swap(dev_denoised_image1, dev_denoised_image2); +#else + int N = int(ceil(log2((filterSize - 5) / 4.f))) + 1; + for (int i = 0; i < N; i++) { + ATrousFilter << > > (1 << i, sigma_c / (1 << i), sigma_n, sigma_x, cam.resolution, dev_gBuffer, dev_denoised_image1, dev_denoised_image2, cam); + std::swap(dev_denoised_image1, dev_denoised_image2); + } +#endif +} + +void update(bool d) { + const Camera& cam = hst_scene->state.camera; + if (d) { + cudaMemcpy(hst_scene->state.image.data(), dev_denoised_image1, + cam.resolution.x * cam.resolution.y * sizeof(glm::vec3), cudaMemcpyDeviceToHost); + } + else { + cudaMemcpy(hst_scene->state.image.data(), dev_image, + cam.resolution.x * cam.resolution.y * sizeof(glm::vec3), cudaMemcpyDeviceToHost); + } +} + // CHECKITOUT: this kernel "post-processes" the gbuffer/gbuffers into something that you can visualize for debugging. void showGBuffer(uchar4* pbo) { const Camera &cam = hst_scene->state.camera; @@ -431,3 +610,14 @@ const Camera &cam = hst_scene->state.camera; // Send results to OpenGL buffer for rendering sendImageToPBO<<>>(pbo, cam.resolution, iter, dev_image); } + +void showDenoisedImage(uchar4* pbo, int iter) { + const Camera& cam = hst_scene->state.camera; + const dim3 blockSize2d(8, 8); + const dim3 blocksPerGrid2d( + (cam.resolution.x + blockSize2d.x - 1) / blockSize2d.x, + (cam.resolution.y + blockSize2d.y - 1) / blockSize2d.y); + + // Send results to OpenGL buffer for rendering + sendImageToPBO <<>> (pbo, cam.resolution, iter, dev_denoised_image1); +} diff --git a/src/pathtrace.h b/src/pathtrace.h index 9e12f440..b09e20d4 100644 --- a/src/pathtrace.h +++ b/src/pathtrace.h @@ -8,3 +8,6 @@ void pathtraceFree(); void pathtrace(int frame, int iteration); void showGBuffer(uchar4 *pbo); void showImage(uchar4 *pbo, int iter); +void denoise(float sigma_c, float sigma_n, float sigma_x, int filterSize); +void showDenoisedImage(uchar4* pbo, int iter); +void update(bool d); diff --git a/src/sceneStructs.h b/src/sceneStructs.h index da7e558a..06226c71 100644 --- a/src/sceneStructs.h +++ b/src/sceneStructs.h @@ -7,6 +7,9 @@ #define BACKGROUND_COLOR (glm::vec3(0.0f)) +#define OPTIMAL_N 0 +#define OPTIMAL_X 0 + enum GeomType { SPHERE, CUBE, @@ -78,5 +81,14 @@ struct ShadeableIntersection { // CHECKITOUT - a simple struct for storing scene geometry information per-pixel. // What information might be helpful for guiding a denoising filter? struct GBufferPixel { - float t; +#if OPTIMAL_N + glm::vec2 n; +#else + glm::vec3 n; +#endif +#if OPTIMAL_X + float x; +#else + glm::vec3 x; +#endif };