├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── atmosphere ├── constants.h ├── definitions.glsl ├── demo │ ├── demo.cc │ ├── demo.glsl │ ├── demo.h │ ├── demo_main.cc │ └── webgl │ │ ├── demo.html │ │ ├── demo.js │ │ └── precompute.cc ├── functions.glsl ├── model.cc ├── model.h └── reference │ ├── definitions.h │ ├── functions.cc │ ├── functions.h │ ├── functions_test.cc │ ├── model.cc │ ├── model.h │ ├── model_test.cc │ └── model_test.glsl ├── external └── glad │ ├── include │ └── glad │ │ └── glad.h │ ├── regenerate.sh │ └── src │ └── glad.cc ├── index ├── platform └── windows │ ├── CMakelists.txt │ ├── README.txt │ ├── build.bat │ ├── create_glsl_inc.bat │ ├── download_build_run.bat │ ├── download_dependencies.bat │ ├── external │ └── dummy.txt │ ├── generate_project.bat │ └── run.bat ├── precomputed_atmopheric_scattering.cbp ├── text ├── font.inc ├── text_renderer.cc └── text_renderer.h └── tools ├── docgen_main.cc └── docgen_template.html /.gitignore: -------------------------------------------------------------------------------- 1 | output/** 2 | *.glsl.inc 3 | 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/dimensional_types"] 2 | path = external/dimensional_types 3 | url = https://github.com/ebruneton/dimensional_types 4 | [submodule "external/progress_bar"] 5 | path = external/progress_bar 6 | url = https://github.com/ebruneton/progress_bar 7 | [submodule "external/minpng"] 8 | path = external/minpng 9 | url = https://github.com/jrmuizel/minpng 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Eric Bruneton 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Eric Bruneton 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # 3. Neither the name of the copyright holder nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software without 16 | # specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | GPP := g++ 30 | GPP_FLAGS := -Wall -Wmain -pedantic -pedantic-errors -std=c++11 31 | INCLUDE_FLAGS := \ 32 | -I. -Iexternal -Iexternal/dimensional_types -Iexternal/glad/include \ 33 | -Iexternal/progress_bar 34 | DEBUG_FLAGS := -g 35 | RELEASE_FLAGS := -DNDEBUG -O3 -fexpensive-optimizations 36 | 37 | DIRS := atmosphere text tools 38 | HEADERS := $(shell find $(DIRS) -name "*.h") 39 | SOURCES := $(shell find $(DIRS) -name "*.cc") 40 | GLSL_SOURCES := $(shell find $(DIRS) -name "*.glsl") 41 | JS_SOURCES := $(shell find $(DIRS) -name "*.js") 42 | DOC_SOURCES := $(HEADERS) $(SOURCES) $(GLSL_SOURCES) $(JS_SOURCES) index 43 | 44 | all: lint doc test integration_test webgl demo 45 | 46 | # cpplint can be installed with "pip install cpplint". 47 | # We exclude runtime/references checking for functions.h and model_test.cc 48 | # because we can't avoid using non-const references in these files, due to the 49 | # constraints of double C++/GLSL compilation of functions.glsl. 50 | # We also exclude build/c++11 checking for docgen_main.cc to allow the use of 51 | # . 52 | lint: $(HEADERS) $(SOURCES) 53 | cpplint --exclude=tools/docgen_main.cc \ 54 | --exclude=atmosphere/reference/functions.h \ 55 | --exclude=atmosphere/reference/model_test.cc --root=$(PWD) $^ 56 | cpplint --filter=-runtime/references --root=$(PWD) \ 57 | atmosphere/reference/functions.h \ 58 | atmosphere/reference/model_test.cc 59 | cpplint --filter=-build/c++11 --root=$(PWD) tools/docgen_main.cc 60 | 61 | doc: $(DOC_SOURCES:%=output/Doc/%.html) 62 | 63 | test: output/Debug/atmosphere_test 64 | output/Debug/atmosphere_test 65 | 66 | integration_test: output/Release/atmosphere_integration_test 67 | mkdir -p output/Doc/atmosphere/reference 68 | output/Release/atmosphere_integration_test 69 | 70 | webgl: output/Doc/scattering.dat output/Doc/demo.html output/Doc/demo.js 71 | 72 | demo: output/Debug/atmosphere_demo 73 | output/Debug/atmosphere_demo 74 | 75 | clean: 76 | rm -f $(GLSL_SOURCES:%=%.inc) 77 | rm -rf output/Debug output/Release output/Doc 78 | 79 | output/Doc/%.html: % output/Debug/tools/docgen tools/docgen_template.html 80 | mkdir -p $(@D) 81 | output/Debug/tools/docgen $< tools/docgen_template.html $@ 82 | 83 | output/Doc/scattering.dat: output/Debug/precompute 84 | mkdir -p $(@D) 85 | output/Debug/precompute $(@D)/ 86 | 87 | output/Doc/demo.html: atmosphere/demo/webgl/demo.html 88 | mkdir -p $(@D) 89 | cp $< $@ 90 | 91 | output/Doc/demo.js: atmosphere/demo/webgl/demo.js 92 | mkdir -p $(@D) 93 | cp $< $@ 94 | 95 | output/Debug/tools/docgen: output/Debug/tools/docgen_main.o 96 | $(GPP) $< -o $@ 97 | 98 | output/Debug/atmosphere_test: \ 99 | output/Debug/atmosphere/reference/functions.o \ 100 | output/Debug/atmosphere/reference/functions_test.o \ 101 | output/Debug/external/dimensional_types/test/test_main.o 102 | $(GPP) $^ -o $@ 103 | 104 | output/Release/atmosphere_integration_test: \ 105 | output/Release/atmosphere/model.o \ 106 | output/Release/atmosphere/reference/functions.o \ 107 | output/Release/atmosphere/reference/model.o \ 108 | output/Release/atmosphere/reference/model_test.o \ 109 | output/Release/external/dimensional_types/test/test_main.o \ 110 | output/Release/external/glad/src/glad.o \ 111 | output/Release/external/progress_bar/util/progress_bar.o 112 | $(GPP) $^ -pthread -ldl -lglut -lGL -o $@ 113 | 114 | output/Debug/precompute: \ 115 | output/Debug/atmosphere/demo/demo.o \ 116 | output/Debug/atmosphere/demo/webgl/precompute.o \ 117 | output/Debug/atmosphere/model.o \ 118 | output/Debug/text/text_renderer.o \ 119 | output/Debug/external/glad/src/glad.o 120 | $(GPP) $^ -pthread -ldl -lglut -lGL -o $@ 121 | 122 | output/Debug/atmosphere_demo: \ 123 | output/Debug/atmosphere/demo/demo.o \ 124 | output/Debug/atmosphere/demo/demo_main.o \ 125 | output/Debug/atmosphere/model.o \ 126 | output/Debug/text/text_renderer.o \ 127 | output/Debug/external/glad/src/glad.o 128 | $(GPP) $^ -pthread -ldl -lglut -lGL -o $@ 129 | 130 | output/Debug/%.o: %.cc 131 | mkdir -p $(@D) 132 | $(GPP) $(GPP_FLAGS) $(INCLUDE_FLAGS) $(DEBUG_FLAGS) -c $< -o $@ 133 | 134 | output/Release/%.o: %.cc 135 | mkdir -p $(@D) 136 | $(GPP) $(GPP_FLAGS) $(INCLUDE_FLAGS) $(RELEASE_FLAGS) -c $< -o $@ 137 | 138 | output/Debug/atmosphere/model.o output/Release/atmosphere/model.o: \ 139 | atmosphere/definitions.glsl.inc \ 140 | atmosphere/functions.glsl.inc 141 | 142 | output/Debug/atmosphere/reference/model_test.o \ 143 | output/Release/atmosphere/reference/model_test.o: \ 144 | atmosphere/definitions.glsl.inc \ 145 | atmosphere/reference/model_test.glsl.inc 146 | 147 | output/Debug/atmosphere/demo/demo.o output/Release/atmosphere/demo/demo.o: \ 148 | atmosphere/demo/demo.glsl.inc 149 | 150 | %.glsl.inc: %.glsl 151 | sed -e '1i const char $(*F)_glsl[] = R"***(' -e '$$a )***";' \ 152 | -e '/^\/\*/,/\*\/$$/d' -e '/^ *\/\//d' -e '/^$$/d' $< > $@ 153 | 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | This project provides a new implementation of our [Precomputed Atmospheric 4 | Scattering](https://hal.inria.fr/inria-00288758/en) paper. This new 5 | implementation uses more descriptive function and variable names, adds 6 | extensive comments and documentation, unit tests, and more. See the [full 7 | documentation](https://ebruneton.github.io/precomputed_atmospheric_scattering/) 8 | for more details, as well as the [online 9 | demo](https://ebruneton.github.io/precomputed_atmospheric_scattering/demo.html). 10 | 11 | ## License 12 | 13 | This project is released under the BSD license. 14 | -------------------------------------------------------------------------------- /atmosphere/constants.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/constants.h

31 | 32 |

This file defines the size of the precomputed texures used in our atmosphere 33 | model. It also provides tabulated values of the CIE color matching functions and the conversion matrix from the XYZ to the 37 | sRGB color spaces (which are 38 | needed to convert the spectral radiance samples computed by our algorithm to 39 | sRGB luminance values). 40 | */ 41 | 42 | #ifndef ATMOSPHERE_CONSTANTS_H_ 43 | #define ATMOSPHERE_CONSTANTS_H_ 44 | 45 | namespace atmosphere { 46 | 47 | constexpr int TRANSMITTANCE_TEXTURE_WIDTH = 256; 48 | constexpr int TRANSMITTANCE_TEXTURE_HEIGHT = 64; 49 | 50 | constexpr int SCATTERING_TEXTURE_R_SIZE = 32; 51 | constexpr int SCATTERING_TEXTURE_MU_SIZE = 128; 52 | constexpr int SCATTERING_TEXTURE_MU_S_SIZE = 32; 53 | constexpr int SCATTERING_TEXTURE_NU_SIZE = 8; 54 | 55 | constexpr int SCATTERING_TEXTURE_WIDTH = 56 | SCATTERING_TEXTURE_NU_SIZE * SCATTERING_TEXTURE_MU_S_SIZE; 57 | constexpr int SCATTERING_TEXTURE_HEIGHT = SCATTERING_TEXTURE_MU_SIZE; 58 | constexpr int SCATTERING_TEXTURE_DEPTH = SCATTERING_TEXTURE_R_SIZE; 59 | 60 | constexpr int IRRADIANCE_TEXTURE_WIDTH = 64; 61 | constexpr int IRRADIANCE_TEXTURE_HEIGHT = 16; 62 | 63 | // The conversion factor between watts and lumens. 64 | constexpr double MAX_LUMINOUS_EFFICACY = 683.0; 65 | 66 | // Values from "CIE (1931) 2-deg color matching functions", see 67 | // "http://web.archive.org/web/20081228084047/ 68 | // http://www.cvrl.org/database/data/cmfs/ciexyz31.txt". 69 | constexpr double CIE_2_DEG_COLOR_MATCHING_FUNCTIONS[380] = { 70 | 360, 0.000129900000, 0.000003917000, 0.000606100000, 71 | 365, 0.000232100000, 0.000006965000, 0.001086000000, 72 | 370, 0.000414900000, 0.000012390000, 0.001946000000, 73 | 375, 0.000741600000, 0.000022020000, 0.003486000000, 74 | 380, 0.001368000000, 0.000039000000, 0.006450001000, 75 | 385, 0.002236000000, 0.000064000000, 0.010549990000, 76 | 390, 0.004243000000, 0.000120000000, 0.020050010000, 77 | 395, 0.007650000000, 0.000217000000, 0.036210000000, 78 | 400, 0.014310000000, 0.000396000000, 0.067850010000, 79 | 405, 0.023190000000, 0.000640000000, 0.110200000000, 80 | 410, 0.043510000000, 0.001210000000, 0.207400000000, 81 | 415, 0.077630000000, 0.002180000000, 0.371300000000, 82 | 420, 0.134380000000, 0.004000000000, 0.645600000000, 83 | 425, 0.214770000000, 0.007300000000, 1.039050100000, 84 | 430, 0.283900000000, 0.011600000000, 1.385600000000, 85 | 435, 0.328500000000, 0.016840000000, 1.622960000000, 86 | 440, 0.348280000000, 0.023000000000, 1.747060000000, 87 | 445, 0.348060000000, 0.029800000000, 1.782600000000, 88 | 450, 0.336200000000, 0.038000000000, 1.772110000000, 89 | 455, 0.318700000000, 0.048000000000, 1.744100000000, 90 | 460, 0.290800000000, 0.060000000000, 1.669200000000, 91 | 465, 0.251100000000, 0.073900000000, 1.528100000000, 92 | 470, 0.195360000000, 0.090980000000, 1.287640000000, 93 | 475, 0.142100000000, 0.112600000000, 1.041900000000, 94 | 480, 0.095640000000, 0.139020000000, 0.812950100000, 95 | 485, 0.057950010000, 0.169300000000, 0.616200000000, 96 | 490, 0.032010000000, 0.208020000000, 0.465180000000, 97 | 495, 0.014700000000, 0.258600000000, 0.353300000000, 98 | 500, 0.004900000000, 0.323000000000, 0.272000000000, 99 | 505, 0.002400000000, 0.407300000000, 0.212300000000, 100 | 510, 0.009300000000, 0.503000000000, 0.158200000000, 101 | 515, 0.029100000000, 0.608200000000, 0.111700000000, 102 | 520, 0.063270000000, 0.710000000000, 0.078249990000, 103 | 525, 0.109600000000, 0.793200000000, 0.057250010000, 104 | 530, 0.165500000000, 0.862000000000, 0.042160000000, 105 | 535, 0.225749900000, 0.914850100000, 0.029840000000, 106 | 540, 0.290400000000, 0.954000000000, 0.020300000000, 107 | 545, 0.359700000000, 0.980300000000, 0.013400000000, 108 | 550, 0.433449900000, 0.994950100000, 0.008749999000, 109 | 555, 0.512050100000, 1.000000000000, 0.005749999000, 110 | 560, 0.594500000000, 0.995000000000, 0.003900000000, 111 | 565, 0.678400000000, 0.978600000000, 0.002749999000, 112 | 570, 0.762100000000, 0.952000000000, 0.002100000000, 113 | 575, 0.842500000000, 0.915400000000, 0.001800000000, 114 | 580, 0.916300000000, 0.870000000000, 0.001650001000, 115 | 585, 0.978600000000, 0.816300000000, 0.001400000000, 116 | 590, 1.026300000000, 0.757000000000, 0.001100000000, 117 | 595, 1.056700000000, 0.694900000000, 0.001000000000, 118 | 600, 1.062200000000, 0.631000000000, 0.000800000000, 119 | 605, 1.045600000000, 0.566800000000, 0.000600000000, 120 | 610, 1.002600000000, 0.503000000000, 0.000340000000, 121 | 615, 0.938400000000, 0.441200000000, 0.000240000000, 122 | 620, 0.854449900000, 0.381000000000, 0.000190000000, 123 | 625, 0.751400000000, 0.321000000000, 0.000100000000, 124 | 630, 0.642400000000, 0.265000000000, 0.000049999990, 125 | 635, 0.541900000000, 0.217000000000, 0.000030000000, 126 | 640, 0.447900000000, 0.175000000000, 0.000020000000, 127 | 645, 0.360800000000, 0.138200000000, 0.000010000000, 128 | 650, 0.283500000000, 0.107000000000, 0.000000000000, 129 | 655, 0.218700000000, 0.081600000000, 0.000000000000, 130 | 660, 0.164900000000, 0.061000000000, 0.000000000000, 131 | 665, 0.121200000000, 0.044580000000, 0.000000000000, 132 | 670, 0.087400000000, 0.032000000000, 0.000000000000, 133 | 675, 0.063600000000, 0.023200000000, 0.000000000000, 134 | 680, 0.046770000000, 0.017000000000, 0.000000000000, 135 | 685, 0.032900000000, 0.011920000000, 0.000000000000, 136 | 690, 0.022700000000, 0.008210000000, 0.000000000000, 137 | 695, 0.015840000000, 0.005723000000, 0.000000000000, 138 | 700, 0.011359160000, 0.004102000000, 0.000000000000, 139 | 705, 0.008110916000, 0.002929000000, 0.000000000000, 140 | 710, 0.005790346000, 0.002091000000, 0.000000000000, 141 | 715, 0.004109457000, 0.001484000000, 0.000000000000, 142 | 720, 0.002899327000, 0.001047000000, 0.000000000000, 143 | 725, 0.002049190000, 0.000740000000, 0.000000000000, 144 | 730, 0.001439971000, 0.000520000000, 0.000000000000, 145 | 735, 0.000999949300, 0.000361100000, 0.000000000000, 146 | 740, 0.000690078600, 0.000249200000, 0.000000000000, 147 | 745, 0.000476021300, 0.000171900000, 0.000000000000, 148 | 750, 0.000332301100, 0.000120000000, 0.000000000000, 149 | 755, 0.000234826100, 0.000084800000, 0.000000000000, 150 | 760, 0.000166150500, 0.000060000000, 0.000000000000, 151 | 765, 0.000117413000, 0.000042400000, 0.000000000000, 152 | 770, 0.000083075270, 0.000030000000, 0.000000000000, 153 | 775, 0.000058706520, 0.000021200000, 0.000000000000, 154 | 780, 0.000041509940, 0.000014990000, 0.000000000000, 155 | 785, 0.000029353260, 0.000010600000, 0.000000000000, 156 | 790, 0.000020673830, 0.000007465700, 0.000000000000, 157 | 795, 0.000014559770, 0.000005257800, 0.000000000000, 158 | 800, 0.000010253980, 0.000003702900, 0.000000000000, 159 | 805, 0.000007221456, 0.000002607800, 0.000000000000, 160 | 810, 0.000005085868, 0.000001836600, 0.000000000000, 161 | 815, 0.000003581652, 0.000001293400, 0.000000000000, 162 | 820, 0.000002522525, 0.000000910930, 0.000000000000, 163 | 825, 0.000001776509, 0.000000641530, 0.000000000000, 164 | 830, 0.000001251141, 0.000000451810, 0.000000000000, 165 | }; 166 | 167 | // The conversion matrix from XYZ to linear sRGB color spaces. 168 | // Values from https://en.wikipedia.org/wiki/SRGB. 169 | constexpr double XYZ_TO_SRGB[9] = { 170 | +3.2406, -1.5372, -0.4986, 171 | -0.9689, +1.8758, +0.0415, 172 | +0.0557, -0.2040, +1.0570 173 | }; 174 | 175 | } // namespace atmosphere 176 | 177 | #endif // ATMOSPHERE_CONSTANTS_H_ 178 | -------------------------------------------------------------------------------- /atmosphere/definitions.glsl: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/definitions.glsl

31 | 32 |

This GLSL file defines the physical types and constants which are used in the 33 | main functions of our atmosphere model, in 34 | such a way that they can be compiled by a GLSL compiler (a 35 | C++ equivalent of this file 36 | provides the same types and constants in C++, to allow the same functions to be 37 | compiled by a C++ compiler - see the Introduction). 38 | 39 |

Physical quantities

40 | 41 |

The physical quantities we need for our atmosphere model are 42 | radiometric and 43 | photometric 44 | quantities. In GLSL we can't define custom numeric types to enforce the 45 | homogeneity of expressions at compile time, so we define all the physical 46 | quantities as float, with preprocessor macros (there is no 47 | typedef in GLSL). 48 | 49 |

We start with six base quantities: length, wavelength, angle, solid angle, 50 | power and luminous power (wavelength is also a length, but we distinguish the 51 | two for increased clarity). 52 | */ 53 | 54 | #define Length float 55 | #define Wavelength float 56 | #define Angle float 57 | #define SolidAngle float 58 | #define Power float 59 | #define LuminousPower float 60 | 61 | /* 62 |

From this we "derive" the irradiance, radiance, spectral irradiance, 63 | spectral radiance, luminance, etc, as well pure numbers, area, volume, etc (the 64 | actual derivation is done in the C++ 65 | equivalent of this file). 66 | */ 67 | 68 | #define Number float 69 | #define InverseLength float 70 | #define Area float 71 | #define Volume float 72 | #define NumberDensity float 73 | #define Irradiance float 74 | #define Radiance float 75 | #define SpectralPower float 76 | #define SpectralIrradiance float 77 | #define SpectralRadiance float 78 | #define SpectralRadianceDensity float 79 | #define ScatteringCoefficient float 80 | #define InverseSolidAngle float 81 | #define LuminousIntensity float 82 | #define Luminance float 83 | #define Illuminance float 84 | 85 | /* 86 |

We also need vectors of physical quantities, mostly to represent functions 87 | depending on the wavelength. In this case the vector elements correspond to 88 | values of a function at some predefined wavelengths. Again, in GLSL we can't 89 | define custom vector types to enforce the homogeneity of expressions at compile 90 | time, so we define these vector types as vec3, with preprocessor 91 | macros. The full definitions are given in the 92 | C++ equivalent of this file). 93 | */ 94 | 95 | // A generic function from Wavelength to some other type. 96 | #define AbstractSpectrum vec3 97 | // A function from Wavelength to Number. 98 | #define DimensionlessSpectrum vec3 99 | // A function from Wavelength to SpectralPower. 100 | #define PowerSpectrum vec3 101 | // A function from Wavelength to SpectralIrradiance. 102 | #define IrradianceSpectrum vec3 103 | // A function from Wavelength to SpectralRadiance. 104 | #define RadianceSpectrum vec3 105 | // A function from Wavelength to SpectralRadianceDensity. 106 | #define RadianceDensitySpectrum vec3 107 | // A function from Wavelength to ScaterringCoefficient. 108 | #define ScatteringSpectrum vec3 109 | 110 | // A position in 3D (3 length values). 111 | #define Position vec3 112 | // A unit direction vector in 3D (3 unitless values). 113 | #define Direction vec3 114 | // A vector of 3 luminance values. 115 | #define Luminance3 vec3 116 | // A vector of 3 illuminance values. 117 | #define Illuminance3 vec3 118 | 119 | /* 120 |

Finally, we also need precomputed textures containing physical quantities in 121 | each texel. Since we can't define custom sampler types to enforce the 122 | homogeneity of expressions at compile time in GLSL, we define these texture 123 | types as sampler2D and sampler3D, with preprocessor 124 | macros. The full definitions are given in the 125 | C++ equivalent of this file). 126 | */ 127 | 128 | #define TransmittanceTexture sampler2D 129 | #define AbstractScatteringTexture sampler3D 130 | #define ReducedScatteringTexture sampler3D 131 | #define ScatteringTexture sampler3D 132 | #define ScatteringDensityTexture sampler3D 133 | #define IrradianceTexture sampler2D 134 | 135 | /* 136 |

Physical units

137 | 138 |

We can then define the units for our six base physical quantities: 139 | meter (m), nanometer (nm), radian (rad), steradian (sr), watt (watt) and lumen 140 | (lm): 141 | */ 142 | 143 | const Length m = 1.0; 144 | const Wavelength nm = 1.0; 145 | const Angle rad = 1.0; 146 | const SolidAngle sr = 1.0; 147 | const Power watt = 1.0; 148 | const LuminousPower lm = 1.0; 149 | 150 | /* 151 |

From which we can derive the units for some derived physical quantities, 152 | as well as some derived units (kilometer km, kilocandela kcd, degree deg): 153 | */ 154 | 155 | const float PI = 3.14159265358979323846; 156 | 157 | const Length km = 1000.0 * m; 158 | const Area m2 = m * m; 159 | const Volume m3 = m * m * m; 160 | const Angle pi = PI * rad; 161 | const Angle deg = pi / 180.0; 162 | const Irradiance watt_per_square_meter = watt / m2; 163 | const Radiance watt_per_square_meter_per_sr = watt / (m2 * sr); 164 | const SpectralIrradiance watt_per_square_meter_per_nm = watt / (m2 * nm); 165 | const SpectralRadiance watt_per_square_meter_per_sr_per_nm = 166 | watt / (m2 * sr * nm); 167 | const SpectralRadianceDensity watt_per_cubic_meter_per_sr_per_nm = 168 | watt / (m3 * sr * nm); 169 | const LuminousIntensity cd = lm / sr; 170 | const LuminousIntensity kcd = 1000.0 * cd; 171 | const Luminance cd_per_square_meter = cd / m2; 172 | const Luminance kcd_per_square_meter = kcd / m2; 173 | 174 | /* 175 |

Atmosphere parameters

176 | 177 |

Using the above types, we can now define the parameters of our atmosphere 178 | model. We start with the definition of density profiles, which are needed for 179 | parameters that depend on the altitude: 180 | */ 181 | 182 | // An atmosphere layer of width 'width', and whose density is defined as 183 | // 'exp_term' * exp('exp_scale' * h) + 'linear_term' * h + 'constant_term', 184 | // clamped to [0,1], and where h is the altitude. 185 | struct DensityProfileLayer { 186 | Length width; 187 | Number exp_term; 188 | InverseLength exp_scale; 189 | InverseLength linear_term; 190 | Number constant_term; 191 | }; 192 | 193 | // An atmosphere density profile made of several layers on top of each other 194 | // (from bottom to top). The width of the last layer is ignored, i.e. it always 195 | // extend to the top atmosphere boundary. The profile values vary between 0 196 | // (null density) to 1 (maximum density). 197 | struct DensityProfile { 198 | DensityProfileLayer layers[2]; 199 | }; 200 | 201 | /* 202 | The atmosphere parameters are then defined by the following struct: 203 | */ 204 | 205 | struct AtmosphereParameters { 206 | // The solar irradiance at the top of the atmosphere. 207 | IrradianceSpectrum solar_irradiance; 208 | // The sun's angular radius. Warning: the implementation uses approximations 209 | // that are valid only if this angle is smaller than 0.1 radians. 210 | Angle sun_angular_radius; 211 | // The distance between the planet center and the bottom of the atmosphere. 212 | Length bottom_radius; 213 | // The distance between the planet center and the top of the atmosphere. 214 | Length top_radius; 215 | // The density profile of air molecules, i.e. a function from altitude to 216 | // dimensionless values between 0 (null density) and 1 (maximum density). 217 | DensityProfile rayleigh_density; 218 | // The scattering coefficient of air molecules at the altitude where their 219 | // density is maximum (usually the bottom of the atmosphere), as a function of 220 | // wavelength. The scattering coefficient at altitude h is equal to 221 | // 'rayleigh_scattering' times 'rayleigh_density' at this altitude. 222 | ScatteringSpectrum rayleigh_scattering; 223 | // The density profile of aerosols, i.e. a function from altitude to 224 | // dimensionless values between 0 (null density) and 1 (maximum density). 225 | DensityProfile mie_density; 226 | // The scattering coefficient of aerosols at the altitude where their density 227 | // is maximum (usually the bottom of the atmosphere), as a function of 228 | // wavelength. The scattering coefficient at altitude h is equal to 229 | // 'mie_scattering' times 'mie_density' at this altitude. 230 | ScatteringSpectrum mie_scattering; 231 | // The extinction coefficient of aerosols at the altitude where their density 232 | // is maximum (usually the bottom of the atmosphere), as a function of 233 | // wavelength. The extinction coefficient at altitude h is equal to 234 | // 'mie_extinction' times 'mie_density' at this altitude. 235 | ScatteringSpectrum mie_extinction; 236 | // The asymetry parameter for the Cornette-Shanks phase function for the 237 | // aerosols. 238 | Number mie_phase_function_g; 239 | // The density profile of air molecules that absorb light (e.g. ozone), i.e. 240 | // a function from altitude to dimensionless values between 0 (null density) 241 | // and 1 (maximum density). 242 | DensityProfile absorption_density; 243 | // The extinction coefficient of molecules that absorb light (e.g. ozone) at 244 | // the altitude where their density is maximum, as a function of wavelength. 245 | // The extinction coefficient at altitude h is equal to 246 | // 'absorption_extinction' times 'absorption_density' at this altitude. 247 | ScatteringSpectrum absorption_extinction; 248 | // The average albedo of the ground. 249 | DimensionlessSpectrum ground_albedo; 250 | // The cosine of the maximum Sun zenith angle for which atmospheric scattering 251 | // must be precomputed (for maximum precision, use the smallest Sun zenith 252 | // angle yielding negligible sky light radiance values. For instance, for the 253 | // Earth case, 102 degrees is a good choice - yielding mu_s_min = -0.2). 254 | Number mu_s_min; 255 | }; 256 | -------------------------------------------------------------------------------- /atmosphere/demo/demo.glsl: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/demo/demo.glsl

31 | 32 |

This GLSL fragment shader is used to render our demo scene, which consists of 33 | a sphere S on a purely spherical planet P. It is rendered by "ray tracing", i.e. 34 | the vertex shader outputs the view ray direction, and the fragment shader 35 | computes the intersection of this ray with the spheres S and P to produce the 36 | final pixels. The fragment shader also computes the intersection of the light 37 | rays with the sphere S, to compute shadows, as well as the intersections of the 38 | view ray with the shadow volume of S, in order to compute light shafts. 39 | 40 |

Our fragment shader has the following inputs and outputs: 41 | */ 42 | 43 | uniform vec3 camera; 44 | uniform float exposure; 45 | uniform vec3 white_point; 46 | uniform vec3 earth_center; 47 | uniform vec3 sun_direction; 48 | uniform vec2 sun_size; 49 | in vec3 view_ray; 50 | layout(location = 0) out vec4 color; 51 | 52 | /* 53 |

It uses the following constants, as well as the following atmosphere 54 | rendering functions, defined externally (by the Model's 55 | GetShader() shader). The USE_LUMINANCE option is used 56 | to select either the functions returning radiance values, or those returning 57 | luminance values (see model.h). 58 | */ 59 | 60 | const float PI = 3.14159265; 61 | const vec3 kSphereCenter = vec3(0.0, 0.0, 1000.0) / kLengthUnitInMeters; 62 | const float kSphereRadius = 1000.0 / kLengthUnitInMeters; 63 | const vec3 kSphereAlbedo = vec3(0.8); 64 | const vec3 kGroundAlbedo = vec3(0.0, 0.0, 0.04); 65 | 66 | #ifdef USE_LUMINANCE 67 | #define GetSolarRadiance GetSolarLuminance 68 | #define GetSkyRadiance GetSkyLuminance 69 | #define GetSkyRadianceToPoint GetSkyLuminanceToPoint 70 | #define GetSunAndSkyIrradiance GetSunAndSkyIlluminance 71 | #endif 72 | 73 | vec3 GetSolarRadiance(); 74 | vec3 GetSkyRadiance(vec3 camera, vec3 view_ray, float shadow_length, 75 | vec3 sun_direction, out vec3 transmittance); 76 | vec3 GetSkyRadianceToPoint(vec3 camera, vec3 point, float shadow_length, 77 | vec3 sun_direction, out vec3 transmittance); 78 | vec3 GetSunAndSkyIrradiance( 79 | vec3 p, vec3 normal, vec3 sun_direction, out vec3 sky_irradiance); 80 | 81 | /*

Shadows and light shafts

82 | 83 |

The functions to compute shadows and light shafts must be defined before we 84 | can use them in the main shader function, so we define them first. Testing if 85 | a point is in the shadow of the sphere S is equivalent to test if the 86 | corresponding light ray intersects the sphere, which is very simple to do. 87 | However, this is only valid for a punctual light source, which is not the case 88 | of the Sun. In the following function we compute an approximate (and biased) 89 | soft shadow by taking the angular size of the Sun into account: 90 | */ 91 | 92 | float GetSunVisibility(vec3 point, vec3 sun_direction) { 93 | vec3 p = point - kSphereCenter; 94 | float p_dot_v = dot(p, sun_direction); 95 | float p_dot_p = dot(p, p); 96 | float ray_sphere_center_squared_distance = p_dot_p - p_dot_v * p_dot_v; 97 | float distance_to_intersection = -p_dot_v - sqrt( 98 | kSphereRadius * kSphereRadius - ray_sphere_center_squared_distance); 99 | if (distance_to_intersection > 0.0) { 100 | // Compute the distance between the view ray and the sphere, and the 101 | // corresponding (tangent of the) subtended angle. Finally, use this to 102 | // compute an approximate sun visibility. 103 | float ray_sphere_distance = 104 | kSphereRadius - sqrt(ray_sphere_center_squared_distance); 105 | float ray_sphere_angular_distance = -ray_sphere_distance / p_dot_v; 106 | return smoothstep(1.0, 0.0, ray_sphere_angular_distance / sun_size.x); 107 | } 108 | return 1.0; 109 | } 110 | 111 | /* 112 |

The sphere also partially occludes the sky light, and we approximate this 113 | effect with an ambient occlusion factor. The ambient occlusion factor due to a 114 | sphere is given in Radiation View Factors (Isidoro Martinez, 1995). In the simple case where 117 | the sphere is fully visible, it is given by the following function: 118 | */ 119 | 120 | float GetSkyVisibility(vec3 point) { 121 | vec3 p = point - kSphereCenter; 122 | float p_dot_p = dot(p, p); 123 | return 124 | 1.0 + p.z / sqrt(p_dot_p) * kSphereRadius * kSphereRadius / p_dot_p; 125 | } 126 | 127 | /* 128 |

To compute light shafts we need the intersections of the view ray with the 129 | shadow volume of the sphere S. Since the Sun is not a punctual light source this 130 | shadow volume is not a cylinder but a cone (for the umbra, plus another cone for 131 | the penumbra, but we ignore it here): 132 | 133 | 134 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | p 159 | q 160 | s 161 | v 162 | R 163 | r 164 | ρ 165 | d 166 | δ 167 | α 168 | 169 | 170 |

Noting, as in the above figure, $\bp$ the camera position, $\bv$ and $\bs$ 171 | the unit view ray and sun direction vectors and $R$ the sphere radius (supposed 172 | to be centered on the origin), the point at distance $d$ from the camera is 173 | $\bq=\bp+d\bv$. This point is at a distance $\delta=-\bq\cdot\bs$ from the 174 | sphere center along the umbra cone axis, and at a distance $r$ from this axis 175 | given by $r^2=\bq\cdot\bq-\delta^2$. Finally, at distance $\delta$ along the 176 | axis the umbra cone has radius $\rho=R-\delta\tan\alpha$, where $\alpha$ is 177 | the Sun's angular radius. The point at distance $d$ from the camera is on the 178 | shadow cone only if $r^2=\rho^2$, i.e. only if 179 | \begin{equation} 180 | (\bp+d\bv)\cdot(\bp+d\bv)-((\bp+d\bv)\cdot\bs)^2= 181 | (R+((\bp+d\bv)\cdot\bs)\tan\alpha)^2 182 | \end{equation} 183 | Developping this gives a quadratic equation for $d$: 184 | \begin{equation} 185 | ad^2+2bd+c=0 186 | \end{equation} 187 | where 188 |

194 | From this we deduce the two possible solutions for $d$, which must be clamped to 195 | the actual shadow part of the mathematical cone (i.e. the slab between the 196 | sphere center and the cone apex or, in other words, the points for which 197 | $\delta$ is between $0$ and $R/\tan\alpha$). The following function implements 198 | these equations: 199 | */ 200 | 201 | void GetSphereShadowInOut(vec3 view_direction, vec3 sun_direction, 202 | out float d_in, out float d_out) { 203 | vec3 pos = camera - kSphereCenter; 204 | float pos_dot_sun = dot(pos, sun_direction); 205 | float view_dot_sun = dot(view_direction, sun_direction); 206 | float k = sun_size.x; 207 | float l = 1.0 + k * k; 208 | float a = 1.0 - l * view_dot_sun * view_dot_sun; 209 | float b = dot(pos, view_direction) - l * pos_dot_sun * view_dot_sun - 210 | k * kSphereRadius * view_dot_sun; 211 | float c = dot(pos, pos) - l * pos_dot_sun * pos_dot_sun - 212 | 2.0 * k * kSphereRadius * pos_dot_sun - kSphereRadius * kSphereRadius; 213 | float discriminant = b * b - a * c; 214 | if (discriminant > 0.0) { 215 | d_in = max(0.0, (-b - sqrt(discriminant)) / a); 216 | d_out = (-b + sqrt(discriminant)) / a; 217 | // The values of d for which delta is equal to 0 and kSphereRadius / k. 218 | float d_base = -pos_dot_sun / view_dot_sun; 219 | float d_apex = -(pos_dot_sun + kSphereRadius / k) / view_dot_sun; 220 | if (view_dot_sun > 0.0) { 221 | d_in = max(d_in, d_apex); 222 | d_out = a > 0.0 ? min(d_out, d_base) : d_base; 223 | } else { 224 | d_in = a > 0.0 ? max(d_in, d_base) : d_base; 225 | d_out = min(d_out, d_apex); 226 | } 227 | } else { 228 | d_in = 0.0; 229 | d_out = 0.0; 230 | } 231 | } 232 | 233 | /*

Main shading function

234 | 235 |

Using these functions we can now implement the main shader function, which 236 | computes the radiance from the scene for a given view ray. This function first 237 | tests if the view ray intersects the sphere S. If so it computes the sun and 238 | sky light received by the sphere at the intersection point, combines this with 239 | the sphere BRDF and the aerial perspective between the camera and the sphere. 240 | It then does the same with the ground, i.e. with the planet sphere P, and then 241 | computes the sky radiance and transmittance. Finally, all these terms are 242 | composited together (an opacity is also computed for each object, using an 243 | approximate view cone - sphere intersection factor) to get the final radiance. 244 | 245 |

We start with the computation of the intersections of the view ray with the 246 | shadow volume of the sphere, because they are needed to get the aerial 247 | perspective for the sphere and the planet: 248 | */ 249 | 250 | void main() { 251 | // Normalized view direction vector. 252 | vec3 view_direction = normalize(view_ray); 253 | // Tangent of the angle subtended by this fragment. 254 | float fragment_angular_size = 255 | length(dFdx(view_ray) + dFdy(view_ray)) / length(view_ray); 256 | 257 | float shadow_in; 258 | float shadow_out; 259 | GetSphereShadowInOut(view_direction, sun_direction, shadow_in, shadow_out); 260 | 261 | // Hack to fade out light shafts when the Sun is very close to the horizon. 262 | float lightshaft_fadein_hack = smoothstep( 263 | 0.02, 0.04, dot(normalize(camera - earth_center), sun_direction)); 264 | 265 | /* 266 |

We then test whether the view ray intersects the sphere S or not. If it does, 267 | we compute an approximate (and biased) opacity value, using the same 268 | approximation as in GetSunVisibility: 269 | */ 270 | 271 | // Compute the distance between the view ray line and the sphere center, 272 | // and the distance between the camera and the intersection of the view 273 | // ray with the sphere (or NaN if there is no intersection). 274 | vec3 p = camera - kSphereCenter; 275 | float p_dot_v = dot(p, view_direction); 276 | float p_dot_p = dot(p, p); 277 | float ray_sphere_center_squared_distance = p_dot_p - p_dot_v * p_dot_v; 278 | float distance_to_intersection = -p_dot_v - sqrt( 279 | kSphereRadius * kSphereRadius - ray_sphere_center_squared_distance); 280 | 281 | // Compute the radiance reflected by the sphere, if the ray intersects it. 282 | float sphere_alpha = 0.0; 283 | vec3 sphere_radiance = vec3(0.0); 284 | if (distance_to_intersection > 0.0) { 285 | // Compute the distance between the view ray and the sphere, and the 286 | // corresponding (tangent of the) subtended angle. Finally, use this to 287 | // compute the approximate analytic antialiasing factor sphere_alpha. 288 | float ray_sphere_distance = 289 | kSphereRadius - sqrt(ray_sphere_center_squared_distance); 290 | float ray_sphere_angular_distance = -ray_sphere_distance / p_dot_v; 291 | sphere_alpha = 292 | min(ray_sphere_angular_distance / fragment_angular_size, 1.0); 293 | 294 | /* 295 |

We can then compute the intersection point and its normal, and use them to 296 | get the sun and sky irradiance received at this point. The reflected radiance 297 | follows, by multiplying the irradiance with the sphere BRDF: 298 | */ 299 | vec3 point = camera + view_direction * distance_to_intersection; 300 | vec3 normal = normalize(point - kSphereCenter); 301 | 302 | // Compute the radiance reflected by the sphere. 303 | vec3 sky_irradiance; 304 | vec3 sun_irradiance = GetSunAndSkyIrradiance( 305 | point - earth_center, normal, sun_direction, sky_irradiance); 306 | sphere_radiance = 307 | kSphereAlbedo * (1.0 / PI) * (sun_irradiance + sky_irradiance); 308 | 309 | /* 310 |

Finally, we take into account the aerial perspective between the camera and 311 | the sphere, which depends on the length of this segment which is in shadow: 312 | */ 313 | float shadow_length = 314 | max(0.0, min(shadow_out, distance_to_intersection) - shadow_in) * 315 | lightshaft_fadein_hack; 316 | vec3 transmittance; 317 | vec3 in_scatter = GetSkyRadianceToPoint(camera - earth_center, 318 | point - earth_center, shadow_length, sun_direction, transmittance); 319 | sphere_radiance = sphere_radiance * transmittance + in_scatter; 320 | } 321 | 322 | /* 323 |

In the following we repeat the same steps as above, but for the planet sphere 324 | P instead of the sphere S (a smooth opacity is not really needed here, so we 325 | don't compute it. Note also how we modulate the sun and sky irradiance received 326 | on the ground by the sun and sky visibility factors): 327 | */ 328 | 329 | // Compute the distance between the view ray line and the Earth center, 330 | // and the distance between the camera and the intersection of the view 331 | // ray with the ground (or NaN if there is no intersection). 332 | p = camera - earth_center; 333 | p_dot_v = dot(p, view_direction); 334 | p_dot_p = dot(p, p); 335 | float ray_earth_center_squared_distance = p_dot_p - p_dot_v * p_dot_v; 336 | distance_to_intersection = -p_dot_v - sqrt( 337 | earth_center.z * earth_center.z - ray_earth_center_squared_distance); 338 | 339 | // Compute the radiance reflected by the ground, if the ray intersects it. 340 | float ground_alpha = 0.0; 341 | vec3 ground_radiance = vec3(0.0); 342 | if (distance_to_intersection > 0.0) { 343 | vec3 point = camera + view_direction * distance_to_intersection; 344 | vec3 normal = normalize(point - earth_center); 345 | 346 | // Compute the radiance reflected by the ground. 347 | vec3 sky_irradiance; 348 | vec3 sun_irradiance = GetSunAndSkyIrradiance( 349 | point - earth_center, normal, sun_direction, sky_irradiance); 350 | ground_radiance = kGroundAlbedo * (1.0 / PI) * ( 351 | sun_irradiance * GetSunVisibility(point, sun_direction) + 352 | sky_irradiance * GetSkyVisibility(point)); 353 | 354 | float shadow_length = 355 | max(0.0, min(shadow_out, distance_to_intersection) - shadow_in) * 356 | lightshaft_fadein_hack; 357 | vec3 transmittance; 358 | vec3 in_scatter = GetSkyRadianceToPoint(camera - earth_center, 359 | point - earth_center, shadow_length, sun_direction, transmittance); 360 | ground_radiance = ground_radiance * transmittance + in_scatter; 361 | ground_alpha = 1.0; 362 | } 363 | 364 | /* 365 |

Finally, we compute the radiance and transmittance of the sky, and composite 366 | together, from back to front, the radiance and opacities of all the objects of 367 | the scene: 368 | */ 369 | 370 | // Compute the radiance of the sky. 371 | float shadow_length = max(0.0, shadow_out - shadow_in) * 372 | lightshaft_fadein_hack; 373 | vec3 transmittance; 374 | vec3 radiance = GetSkyRadiance( 375 | camera - earth_center, view_direction, shadow_length, sun_direction, 376 | transmittance); 377 | 378 | // If the view ray intersects the Sun, add the Sun radiance. 379 | if (dot(view_direction, sun_direction) > sun_size.y) { 380 | radiance = radiance + transmittance * GetSolarRadiance(); 381 | } 382 | radiance = mix(radiance, ground_radiance, ground_alpha); 383 | radiance = mix(radiance, sphere_radiance, sphere_alpha); 384 | color.rgb = 385 | pow(vec3(1.0) - exp(-radiance / white_point * exposure), vec3(1.0 / 2.2)); 386 | color.a = 1.0; 387 | } 388 | -------------------------------------------------------------------------------- /atmosphere/demo/demo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/demo/demo.h

31 | 32 |

Our demo application consists of a single class, whose header is defined 33 | below. Besides a constructor and an initialization method, this class has one 34 | method per user interface event, and a few fields to store the current rendering 35 | options, the current camera and Sun parameters, as well as references to the 36 | atmosphere model and to the GLSL program, vertex buffers and text renderer used 37 | to render the scene and the help messages: 38 | */ 39 | 40 | #ifndef ATMOSPHERE_DEMO_DEMO_H_ 41 | #define ATMOSPHERE_DEMO_DEMO_H_ 42 | 43 | #include 44 | 45 | #include 46 | 47 | #include "atmosphere/model.h" 48 | #include "text/text_renderer.h" 49 | 50 | namespace atmosphere { 51 | namespace demo { 52 | 53 | class Demo { 54 | public: 55 | Demo(int viewport_width, int viewport_height); 56 | ~Demo(); 57 | 58 | const Model& model() const { return *model_; } 59 | const GLuint vertex_shader() const { return vertex_shader_; } 60 | const GLuint fragment_shader() const { return fragment_shader_; } 61 | const GLuint program() const { return program_; } 62 | 63 | private: 64 | enum Luminance { 65 | // Render the spectral radiance at kLambdaR, kLambdaG, kLambdaB. 66 | NONE, 67 | // Render the sRGB luminance, using an approximate (on the fly) conversion 68 | // from 3 spectral radiance values only (see section 14.3 in A Qualitative and Quantitative 70 | // Evaluation of 8 Clear Sky Models). 71 | APPROXIMATE, 72 | // Render the sRGB luminance, precomputed from 15 spectral radiance values 73 | // (see section 4.4 in Real-time 75 | // Spectral Scattering in Large-scale Natural Participating Media). 76 | PRECOMPUTED 77 | }; 78 | 79 | void InitModel(); 80 | void HandleRedisplayEvent() const; 81 | void HandleReshapeEvent(int viewport_width, int viewport_height); 82 | void HandleKeyboardEvent(unsigned char key); 83 | void HandleMouseClickEvent(int button, int state, int mouse_x, int mouse_y); 84 | void HandleMouseDragEvent(int mouse_x, int mouse_y); 85 | void HandleMouseWheelEvent(int mouse_wheel_direction); 86 | void SetView(double view_distance_meters, double view_zenith_angle_radians, 87 | double view_azimuth_angle_radians, double sun_zenith_angle_radians, 88 | double sun_azimuth_angle_radians, double exposure); 89 | 90 | bool use_constant_solar_spectrum_; 91 | bool use_ozone_; 92 | bool use_combined_textures_; 93 | bool use_half_precision_; 94 | Luminance use_luminance_; 95 | bool do_white_balance_; 96 | bool show_help_; 97 | 98 | std::unique_ptr model_; 99 | GLuint vertex_shader_; 100 | GLuint fragment_shader_; 101 | GLuint program_; 102 | GLuint full_screen_quad_vao_; 103 | GLuint full_screen_quad_vbo_; 104 | std::unique_ptr text_renderer_; 105 | int window_id_; 106 | 107 | double view_distance_meters_; 108 | double view_zenith_angle_radians_; 109 | double view_azimuth_angle_radians_; 110 | double sun_zenith_angle_radians_; 111 | double sun_azimuth_angle_radians_; 112 | double exposure_; 113 | 114 | int previous_mouse_x_; 115 | int previous_mouse_y_; 116 | bool is_ctrl_key_pressed_; 117 | }; 118 | 119 | } // namespace demo 120 | } // namespace atmosphere 121 | 122 | #endif // ATMOSPHERE_DEMO_DEMO_H_ 123 | -------------------------------------------------------------------------------- /atmosphere/demo/demo_main.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/demo/demo_main.cc

31 | 32 |

This very simple file provides the main() function of our demo 33 | application. 34 | */ 35 | 36 | #include "atmosphere/demo/demo.h" 37 | 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | using atmosphere::demo::Demo; 44 | 45 | int main(int argc, char** argv) { 46 | glutInitContextVersion(3, 3); 47 | glutInitContextProfile(GLUT_CORE_PROFILE); 48 | glutInit(&argc, argv); 49 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); 50 | glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); 51 | 52 | std::unique_ptr demo(new Demo(1024, 576)); 53 | glutMainLoop(); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /atmosphere/demo/webgl/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Precomputed Atmospheric Scattering - WebGL Demo 5 | 6 | 29 | 30 | 31 | 32 |

50 | 51 | 52 | -------------------------------------------------------------------------------- /atmosphere/demo/webgl/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/demo/webgl/demo.js

31 | 32 |

This file is a Javascript transcription of the original 33 | C++ code of the demo, where the code related to 34 | the atmosphere model initialisation is replaced with code to load precomputed 35 | textures and shaders from the network (those are precomputed with 36 | precompute.cc). 37 | 38 |

The following constants must have the same values as in 39 | constants.h and 40 | demo.cc: 41 | */ 42 | 43 | const TRANSMITTANCE_TEXTURE_WIDTH = 256; 44 | const TRANSMITTANCE_TEXTURE_HEIGHT = 64; 45 | const SCATTERING_TEXTURE_WIDTH = 256; 46 | const SCATTERING_TEXTURE_HEIGHT = 128; 47 | const SCATTERING_TEXTURE_DEPTH = 32; 48 | const IRRADIANCE_TEXTURE_WIDTH = 64; 49 | const IRRADIANCE_TEXTURE_HEIGHT = 16; 50 | 51 | const kSunAngularRadius = 0.00935 / 2; 52 | const kSunSolidAngle = Math.PI * kSunAngularRadius * kSunAngularRadius; 53 | const kLengthUnitInMeters = 1000; 54 | 55 | /* 56 |

As in the C++ version, the code consists in a single class. Its constructor 57 | initializes the WebGL canvas, declares the fields of the class, sets up the 58 | event handlers and starts the resource loading and the render loop: 59 | */ 60 | 61 | class Demo { 62 | constructor(rootElement) { 63 | this.canvas = rootElement.querySelector('#glcanvas'); 64 | this.canvas.style.width = `${rootElement.clientWidth}px`; 65 | this.canvas.style.height = `${rootElement.clientHeight}px`; 66 | this.canvas.width = rootElement.clientWidth * window.devicePixelRatio; 67 | this.canvas.height = rootElement.clientHeight * window.devicePixelRatio; 68 | this.help = rootElement.querySelector('#help'); 69 | this.gl = this.canvas.getContext('webgl2'); 70 | 71 | this.vertexBuffer = null; 72 | this.transmittanceTexture = null; 73 | this.scatteringTexture = null; 74 | this.irradianceTexture = null; 75 | this.vertexShaderSource = null; 76 | this.fragmentShaderSource = null; 77 | this.atmosphereShaderSource = null; 78 | this.program = null; 79 | 80 | this.viewFromClip = new Float32Array(16); 81 | this.modelFromView = new Float32Array(16); 82 | this.viewDistanceMeters = 9000; 83 | this.viewZenithAngleRadians = 1.47; 84 | this.viewAzimuthAngleRadians = -0.1; 85 | this.sunZenithAngleRadians = 1.3; 86 | this.sunAzimuthAngleRadians = 2.9; 87 | this.exposure = 10; 88 | 89 | this.drag = undefined; 90 | this.previousMouseX = undefined; 91 | this.previousMouseY = undefined; 92 | 93 | rootElement.addEventListener('keypress', (e) => this.onKeyPress(e)); 94 | rootElement.addEventListener('mousedown', (e) => this.onMouseDown(e)); 95 | rootElement.addEventListener('mousemove', (e) => this.onMouseMove(e)); 96 | rootElement.addEventListener('mouseup', (e) => this.onMouseUp(e)); 97 | rootElement.addEventListener('wheel', (e) => this.onMouseWheel(e)); 98 | 99 | this.init(); 100 | requestAnimationFrame(() => this.onRender()); 101 | } 102 | 103 | /* 104 |

The init method creates a vertex buffer for a full screen quad, and loads the 105 | precomputed textures and the shaders for the demo (using utility methods defined 106 | in the Utils class below): 107 | */ 108 | 109 | init() { 110 | const gl = this.gl; 111 | this.vertexBuffer = gl.createBuffer(); 112 | gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); 113 | gl.bufferData(gl.ARRAY_BUFFER, 114 | new Float32Array([-1, -1, +1, -1, -1, +1, +1, +1]), gl.STATIC_DRAW); 115 | 116 | Utils.loadTextureData('transmittance.dat', (data) => { 117 | this.transmittanceTexture = 118 | Utils.createTexture(gl, gl.TEXTURE0, gl.TEXTURE_2D); 119 | gl.texImage2D(gl.TEXTURE_2D, 0, 120 | gl.getExtension('OES_texture_float_linear') ? gl.RGBA32F : gl.RGBA16F, 121 | TRANSMITTANCE_TEXTURE_WIDTH, TRANSMITTANCE_TEXTURE_HEIGHT, 0, gl.RGBA, 122 | gl.FLOAT, data); 123 | }); 124 | Utils.loadTextureData('scattering.dat', (data) => { 125 | this.scatteringTexture = 126 | Utils.createTexture(gl, gl.TEXTURE1, gl.TEXTURE_3D); 127 | gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); 128 | gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA16F, SCATTERING_TEXTURE_WIDTH, 129 | SCATTERING_TEXTURE_HEIGHT, SCATTERING_TEXTURE_DEPTH, 0, gl.RGBA, 130 | gl.FLOAT, data); 131 | }); 132 | Utils.loadTextureData('irradiance.dat', (data) => { 133 | this.irradianceTexture = 134 | Utils.createTexture(gl, gl.TEXTURE2, gl.TEXTURE_2D); 135 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, IRRADIANCE_TEXTURE_WIDTH, 136 | IRRADIANCE_TEXTURE_HEIGHT, 0, gl.RGBA, gl.FLOAT, data); 137 | }); 138 | 139 | Utils.loadShaderSource('vertex_shader.txt', (source) => { 140 | this.vertexShaderSource = source; 141 | }); 142 | Utils.loadShaderSource('fragment_shader.txt', (source) => { 143 | this.fragmentShaderSource = source; 144 | }); 145 | Utils.loadShaderSource('atmosphere_shader.txt', (source) => { 146 | this.atmosphereShaderSource = source; 147 | }); 148 | } 149 | 150 | /* 151 |

The WebGL program cannot be created before all the shaders are loaded. This 152 | is done in the following method, which is called at each frame (the precomputed 153 | shaders are OpenGL 3.3 shaders, so they must be adapted for WebGL2. In 154 | particular, the version id must be changed, and the fragment shaders must be 155 | concatenated because WebGL2 does not support multiple shaders of the same type): 156 | */ 157 | 158 | maybeInitProgram() { 159 | if (this.program || 160 | !this.vertexShaderSource || 161 | !this.fragmentShaderSource || 162 | !this.atmosphereShaderSource) { 163 | return; 164 | } 165 | const gl = this.gl; 166 | const vertexShader = 167 | Utils.createShader(gl, gl.VERTEX_SHADER, 168 | this.vertexShaderSource.replace('#version 330', '#version 300 es')); 169 | const fragmentShader = Utils.createShader( 170 | gl, 171 | gl.FRAGMENT_SHADER, 172 | this.atmosphereShaderSource 173 | .replace('#version 330', 174 | '#version 300 es\n' + 175 | 'precision highp float;\n' + 176 | 'precision highp sampler3D;') + 177 | this.fragmentShaderSource 178 | .replace('#version 330', '') 179 | .replace('const float PI = 3.14159265;', '') 180 | ); 181 | this.program = gl.createProgram(); 182 | gl.attachShader(this.program, vertexShader); 183 | gl.attachShader(this.program, fragmentShader); 184 | gl.linkProgram(this.program); 185 | } 186 | 187 | /* 188 |

The render loop body sets the program attributes and uniforms, and renders 189 | a full screen quad with it: 190 | */ 191 | 192 | onRender() { 193 | const gl = this.gl; 194 | gl.clearColor(0, 0, 0, 1); 195 | gl.clear(gl.COLOR_BUFFER_BIT); 196 | 197 | this.maybeInitProgram(); 198 | if (!this.program) { 199 | requestAnimationFrame(() => this.onRender()); 200 | return; 201 | } 202 | 203 | const kFovY = 50 / 180 * Math.PI; 204 | const kTanFovY = Math.tan(kFovY / 2); 205 | const aspectRatio = this.canvas.width / this.canvas.height; 206 | this.viewFromClip.set([ 207 | kTanFovY * aspectRatio, 0, 0, 0, 208 | 0, kTanFovY, 0, 0, 209 | 0, 0, 0, -1, 210 | 0, 0, 1, 1]); 211 | 212 | const cosZ = Math.cos(this.viewZenithAngleRadians); 213 | const sinZ = Math.sin(this.viewZenithAngleRadians); 214 | const cosA = Math.cos(this.viewAzimuthAngleRadians); 215 | const sinA = Math.sin(this.viewAzimuthAngleRadians); 216 | const viewDistance = this.viewDistanceMeters / kLengthUnitInMeters; 217 | this.modelFromView.set([ 218 | -sinA, -cosZ * cosA, sinZ * cosA, sinZ * cosA * viewDistance, 219 | cosA, -cosZ * sinA, sinZ * sinA, sinZ * sinA * viewDistance, 220 | 0, sinZ, cosZ, cosZ * viewDistance, 221 | 0, 0, 0, 1]); 222 | 223 | const program = this.program; 224 | gl.useProgram(program); 225 | gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); 226 | gl.vertexAttribPointer( 227 | gl.getAttribLocation(program, 'vertex'), 228 | /*numComponents=*/ 2, 229 | /*type=*/ this.gl.FLOAT, 230 | /*normalize=*/ false, 231 | /*stride=*/ 0, 232 | /*offset=*/ 0); 233 | gl.enableVertexAttribArray(gl.getAttribLocation(program, 'vertex')); 234 | gl.uniformMatrix4fv(gl.getUniformLocation(program, 'view_from_clip'), 235 | true, this.viewFromClip); 236 | gl.uniformMatrix4fv(gl.getUniformLocation(program, 'model_from_view'), 237 | true, this.modelFromView); 238 | gl.uniform1i(gl.getUniformLocation(program, 'transmittance_texture'), 0); 239 | gl.uniform1i(gl.getUniformLocation(program, 'scattering_texture'), 1); 240 | // Unused texture sampler, but bind a 3D texture to it anyway, just in case. 241 | gl.uniform1i( 242 | gl.getUniformLocation(program, 'single_mie_scattering_texture'), 1); 243 | gl.uniform1i(gl.getUniformLocation(program, 'irradiance_texture'), 2); 244 | gl.uniform3f(gl.getUniformLocation(program, 'camera'), 245 | this.modelFromView[3], this.modelFromView[7], this.modelFromView[11]); 246 | gl.uniform3f(gl.getUniformLocation(program, 'white_point'), 1, 1, 1); 247 | gl.uniform1f(gl.getUniformLocation(program, 'exposure'), this.exposure); 248 | gl.uniform3f(gl.getUniformLocation(program, 'earth_center'), 249 | 0, 0, -6360000 / kLengthUnitInMeters); 250 | gl.uniform3f(gl.getUniformLocation(program, 'sun_direction'), 251 | Math.cos(this.sunAzimuthAngleRadians) * 252 | Math.sin(this.sunZenithAngleRadians), 253 | Math.sin(this.sunAzimuthAngleRadians) * 254 | Math.sin(this.sunZenithAngleRadians), 255 | Math.cos(this.sunZenithAngleRadians)); 256 | gl.uniform2f(gl.getUniformLocation(program, 'sun_size'), 257 | Math.tan(kSunAngularRadius), Math.cos(kSunAngularRadius)); 258 | gl.drawArrays(gl.TRIANGLE_STRIP, /*offset=*/ 0, /*vertexCount=*/ 4); 259 | requestAnimationFrame(() => this.onRender()); 260 | } 261 | 262 | /* 263 |

The last part of the Demo class are the event handler methods, which are 264 | directly adapted from the C++ code: 265 | */ 266 | 267 | onKeyPress(event) { 268 | const key = event.key; 269 | if (key == 'h') { 270 | const hidden = this.help.style.display == 'none'; 271 | this.help.style.display = hidden ? 'block' : 'none'; 272 | } else if (key == '+') { 273 | this.exposure *= 1.1; 274 | } else if (key == '-') { 275 | this.exposure /= 1.1; 276 | } else if (key == '1') { 277 | this.setView(9000, 1.47, 0, 1.3, 3, 10); 278 | } else if (key == '2') { 279 | this.setView(9000, 1.47, 0, 1.564, -3, 10); 280 | } else if (key == '3') { 281 | this.setView(7000, 1.57, 0, 1.54, -2.96, 10); 282 | } else if (key == '4') { 283 | this.setView(7000, 1.57, 0, 1.328, -3.044, 10); 284 | } else if (key == '5') { 285 | this.setView(9000, 1.39, 0, 1.2, 0.7, 10); 286 | } else if (key == '6') { 287 | this.setView(9000, 1.5, 0, 1.628, 1.05, 200); 288 | } else if (key == '7') { 289 | this.setView(7000, 1.43, 0, 1.57, 1.34, 40); 290 | } else if (key == '8') { 291 | this.setView(2.7e6, 0.81, 0, 1.57, 2, 10); 292 | } else if (key == '9') { 293 | this.setView(1.2e7, 0.0, 0, 0.93, -2, 10); 294 | } 295 | } 296 | 297 | setView(viewDistanceMeters, viewZenithAngleRadians, viewAzimuthAngleRadians, 298 | sunZenithAngleRadians, sunAzimuthAngleRadians, exposure) { 299 | this.viewDistanceMeters = viewDistanceMeters; 300 | this.viewZenithAngleRadians = viewZenithAngleRadians; 301 | this.viewAzimuthAngleRadians = viewAzimuthAngleRadians; 302 | this.sunZenithAngleRadians = sunZenithAngleRadians; 303 | this.sunAzimuthAngleRadians = sunAzimuthAngleRadians; 304 | this.exposure = exposure; 305 | } 306 | 307 | onMouseDown(event) { 308 | this.previousMouseX = event.offsetX; 309 | this.previousMouseY = event.offsetY; 310 | this.drag = event.ctrlKey ? 'sun' : 'camera'; 311 | } 312 | 313 | onMouseMove(event) { 314 | const kScale = 500; 315 | const mouseX = event.offsetX; 316 | const mouseY = event.offsetY; 317 | if (this.drag == 'sun') { 318 | this.sunZenithAngleRadians -= (this.previousMouseY - mouseY) / kScale; 319 | this.sunZenithAngleRadians = 320 | Math.max(0, Math.min(Math.PI, this.sunZenithAngleRadians)); 321 | this.sunAzimuthAngleRadians += (this.previousMouseX - mouseX) / kScale; 322 | } else if (this.drag == 'camera') { 323 | this.viewZenithAngleRadians += (this.previousMouseY - mouseY) / kScale; 324 | this.viewZenithAngleRadians = 325 | Math.max(0, Math.min(Math.PI / 2, this.viewZenithAngleRadians)); 326 | this.viewAzimuthAngleRadians += (this.previousMouseX - mouseX) / kScale; 327 | } 328 | this.previousMouseX = mouseX; 329 | this.previousMouseY = mouseY; 330 | } 331 | 332 | onMouseUp(event) { 333 | this.drag = undefined; 334 | } 335 | 336 | onMouseWheel(event) { 337 | this.viewDistanceMeters *= event.deltaY > 0 ? 1.05 : 1 / 1.05; 338 | } 339 | } 340 | 341 | /* 342 |

The Utils class used above provides 4 methods, to load shader 343 | and texture data using XML http requests, and to create WebGL shader and 344 | texture objects from them: 345 | */ 346 | 347 | class Utils { 348 | 349 | static loadShaderSource(shaderName, callback) { 350 | const xhr = new XMLHttpRequest(); 351 | xhr.open('GET', shaderName); 352 | xhr.responseType = 'text'; 353 | xhr.onload = (event) => callback(xhr.responseText.trim()); 354 | xhr.send(); 355 | } 356 | 357 | static createShader(gl, type, source) { 358 | const shader = gl.createShader(type); 359 | gl.shaderSource(shader, source); 360 | gl.compileShader(shader); 361 | return shader; 362 | } 363 | 364 | static loadTextureData(textureName, callback) { 365 | const xhr = new XMLHttpRequest(); 366 | xhr.open('GET', textureName); 367 | xhr.responseType = 'arraybuffer'; 368 | xhr.onload = (event) => { 369 | const data = new DataView(xhr.response); 370 | const array = 371 | new Float32Array(data.byteLength / Float32Array.BYTES_PER_ELEMENT); 372 | for (var i = 0; i < array.length; ++i) { 373 | array[i] = data.getFloat32(i * Float32Array.BYTES_PER_ELEMENT, true); 374 | } 375 | callback(array); 376 | }; 377 | xhr.send(); 378 | } 379 | 380 | static createTexture(gl, textureUnit, target) { 381 | const texture = gl.createTexture(); 382 | gl.activeTexture(textureUnit); 383 | gl.bindTexture(target, texture); 384 | gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 385 | gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 386 | gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 387 | gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 388 | return texture; 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /atmosphere/demo/webgl/precompute.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/demo/webgl/precompute.cc

31 | 32 |

This file precomputes the atmosphere textures and saves them to disk. It also 33 | saves to disk the shaders necessary for the demo. For this a C++ 34 | Demo instance is created (which precomputes the 35 | textures and creates the shaders), its shaders and textures are read back using 36 | the OpenGL API, and are saved to disk: 37 | */ 38 | 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | 45 | #include "atmosphere/demo/demo.h" 46 | #include "atmosphere/constants.h" 47 | 48 | using atmosphere::demo::Demo; 49 | 50 | void SaveShader(const GLuint shader, const std::string& filename) { 51 | GLint sourceLength; 52 | glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceLength); 53 | 54 | GLsizei actualLength; 55 | std::unique_ptr buffer(new GLchar[sourceLength]); 56 | glGetShaderSource(shader, sourceLength, &actualLength, buffer.get()); 57 | 58 | std::ofstream output_stream(filename, std::ofstream::out); 59 | output_stream << std::string(buffer.get()); 60 | output_stream.close(); 61 | } 62 | 63 | void SaveTexture(const GLenum texture_unit, const GLenum texture_target, 64 | const int texture_size, const std::string& filename) { 65 | std::unique_ptr pixels(new float[texture_size * 4]); 66 | glActiveTexture(texture_unit); 67 | glGetTexImage(texture_target, 0, GL_RGBA, GL_FLOAT, pixels.get()); 68 | 69 | std::ofstream output_stream( 70 | filename, std::ofstream::out | std::ofstream::binary); 71 | output_stream.write((const char*) pixels.get(), texture_size * 16); 72 | output_stream.close(); 73 | } 74 | 75 | int main(int argc, char** argv) { 76 | glutInitContextVersion(3, 3); 77 | glutInitContextProfile(GLUT_CORE_PROFILE); 78 | glutInit(&argc, argv); 79 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); 80 | 81 | std::unique_ptr demo(new Demo(0, 0)); 82 | demo->model().SetProgramUniforms(demo->program(), 0, 1, 2); 83 | 84 | const std::string output_dir(argv[1]); 85 | SaveShader(demo->model().shader(), output_dir + "atmosphere_shader.txt"); 86 | SaveShader(demo->vertex_shader(), output_dir + "vertex_shader.txt"); 87 | SaveShader(demo->fragment_shader(), output_dir + "fragment_shader.txt"); 88 | SaveTexture( 89 | GL_TEXTURE0, 90 | GL_TEXTURE_2D, 91 | atmosphere::TRANSMITTANCE_TEXTURE_WIDTH * 92 | atmosphere::TRANSMITTANCE_TEXTURE_HEIGHT, 93 | output_dir + "transmittance.dat"); 94 | SaveTexture( 95 | GL_TEXTURE1, 96 | GL_TEXTURE_3D, 97 | atmosphere::SCATTERING_TEXTURE_WIDTH * 98 | atmosphere::SCATTERING_TEXTURE_HEIGHT * 99 | atmosphere::SCATTERING_TEXTURE_DEPTH, 100 | output_dir + "scattering.dat"); 101 | SaveTexture( 102 | GL_TEXTURE2, 103 | GL_TEXTURE_2D, 104 | atmosphere::IRRADIANCE_TEXTURE_WIDTH * 105 | atmosphere::IRRADIANCE_TEXTURE_HEIGHT, 106 | output_dir + "irradiance.dat"); 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /atmosphere/model.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/model.h

31 | 32 |

This file defines the API to use our atmosphere model in OpenGL applications. 33 | To use it: 34 |

46 | 47 |

The shader returned by GetShader provides the following 48 | functions (that you need to forward declare in your own shaders to be able to 49 | compile them separately): 50 | 51 |

 52 | // Returns the radiance of the Sun, outside the atmosphere.
 53 | vec3 GetSolarRadiance();
 54 | 
 55 | // Returns the sky radiance along the segment from 'camera' to the nearest
 56 | // atmosphere boundary in direction 'view_ray', as well as the transmittance
 57 | // along this segment.
 58 | vec3 GetSkyRadiance(vec3 camera, vec3 view_ray, double shadow_length,
 59 |     vec3 sun_direction, out vec3 transmittance);
 60 | 
 61 | // Returns the sky radiance along the segment from 'camera' to 'p', as well as
 62 | // the transmittance along this segment.
 63 | vec3 GetSkyRadianceToPoint(vec3 camera, vec3 p, double shadow_length,
 64 |     vec3 sun_direction, out vec3 transmittance);
 65 | 
 66 | // Returns the sun and sky irradiance received on a surface patch located at 'p'
 67 | // and whose normal vector is 'normal'.
 68 | vec3 GetSunAndSkyIrradiance(vec3 p, vec3 normal, vec3 sun_direction,
 69 |     out vec3 sky_irradiance);
 70 | 
 71 | // Returns the luminance of the Sun, outside the atmosphere.
 72 | vec3 GetSolarLuminance();
 73 | 
 74 | // Returns the sky luminance along the segment from 'camera' to the nearest
 75 | // atmosphere boundary in direction 'view_ray', as well as the transmittance
 76 | // along this segment.
 77 | vec3 GetSkyLuminance(vec3 camera, vec3 view_ray, double shadow_length,
 78 |     vec3 sun_direction, out vec3 transmittance);
 79 | 
 80 | // Returns the sky luminance along the segment from 'camera' to 'p', as well as
 81 | // the transmittance along this segment.
 82 | vec3 GetSkyLuminanceToPoint(vec3 camera, vec3 p, double shadow_length,
 83 |     vec3 sun_direction, out vec3 transmittance);
 84 | 
 85 | // Returns the sun and sky illuminance received on a surface patch located at
 86 | // 'p' and whose normal vector is 'normal'.
 87 | vec3 GetSunAndSkyIlluminance(vec3 p, vec3 normal, vec3 sun_direction,
 88 |     out vec3 sky_illuminance);
 89 | 
90 | 91 |

where 92 |

105 | 106 |

and where 107 |

119 | 120 |

Note The precomputed atmosphere textures can store either irradiance 121 | or illuminance values (see the num_precomputed_wavelengths 122 | parameter): 123 |

145 | 146 |

The concrete API definition is the following: 147 | */ 148 | 149 | #ifndef ATMOSPHERE_MODEL_H_ 150 | #define ATMOSPHERE_MODEL_H_ 151 | 152 | #include 153 | #include 154 | #include 155 | #include 156 | #include 157 | 158 | namespace atmosphere { 159 | 160 | // An atmosphere layer of width 'width' (in m), and whose density is defined as 161 | // 'exp_term' * exp('exp_scale' * h) + 'linear_term' * h + 'constant_term', 162 | // clamped to [0,1], and where h is the altitude (in m). 'exp_term' and 163 | // 'constant_term' are unitless, while 'exp_scale' and 'linear_term' are in 164 | // m^-1. 165 | class DensityProfileLayer { 166 | public: 167 | DensityProfileLayer() : DensityProfileLayer(0.0, 0.0, 0.0, 0.0, 0.0) {} 168 | DensityProfileLayer(double width, double exp_term, double exp_scale, 169 | double linear_term, double constant_term) 170 | : width(width), exp_term(exp_term), exp_scale(exp_scale), 171 | linear_term(linear_term), constant_term(constant_term) { 172 | } 173 | double width; 174 | double exp_term; 175 | double exp_scale; 176 | double linear_term; 177 | double constant_term; 178 | }; 179 | 180 | class Model { 181 | public: 182 | Model( 183 | // The wavelength values, in nanometers, and sorted in increasing order, for 184 | // which the solar_irradiance, rayleigh_scattering, mie_scattering, 185 | // mie_extinction and ground_albedo samples are provided. If your shaders 186 | // use luminance values (as opposed to radiance values, see above), use a 187 | // large number of wavelengths (e.g. between 15 and 50) to get accurate 188 | // results (this number of wavelengths has absolutely no impact on the 189 | // shader performance). 190 | const std::vector& wavelengths, 191 | // The solar irradiance at the top of the atmosphere, in W/m^2/nm. This 192 | // vector must have the same size as the wavelengths parameter. 193 | const std::vector& solar_irradiance, 194 | // The sun's angular radius, in radians. Warning: the implementation uses 195 | // approximations that are valid only if this value is smaller than 0.1. 196 | double sun_angular_radius, 197 | // The distance between the planet center and the bottom of the atmosphere, 198 | // in m. 199 | double bottom_radius, 200 | // The distance between the planet center and the top of the atmosphere, 201 | // in m. 202 | double top_radius, 203 | // The density profile of air molecules, i.e. a function from altitude to 204 | // dimensionless values between 0 (null density) and 1 (maximum density). 205 | // Layers must be sorted from bottom to top. The width of the last layer is 206 | // ignored, i.e. it always extend to the top atmosphere boundary. At most 2 207 | // layers can be specified. 208 | const std::vector& rayleigh_density, 209 | // The scattering coefficient of air molecules at the altitude where their 210 | // density is maximum (usually the bottom of the atmosphere), as a function 211 | // of wavelength, in m^-1. The scattering coefficient at altitude h is equal 212 | // to 'rayleigh_scattering' times 'rayleigh_density' at this altitude. This 213 | // vector must have the same size as the wavelengths parameter. 214 | const std::vector& rayleigh_scattering, 215 | // The density profile of aerosols, i.e. a function from altitude to 216 | // dimensionless values between 0 (null density) and 1 (maximum density). 217 | // Layers must be sorted from bottom to top. The width of the last layer is 218 | // ignored, i.e. it always extend to the top atmosphere boundary. At most 2 219 | // layers can be specified. 220 | const std::vector& mie_density, 221 | // The scattering coefficient of aerosols at the altitude where their 222 | // density is maximum (usually the bottom of the atmosphere), as a function 223 | // of wavelength, in m^-1. The scattering coefficient at altitude h is equal 224 | // to 'mie_scattering' times 'mie_density' at this altitude. This vector 225 | // must have the same size as the wavelengths parameter. 226 | const std::vector& mie_scattering, 227 | // The extinction coefficient of aerosols at the altitude where their 228 | // density is maximum (usually the bottom of the atmosphere), as a function 229 | // of wavelength, in m^-1. The extinction coefficient at altitude h is equal 230 | // to 'mie_extinction' times 'mie_density' at this altitude. This vector 231 | // must have the same size as the wavelengths parameter. 232 | const std::vector& mie_extinction, 233 | // The asymetry parameter for the Cornette-Shanks phase function for the 234 | // aerosols. 235 | double mie_phase_function_g, 236 | // The density profile of air molecules that absorb light (e.g. ozone), i.e. 237 | // a function from altitude to dimensionless values between 0 (null density) 238 | // and 1 (maximum density). Layers must be sorted from bottom to top. The 239 | // width of the last layer is ignored, i.e. it always extend to the top 240 | // atmosphere boundary. At most 2 layers can be specified. 241 | const std::vector& absorption_density, 242 | // The extinction coefficient of molecules that absorb light (e.g. ozone) at 243 | // the altitude where their density is maximum, as a function of wavelength, 244 | // in m^-1. The extinction coefficient at altitude h is equal to 245 | // 'absorption_extinction' times 'absorption_density' at this altitude. This 246 | // vector must have the same size as the wavelengths parameter. 247 | const std::vector& absorption_extinction, 248 | // The average albedo of the ground, as a function of wavelength. This 249 | // vector must have the same size as the wavelengths parameter. 250 | const std::vector& ground_albedo, 251 | // The maximum Sun zenith angle for which atmospheric scattering must be 252 | // precomputed, in radians (for maximum precision, use the smallest Sun 253 | // zenith angle yielding negligible sky light radiance values. For instance, 254 | // for the Earth case, 102 degrees is a good choice for most cases (120 255 | // degrees is necessary for very high exposure values). 256 | double max_sun_zenith_angle, 257 | // The length unit used in your shaders and meshes. This is the length unit 258 | // which must be used when calling the atmosphere model shader functions. 259 | double length_unit_in_meters, 260 | // The number of wavelengths for which atmospheric scattering must be 261 | // precomputed (the temporary GPU memory used during precomputations, and 262 | // the GPU memory used by the precomputed results, is independent of this 263 | // number, but the precomputation time is directly proportional to this 264 | // number): 265 | // - if this number is less than or equal to 3, scattering is precomputed 266 | // for 3 wavelengths, and stored as irradiance values. Then both the 267 | // radiance-based and the luminance-based API functions are provided (see 268 | // the above note). 269 | // - otherwise, scattering is precomputed for this number of wavelengths 270 | // (rounded up to a multiple of 3), integrated with the CIE color matching 271 | // functions, and stored as illuminance values. Then only the 272 | // luminance-based API functions are provided (see the above note). 273 | unsigned int num_precomputed_wavelengths, 274 | // Whether to pack the (red component of the) single Mie scattering with the 275 | // Rayleigh and multiple scattering in a single texture, or to store the 276 | // (3 components of the) single Mie scattering in a separate texture. 277 | bool combine_scattering_textures, 278 | // Whether to use half precision floats (16 bits) or single precision floats 279 | // (32 bits) for the precomputed textures. Half precision is sufficient for 280 | // most cases, except for very high exposure values. 281 | bool half_precision); 282 | 283 | ~Model(); 284 | 285 | void Init(unsigned int num_scattering_orders = 4); 286 | 287 | GLuint shader() const { return atmosphere_shader_; } 288 | 289 | void SetProgramUniforms( 290 | GLuint program, 291 | GLuint transmittance_texture_unit, 292 | GLuint scattering_texture_unit, 293 | GLuint irradiance_texture_unit, 294 | GLuint optional_single_mie_scattering_texture_unit = 0) const; 295 | 296 | // Utility method to convert a function of the wavelength to linear sRGB. 297 | // 'wavelengths' and 'spectrum' must have the same size. The integral of 298 | // 'spectrum' times each CIE_2_DEG_COLOR_MATCHING_FUNCTIONS (and times 299 | // MAX_LUMINOUS_EFFICACY) is computed to get XYZ values, which are then 300 | // converted to linear sRGB with the XYZ_TO_SRGB matrix. 301 | static void ConvertSpectrumToLinearSrgb( 302 | const std::vector& wavelengths, 303 | const std::vector& spectrum, 304 | double* r, double* g, double* b); 305 | 306 | static constexpr double kLambdaR = 680.0; 307 | static constexpr double kLambdaG = 550.0; 308 | static constexpr double kLambdaB = 440.0; 309 | 310 | private: 311 | typedef std::array vec3; 312 | typedef std::array mat3; 313 | 314 | void Precompute( 315 | GLuint fbo, 316 | GLuint delta_irradiance_texture, 317 | GLuint delta_rayleigh_scattering_texture, 318 | GLuint delta_mie_scattering_texture, 319 | GLuint delta_scattering_density_texture, 320 | GLuint delta_multiple_scattering_texture, 321 | const vec3& lambdas, 322 | const mat3& luminance_from_radiance, 323 | bool blend, 324 | unsigned int num_scattering_orders); 325 | 326 | unsigned int num_precomputed_wavelengths_; 327 | bool half_precision_; 328 | bool rgb_format_supported_; 329 | std::function glsl_header_factory_; 330 | GLuint transmittance_texture_; 331 | GLuint scattering_texture_; 332 | GLuint optional_single_mie_scattering_texture_; 333 | GLuint irradiance_texture_; 334 | GLuint atmosphere_shader_; 335 | GLuint full_screen_quad_vao_; 336 | GLuint full_screen_quad_vbo_; 337 | }; 338 | 339 | } // namespace atmosphere 340 | 341 | #endif // ATMOSPHERE_MODEL_H_ 342 | -------------------------------------------------------------------------------- /atmosphere/reference/definitions.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/reference/definitions.h

31 | 32 |

This C++ file defines the physical types and constants which are used in the 33 | main GLSL functions of our atmosphere 34 | model, in such a way that they can be compiled by the C++ compiler. The GLSL equivalent of this file provides the same 36 | types and constants in GLSL, to allow the same functions to be compiled by the 37 | GLSL compiler. 38 | 39 |

The main purpose of this C++ compilation is to check the dimensional homogeneity of the GLSL expressions (see the 42 | Introduction). For this we define the C++ physical 43 | types by using generic templates parameterized by physical dimensions, 44 | inspired from Boost.Unit, 46 | and provided by the following included files: 47 | */ 48 | 49 | #ifndef ATMOSPHERE_REFERENCE_DEFINITIONS_H_ 50 | #define ATMOSPHERE_REFERENCE_DEFINITIONS_H_ 51 | 52 | #include "atmosphere/constants.h" 53 | #include "math/angle.h" 54 | #include "math/binary_function.h" 55 | #include "math/scalar.h" 56 | #include "math/scalar_function.h" 57 | #include "math/ternary_function.h" 58 | #include "math/vector.h" 59 | 60 | namespace atmosphere { 61 | namespace reference { 62 | 63 | /* 64 |

Physical quantities

65 | 66 |

The physical quantities we need for our atmosphere model are 67 | radiometric and 68 | photometric 69 | quantities. We start with six base quantities: angle, length, wavelength, solid 70 | angle, power and luminous power (wavelength is also a length, but we distinguish 71 | the two for increased clarity). 72 | */ 73 | 74 | typedef dimensional::Angle Angle; 75 | typedef dimensional::Scalar<1, 0, 0, 0, 0> Length; 76 | typedef dimensional::Scalar<0, 1, 0, 0, 0> Wavelength; 77 | typedef dimensional::Scalar<0, 0, 1, 0, 0> SolidAngle; 78 | typedef dimensional::Scalar<0, 0, 0, 1, 0> Power; 79 | typedef dimensional::Scalar<0, 0, 0, 0, 1> LuminousPower; 80 | 81 | /* 82 |

From this we derive the irradiance, radiance, spectral irradiance, 83 | spectral radiance, luminance, etc, as well pure numbers, area, volume, etc. 84 | */ 85 | 86 | typedef dimensional::Scalar<0, 0, 0, 0, 0> Number; 87 | typedef dimensional::Scalar<-1, 0, 0, 0, 0> InverseLength; 88 | typedef dimensional::Scalar<2, 0, 0, 0, 0> Area; 89 | typedef dimensional::Scalar<3, 0, 0, 0, 0> Volume; 90 | typedef dimensional::Scalar<-2, 0, 0, 1, 0> Irradiance; 91 | typedef dimensional::Scalar<-2, 0, -1, 1, 0> Radiance; 92 | typedef dimensional::Scalar<0, -1, 0, 1, 0> SpectralPower; 93 | typedef dimensional::Scalar<-2, -1, 0, 1, 0> SpectralIrradiance; 94 | typedef dimensional::Scalar<-2, -1, -1, 1, 0> SpectralRadiance; 95 | typedef dimensional::Scalar<-3, -1, -1, 1, 0> SpectralRadianceDensity; 96 | typedef dimensional::Scalar<-1, 0, 0, 0, 0> ScatteringCoefficient; 97 | typedef dimensional::Scalar<0, 0, -1, 0, 0> InverseSolidAngle; 98 | typedef dimensional::Scalar<-3, 0, 0, 0, 0> NumberDensity; 99 | typedef dimensional::Scalar<0, 0, -1, 0, 1> LuminousIntensity; 100 | typedef dimensional::Scalar<-2, 0, -1, 0, 1> Luminance; 101 | typedef dimensional::Scalar<-2, 0, 0, 0, 1> Illuminance; 102 | 103 | /* 104 |

We also need vectors of physical quantities, mostly to represent functions 105 | depending on the wavelength. In this case the vector elements correspond to 106 | values of a function at some predefined wavelengths. Here we use 47 predefined 107 | wavelengths, uniformly distributed between 360 and 830 nanometers: 108 | */ 109 | 110 | template 111 | using WavelengthFunction = dimensional::ScalarFunction< 112 | 0, 1, 0, 0, 0, U1, U2, U3, U4, U5, 47, 360, 830>; 113 | 114 | // A function from Wavelength to Number. 115 | typedef WavelengthFunction<0, 0, 0, 0, 0> DimensionlessSpectrum; 116 | // A function from Wavelength to SpectralPower. 117 | typedef WavelengthFunction<0, -1, 0, 1, 0> PowerSpectrum; 118 | // A function from Wavelength to SpectralIrradiance. 119 | typedef WavelengthFunction<-2, -1, 0, 1, 0> IrradianceSpectrum; 120 | // A function from Wavelength to SpectralRadiance. 121 | typedef WavelengthFunction<-2, -1, -1, 1, 0> RadianceSpectrum; 122 | // A function from Wavelength to SpectralRadianceDensity. 123 | typedef WavelengthFunction<-3, -1, -1, 1, 0> RadianceDensitySpectrum; 124 | // A function from Wavelength to ScaterringCoefficient. 125 | typedef WavelengthFunction<-1, 0, 0, 0, 0> ScatteringSpectrum; 126 | 127 | // A position in 3D (3 length values). 128 | typedef dimensional::Vector3 Position; 129 | // A unit direction vector in 3D (3 unitless values). 130 | typedef dimensional::Vector3 Direction; 131 | // A vector of 3 luminance values. 132 | typedef dimensional::Vector3 Luminance3; 133 | // A vector of 3 illuminance values. 134 | typedef dimensional::Vector3 Illuminance3; 135 | 136 | /* 137 |

Finally, we also need precomputed textures containing physical quantities in 138 | each texel (the texture sizes are defined in 139 | constants.h): 140 | */ 141 | 142 | typedef dimensional::BinaryFunction< 143 | TRANSMITTANCE_TEXTURE_WIDTH, 144 | TRANSMITTANCE_TEXTURE_HEIGHT, 145 | DimensionlessSpectrum> TransmittanceTexture; 146 | 147 | template 148 | using AbstractScatteringTexture = dimensional::TernaryFunction< 149 | SCATTERING_TEXTURE_WIDTH, 150 | SCATTERING_TEXTURE_HEIGHT, 151 | SCATTERING_TEXTURE_DEPTH, 152 | T>; 153 | 154 | typedef AbstractScatteringTexture 155 | ReducedScatteringTexture; 156 | 157 | typedef AbstractScatteringTexture 158 | ScatteringTexture; 159 | 160 | typedef AbstractScatteringTexture 161 | ScatteringDensityTexture; 162 | 163 | typedef dimensional::BinaryFunction< 164 | IRRADIANCE_TEXTURE_WIDTH, 165 | IRRADIANCE_TEXTURE_HEIGHT, 166 | IrradianceSpectrum> IrradianceTexture; 167 | 168 | /* 169 |

Physical units

170 | 171 |

We can then define the units for our base physical quantities: 172 | radians (rad), meter (m), nanometer (nm), steradian (sr), watt (watt) and lumen 173 | (lm): 174 | */ 175 | 176 | constexpr Angle rad = dimensional::rad; 177 | constexpr Length m = Length::Unit(); 178 | constexpr Wavelength nm = Wavelength::Unit(); 179 | constexpr SolidAngle sr = SolidAngle::Unit(); 180 | constexpr Power watt = Power::Unit(); 181 | constexpr LuminousPower lm = LuminousPower::Unit(); 182 | 183 | /* 184 |

From which we can derive the units for some derived physical quantities, 185 | as well as some derived units (degress deg, kilometer km, kilocandela kcd): 186 | */ 187 | 188 | constexpr double PI = dimensional::PI; 189 | constexpr Angle pi = dimensional::pi; 190 | constexpr Angle deg = dimensional::deg; 191 | constexpr Length km = 1000.0 * m; 192 | constexpr Area m2 = m * m; 193 | constexpr Volume m3 = m * m * m; 194 | constexpr Irradiance watt_per_square_meter = watt / m2; 195 | constexpr Radiance watt_per_square_meter_per_sr = watt / (m2 * sr); 196 | constexpr SpectralIrradiance watt_per_square_meter_per_nm = watt / (m2 * nm); 197 | constexpr SpectralRadiance watt_per_square_meter_per_sr_per_nm = 198 | watt / (m2 * sr * nm); 199 | constexpr SpectralRadianceDensity watt_per_cubic_meter_per_sr_per_nm = 200 | watt / (m3 * sr * nm); 201 | constexpr LuminousIntensity cd = lm / sr; 202 | constexpr LuminousIntensity kcd = 1000.0 * cd; 203 | constexpr Luminance cd_per_square_meter = cd / m2; 204 | constexpr Luminance kcd_per_square_meter = kcd / m2; 205 | 206 | /* 207 |

Atmosphere parameters

208 | 209 |

Using the above types, we can now define the parameters of our atmosphere 210 | model. We start with the definition of density profiles, which are needed for 211 | parameters that depend on the altitude: 212 | */ 213 | 214 | // An atmosphere layer of width 'width', and whose density is defined as 215 | // 'exp_term' * exp('exp_scale' * h) + 'linear_term' * h + 'constant_term', 216 | // clamped to [0,1], and where h is the altitude. 217 | struct DensityProfileLayer { 218 | DensityProfileLayer() : 219 | DensityProfileLayer(0.0 * m, 0.0, 0.0 / m, 0.0 / m, 0.0) {} 220 | DensityProfileLayer(Length width, Number exp_term, InverseLength exp_scale, 221 | InverseLength linear_term, Number constant_term) 222 | : width(width), exp_term(exp_term), exp_scale(exp_scale), 223 | linear_term(linear_term), constant_term(constant_term) { 224 | } 225 | Length width; 226 | Number exp_term; 227 | InverseLength exp_scale; 228 | InverseLength linear_term; 229 | Number constant_term; 230 | }; 231 | 232 | // An atmosphere density profile made of several layers on top of each other 233 | // (from bottom to top). The width of the last layer is ignored, i.e. it always 234 | // extend to the top atmosphere boundary. The profile values vary between 0 235 | // (null density) to 1 (maximum density). 236 | struct DensityProfile { 237 | DensityProfileLayer layers[2]; 238 | }; 239 | 240 | struct AtmosphereParameters { 241 | // The solar irradiance at the top of the atmosphere. 242 | IrradianceSpectrum solar_irradiance; 243 | // The sun's angular radius. Warning: the implementation uses approximations 244 | // that are valid only if this angle is smaller than 0.1 radians. 245 | Angle sun_angular_radius; 246 | // The distance between the planet center and the bottom of the atmosphere. 247 | Length bottom_radius; 248 | // The distance between the planet center and the top of the atmosphere. 249 | Length top_radius; 250 | // The density profile of air molecules, i.e. a function from altitude to 251 | // dimensionless values between 0 (null density) and 1 (maximum density). 252 | DensityProfile rayleigh_density; 253 | // The scattering coefficient of air molecules at the altitude where their 254 | // density is maximum (usually the bottom of the atmosphere), as a function of 255 | // wavelength. The scattering coefficient at altitude h is equal to 256 | // 'rayleigh_scattering' times 'rayleigh_density' at this altitude. 257 | ScatteringSpectrum rayleigh_scattering; 258 | // The density profile of aerosols, i.e. a function from altitude to 259 | // dimensionless values between 0 (null density) and 1 (maximum density). 260 | DensityProfile mie_density; 261 | // The scattering coefficient of aerosols at the altitude where their density 262 | // is maximum (usually the bottom of the atmosphere), as a function of 263 | // wavelength. The scattering coefficient at altitude h is equal to 264 | // 'mie_scattering' times 'mie_density' at this altitude. 265 | ScatteringSpectrum mie_scattering; 266 | // The extinction coefficient of aerosols at the altitude where their density 267 | // is maximum (usually the bottom of the atmosphere), as a function of 268 | // wavelength. The extinction coefficient at altitude h is equal to 269 | // 'mie_extinction' times 'mie_density' at this altitude. 270 | ScatteringSpectrum mie_extinction; 271 | // The asymetry parameter for the Cornette-Shanks phase function for the 272 | // aerosols. 273 | Number mie_phase_function_g; 274 | // The density profile of air molecules that absorb light (e.g. ozone), i.e. 275 | // a function from altitude to dimensionless values between 0 (null density) 276 | // and 1 (maximum density). 277 | DensityProfile absorption_density; 278 | // The extinction coefficient of molecules that absorb light (e.g. ozone) at 279 | // the altitude where their density is maximum, as a function of wavelength. 280 | // The extinction coefficient at altitude h is equal to 281 | // 'absorption_extinction' times 'absorption_density' at this altitude. 282 | ScatteringSpectrum absorption_extinction; 283 | // The average albedo of the ground. 284 | DimensionlessSpectrum ground_albedo; 285 | // The cosine of the maximum Sun zenith angle for which atmospheric scattering 286 | // must be precomputed (for maximum precision, use the smallest Sun zenith 287 | // angle yielding negligible sky light radiance values. For instance, for the 288 | // Earth case, 102 degrees is a good choice - yielding mu_s_min = -0.2). 289 | Number mu_s_min; 290 | }; 291 | 292 | } // namespace reference 293 | } // namespace atmosphere 294 | 295 | #endif // ATMOSPHERE_REFERENCE_DEFINITIONS_H_ 296 | -------------------------------------------------------------------------------- /atmosphere/reference/functions.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/reference/functions.cc

31 | 32 |

This file "provides" the C++ implementation of our atmosphere model. In fact 33 | the real implementation is provided in the 34 | corresponding GLSL file, which is included here, 35 | after the definition of the macros which are needed to be able to compile this 36 | GLSL code as C++. 37 | */ 38 | 39 | #include "atmosphere/reference/functions.h" 40 | 41 | #include 42 | 43 | #define IN(x) const x& 44 | #define OUT(x) x& 45 | #define TEMPLATE(x) template 46 | #define TEMPLATE_ARGUMENT(x) 47 | 48 | namespace atmosphere { 49 | namespace reference { 50 | 51 | using std::max; 52 | using std::min; 53 | 54 | #include "atmosphere/functions.glsl" 55 | 56 | } // namespace reference 57 | } // namespace atmosphere 58 | -------------------------------------------------------------------------------- /atmosphere/reference/functions.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/reference/functions.h

31 | 32 |

This file provides a C++ header for the GLSL 33 | functions that implement our atmosphere model. The C++ "implementation" is 34 | provided in functions.cc (this file simply 35 | includes the GLSL file after defining the macros it depends on). The 36 | documentation is provided in the GLSL file. 37 | */ 38 | 39 | #ifndef ATMOSPHERE_REFERENCE_FUNCTIONS_H_ 40 | #define ATMOSPHERE_REFERENCE_FUNCTIONS_H_ 41 | 42 | #include "atmosphere/reference/definitions.h" 43 | 44 | namespace atmosphere { 45 | namespace reference { 46 | 47 | typedef dimensional::vec2 vec2; 48 | typedef dimensional::vec3 vec3; 49 | typedef dimensional::vec4 vec4; 50 | 51 | // Transmittance. 52 | 53 | Length DistanceToTopAtmosphereBoundary( 54 | const AtmosphereParameters& atmosphere, Length r, Number mu); 55 | 56 | Length DistanceToBottomAtmosphereBoundary( 57 | const AtmosphereParameters& atmosphere, Length r, Number mu); 58 | 59 | bool RayIntersectsGround( 60 | const AtmosphereParameters& atmosphere, Length r, Number mu); 61 | 62 | Number GetLayerDensity(const DensityProfileLayer& layer, Length altitude); 63 | 64 | Number GetProfileDensity(const DensityProfile& profile, Length altitude); 65 | 66 | Length ComputeOpticalLengthToTopAtmosphereBoundary( 67 | const AtmosphereParameters& atmosphere, const DensityProfile& profile, 68 | Length r, Number mu); 69 | 70 | DimensionlessSpectrum ComputeTransmittanceToTopAtmosphereBoundary( 71 | const AtmosphereParameters& atmosphere, Length r, Number mu); 72 | 73 | Number GetTextureCoordFromUnitRange(Number x, int texture_size); 74 | 75 | Number GetUnitRangeFromTextureCoord(Number u, int texture_size); 76 | 77 | vec2 GetTransmittanceTextureUvFromRMu(const AtmosphereParameters& atmosphere, 78 | Length r, Number mu); 79 | 80 | void GetRMuFromTransmittanceTextureUv(const AtmosphereParameters& atmosphere, 81 | const vec2& uv, Length& r, Number& mu); 82 | 83 | DimensionlessSpectrum ComputeTransmittanceToTopAtmosphereBoundaryTexture( 84 | const AtmosphereParameters& atmosphere, const vec2& gl_frag_coord); 85 | 86 | DimensionlessSpectrum GetTransmittanceToTopAtmosphereBoundary( 87 | const AtmosphereParameters& atmosphere, 88 | const TransmittanceTexture& transmittance_texture, 89 | Length r, Number mu); 90 | 91 | DimensionlessSpectrum GetTransmittance( 92 | const AtmosphereParameters& atmosphere, 93 | const TransmittanceTexture& transmittance_texture, 94 | Length r, Number mu, Length d, bool ray_r_mu_intersects_ground); 95 | 96 | // Single scattering. 97 | 98 | void ComputeSingleScatteringIntegrand( 99 | const AtmosphereParameters& atmosphere, 100 | const TransmittanceTexture& transmittance_texture, 101 | Length r, Number mu, Number mu_s, Number nu, Length d, 102 | bool ray_r_mu_intersects_ground, 103 | DimensionlessSpectrum& rayleigh, DimensionlessSpectrum& mie); 104 | 105 | Length DistanceToNearestAtmosphereBoundary( 106 | const AtmosphereParameters& atmosphere, Length r, Number mu, 107 | bool ray_r_mu_intersects_ground); 108 | 109 | void ComputeSingleScattering( 110 | const AtmosphereParameters& atmosphere, 111 | const TransmittanceTexture& transmittance_texture, 112 | Length r, Number mu, Number mu_s, Number nu, 113 | bool ray_r_mu_intersects_ground, 114 | IrradianceSpectrum& rayleigh, IrradianceSpectrum& mie); 115 | 116 | InverseSolidAngle RayleighPhaseFunction(Number nu); 117 | InverseSolidAngle MiePhaseFunction(Number g, Number nu); 118 | 119 | vec4 GetScatteringTextureUvwzFromRMuMuSNu( 120 | const AtmosphereParameters& atmosphere, 121 | Length r, Number mu, Number mu_s, Number nu, 122 | bool ray_r_mu_intersects_ground); 123 | 124 | void GetRMuMuSNuFromScatteringTextureUvwz( 125 | const AtmosphereParameters& atmosphere, const vec4& uvwz, 126 | Length& r, Number& mu, Number& mu_s, Number& nu, 127 | bool& ray_r_mu_intersects_ground); 128 | 129 | void ComputeSingleScatteringTexture(const AtmosphereParameters& atmosphere, 130 | const TransmittanceTexture& transmittance_texture, 131 | const vec3& gl_frag_coord, IrradianceSpectrum& rayleigh, 132 | IrradianceSpectrum& mie); 133 | 134 | template 135 | T GetScattering( 136 | const AtmosphereParameters& atmosphere, 137 | const AbstractScatteringTexture& scattering_texture, 138 | Length r, Number mu, Number mu_s, Number nu, 139 | bool ray_r_mu_intersects_ground); 140 | 141 | RadianceSpectrum GetScattering( 142 | const AtmosphereParameters& atmosphere, 143 | const ReducedScatteringTexture& single_rayleigh_scattering_texture, 144 | const ReducedScatteringTexture& single_mie_scattering_texture, 145 | const ScatteringTexture& multiple_scattering_texture, 146 | Length r, Number mu, Number mu_s, Number nu, 147 | bool ray_r_mu_intersects_ground, 148 | int scattering_order); 149 | 150 | // Multiple scattering. 151 | 152 | RadianceDensitySpectrum ComputeScatteringDensity( 153 | const AtmosphereParameters& atmosphere, 154 | const TransmittanceTexture& transmittance_texture, 155 | const ReducedScatteringTexture& single_rayleigh_scattering_texture, 156 | const ReducedScatteringTexture& single_mie_scattering_texture, 157 | const ScatteringTexture& multiple_scattering_texture, 158 | const IrradianceTexture& irradiance_texture, 159 | Length r, Number mu, Number mu_s, Number nu, 160 | int scattering_order); 161 | 162 | RadianceSpectrum ComputeMultipleScattering( 163 | const AtmosphereParameters& atmosphere, 164 | const TransmittanceTexture& transmittance_texture, 165 | const ScatteringDensityTexture& scattering_density_texture, 166 | Length r, Number mu, Number mu_s, Number nu, 167 | bool ray_r_mu_intersects_ground); 168 | 169 | RadianceDensitySpectrum ComputeScatteringDensityTexture( 170 | const AtmosphereParameters& atmosphere, 171 | const TransmittanceTexture& transmittance_texture, 172 | const ReducedScatteringTexture& single_rayleigh_scattering_texture, 173 | const ReducedScatteringTexture& single_mie_scattering_texture, 174 | const ScatteringTexture& multiple_scattering_texture, 175 | const IrradianceTexture& irradiance_texture, 176 | const vec3& gl_frag_coord, int scattering_order); 177 | 178 | RadianceSpectrum ComputeMultipleScatteringTexture( 179 | const AtmosphereParameters& atmosphere, 180 | const TransmittanceTexture& transmittance_texture, 181 | const ScatteringDensityTexture& scattering_density_texture, 182 | const vec3& gl_frag_coord, Number& nu); 183 | 184 | // Ground irradiance. 185 | 186 | IrradianceSpectrum ComputeDirectIrradiance( 187 | const AtmosphereParameters& atmosphere, 188 | const TransmittanceTexture& transmittance_texture, 189 | Length r, Number mu_s); 190 | 191 | IrradianceSpectrum ComputeIndirectIrradiance( 192 | const AtmosphereParameters& atmosphere, 193 | const ReducedScatteringTexture& single_rayleigh_scattering_texture, 194 | const ReducedScatteringTexture& single_mie_scattering_texture, 195 | const ScatteringTexture& multiple_scattering_texture, 196 | Length r, Number mu_s, int scattering_order); 197 | 198 | vec2 GetIrradianceTextureUvFromRMuS(const AtmosphereParameters& atmosphere, 199 | Length r, Number mu_s); 200 | 201 | void GetRMuSFromIrradianceTextureUv(const AtmosphereParameters& atmosphere, 202 | const vec2& uv, Length& r, Number& mu_s); 203 | 204 | IrradianceSpectrum ComputeDirectIrradianceTexture( 205 | const AtmosphereParameters& atmosphere, 206 | const TransmittanceTexture& transmittance_texture, 207 | const vec2& gl_frag_coord); 208 | 209 | IrradianceSpectrum ComputeIndirectIrradianceTexture( 210 | const AtmosphereParameters& atmosphere, 211 | const ReducedScatteringTexture& single_rayleigh_scattering_texture, 212 | const ReducedScatteringTexture& single_mie_scattering_texture, 213 | const ScatteringTexture& multiple_scattering_texture, 214 | const vec2& gl_frag_coord, int scattering_order); 215 | 216 | IrradianceSpectrum GetIrradiance( 217 | const AtmosphereParameters& atmosphere, 218 | const IrradianceTexture& irradiance_texture, 219 | Length r, Number mu_s); 220 | 221 | // Rendering. 222 | 223 | RadianceSpectrum GetSkyRadiance( 224 | const AtmosphereParameters& atmosphere, 225 | const TransmittanceTexture& transmittance_texture, 226 | const ReducedScatteringTexture& scattering_texture, 227 | const ReducedScatteringTexture& single_mie_scattering_texture, 228 | Position camera, const Direction& view_ray, Length shadow_length, 229 | const Direction& sun_direction, DimensionlessSpectrum& transmittance); 230 | 231 | RadianceSpectrum GetSkyRadianceToPoint( 232 | const AtmosphereParameters& atmosphere, 233 | const TransmittanceTexture& transmittance_texture, 234 | const ReducedScatteringTexture& scattering_texture, 235 | const ReducedScatteringTexture& single_mie_scattering_texture, 236 | Position camera, const Position& point, Length shadow_length, 237 | const Direction& sun_direction, DimensionlessSpectrum& transmittance); 238 | 239 | IrradianceSpectrum GetSunAndSkyIrradiance( 240 | const AtmosphereParameters& atmosphere, 241 | const TransmittanceTexture& transmittance_texture, 242 | const IrradianceTexture& irradiance_texture, 243 | const Position& point, const Direction& normal, 244 | const Direction& sun_direction, IrradianceSpectrum& sky_irradiance); 245 | 246 | } // namespace reference 247 | } // namespace atmosphere 248 | 249 | #endif // ATMOSPHERE_REFERENCE_FUNCTIONS_H_ 250 | -------------------------------------------------------------------------------- /atmosphere/reference/model.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/reference/model.cc

31 | 32 |

This file implements our atmosphere model on CPU. Its main role is to 33 | precompute the transmittance, scattering and irradiance textures. The C++ 34 | functions to precompute them are provided in 35 | functions.h, but they are not sufficient. 36 | They must be called on each texel of the precomputed textures, and these 37 | textures must be computed in the correct order, with the correct input an output 38 | textures, to precompute each scattering order in sequence, as described in 39 | Algorithm 4.1 of our paper. 40 | This is the role of the following code. 41 | 42 |

We start by including the files we need: 43 | */ 44 | 45 | #include "atmosphere/reference/model.h" 46 | 47 | #include "atmosphere/reference/functions.h" 48 | #include "util/progress_bar.h" 49 | 50 | /* 51 |

The constructor of the Model class allocates the precomputed 52 | textures, but does not initialize them. 53 | */ 54 | 55 | namespace atmosphere { 56 | namespace reference { 57 | 58 | Model::Model(const AtmosphereParameters& atmosphere, 59 | const std::string& cache_directory) 60 | : atmosphere_(atmosphere), 61 | cache_directory_(cache_directory) { 62 | transmittance_texture_.reset(new TransmittanceTexture()); 63 | scattering_texture_.reset(new ReducedScatteringTexture()); 64 | single_mie_scattering_texture_.reset(new ReducedScatteringTexture()); 65 | irradiance_texture_.reset(new IrradianceTexture()); 66 | } 67 | 68 | /* 69 |

The initialization is done in the following method, which first tries to load 70 | the textures from disk, if they have already been precomputed. 71 | */ 72 | 73 | void Model::Init(unsigned int num_scattering_orders) { 74 | std::ifstream file; 75 | file.open(cache_directory_ + "transmittance.dat"); 76 | if (file.good()) { 77 | file.close(); 78 | transmittance_texture_->Load(cache_directory_ + "transmittance.dat"); 79 | scattering_texture_->Load(cache_directory_ + "scattering.dat"); 80 | single_mie_scattering_texture_->Load( 81 | cache_directory_ + "single_mie_scattering.dat"); 82 | irradiance_texture_->Load(cache_directory_ + "irradiance.dat"); 83 | return; 84 | } 85 | 86 | /* 87 |

If they have not already been precomputed, we must compute them here. This 88 | computation requires some temporary textures, in particular to store the 89 | contribution of one scattering order, which is needed to compute the next order 90 | of scattering (the final precomputed textures store the sum of all the 91 | scattering orders). We allocate these textures here (they are automatically 92 | destroyed at the end of this method). 93 | */ 94 | 95 | std::unique_ptr 96 | delta_irradiance_texture(new IrradianceTexture()); 97 | std::unique_ptr 98 | delta_rayleigh_scattering_texture(new ReducedScatteringTexture()); 99 | ReducedScatteringTexture* delta_mie_scattering_texture = 100 | single_mie_scattering_texture_.get(); 101 | std::unique_ptr 102 | delta_scattering_density_texture(new ScatteringDensityTexture()); 103 | std::unique_ptr 104 | delta_multiple_scattering_texture(new ScatteringTexture()); 105 | 106 | /* 107 |

Since the computation phase takes several minutes, we show a progress bar to 108 | provide feedback to the user. The following constants roughly represent the 109 | relative duration of each computation phase, and are used to display a progress 110 | value which is roughly proportional to the elapsed time. 111 | */ 112 | 113 | constexpr unsigned int kTransmittanceProgress = 1; 114 | constexpr unsigned int kDirectIrradianceProgress = 1; 115 | constexpr unsigned int kSingleScatteringProgress = 10; 116 | constexpr unsigned int kScatteringDensityProgress = 100; 117 | constexpr unsigned int kIndirectIrradianceProgress = 10; 118 | constexpr unsigned int kMultipleScatteringProgress = 10; 119 | const unsigned int kTotalProgress = 120 | TRANSMITTANCE_TEXTURE_WIDTH * TRANSMITTANCE_TEXTURE_HEIGHT * 121 | kTransmittanceProgress + 122 | IRRADIANCE_TEXTURE_WIDTH * IRRADIANCE_TEXTURE_HEIGHT * ( 123 | kDirectIrradianceProgress + 124 | kIndirectIrradianceProgress * (num_scattering_orders - 1)) + 125 | SCATTERING_TEXTURE_WIDTH * SCATTERING_TEXTURE_HEIGHT * 126 | SCATTERING_TEXTURE_DEPTH * ( 127 | kSingleScatteringProgress + 128 | (kScatteringDensityProgress + kMultipleScatteringProgress) * 129 | (num_scattering_orders - 1)); 130 | 131 | ProgressBar progress_bar(kTotalProgress); 132 | 133 | /* 134 |

The remaining code of this method implements Algorithm 4.1 of our paper, 135 | using several threads to speed up computations (by computing several texels of 136 | a texture in parallel). 137 | */ 138 | 139 | // Compute the transmittance, and store it in transmittance_texture_. 140 | RunJobs([&](unsigned int j) { 141 | for (unsigned int i = 0; i < TRANSMITTANCE_TEXTURE_WIDTH; ++i) { 142 | transmittance_texture_->Set(i, j, 143 | ComputeTransmittanceToTopAtmosphereBoundaryTexture( 144 | atmosphere_, vec2(i + 0.5, j + 0.5))); 145 | progress_bar.Increment(kTransmittanceProgress); 146 | } 147 | }, TRANSMITTANCE_TEXTURE_HEIGHT); 148 | 149 | // Compute the direct irradiance, store it in delta_irradiance_texture, and 150 | // initialize irradiance_texture_ with zeros (we don't want the direct 151 | // irradiance in irradiance_texture_, but only the irradiance from the sky). 152 | RunJobs([&](unsigned int j) { 153 | for (unsigned int i = 0; i < IRRADIANCE_TEXTURE_WIDTH; ++i) { 154 | delta_irradiance_texture->Set(i, j, 155 | ComputeDirectIrradianceTexture( 156 | atmosphere_, *transmittance_texture_, vec2(i + 0.5, j + 0.5))); 157 | irradiance_texture_->Set( 158 | i, j, IrradianceSpectrum(0.0 * watt_per_square_meter_per_nm)); 159 | progress_bar.Increment(kDirectIrradianceProgress); 160 | } 161 | }, IRRADIANCE_TEXTURE_HEIGHT); 162 | 163 | // Compute the rayleigh and mie single scattering, and store them in 164 | // delta_rayleigh_scattering_texture and delta_mie_scattering_texture, as well 165 | // as in scattering_texture. 166 | RunJobs([&](unsigned int k) { 167 | for (unsigned int j = 0; j < SCATTERING_TEXTURE_HEIGHT; ++j) { 168 | for (unsigned int i = 0; i < SCATTERING_TEXTURE_WIDTH; ++i) { 169 | IrradianceSpectrum rayleigh; 170 | IrradianceSpectrum mie; 171 | ComputeSingleScatteringTexture(atmosphere_, *transmittance_texture_, 172 | vec3(i + 0.5, j + 0.5, k + 0.5), rayleigh, mie); 173 | delta_rayleigh_scattering_texture->Set(i, j, k, rayleigh); 174 | delta_mie_scattering_texture->Set(i, j, k, mie); 175 | scattering_texture_->Set(i, j, k, rayleigh); 176 | progress_bar.Increment(kSingleScatteringProgress); 177 | } 178 | } 179 | }, SCATTERING_TEXTURE_DEPTH); 180 | 181 | // Compute the 2nd, 3rd and 4th order of scattering, in sequence. 182 | for (unsigned int scattering_order = 2; 183 | scattering_order <= num_scattering_orders; 184 | ++scattering_order) { 185 | // Compute the scattering density, and store it in 186 | // delta_scattering_density_texture. 187 | RunJobs([&](unsigned int k) { 188 | for (unsigned int j = 0; j < SCATTERING_TEXTURE_HEIGHT; ++j) { 189 | for (unsigned int i = 0; i < SCATTERING_TEXTURE_WIDTH; ++i) { 190 | RadianceDensitySpectrum scattering_density; 191 | scattering_density = ComputeScatteringDensityTexture(atmosphere_, 192 | *transmittance_texture_, *delta_rayleigh_scattering_texture, 193 | *delta_mie_scattering_texture, 194 | *delta_multiple_scattering_texture, *delta_irradiance_texture, 195 | vec3(i + 0.5, j + 0.5, k + 0.5), scattering_order); 196 | delta_scattering_density_texture->Set(i, j, k, scattering_density); 197 | progress_bar.Increment(kScatteringDensityProgress); 198 | } 199 | } 200 | }, SCATTERING_TEXTURE_DEPTH); 201 | 202 | // Compute the indirect irradiance, store it in delta_irradiance_texture and 203 | // accumulate it in irradiance_texture_. 204 | RunJobs([&](unsigned int j) { 205 | for (unsigned int i = 0; i < IRRADIANCE_TEXTURE_WIDTH; ++i) { 206 | IrradianceSpectrum delta_irradiance; 207 | delta_irradiance = ComputeIndirectIrradianceTexture( 208 | atmosphere_, *delta_rayleigh_scattering_texture, 209 | *delta_mie_scattering_texture, *delta_multiple_scattering_texture, 210 | vec2(i + 0.5, j + 0.5), scattering_order - 1); 211 | delta_irradiance_texture->Set(i, j, delta_irradiance); 212 | progress_bar.Increment(kIndirectIrradianceProgress); 213 | } 214 | }, IRRADIANCE_TEXTURE_HEIGHT); 215 | (*irradiance_texture_) += *delta_irradiance_texture; 216 | 217 | // Compute the multiple scattering, store it in 218 | // delta_multiple_scattering_texture, and accumulate it in 219 | // scattering_texture_. 220 | RunJobs([&](unsigned int k) { 221 | for (unsigned int j = 0; j < SCATTERING_TEXTURE_HEIGHT; ++j) { 222 | for (unsigned int i = 0; i < SCATTERING_TEXTURE_WIDTH; ++i) { 223 | RadianceSpectrum delta_multiple_scattering; 224 | Number nu; 225 | delta_multiple_scattering = ComputeMultipleScatteringTexture( 226 | atmosphere_, *transmittance_texture_, 227 | *delta_scattering_density_texture, 228 | vec3(i + 0.5, j + 0.5, k + 0.5), nu); 229 | delta_multiple_scattering_texture->Set( 230 | i, j, k, delta_multiple_scattering); 231 | scattering_texture_->Set(i, j, k, 232 | scattering_texture_->Get(i, j, k) + 233 | delta_multiple_scattering * (1.0 / RayleighPhaseFunction(nu))); 234 | progress_bar.Increment(kMultipleScatteringProgress); 235 | } 236 | } 237 | }, SCATTERING_TEXTURE_DEPTH); 238 | } 239 | 240 | transmittance_texture_->Save(cache_directory_ + "transmittance.dat"); 241 | scattering_texture_->Save(cache_directory_ + "scattering.dat"); 242 | single_mie_scattering_texture_->Save( 243 | cache_directory_ + "single_mie_scattering.dat"); 244 | irradiance_texture_->Save(cache_directory_ + "irradiance.dat"); 245 | } 246 | 247 | /* 248 |

Once the textures have been computed or loaded from the cache, they can be 249 | used to compute the sky radiance and the sun and sky irradiance. The functions 250 | for doing that are provided in functions.h and we 251 | just need here to wrap them in their corresponding methods (except for the solar 252 | radiance, which can be directly computed from the model parameters): 253 | */ 254 | 255 | RadianceSpectrum Model::GetSolarRadiance() const { 256 | SolidAngle sun_solid_angle = 2.0 * PI * 257 | (1.0 - cos(atmosphere_.sun_angular_radius)) * sr; 258 | return atmosphere_.solar_irradiance * (1.0 / sun_solid_angle); 259 | } 260 | 261 | RadianceSpectrum Model::GetSkyRadiance(Position camera, Direction view_ray, 262 | Length shadow_length, Direction sun_direction, 263 | DimensionlessSpectrum* transmittance) const { 264 | return reference::GetSkyRadiance(atmosphere_, *transmittance_texture_, 265 | *scattering_texture_, *single_mie_scattering_texture_, 266 | camera, view_ray, shadow_length, sun_direction, *transmittance); 267 | } 268 | 269 | RadianceSpectrum Model::GetSkyRadianceToPoint(Position camera, Position point, 270 | Length shadow_length, Direction sun_direction, 271 | DimensionlessSpectrum* transmittance) const { 272 | return reference::GetSkyRadianceToPoint(atmosphere_, *transmittance_texture_, 273 | *scattering_texture_, *single_mie_scattering_texture_, 274 | camera, point, shadow_length, sun_direction, *transmittance); 275 | } 276 | 277 | IrradianceSpectrum Model::GetSunAndSkyIrradiance(Position point, 278 | Direction normal, Direction sun_direction, 279 | IrradianceSpectrum* sky_irradiance) const { 280 | return reference::GetSunAndSkyIrradiance(atmosphere_, *transmittance_texture_, 281 | *irradiance_texture_, point, normal, sun_direction, *sky_irradiance); 282 | } 283 | 284 | } // namespace reference 285 | } // namespace atmosphere 286 | -------------------------------------------------------------------------------- /atmosphere/reference/model.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/reference/model.h

31 | 32 |

This file defines the API to use our atmosphere model on CPU. 33 | To use it: 34 |

45 | */ 46 | 47 | #ifndef ATMOSPHERE_REFERENCE_MODEL_H_ 48 | #define ATMOSPHERE_REFERENCE_MODEL_H_ 49 | 50 | #include 51 | #include 52 | #include 53 | 54 | #include "atmosphere/reference/definitions.h" 55 | 56 | namespace atmosphere { 57 | namespace reference { 58 | 59 | class Model { 60 | public: 61 | Model(const AtmosphereParameters& atmosphere, 62 | const std::string& cache_directory); 63 | 64 | void Init(unsigned int num_scattering_orders = 4); 65 | 66 | RadianceSpectrum GetSolarRadiance() const; 67 | 68 | RadianceSpectrum GetSkyRadiance(Position camera, Direction view_ray, 69 | Length shadow_length, Direction sun_direction, 70 | DimensionlessSpectrum* transmittance) const; 71 | 72 | RadianceSpectrum GetSkyRadianceToPoint(Position camera, Position point, 73 | Length shadow_length, Direction sun_direction, 74 | DimensionlessSpectrum* transmittance) const; 75 | 76 | IrradianceSpectrum GetSunAndSkyIrradiance(Position p, Direction normal, 77 | Direction sun_direction, IrradianceSpectrum* sky_irradiance) const; 78 | 79 | private: 80 | const AtmosphereParameters atmosphere_; 81 | const std::string cache_directory_; 82 | std::unique_ptr transmittance_texture_; 83 | std::unique_ptr scattering_texture_; 84 | std::unique_ptr single_mie_scattering_texture_; 85 | std::unique_ptr irradiance_texture_; 86 | }; 87 | 88 | } // namespace reference 89 | } // namespace atmosphere 90 | 91 | #endif // ATMOSPHERE_REFERENCE_MODEL_H_ 92 | -------------------------------------------------------------------------------- /atmosphere/reference/model_test.glsl: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*

atmosphere/reference/model_test.glsl

31 | 32 |

This GLSL file is used to render the test scene in 33 | model_test.cc, on GPU and CPU, in order to 34 | evaluate the approximations made in the GPU atmosphere model. For this reason, 35 | as for the GPU model shaders, it is written in such a way that it can be 36 | compiled either with a GLSL compiler, or with a C++ compiler. 37 | 38 |

39 | 40 |

The test scene, shown above, is a sphere S on a purely spherical planet P. It 41 | is rendered by "ray tracing", i.e. the vertex shader outputs the view ray 42 | direction, and the fragment shader computes the intersection of this ray with 43 | the spheres S and P to produce the final pixels. The fragment shader also 44 | computes the intersection of the light rays with the sphere S, to compute 45 | shadows, as well as the intersections of the view ray with the shadow volume of 46 | S, in order to compute light shafts. 47 | 48 |

Shadows and light shafts

49 | 50 |

The functions to compute shadows and light shafts must be defined before we 51 | can use them in the main shader function, so we define them first. Testing if 52 | a point is in the shadow of the sphere S is equivalent to test if the 53 | corresponding light ray intersects the sphere, which is very simple to do. 54 | However, this is only valid for a punctual light source, which is not the case 55 | of the Sun. In the following function we compute an approximate (and biased) 56 | soft shadow by taking the angular size of the Sun into account: 57 | */ 58 | 59 | Number GetSunVisibility(Position point, Direction sun_direction) { 60 | Position p = point - kSphereCenter; 61 | Length p_dot_v = dot(p, sun_direction); 62 | Area p_dot_p = dot(p, p); 63 | Area ray_sphere_center_squared_distance = p_dot_p - p_dot_v * p_dot_v; 64 | Length distance_to_intersection = -p_dot_v - sqrt( 65 | kSphereRadius * kSphereRadius - ray_sphere_center_squared_distance); 66 | if (distance_to_intersection > 0.0 * m) { 67 | // Compute the distance between the view ray and the sphere, and the 68 | // corresponding (tangent of the) subtended angle. Finally, use this to 69 | // compute an approximate sun visibility. 70 | Length ray_sphere_distance = 71 | kSphereRadius - sqrt(ray_sphere_center_squared_distance); 72 | Number ray_sphere_angular_distance = -ray_sphere_distance / p_dot_v; 73 | return smoothstep( 74 | Number(1.0), Number(0.0), ray_sphere_angular_distance / sun_size_.x); 75 | } 76 | return 1.0; 77 | } 78 | 79 | /* 80 |

The sphere also partially occludes the sky light, and we approximate this 81 | effect with an ambient occlusion factor. The ambient occlusion factor due to a 82 | sphere is given in Radiation View Factors (Isidoro Martinez, 1995). In the simple case where 85 | the sphere is fully visible, it is given by the following function: 86 | */ 87 | 88 | Number GetSkyVisibility(Position point) { 89 | Position p = point - kSphereCenter; 90 | Area p_dot_p = dot(p, p); 91 | return 92 | 1.0 + p.z / sqrt(p_dot_p) * kSphereRadius * kSphereRadius / p_dot_p; 93 | } 94 | 95 | /* 96 |

To compute light shafts we need the intersections of the view ray with the 97 | shadow volume of the sphere S. Since the Sun is not a punctual light source this 98 | shadow volume is not a cylinder but a cone (for the umbra, plus another cone for 99 | the penumbra, but we ignore it here): 100 | 101 | 102 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | p 127 | q 128 | s 129 | v 130 | R 131 | r 132 | ρ 133 | d 134 | δ 135 | α 136 | 137 | 138 |

Noting, as in the above figure, $\bp$ the camera position, $\bv$ and $\bs$ 139 | the unit view ray and sun direction vectors and $R$ the sphere radius (supposed 140 | to be centered on the origin), the point at distance $d$ from the camera is 141 | $\bq=\bp+d\bv$. This point is at a distance $\delta=-\bq\cdot\bs$ from the 142 | sphere center along the umbra cone axis, and at a distance $r$ from this axis 143 | given by $r^2=\bq\cdot\bq-\delta^2$. Finally, at distance $\delta$ along the 144 | axis the umbra cone has radius $\rho=R-\delta\tan\alpha$, where $\alpha$ is 145 | the Sun's angular radius. The point at distance $d$ from the camera is on the 146 | shadow cone only if $r^2=\rho^2$, i.e. only if 147 | \begin{equation} 148 | (\bp+d\bv)\cdot(\bp+d\bv)-((\bp+d\bv)\cdot\bs)^2= 149 | (R+((\bp+d\bv)\cdot\bs)\tan\alpha)^2 150 | \end{equation} 151 | Developping this gives a quadratic equation for $d$: 152 | \begin{equation} 153 | ad^2+2bd+c=0 154 | \end{equation} 155 | where 156 |

    157 |
  • $a=1-l(\bv\cdot\bs)^2$,
  • 158 |
  • $b=\bp\cdot\bv-l(\bp\cdot\bs)(\bv\cdot\bs)-\tan(\alpha)R(\bv\cdot\bs)$,
  • 159 |
  • $c=\bp\cdot\bp-l(\bp\cdot\bs)^2-2\tan(\alpha)R(\bp\cdot\bs)-R^2$,
  • 160 |
  • $l=1+\tan^2\alpha$
  • 161 |
162 | From this we deduce the two possible solutions for $d$, which must be clamped to 163 | the actual shadow part of the mathematical cone (i.e. the slab between the 164 | sphere center and the cone apex or, in other words, the points for which 165 | $\delta$ is between $0$ and $R/\tan\alpha$). The following function implements 166 | these equations: 167 | */ 168 | 169 | void GetSphereShadowInOut(Direction view_direction, Direction sun_direction, 170 | OUT(Length) d_in, OUT(Length) d_out) { 171 | Position pos = camera_ - kSphereCenter; 172 | Length pos_dot_sun = dot(pos, sun_direction_); 173 | Number view_dot_sun = dot(view_direction, sun_direction_); 174 | Number k = sun_size_.x; 175 | Number l = 1.0 + k * k; 176 | Number a = 1.0 - l * view_dot_sun * view_dot_sun; 177 | Length b = dot(pos, view_direction) - l * pos_dot_sun * view_dot_sun - 178 | k * kSphereRadius * view_dot_sun; 179 | Area c = dot(pos, pos) - l * pos_dot_sun * pos_dot_sun - 180 | 2.0 * k * kSphereRadius * pos_dot_sun - kSphereRadius * kSphereRadius; 181 | Area discriminant = b * b - a * c; 182 | if (discriminant > 0.0 * m2) { 183 | d_in = max(0.0 * m, (-b - sqrt(discriminant)) / a); 184 | d_out = (-b + sqrt(discriminant)) / a; 185 | // The values of d for which delta is equal to 0 and kSphereRadius / k. 186 | Length d_base = -pos_dot_sun / view_dot_sun; 187 | Length d_apex = -(pos_dot_sun + kSphereRadius / k) / view_dot_sun; 188 | if (view_dot_sun > 0.0) { 189 | d_in = max(d_in, d_apex); 190 | d_out = a > 0.0 ? min(d_out, d_base) : d_base; 191 | } else { 192 | d_in = a > 0.0 ? max(d_in, d_base) : d_base; 193 | d_out = min(d_out, d_apex); 194 | } 195 | } else { 196 | d_in = 0.0 * m; 197 | d_out = 0.0 * m; 198 | } 199 | } 200 | 201 | /*

Main shading function

202 | 203 |

Using these functions we can now implement the main shader function, which 204 | computes the radiance from the scene for a given view ray. This function first 205 | tests if the view ray intersects the sphere S. If so it computes the sun and 206 | sky light received by the sphere at the intersection point, combines this with 207 | the sphere BRDF and the aerial perspective between the camera and the sphere. 208 | It then does the same with the ground, i.e. with the planet sphere P, and then 209 | computes the sky radiance and transmittance. Finally, all these terms are 210 | composited together (an opacity is also computed for each object, using an 211 | approximate view cone - sphere intersection factor) to get the final radiance. 212 | 213 |

We start with the computation of the intersections of the view ray with the 214 | shadow volume of the sphere, because they are needed to get the aerial 215 | perspective for the sphere and the planet: 216 | */ 217 | 218 | RadianceSpectrum GetViewRayRadiance(Direction view_ray, 219 | Direction view_ray_diff) { 220 | // Normalized view direction vector. 221 | Direction view_direction = normalize(view_ray); 222 | // Tangent of the angle subtended by this fragment. 223 | Number fragment_angular_size = length(view_ray_diff) / length(view_ray); 224 | 225 | Length shadow_in; 226 | Length shadow_out; 227 | GetSphereShadowInOut(view_direction, sun_direction_, shadow_in, shadow_out); 228 | 229 | /* 230 |

We then test whether the view ray intersects the sphere S or not. If it does, 231 | we compute an approximate (and biased) opacity value, using the same 232 | approximation as in GetSunVisibility: 233 | */ 234 | 235 | // Compute the distance between the view ray line and the sphere center, 236 | // and the distance between the camera and the intersection of the view 237 | // ray with the sphere (or NaN if there is no intersection). 238 | Position p = camera_ - kSphereCenter; 239 | Length p_dot_v = dot(p, view_direction); 240 | Area p_dot_p = dot(p, p); 241 | Area ray_sphere_center_squared_distance = p_dot_p - p_dot_v * p_dot_v; 242 | Length distance_to_intersection = -p_dot_v - sqrt( 243 | kSphereRadius * kSphereRadius - ray_sphere_center_squared_distance); 244 | 245 | // Compute the radiance reflected by the sphere, if the ray intersects it. 246 | Number sphere_alpha = 0.0; 247 | RadianceSpectrum sphere_radiance = 248 | RadianceSpectrum(0.0 * watt_per_square_meter_per_sr_per_nm); 249 | if (distance_to_intersection > 0.0 * m) { 250 | // Compute the distance between the view ray and the sphere, and the 251 | // corresponding (tangent of the) subtended angle. Finally, use this to 252 | // compute the approximate analytic antialiasing factor sphere_alpha. 253 | Length ray_sphere_distance = 254 | kSphereRadius - sqrt(ray_sphere_center_squared_distance); 255 | Number ray_sphere_angular_distance = -ray_sphere_distance / p_dot_v; 256 | sphere_alpha = 257 | min(ray_sphere_angular_distance / fragment_angular_size, 1.0); 258 | 259 | /* 260 |

We can then compute the intersection point and its normal, and use them to 261 | get the sun and sky irradiance received at this point. The reflected radiance 262 | follows, by multiplying the irradiance with the sphere BRDF: 263 | */ 264 | Position point = camera_ + view_direction * distance_to_intersection; 265 | Direction normal = normalize(point - kSphereCenter); 266 | 267 | // Compute the radiance reflected by the sphere. 268 | IrradianceSpectrum sky_irradiance; 269 | IrradianceSpectrum sun_irradiance = GetSunAndSkyIrradiance( 270 | point - earth_center_, normal, sun_direction_, sky_irradiance); 271 | sphere_radiance = 272 | sphere_albedo_ * (1.0 / (PI * sr)) * (sun_irradiance + sky_irradiance); 273 | 274 | /* 275 |

Finally, we take into account the aerial perspective between the camera and 276 | the sphere, which depends on the length of this segment which is in shadow: 277 | */ 278 | Length shadow_length = 279 | max(0.0 * m, min(shadow_out, distance_to_intersection) - shadow_in); 280 | DimensionlessSpectrum transmittance; 281 | RadianceSpectrum in_scatter = GetSkyRadianceToPoint(camera_ - earth_center_, 282 | point - earth_center_, shadow_length, sun_direction_, transmittance); 283 | sphere_radiance = sphere_radiance * transmittance + in_scatter; 284 | } 285 | 286 | /* 287 |

In the following we repeat the same steps as above, but for the planet sphere 288 | P instead of the sphere S (a smooth opacity is not really needed here, so we 289 | don't compute it. Note also how we modulate the sun and sky irradiance received 290 | on the ground by the sun and sky visibility factors): 291 | */ 292 | 293 | // Compute the distance between the view ray line and the Earth center, 294 | // and the distance between the camera and the intersection of the view 295 | // ray with the ground (or NaN if there is no intersection). 296 | p = camera_ - earth_center_; 297 | p_dot_v = dot(p, view_direction); 298 | p_dot_p = dot(p, p); 299 | Area ray_earth_center_squared_distance = p_dot_p - p_dot_v * p_dot_v; 300 | distance_to_intersection = -p_dot_v - sqrt( 301 | earth_center_.z * earth_center_.z - ray_earth_center_squared_distance); 302 | 303 | // Compute the radiance reflected by the ground, if the ray intersects it. 304 | Number ground_alpha = 0.0; 305 | RadianceSpectrum ground_radiance = 306 | RadianceSpectrum(0.0 * watt_per_square_meter_per_sr_per_nm); 307 | if (distance_to_intersection > 0.0 * m) { 308 | Position point = camera_ + view_direction * distance_to_intersection; 309 | Direction normal = normalize(point - earth_center_); 310 | 311 | // Compute the radiance reflected by the ground. 312 | IrradianceSpectrum sky_irradiance; 313 | IrradianceSpectrum sun_irradiance = GetSunAndSkyIrradiance( 314 | point - earth_center_, normal, sun_direction_, sky_irradiance); 315 | ground_radiance = ground_albedo_ * (1.0 / (PI * sr)) * ( 316 | sun_irradiance * GetSunVisibility(point, sun_direction_) + 317 | sky_irradiance * GetSkyVisibility(point)); 318 | 319 | Length shadow_length = 320 | max(0.0 * m, min(shadow_out, distance_to_intersection) - shadow_in); 321 | DimensionlessSpectrum transmittance; 322 | RadianceSpectrum in_scatter = GetSkyRadianceToPoint(camera_ - earth_center_, 323 | point - earth_center_, shadow_length, sun_direction_, transmittance); 324 | ground_radiance = ground_radiance * transmittance + in_scatter; 325 | ground_alpha = 1.0; 326 | } 327 | 328 | /* 329 |

Finally, we compute the radiance and transmittance of the sky, and composite 330 | together, from back to front, the radiance and opacities of all the ojects of 331 | the scene: 332 | */ 333 | 334 | // Compute the radiance of the sky. 335 | Length shadow_length = max(0.0 * m, shadow_out - shadow_in); 336 | DimensionlessSpectrum transmittance; 337 | RadianceSpectrum radiance = GetSkyRadiance( 338 | camera_ - earth_center_, view_direction, shadow_length, sun_direction_, 339 | transmittance); 340 | 341 | // If the view ray intersects the Sun, add the Sun radiance. 342 | if (dot(view_direction, sun_direction_) > sun_size_.y) { 343 | radiance = radiance + transmittance * GetSolarRadiance(); 344 | } 345 | radiance = radiance * (1.0 - ground_alpha) + ground_radiance * ground_alpha; 346 | radiance = radiance * (1.0 - sphere_alpha) + sphere_radiance * sphere_alpha; 347 | return radiance; 348 | } 349 | -------------------------------------------------------------------------------- /external/glad/regenerate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # NOTE: to use local API description, put gl.xml into glad/ directory so that glad sees it in its $PWD 4 | cd "`dirname $0`" 5 | glad --out-path=. --generator=c --omit-khrplatform --api="gl=3.3" --profile=core --extensions= 6 | mv src/glad.c src/glad.cc 7 | -------------------------------------------------------------------------------- /index: -------------------------------------------------------------------------------- 1 |

Precomputed Atmospheric Scattering:
a New Implementation

2 | 3 |

Eric Bruneton, 2017

4 | 5 |
6 | 7 |
8 | 9 |

Introduction

10 | 11 |

This document presents a new implementation of our 12 | Precomputed Atmospheric 13 | Scattering paper. This new 15 | implementation is motivated by the fact that the 16 | original implementation: 19 |

    20 |
  • has almost no comments and no documentation, and as a result is difficult to understand and to reuse, 23 |
  • 24 |
  • has absolutely no tests, despite the high risk of implementation errors 25 | due to the complexity of the atmospheric scattering equations, 26 |
  • 27 |
  • contains ad-hoc constants in its texture coordinates mapping functions 28 | which are adapted to the Earth case, but cannot be reused for other planets, 29 |
  • 30 |
  • provides only one of the two options presented in the paper to store the 31 | single Mie scattering components (i.e. store the 3 components, or store only 32 | one and reconstruct the others with an approximation), 33 |
  • 34 |
  • does not implement the light shaft algorithm presented in the paper, 35 |
  • 36 |
  • uses an extra-terrestrial solar spectrum independent of the wavelength 37 | (with an arbitrary and completely unphysical value "100") and displays the 38 | radiance values directly instead of converting them first to luminance 39 | values (via the CIE color matching functions). 40 |
  • 41 |
42 | To address these concerns, our new 44 | implementation: 45 |
    46 |
  • uses more descriptive function and variable names, and adds extensive 47 | comments and documentation. 48 |
  • 49 |
  • uses static type checking to verify the dimensional homogeneity of all the expressions, and uses unit tests to 52 | check more complex constraints, 53 |
  • 54 |
  • uses slightly improved texture coordinates mapping functions which, in 55 | particular, no longer use ad-hoc constants, 56 |
  • 57 |
  • provides the two options presented in the paper to store the single Mie 58 | scattering components (which are then compared in our tests), 59 |
  • 60 |
  • partially implement the light shaft algorithm presented in the paper (it 61 | implements Eqs. 17 and 18, but not the shadow volume algorithm), 62 |
  • 63 |
  • uses a configurable extra-terrestrial solar spectrum, and either 64 | 75 | This gives almost the same results as with a full spectral rendering method, 76 | at a fraction of the cost (we check this by comparing the GPU results 77 | against full spectral CPU renderings). 78 |
  • 79 |
80 | In addition, the new implementation adds support for the ozone layer, and for 81 | custom density profiles for air molecules and aerosols. 82 | 83 |

The sections below explain how this new implementation can be used, present 84 | its structure and its documentation and give more details about its tests. 85 | 86 |

Usage

87 | 88 |

Our new 90 | implementation can be used in C++ / OpenGL applications as explained 91 | in model.h, and as demonstrated in the 92 | demo in atmosphere/demo. To run this demo, simply type make 93 | demo in the main directory. A WebGL2 version of this demo is also 94 | available online. 95 | 96 |

The default settings of this demo use the real solar spectrum, with an ozone 97 | layer. To simulate the settings of the original implementation, set the solar 98 | spectrum to "constant", and turn off the ozone layer. 99 | 100 |

Structure

101 | 102 |

The source code is organized as follows: 103 | 104 |

    105 |
  • atmosphere/
      106 |
    • demo/
      • ...
    • 107 |
    • reference/
      • ...
    • 108 |
    • constants.h
    • 109 |
    • definitions.glsl
    • 110 |
    • functions.glsl
    • 111 |
    • model.h
    • 112 |
    • model.cc
    • 113 |
  • 114 |
115 | 116 |

The most important files are the 5 files in the atmosphere 117 | directory. They contain the GLSL shaders that implement our atmosphere model, 118 | and provide a C++ API to precompute the atmosphere textures and to use them in 119 | an OpenGL application. This code does not depend on the content of the other 120 | directories, and is the only piece which is needed in order to use our 121 | atmosphere model on GPU. 122 | 123 |

The other directories provide examples and tests: 124 |

    125 |
  • The atmosphere/demo directory shows how the API provided in 126 | atmosphere can be used in practice, using a small C++/OpenGL 127 | demo application. A WebGL2 version of this demo is also available, in the 128 | webgl subdirectory. 129 |
  • 130 |
  • The atmosphere/reference directory provides a way to execute 131 | our GLSL code on CPU. Its main purpose is to provide unit tests for the GLSL 132 | shaders, and to statically check the dimensional homogeneity of all the expressions. This process is 135 | explained in more details in the Tests section. 136 | This code is also used to compute reference images on CPU using full 137 | spectral rendering, in order to evaluate the accuracy of the approximate 138 | "radiance to RGB luminance" conversion performed by the GPU shaders. It 139 | depends on external libraries such as dimensional_types 141 | (to check the dimensional homogeneity) and 142 | minpng. 143 |
  • 144 |
145 | 146 |

Documentation

147 | 148 |

The documentation consists of a set of web pages, generated from the 149 | extensive comments in each source code file: 150 |

185 | 186 |

Tests

187 | 188 |

To reduce the risk of implementation errors, two kinds of verifications are 189 | performed: 190 |

    191 |
  • the dimensional homogeneity is checked at compile time, via static type 194 | checking, 195 |
  • 196 |
  • the behavior of each function is checked at runtime, via unit tests. 197 |
  • 198 |
199 | 200 |

The main issue to implement this is that a GLSL compiler cannot check the 201 | dimensional homogeneity, unlike a C++ compiler (see for instance 203 | Boost.Units). Our solution to this problem is to write our GLSL code in such 204 | a way that it can be compiled both by a GLSL compiler and by a C++ compiler. 205 | For this: 206 |

    207 |
  • we use macros to hide the few syntactic differences between GLSL and C++. 208 | For instance, we define OUT(x) as out x in GLSL, 209 | and as x& in C++, and declare output variables as 210 | OUT(SomeType) someName in our shaders. 211 |
  • 212 |
  • we define the physical types, such as length or power, in a separate file, 213 | which we provide in two versions: 214 |
      215 |
    • the GLSL version 216 | defines the physical types as aliases of predefined types, such as 217 | float, 218 |
    • 219 |
    • the C++ version 220 | defines the physical types based on dimensional_types 222 | abstractions, which are designed to produce compile errors when 223 | attempting to add, subtract or compare expressions with different 224 | physical dimensions. 225 |
    • 226 |
    227 |
  • 228 |
  • we use the predefined GLSL variables such as gl_FragCoord 229 | only in the main functions, which we reduce to the minimum 230 | (e.g. main() { gl_FragColor = Main(gl_FragCoord); }) and 231 | exclude from the C++ compilation. 232 |
  • 233 |
234 | 235 |

Thanks to this double GLSL and C++ compilation, the unit tests for the GLSL 236 | code can then be implemented either in GLSL or in C++. We chose C++ because it 237 | is much more practical. Indeed, a C++ unit test does not need to send data to 238 | the GPU and to read back the test result, unlike a GLSL unit test. 239 | -------------------------------------------------------------------------------- /platform/windows/CMakelists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.5) 2 | 3 | macro(make_absolute files) 4 | set (_out) 5 | foreach(file ${${files}}) 6 | get_filename_component(file_abs ${file} ABSOLUTE) 7 | LIST(APPEND _out ${file_abs}) 8 | endforeach() 9 | set (${files} ${_out}) 10 | endmacro() 11 | 12 | macro(make_definitions definitions) 13 | set (_out) 14 | foreach(definition ${${definitions}}) 15 | LIST(APPEND _out -D${definition}) 16 | endforeach() 17 | set (${definitions} ${_out}) 18 | endmacro() 19 | 20 | set (GENERATED_FILES_PATH ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/generated/) 21 | set (GENERATED_FILES_INCLUDES ${GENERATED_FILES_PATH}) 22 | make_absolute(GENERATED_FILES_INCLUDES) 23 | 24 | set (ROOT ../../) 25 | set (SRC ${ROOT}atmosphere/) 26 | set (TEXT ${ROOT}text/) 27 | set (PUB ${ROOT}bin/) 28 | set (PUB_ABS ${PUB}) 29 | make_absolute(PUB_ABS) 30 | 31 | set (EXT ${ROOT}external/) 32 | 33 | set (PLATFORM_WIN_DIR ./) 34 | set (EXT_WIN ${PLATFORM_WIN_DIR}external/) 35 | 36 | set (GLAD ${EXT}glad/) 37 | set (GLAD_INCLUDES ${GLAD}include) 38 | make_absolute(GLAD_INCLUDES) 39 | 40 | set (FREEGLUT ${EXT_WIN}freeglut/) 41 | set (FREEGLUT_INCLUDES ${FREEGLUT}include) 42 | make_absolute(FREEGLUT_INCLUDES) 43 | set (FREEGLUT_LIBS ${FREEGLUT}lib/x64/freeglut.lib) 44 | make_absolute(FREEGLUT_LIBS) 45 | set (FREEGLUT_DLL ${FREEGLUT}bin/x64/freeglut.dll) 46 | make_absolute(FREEGLUT_DLL) 47 | 48 | set (CREATE_GLSL_INC ${PLATFORM_WIN_DIR}create_glsl_inc.bat) 49 | 50 | macro(set_output_dir OUTPUTDIR) 51 | set (OUTPUTDIR_ABS ${OUTPUTDIR}) 52 | make_absolute(OUTPUTDIR_ABS) 53 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUTDIR_ABS}) 54 | foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) 55 | string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) 56 | set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${OUTPUTDIR_ABS} ) 57 | endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) 58 | endmacro() 59 | 60 | macro(add_glsl_inc_cmd glslFilepath glslStartOfRelativePathForOutput GeneratedOutputPath) 61 | GET_FILENAME_COMPONENT(inputFullpath ${glslFilepath} ABSOLUTE) 62 | GET_FILENAME_COMPONENT(outputDir ${GeneratedOutputPath} ABSOLUTE) 63 | 64 | string(LENGTH ${glslStartOfRelativePathForOutput} len) 65 | string(SUBSTRING ${glslFilepath} ${len} -1 relativePathForOutput) 66 | SET (outputFile ${outputDir}/${relativePathForOutput}.inc) 67 | 68 | GET_FILENAME_COMPONENT(createCmdFullpath ${CREATE_GLSL_INC} ABSOLUTE) 69 | FILE(TO_NATIVE_PATH ${outputFile} outputFile) 70 | 71 | add_custom_command(OUTPUT ${outputFile} 72 | COMMAND ${createCmdFullpath} ${inputFullpath} ${outputFile} 73 | MAIN_DEPENDENCY ${glslFilepath} 74 | COMMENT "Generating ${glslfile}.inc in ${outputDir}" 75 | ) 76 | endmacro(add_glsl_inc_cmd) 77 | 78 | project (Atmosphere) 79 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 80 | 81 | set_output_dir(${PUB_ABS}) 82 | 83 | set(ROOT_INCLUDES ${ROOT}) 84 | make_absolute(ROOT_INCLUDES) 85 | 86 | set(PROJ Atmosphere) 87 | 88 | set(PROJSRC ${SRC}) 89 | 90 | set (DEFINITIONS 91 | _CRT_SECURE_NO_WARNINGS 92 | NOMINMAX 93 | ) 94 | make_definitions(DEFINITIONS) 95 | 96 | set (INCLUDES 97 | ${GENERATED_FILES_INCLUDES} 98 | ${ROOT_INCLUDES} 99 | ${GLAD_INCLUDES} 100 | ${FREEGLUT_INCLUDES} 101 | ) 102 | 103 | set (TEXT_PATH ${TEXT}) 104 | set (TEXT_FILES 105 | ${TEXT_PATH}font.inc 106 | ${TEXT_PATH}text_renderer.cc 107 | ${TEXT_PATH}text_renderer.h 108 | ) 109 | source_group(Text FILES ${TEXT_FILES}) 110 | 111 | set (MODEL_PATH ${PROJSRC}) 112 | set (MODEL_FILES 113 | ${MODEL_PATH}model.cc 114 | ${MODEL_PATH}model.h 115 | ${MODEL_PATH}constants.h 116 | ${MODEL_PATH}definitions.glsl 117 | ${MODEL_PATH}functions.glsl 118 | ) 119 | add_glsl_inc_cmd(${PROJSRC}definitions.glsl ${ROOT} ${GENERATED_FILES_PATH}) 120 | add_glsl_inc_cmd(${PROJSRC}functions.glsl ${ROOT} ${GENERATED_FILES_PATH}) 121 | source_group(Model FILES ${MODEL_FILES}) 122 | 123 | set (DEMO_PATH ${SRC}demo/) 124 | set (DEMO_FILES 125 | ${DEMO_PATH}demo.cc 126 | ${DEMO_PATH}demo.h 127 | ${DEMO_PATH}demo.glsl 128 | ${DEMO_PATH}demo_main.cc 129 | ) 130 | add_glsl_inc_cmd(${DEMO_PATH}demo.glsl ${ROOT} ${GENERATED_FILES_PATH}) 131 | source_group(Demo FILES ${DEMO_FILES}) 132 | 133 | set (GLAD_PATH ${GLAD}src/) 134 | set (GLAD_FILES 135 | ${GLAD_PATH}glad.cc 136 | ${GLAD}include/glad/glad.h 137 | ) 138 | source_group(Glad FILES ${GLAD_FILES}) 139 | 140 | set (SRC_FILES 141 | ${TEXT_FILES} 142 | ${MODEL_FILES} 143 | ${DEMO_FILES} 144 | ${GLAD_FILES} 145 | ) 146 | 147 | set (LIBS 148 | Shlwapi.lib 149 | Shell32.lib 150 | ${FREEGLUT_LIBS} 151 | ) 152 | 153 | add_executable (${PROJ} ${SRC_FILES}) 154 | 155 | target_compile_definitions(${PROJ} PUBLIC ${DEFINITIONS}) 156 | set_target_properties(${PROJ} PROPERTIES INCLUDE_DIRECTORIES "${INCLUDES}") 157 | set_target_properties(${PROJ} PROPERTIES DEBUG_POSTFIX "_d" ) 158 | set_target_properties(${PROJ} PROPERTIES RELEASE_POSTFIX "" ) 159 | set_target_properties(${PROJ} PROPERTIES MINSIZEREL_POSTFIX "_s" ) 160 | set_target_properties(${PROJ} PROPERTIES RELWITHDEBINFO_POSTFIX "_r" ) 161 | target_link_libraries(${PROJ} ${LIBS}) 162 | 163 | add_custom_command(TARGET ${PROJ} POST_BUILD 164 | COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FREEGLUT_DLL} $ 165 | ) 166 | -------------------------------------------------------------------------------- /platform/windows/README.txt: -------------------------------------------------------------------------------- 1 | Cmake setup for Visual Studio. 2 | Tested on Windows 10 with VS2017 x64. 3 | 4 | The required dependencies are cmake, freeglut and sed. 5 | A script is provided to download the versions of those dependencies available at the time when this is published. 6 | 7 | They need to be placed in "precomputed_atmospheric_scattering\platform\windows\external" 8 | following this hierarchy: 9 | 10 | precomputed_atmospheric_scattering\platform\windows\external\cmake\bin\cmake.exe 11 | precomputed_atmospheric_scattering\platform\windows\external\freeglut\lib\x64\freeglut.lib 12 | precomputed_atmospheric_scattering\platform\windows\external\sed\bin\sed.exe 13 | 14 | Tested with: 15 | 16 | cmake 3.9 17 | freeglut 3.0.0-1.mp 18 | sed-4.2.1 19 | 20 | download_build_run.bat: download dependencies, generate project, build and run. 21 | 22 | download_dependencies.bat: download the dependencies. 23 | it's using some Powershell modules which might only be available on Windows 8 and above. 24 | 25 | generate_project.bat: generate Visual Studio project, requires the dependencies. 26 | 27 | build.bat: build the project in Release using the cmake project, requires generate_project. 28 | 29 | run.bat: run the built project. 30 | 31 | 32 | -------------------------------------------------------------------------------- /platform/windows/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set CURDIR=%~dp0 4 | set CMAKEPATH=%CURDIR%external\cmake\bin 5 | set CMAKEFOLDER=_intermediate 6 | 7 | pushd %CMAKEFOLDER% 8 | 9 | %CMAKEPATH%\cmake --build . --config Release 10 | 11 | popd 12 | 13 | if NOT '%1' == 'NOPAUSE' pause -------------------------------------------------------------------------------- /platform/windows/create_glsl_inc.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set SED=%~dp0external\sed\bin\sed.exe 4 | 5 | REM create output dir 6 | if not exist %~dp2 md %~dp2 7 | 8 | REM remove comments 9 | %SED% -e "/^\/\*/,/\*\/$/d" -e "/^ *\/\//d" -e "/^$/d" %1 > %2.tmp0 10 | 11 | REM for each line add quotes \r\n and \ : line -> "{line}\r\n" \ 12 | %SED% "s/^\(.*\)$/\"\1\\r\\n\"\\/" %2.tmp0 > %2.tmp1 13 | 14 | REM add variable declaration and ; 15 | echo const char* %~n1_glsl = \> %2 16 | type %2.tmp1 >> %2 17 | echo ""; >> %2 18 | -------------------------------------------------------------------------------- /platform/windows/download_build_run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call "download_dependencies.bat" NOPAUSE 4 | call "generate_project.bat" NOPAUSE 5 | call "build.bat" NOPAUSE 6 | call "run.bat" NOPAUSE -------------------------------------------------------------------------------- /platform/windows/download_dependencies.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM https://cmake.org/files/v3.9/cmake-3.9.6-win64-x64.zip -> cmake 3 | REM https://sourceforge.net/projects/gnuwin32/files/sed/4.2.1/sed-4.2.1-bin.zip/download -> sed 4 | REM https://sourceforge.net/projects/gnuwin32/files/sed/4.2.1/sed-4.2.1-dep.zip/download -> sed 5 | REM https://files.transmissionzero.co.uk/software/development/GLUT/freeglut-MSVC-3.0.0-2.mp.zip -> freeglut 6 | 7 | set EXT=%~dp0external 8 | 9 | echo Installing dependencies into %EXT% 10 | if not exist %EXT% md %EXT% 11 | 12 | REM ------------------ freeglut 13 | 14 | set SRC=https://files.transmissionzero.co.uk/software/development/GLUT/freeglut-MSVC-3.0.0-2.mp.zip 15 | set ZIP=%EXT%\freeglut-MSVC.zip 16 | set DST=%EXT%\. 17 | 18 | if not exist %EXT%\freeglut ( 19 | echo Downloading freeglut from %SRC%... 20 | powershell -Command "Start-BitsTransfer '%SRC%' '%ZIP%'" 21 | powershell -Command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('%ZIP%', '%DST%'); }" 22 | del %ZIP% 23 | ) else echo freeglut detected. skipping. 24 | 25 | REM ------------------ sed 26 | 27 | set SRC=https://sourceforge.net/projects/gnuwin32/files/sed/4.2.1/sed-4.2.1-bin.zip/download 28 | set ZIP=%EXT%\sed-4.2.1-bin.zip 29 | set DST=%EXT%\sed 30 | 31 | if not exist %EXT%\sed\bin\sed.exe ( 32 | echo Downloading sed from %SRC%... 33 | powershell -Command "Start-BitsTransfer '%SRC%' '%ZIP%'" 34 | powershell -Command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('%ZIP%', '%DST%'); }" 35 | del %ZIP% 36 | ) else echo sed.exe detected. skipping. 37 | 38 | REM ------------------ sed-dep 39 | 40 | set SRC=https://sourceforge.net/projects/gnuwin32/files/sed/4.2.1/sed-4.2.1-dep.zip/download 41 | set ZIP=%EXT%\sed-4.2.1-dep.zip 42 | set DST=%EXT%\sed 43 | 44 | if not exist %EXT%\sed\bin\libiconv2.dll ( 45 | echo Downloading sed-dep from %SRC%... 46 | powershell -Command "Start-BitsTransfer '%SRC%' '%ZIP%'" 47 | powershell -Command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('%ZIP%', '%DST%'); }" 48 | del %ZIP% 49 | ) else echo sed-dep detected. skipping. 50 | 51 | REM ------------------ cmake 52 | 53 | set SRC=https://cmake.org/files/v3.9/cmake-3.9.6-win64-x64.zip 54 | set ZIP=%EXT%\cmake-3.9.6-win64-x64.zip 55 | set DST=%EXT%\. 56 | 57 | if not exist %EXT%\cmake ( 58 | echo Downloading cmake from %SRC%... 59 | powershell -Command "Start-BitsTransfer '%SRC%' '%ZIP%'" 60 | powershell -Command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('%ZIP%', '%DST%'); }" 61 | rename %EXT%\cmake-3.9.6-win64-x64 cmake 62 | del %ZIP% 63 | ) else echo cmake detected. skipping. 64 | 65 | :end 66 | if NOT '%1' == 'NOPAUSE' pause 67 | 68 | -------------------------------------------------------------------------------- /platform/windows/external/dummy.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebruneton/precomputed_atmospheric_scattering/d9954923ccd810be2d4443268a182bcb96544c1e/platform/windows/external/dummy.txt -------------------------------------------------------------------------------- /platform/windows/generate_project.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set CURDIR=%~dp0 4 | set CMAKEPATH=%CURDIR%external\cmake\bin 5 | set CMAKEFOLDER=_intermediate 6 | 7 | if exist %CMAKEFOLDER% rmdir %CMAKEFOLDER% /s /q 8 | mkdir %CMAKEFOLDER% 9 | 10 | pushd %CMAKEFOLDER% 11 | 12 | %CMAKEPATH%\cmake -G"Visual Studio 15 2017 Win64" .. 13 | 14 | popd 15 | 16 | if NOT '%1' == 'NOPAUSE' pause -------------------------------------------------------------------------------- /platform/windows/run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set CURDIR=%~dp0 3 | 4 | %CURDIR%\..\..\bin\Atmosphere.exe 5 | -------------------------------------------------------------------------------- /precomputed_atmopheric_scattering.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 264 | 265 | -------------------------------------------------------------------------------- /text/text_renderer.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Ruslan Kabatsayev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "text/text_renderer.h" 31 | 32 | #include 33 | 34 | namespace { 35 | 36 | #include "font.inc" 37 | 38 | const char kVertexShader[] = R"( 39 | #version 330 40 | uniform mat4 clip_from_model; 41 | uniform mat3x2 texture_coord_from_model; 42 | layout(location = 0) in vec4 vertex; 43 | out vec2 texture_coord; 44 | void main() { 45 | gl_Position = clip_from_model * vertex; 46 | texture_coord = texture_coord_from_model * vertex.xyw; 47 | })"; 48 | 49 | const char kFragmentShader[] = R"( 50 | #version 330 51 | uniform vec3 text_color; 52 | uniform sampler2D font_sampler; 53 | in vec2 texture_coord; 54 | layout(location = 0) out vec4 color; 55 | void main() { 56 | color = vec4(text_color, 1) * texture(font_sampler, texture_coord).rrrr; 57 | })"; 58 | 59 | } // anonymous namespace 60 | 61 | void TextRenderer::SetupTexture() { 62 | glGenTextures(1, &font_texture_); 63 | 64 | // Avoid interfering with caller's assumptions. 65 | glActiveTexture(GL_TEXTURE0); 66 | GLint old_texture; 67 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture); 68 | 69 | glBindTexture(GL_TEXTURE_2D, font_texture_); 70 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, font.atlas_width, font.atlas_height, 71 | 0, GL_RED, GL_UNSIGNED_BYTE, font.data.data()); 72 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 73 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 74 | 75 | glBindTexture(GL_TEXTURE_2D, old_texture); 76 | } 77 | 78 | void TextRenderer::SetupBuffers() { 79 | glGenVertexArrays(1, &char_vao_); 80 | glBindVertexArray(char_vao_); 81 | glGenBuffers(1, &char_vbo_); 82 | glBindBuffer(GL_ARRAY_BUFFER, char_vbo_); 83 | const GLfloat vertices[] = { 84 | 0, 0, 85 | 1, 0, 86 | 0, 1, 87 | 1, 1, 88 | }; 89 | glBufferData(GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW); 90 | constexpr GLuint kAttribIndex = 0; 91 | constexpr int kCoordsPerVertex = 2; 92 | glVertexAttribPointer(kAttribIndex, kCoordsPerVertex, GL_FLOAT, false, 0, 0); 93 | glEnableVertexAttribArray(kAttribIndex); 94 | glBindVertexArray(0); 95 | } 96 | 97 | void TextRenderer::SetupProgram() { 98 | const auto vertex_shader = glCreateShader(GL_VERTEX_SHADER); 99 | const char* const vertex_shader_source = kVertexShader; 100 | glShaderSource(vertex_shader, 1, &vertex_shader_source, nullptr); 101 | glCompileShader(vertex_shader); 102 | const auto fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); 103 | const char* const fragment_shader_source = kFragmentShader; 104 | glShaderSource(fragment_shader, 1, &fragment_shader_source, nullptr); 105 | glCompileShader(fragment_shader); 106 | program_ = glCreateProgram(); 107 | glAttachShader(program_, vertex_shader); 108 | glAttachShader(program_, fragment_shader); 109 | glLinkProgram(program_); 110 | glDetachShader(program_, fragment_shader); 111 | glDeleteShader(fragment_shader); 112 | glDetachShader(program_, vertex_shader); 113 | glDeleteShader(vertex_shader); 114 | } 115 | 116 | void TextRenderer::DrawChar(char c, int x, int y, 117 | int viewport_width, int viewport_height) { 118 | if (c < 0x20 || c > 0x7e) { 119 | c = '?'; 120 | } 121 | 122 | const GLfloat char_width = font.char_width; 123 | const GLfloat char_height = font.char_height; 124 | { 125 | const GLfloat scale_x = char_width / font.atlas_width; 126 | const GLfloat scale_y = -char_height / font.atlas_height; 127 | const int characters_per_line = font.atlas_width / font.char_width; 128 | const GLfloat translate_x = 129 | (c % characters_per_line) * char_width / font.atlas_width; 130 | const GLfloat translate_y = 131 | (c / characters_per_line - 1) * char_height / font.atlas_height; 132 | const GLfloat texture_coord_from_model[] = { 133 | scale_x, 0, translate_x, 134 | 0, scale_y, translate_y 135 | }; 136 | glUniformMatrix3x2fv( 137 | glGetUniformLocation(program_, "texture_coord_from_model"), 138 | 1, true, texture_coord_from_model); 139 | } 140 | { 141 | const GLfloat scale_x = 2 * char_width / viewport_width; 142 | const GLfloat scale_y = 2 * char_height / viewport_height; 143 | const GLfloat translate_x = (2.f * x) / viewport_width - 1; 144 | const GLfloat translate_y = (2.f * y) / viewport_height - 1; 145 | const GLfloat clip_from_model[] = { 146 | scale_x, 0, 0, translate_x, 147 | 0, scale_y, 0, translate_y, 148 | 0, 0, 1, 0, 149 | 0, 0, 0, 1 150 | }; 151 | glUniformMatrix4fv(glGetUniformLocation(program_, "clip_from_model"), 152 | 1, true, clip_from_model); 153 | } 154 | 155 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 156 | } 157 | 158 | TextRenderer::TextRenderer() { 159 | SetupTexture(); 160 | SetupBuffers(); 161 | SetupProgram(); 162 | 163 | SetColor(1, 1, 1); 164 | } 165 | 166 | TextRenderer::~TextRenderer() { 167 | glDeleteProgram(program_); 168 | glDeleteBuffers(1, &char_vbo_); 169 | glDeleteVertexArrays(1, &char_vao_); 170 | glDeleteTextures(1, &font_texture_); 171 | } 172 | 173 | void TextRenderer::SetColor(float r, float g, float b) { 174 | color_[0] = r; 175 | color_[1] = g; 176 | color_[2] = b; 177 | GLint old_program; 178 | glGetIntegerv(GL_CURRENT_PROGRAM, &old_program); 179 | glUseProgram(program_); 180 | glUniform3fv(glGetUniformLocation(program_, "text_color"), 1, color_); 181 | glUseProgram(old_program); 182 | } 183 | 184 | void TextRenderer::DrawText(const std::string& text, int left, int top) { 185 | GLint viewport[4]; 186 | glGetIntegerv(GL_VIEWPORT, viewport); 187 | 188 | // Avoid interfering with caller's assumptions. 189 | GLint old_program; 190 | glGetIntegerv(GL_CURRENT_PROGRAM, &old_program); 191 | glActiveTexture(GL_TEXTURE0); 192 | GLint old_texture; 193 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture); 194 | 195 | glBindVertexArray(char_vao_); 196 | glUseProgram(program_); 197 | 198 | glActiveTexture(GL_TEXTURE0); 199 | glBindTexture(GL_TEXTURE_2D, font_texture_); 200 | glUniform1i(glGetUniformLocation(program_, "font_sampler"), 0); 201 | 202 | glEnable(GL_BLEND); 203 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 204 | 205 | int x = left; 206 | int y = viewport[3] - top - font.char_height; 207 | for (char c : text) { 208 | switch (c) { 209 | case ' ': 210 | break; 211 | case '\n': 212 | x = left; 213 | y -= font.char_height + 1; 214 | continue; 215 | default: 216 | DrawChar(c, x, y, viewport[2], viewport[3]); 217 | break; 218 | } 219 | x += font.char_width; 220 | } 221 | 222 | glDisable(GL_BLEND); 223 | glBindVertexArray(0); 224 | 225 | glBindTexture(GL_TEXTURE_2D, old_texture); 226 | glUseProgram(old_program); 227 | } 228 | -------------------------------------------------------------------------------- /text/text_renderer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Ruslan Kabatsayev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef TEXT_TEXT_RENDERER_H_ 31 | #define TEXT_TEXT_RENDERER_H_ 32 | 33 | #include 34 | 35 | #include 36 | 37 | class TextRenderer { 38 | public: 39 | TextRenderer(); 40 | TextRenderer(TextRenderer const&) = delete; 41 | TextRenderer(TextRenderer&&) = delete; 42 | ~TextRenderer(); 43 | 44 | void SetColor(float r, float g, float b); 45 | void DrawText(const std::string& text, int left, int top); 46 | 47 | private: 48 | void SetupTexture(); 49 | void SetupBuffers(); 50 | void SetupProgram(); 51 | void DrawChar(char c, int x, int y, int viewport_width, int viewport_height); 52 | 53 | GLuint font_texture_; 54 | GLuint char_vao_; 55 | GLuint char_vbo_; 56 | GLuint program_; 57 | GLfloat color_[3]; 58 | }; 59 | 60 | #endif // TEXT_TEXT_RENDERER_H_ 61 | -------------------------------------------------------------------------------- /tools/docgen_main.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Eric Bruneton 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holders nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | * THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | namespace { 36 | 37 | std::string GenerateHtml(const std::string& source, 38 | const std::string& html_template) { 39 | 40 | std::string src = source; 41 | // Put the copyright notice in a comment. 42 | src = std::regex_replace(src, std::regex("\\/\\*\\*"), "", 45 | std::regex_constants::format_first_only); 46 | 47 | // Put the code parts in

 blocks.
 48 |   const std::string kPreBegin = "
";
 49 |   const std::string kPreEnd = "
"; 50 | src = std::regex_replace(src, std::regex("\\n\\/\\*"), kPreEnd); 51 | src = std::regex_replace(src, std::regex("\\*\\/\\n"), kPreBegin); 52 | if (src.find(kPreBegin) == std::string::npos) { 53 | src = std::regex_replace(src, std::regex("-->\\n"), "-->\n" + kPreBegin, 54 | std::regex_constants::format_first_only); 55 | } 56 | src += kPreEnd; 57 | 58 | // Escape the < and > characters in
 blocks.
 59 |   std::stringstream body;
 60 |   const std::string kBodyPlaceHolder = "BODY";
 61 |   size_t start_pos = html_template.find(kBodyPlaceHolder);
 62 |   body << html_template.substr(0, start_pos);
 63 |   body << "\n";
 64 |   bool in_pre_block = false;
 65 |   for (unsigned int i = 0; i < src.length(); ++i) {
 66 |     if (src[i] == '>') {
 67 |       if (in_pre_block) {
 68 |         body << ">";
 69 |       } else {
 70 |         if (i + 1 >= kPreBegin.length() &&
 71 |             src.substr(i + 1 - kPreBegin.length(), kPreBegin.length()) ==
 72 |                 kPreBegin) {
 73 |           in_pre_block = true;
 74 |         }
 75 |         body << ">";
 76 |       }
 77 |     } else if (src[i] == '<') {
 78 |       if (in_pre_block &&
 79 |           i + kPreEnd.length() <= src.length() &&
 80 |           src.substr(i, kPreEnd.length()) == kPreEnd) {
 81 |         in_pre_block = false;
 82 |       }
 83 |       body << (in_pre_block ? "<" : "<");
 84 |     } else {
 85 |       body << src[i];
 86 |     }
 87 |   }
 88 |   body << "\n";
 89 |   body << html_template.substr(start_pos + kBodyPlaceHolder.length());
 90 |   return body.str();
 91 | }
 92 | 
 93 | }  // anonymous namespace
 94 | 
 95 | int main(int argc, char** argv) {
 96 |   if (argc != 4) {
 97 |     std::cout << "Usage: " << argv[0]
 98 |               << "   " << std::endl;
 99 |     return -1;
100 |   }
101 | 
102 |   std::ifstream source_stream(argv[1]);
103 |   std::string source(
104 |       (std::istreambuf_iterator(source_stream)),
105 |       std::istreambuf_iterator());
106 | 
107 |   std::ifstream html_template_stream(argv[2]);
108 |   std::string html_template(
109 |       (std::istreambuf_iterator(html_template_stream)),
110 |       std::istreambuf_iterator());
111 | 
112 |   std::ofstream output_file(argv[3]);
113 |   output_file << GenerateHtml(source, html_template);
114 |   output_file.close();
115 | 
116 |   return 0;
117 | }
118 | 


--------------------------------------------------------------------------------
/tools/docgen_template.html:
--------------------------------------------------------------------------------
  1 | 
  2 |   
  3 |     
  4 |       
 25 |       
 28 |       
 31 |       
 34 |       
105 |     
106 |   
107 | BODY
108 | 
109 | 


--------------------------------------------------------------------------------