├── .appveyor.yml ├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── docs ├── _static │ └── theme_overrides.css ├── _templates │ └── page.html ├── conf.py ├── docs_api │ ├── conf.py │ └── list_api.rst ├── examples │ ├── .gitignore │ ├── 01_render_scene │ │ └── render_scene.py │ ├── 02_depth_integrator │ │ └── depth_integrator.py │ ├── 03_direct_integrator │ │ └── direct_integrator.py │ ├── 04_diffuse_bsdf │ │ └── diffuse_bsdf.py │ ├── 05_bsdf_eval │ │ └── bsdf_eval.py │ └── 10_inverse_rendering │ │ ├── forward_diff.py │ │ ├── invert_bunny.py │ │ ├── invert_cbox.py │ │ └── invert_cbox_torch.py ├── exts │ └── sphinxtr │ │ ├── LICENSE │ │ ├── custombackports.py │ │ ├── figtable.py │ │ ├── fix_equation_ref.py │ │ ├── html_mods.py │ │ ├── latex_mods.py │ │ ├── numfig.py │ │ ├── numsec.py │ │ ├── pluginparameters.py │ │ ├── singlehtml_toc.py │ │ ├── singletext.py │ │ └── subfig.py ├── generate_plugin_doc.py ├── generated │ ├── core_api.rst │ ├── extracted_rst_api.rst │ ├── python_api.rst │ └── render_api.rst ├── images │ ├── bsdf_eval.jpg │ ├── chi2_example.png │ ├── features.svg │ ├── integrator_path_figure.pdf │ ├── integrator_path_figure.png │ ├── logo_plain.pdf │ ├── logo_plain.png │ ├── logo_plain.svg │ ├── mitsuba-logo-white-bg.png │ ├── polarization_light_transport.svg │ ├── polarization_mueller_matrix.svg │ ├── polarization_stokes_rotation.svg │ ├── polarization_stokes_vector.svg │ ├── polarization_wave.svg │ ├── polarization_wave_variations.svg │ └── variant.svg ├── index.rst ├── issue_template.md ├── pull_request_template.md ├── references.bib ├── release_notes.rst ├── requirements.txt ├── src │ ├── advanced_topics │ │ └── custom_plugins.rst │ ├── api_reference │ │ └── intro.rst │ ├── developer_guide │ │ ├── building_documentation.rst │ │ ├── debugging.rst │ │ ├── intro.rst │ │ ├── polarization.rst │ │ ├── testing.rst │ │ ├── variants_cpp.rst │ │ └── writing_plugin.rst │ ├── getting_started │ │ ├── cloning.rst │ │ ├── compiling.rst │ │ ├── differences.rst │ │ ├── faq.rst │ │ ├── file_format.rst │ │ ├── intro.rst │ │ └── variants.rst │ ├── inverse_rendering │ │ ├── advanced.rst │ │ ├── diff_render.rst │ │ ├── intro.rst │ │ └── pytorch.rst │ ├── plugin_reference │ │ ├── intro.rst │ │ ├── section_bsdf.rst │ │ ├── section_emitter.rst │ │ ├── section_film.rst │ │ ├── section_integrator.rst │ │ ├── section_phase.rst │ │ ├── section_rfilter.rst │ │ ├── section_sampler.rst │ │ ├── section_sensor.rst │ │ ├── section_shape.rst │ │ ├── section_spectrum.rst │ │ └── section_texture.rst │ └── python_interface │ │ ├── bsdf_eval.rst │ │ ├── intro.rst │ │ ├── parsing_xml.rst │ │ └── rendering_scene.rst └── zz_bibliography.rst ├── ext ├── CMakeLists.txt └── rgb2spec │ ├── CMakeLists.txt │ ├── details │ ├── cie1931.h │ └── lu.h │ ├── rgb2spec.c │ ├── rgb2spec.h │ └── rgb2spec_opt.cpp ├── include └── mitsuba │ ├── core │ ├── appender.h │ ├── argparser.h │ ├── atomic.h │ ├── bbox.h │ ├── bitmap.h │ ├── bsphere.h │ ├── class.h │ ├── distr_1d.h │ ├── distr_2d.h │ ├── dstream.h │ ├── filesystem.h │ ├── formatter.h │ ├── frame.h │ ├── fresolver.h │ ├── fstream.h │ ├── fwd.h │ ├── hash.h │ ├── jit.h │ ├── logger.h │ ├── math.h │ ├── mmap.h │ ├── mstream.h │ ├── object.h │ ├── platform.h │ ├── plugin.h │ ├── profiler.h │ ├── progress.h │ ├── properties.h │ ├── qmc.h │ ├── quad.h │ ├── random.h │ ├── ray.h │ ├── rfilter.h │ ├── simd.h │ ├── spectrum.h │ ├── spline.h │ ├── stream.h │ ├── string.h │ ├── struct.h │ ├── tensor.h │ ├── thread.h │ ├── timer.h │ ├── tls.h │ ├── traits.h │ ├── transform.h │ ├── util.h │ ├── variant.h │ ├── vector.h │ ├── warp.h │ ├── xml.h │ ├── zmq11.h │ └── zstream.h │ ├── mitsuba.h │ ├── python │ ├── docstr.h │ └── python.h │ ├── render │ ├── bsdf.h │ ├── emitter.h │ ├── endpoint.h │ ├── film.h │ ├── fresnel.h │ ├── fwd.h │ ├── imageblock.h │ ├── integrator.h │ ├── interaction.h │ ├── ior.h │ ├── kdtree.h │ ├── medium.h │ ├── mesh.h │ ├── microfacet.h │ ├── mueller.h │ ├── optix │ │ ├── bbox.cuh │ │ ├── common.h │ │ ├── math.cuh │ │ ├── matrix.cuh │ │ ├── ray.cuh │ │ ├── shapes.h │ │ └── vector.cuh │ ├── optix_api.h │ ├── phase.h │ ├── records.h │ ├── sampler.h │ ├── scene.h │ ├── sensor.h │ ├── shape.h │ ├── shapegroup.h │ ├── spiral.h │ ├── srgb.h │ ├── texture.h │ └── volume_texture.h │ └── ui │ ├── fwd.h │ ├── texture.h │ └── viewer.h ├── resources ├── FindSphinx.cmake ├── check-style.sh ├── configure.py ├── mitsuba-logo.png ├── mitsuba.conf.template └── ptx │ ├── Makefile │ └── optix_rt.ptx ├── setpath.bat ├── setpath.ps1 ├── setpath.sh ├── setup.cfg └── src ├── CMakeLists.txt ├── __init__.py ├── bsdfs ├── CMakeLists.txt ├── __init__.py ├── blendbsdf.cpp ├── bumpmap.cpp ├── circular.cpp ├── conductor.cpp ├── dielectric.cpp ├── diffuse.cpp ├── mask.cpp ├── measured.cpp ├── measured_polarized.cpp ├── normalmap.cpp ├── null.cpp ├── plastic.cpp ├── polarizer.cpp ├── pplastic.cpp ├── retarder.cpp ├── roughconductor.cpp ├── roughdielectric.cpp ├── roughplastic.cpp ├── tests │ ├── __init__.py │ ├── test_blendbsdf.py │ ├── test_conductor.py │ ├── test_dielectric.py │ ├── test_diffuse.py │ ├── test_measured_polarized.py │ ├── test_polarizer.py │ ├── test_pplastic.py │ ├── test_retarder.py │ ├── test_rough_conductor.py │ ├── test_rough_dielectric.py │ ├── test_rough_plastic.py │ └── test_twosided.py ├── thindielectric.cpp └── twosided.cpp ├── cmake ├── FindSphinx.cmake ├── docstrings.cmake ├── libpython.cmake └── tests.cmake ├── conftest.py ├── emitters ├── CMakeLists.txt ├── __init__.py ├── area.cpp ├── constant.cpp ├── directional.cpp ├── envmap.cpp ├── point.cpp ├── projector.cpp ├── spot.cpp └── tests │ ├── __init__.py │ ├── data │ ├── __init__.py │ └── triangle.ply │ ├── test_area.py │ ├── test_constant.py │ ├── test_directional.py │ ├── test_point.py │ └── test_spot.py ├── films ├── CMakeLists.txt ├── hdrfilm.cpp └── tests │ └── test_hdrfilm.py ├── integrators ├── CMakeLists.txt ├── __init__.py ├── aov.cpp ├── depth.cpp ├── direct.cpp ├── moment.cpp ├── path.cpp ├── stokes.cpp ├── tests │ └── __init__.py ├── volpath.cpp └── volpathmis.cpp ├── libcore ├── CMakeLists.txt ├── __init__.py ├── appender.cpp ├── argparser.cpp ├── bitmap.cpp ├── class.cpp ├── dither-matrix256.cpp ├── dstream.cpp ├── filesystem.cpp ├── formatter.cpp ├── fresolver.cpp ├── fstream.cpp ├── jit.cpp ├── logger.cpp ├── mmap.cpp ├── mstream.cpp ├── object.cpp ├── plugin.cpp ├── profiler.cpp ├── progress.cpp ├── properties.cpp ├── python │ ├── CMakeLists.txt │ ├── appender.cpp │ ├── argparser.cpp │ ├── atomic.cpp │ ├── bbox_v.cpp │ ├── bitmap.cpp │ ├── bsphere_v.cpp │ ├── cast.cpp │ ├── distr_1d_v.cpp │ ├── distr_2d_v.cpp │ ├── filesystem.cpp │ ├── formatter.cpp │ ├── frame_v.cpp │ ├── fresolver.cpp │ ├── logger.cpp │ ├── main.cpp │ ├── main_v.cpp │ ├── math_v.cpp │ ├── mmap.cpp │ ├── object.cpp │ ├── object_v.cpp │ ├── progress.cpp │ ├── properties_v.cpp │ ├── qmc_v.cpp │ ├── quad.cpp │ ├── random_v.cpp │ ├── ray_v.cpp │ ├── rfilter.cpp │ ├── rfilter_v.cpp │ ├── spectrum_v.cpp │ ├── spline_v.cpp │ ├── stream.cpp │ ├── struct.cpp │ ├── thread.cpp │ ├── transform_v.cpp │ ├── util.cpp │ ├── vector_v.cpp │ ├── warp_v.cpp │ └── xml_v.cpp ├── qmc.cpp ├── quad.cpp ├── rfilter.cpp ├── spectrum.cpp ├── stream.cpp ├── string.cpp ├── struct.cpp ├── tensor.cpp ├── tests │ ├── __init__.py │ ├── test_argparser.py │ ├── test_atomic.py │ ├── test_bbox.py │ ├── test_bitmap.py │ ├── test_bsphere.py │ ├── test_dict.py │ ├── test_distr_1d.py │ ├── test_distr_2d.py │ ├── test_filesystem.py │ ├── test_frame.py │ ├── test_logger.py │ ├── test_math.py │ ├── test_mmap.py │ ├── test_properties.py │ ├── test_python.py │ ├── test_qmc.py │ ├── test_quad.py │ ├── test_random.py │ ├── test_spline.py │ ├── test_stream.py │ ├── test_struct.py │ ├── test_transform.py │ ├── test_util.py │ ├── test_vector.py │ ├── test_warp.py │ ├── test_write_xml.py │ └── test_xml.py ├── thread.cpp ├── tls.cpp ├── transform.cpp ├── util.cpp ├── xml.cpp └── zstream.cpp ├── librender ├── CMakeLists.txt ├── __init__.py ├── bsdf.cpp ├── emitter.cpp ├── endpoint.cpp ├── film.cpp ├── imageblock.cpp ├── integrator.cpp ├── kdtree.cpp ├── medium.cpp ├── mesh.cpp ├── microfacet.cpp ├── optix │ └── optix_rt.cu ├── optix_api.cpp ├── phase.cpp ├── python │ ├── CMakeLists.txt │ ├── bsdf.cpp │ ├── bsdf_v.cpp │ ├── emitter.cpp │ ├── emitter_v.cpp │ ├── endpoint_v.cpp │ ├── film_v.cpp │ ├── fresnel_v.cpp │ ├── imageblock_v.cpp │ ├── integrator_v.cpp │ ├── interaction.cpp │ ├── interaction_v.cpp │ ├── main.cpp │ ├── main_v.cpp │ ├── medium_v.cpp │ ├── microfacet.cpp │ ├── microfacet_v.cpp │ ├── mueller_v.cpp │ ├── old │ │ └── main.cpp │ ├── phase.cpp │ ├── phase_v.cpp │ ├── records_v.cpp │ ├── sampler_v.cpp │ ├── scene_v.cpp │ ├── sensor_v.cpp │ ├── shape_v.cpp │ ├── spiral.cpp │ ├── srgb_v.cpp │ └── texture_v.cpp ├── sampler.cpp ├── scene.cpp ├── scene_embree.inl ├── scene_native.inl ├── scene_optix.inl ├── sensor.cpp ├── shape.cpp ├── shapegroup.cpp ├── spiral.cpp ├── srgb.cpp ├── tests │ ├── __init__.py │ ├── data │ │ ├── triangle.ply │ │ └── triangle_face_colors.ply │ ├── mesh_generation.py │ ├── test_bsdf.py │ ├── test_fresnel.py │ ├── test_imageblock.py │ ├── test_integrator.py │ ├── test_interaction.py │ ├── test_kdtrees.py │ ├── test_mesh.py │ ├── test_microfacet.py │ ├── test_mueller.py │ ├── test_records.py │ ├── test_renders.py │ ├── test_scene.py │ ├── test_spectra.py │ └── test_spiral.py └── texture.cpp ├── libui ├── CMakeLists.txt ├── __init__.py ├── python │ ├── CMakeLists.txt │ └── main.cpp ├── tests │ └── __init__.py ├── texture.cpp └── viewer.cpp ├── media ├── CMakeLists.txt ├── heterogeneous.cpp ├── homogeneous.cpp └── tests │ └── __init__.py ├── mitsuba ├── CMakeLists.txt └── mitsuba.cpp ├── mtsgui ├── CMakeLists.txt └── mtsgui.cpp ├── phase ├── CMakeLists.txt ├── hg.cpp ├── isotropic.cpp └── tests │ ├── test_hg.py │ ├── test_isotropic.py │ └── test_trampoline.py ├── python ├── CMakeLists.txt ├── __init__.py └── python │ ├── __init__.py │ ├── autodiff.py │ ├── chi2.py │ ├── math.py │ ├── test │ ├── __init__.py │ ├── scenes.py │ └── util.py │ ├── util.py │ └── xml.py ├── rfilters ├── CMakeLists.txt ├── __init__.py ├── box.cpp ├── catmullrom.cpp ├── gaussian.cpp ├── lanczos.cpp ├── mitchell.cpp ├── tent.cpp └── tests │ ├── __init__.py │ └── test_rfilter.py ├── samplers ├── CMakeLists.txt ├── __init__.py ├── independent.cpp ├── ldsampler.cpp ├── multijitter.cpp ├── orthogonal.cpp ├── stratified.cpp └── tests │ ├── __init__.py │ ├── test_independent.py │ ├── test_ldsampler.py │ ├── test_multijitter.py │ ├── test_orthogonal.py │ ├── test_stratified.py │ └── utils.py ├── sensors ├── CMakeLists.txt ├── __init__.py ├── irradiancemeter.cpp ├── perspective.cpp ├── radiancemeter.cpp ├── tests │ ├── __init__.py │ ├── test_irradiancemeter.py │ ├── test_perspective.py │ ├── test_radiancemeter.py │ └── test_thinlens.py └── thinlens.cpp ├── shapes ├── CMakeLists.txt ├── __init__.py ├── blender.cpp ├── cylinder.cpp ├── disk.cpp ├── instance.cpp ├── obj.cpp ├── optix │ ├── cylinder.cuh │ ├── disk.cuh │ ├── mesh.cuh │ ├── rectangle.cuh │ └── sphere.cuh ├── ply.cpp ├── rectangle.cpp ├── serialized.cpp ├── shapegroup.cpp ├── sphere.cpp └── tests │ ├── __init__.py │ ├── test_cylinder.py │ ├── test_disk.py │ ├── test_instance.py │ ├── test_rectangle.py │ ├── test_shapegroup.py │ └── test_sphere.py ├── spectra ├── CMakeLists.txt ├── blackbody.cpp ├── d65.cpp ├── irregular.cpp ├── regular.cpp ├── srgb.cpp ├── srgb_d65.cpp ├── tests │ ├── test_d65.py │ ├── test_irregular.py │ └── test_regular.py └── uniform.cpp └── textures ├── CMakeLists.txt ├── bitmap.cpp ├── checkerboard.cpp ├── constant3d.cpp ├── grid3d.cpp ├── mesh_attribute.cpp ├── tests ├── test_bitmap.py ├── test_mesh_attribute.py └── test_texture3d.py └── volume_data.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignConsecutiveAssignments: true 5 | AlignEscapedNewlinesLeft: false 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortIfStatementsOnASingleLine: false 9 | AllowShortLoopsOnASingleLine: false 10 | AlwaysBreakBeforeMultilineStrings: false 11 | AlwaysBreakTemplateDeclarations: false 12 | BinPackParameters: true 13 | BreakBeforeBinaryOperators: false 14 | BreakBeforeBraces: Attach 15 | BreakBeforeTernaryOperators: true 16 | BreakConstructorInitializersBeforeComma: false 17 | ColumnLimit: 80 18 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 19 | ConstructorInitializerIndentWidth: 4 20 | ContinuationIndentWidth: 4 21 | Cpp11BracedListStyle: false 22 | DerivePointerBinding: false 23 | ExperimentalAutoDetectBinPacking: false 24 | IndentCaseLabels: true 25 | IndentFunctionDeclarationAfterType: false 26 | IndentWidth: 4 27 | MaxEmptyLinesToKeep: 1 28 | NamespaceIndentation: None 29 | ObjCSpaceBeforeProtocolList: true 30 | PenaltyBreakBeforeFirstCallParameter: 19 31 | PenaltyBreakComment: 60 32 | PenaltyBreakFirstLessLess: 120 33 | PenaltyBreakString: 1000 34 | PenaltyExcessCharacter: 1000000 35 | PenaltyReturnTypeOnItsOwnLine: 60 36 | PointerBindsToType: false 37 | SpaceAfterControlStatementKeyword: true 38 | SpaceAfterCStyleCast: true 39 | SpaceBeforeAssignmentOperators: true 40 | SpaceInEmptyParentheses: false 41 | SpacesBeforeTrailingComments: 1 42 | SpacesInAngles: false 43 | SpacesInCStyleCastParentheses: false 44 | SpacesInParentheses: false 45 | Standard: Cpp11 46 | TabWidth: 4 47 | UseTab: Never 48 | ... 49 | 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | /CMakeCache.txt 3 | /CPackConfig.cmake 4 | /CPackSourceConfig.cmake 5 | CMakeFiles 6 | cmake_install.cmake 7 | CTestTestfile.cmake 8 | CPackConfig.cmake 9 | CPackSourceConfig.cmake 10 | Makefile 11 | 12 | # Ninja 13 | /.ninja_deps 14 | /.ninja_log 15 | *.ninja 16 | 17 | # Miscellaneous; 18 | /ext_build 19 | /build* 20 | /dist 21 | *.dir 22 | *~ 23 | \.DS_Store 24 | Testing 25 | /.cache 26 | /.ipynb_checkpoints 27 | /.pytest_cache 28 | librender_ptx.h 29 | librender_ptx.cpp 30 | 31 | # SublimeText 32 | *.sublime-project 33 | *.sublime-workspace 34 | 35 | # Visual Studio / VS Code 36 | **/.vscode/* 37 | *.vcxproj 38 | mitsuba.sdf 39 | mitsuba.sln 40 | mitsuba.opensdf 41 | mitsuba.VC.VC.opendb 42 | mitsuba.VC.db 43 | Debug 44 | Release 45 | *.user 46 | *.filters 47 | /.vs 48 | /x64 49 | 50 | # Python 51 | __pycache__ 52 | *.pyc 53 | /.pytest_cache 54 | 55 | # Binaries 56 | *.dylib 57 | *.so 58 | *.dll 59 | /src/mitsuba/mitsuba 60 | /src/mtsgui/mtsgui 61 | /src/libui/libui_resources.* 62 | /html 63 | 64 | # Docs 65 | docs/generated/plugins.rst 66 | docs/_build/ 67 | 68 | # Config 69 | include/mitsuba/core/config.h 70 | src/python/config.py 71 | mitsuba.conf 72 | 73 | # Ignore compilation commands (Generated by CMake if 74 | # -DCMAKE_EXPORT_COMPILE_COMMANDS=1 is specified) 75 | compile_commands.json 76 | /.clangd 77 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/tbb"] 2 | path = ext/tbb 3 | url = https://github.com/wjakob/tbb 4 | [submodule "ext/zlib"] 5 | path = ext/zlib 6 | url = https://github.com/mitsuba-renderer/zlib 7 | [submodule "ext/libpng"] 8 | path = ext/libpng 9 | url = https://github.com/mitsuba-renderer/libpng.git 10 | [submodule "ext/libjpeg"] 11 | path = ext/libjpeg 12 | url = https://github.com/mitsuba-renderer/libjpeg 13 | [submodule "ext/openexr"] 14 | path = ext/openexr 15 | url = https://github.com/mitsuba-renderer/openexr 16 | [submodule "ext/tinyformat"] 17 | path = ext/tinyformat 18 | url = https://github.com/mitsuba-renderer/tinyformat 19 | [submodule "ext/pugixml"] 20 | path = ext/pugixml 21 | url = https://github.com/mitsuba-renderer/pugixml 22 | [submodule "ext/asmjit"] 23 | path = ext/asmjit 24 | url = https://github.com/mitsuba-renderer/asmjit 25 | [submodule "ext/pybind11"] 26 | path = ext/pybind11 27 | url = https://github.com/pybind/pybind11 28 | [submodule "ext/enoki"] 29 | path = ext/enoki 30 | url = https://github.com/mitsuba-renderer/enoki 31 | [submodule "ext/nanogui"] 32 | path = ext/nanogui 33 | url = https://github.com/mitsuba-renderer/nanogui 34 | [submodule "resources/data"] 35 | path = resources/data 36 | url = https://github.com/mitsuba-renderer/mitsuba-data 37 | [submodule "ext/embree"] 38 | path = ext/embree 39 | url = https://github.com/wjakob/embree 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Wenzel Jakob , All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | You are under no obligation whatsoever to provide any bug fixes, patches, or 29 | upgrades to the features, functionality or performance of the source code 30 | ("Enhancements") to anyone; however, if you choose to make your Enhancements 31 | available either publicly, or directly to the author of this software, without 32 | imposing a separate written license agreement for such Enhancements, then you 33 | hereby grant the following license: a non-exclusive, royalty-free perpetual 34 | license to install, use, modify, prepare derivative works, incorporate into 35 | other computer software, distribute, and sublicense such enhancements or 36 | derivative works thereof, in binary and source code form. 37 | -------------------------------------------------------------------------------- /docs/_templates/page.html: -------------------------------------------------------------------------------- 1 | 2 | {% extends "!page.html" %} 3 | 4 | {% block footer %} 5 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /docs/examples/.gitignore: -------------------------------------------------------------------------------- 1 | 01_render_scene/*.jpg 2 | 01_render_scene/*.exr 3 | 02_depth_integrator/*.jpg 4 | 02_depth_integrator/*.exr -------------------------------------------------------------------------------- /docs/examples/01_render_scene/render_scene.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import mitsuba 4 | 5 | # Set the desired mitsuba variant 6 | mitsuba.set_variant('scalar_rgb') 7 | 8 | from mitsuba.core import Bitmap, Struct, Thread 9 | from mitsuba.core.xml import load_file 10 | 11 | # Absolute or relative path to the XML file 12 | filename = 'path/to/my/scene.xml' 13 | 14 | # Add the scene directory to the FileResolver's search path 15 | Thread.thread().file_resolver().append(os.path.dirname(filename)) 16 | 17 | # Load the actual scene 18 | scene = load_file(filename) 19 | 20 | # Call the scene's integrator to render the loaded scene 21 | scene.integrator().render(scene, scene.sensors()[0]) 22 | 23 | # After rendering, the rendered data is stored in the film 24 | film = scene.sensors()[0].film() 25 | 26 | # Write out rendering as high dynamic range OpenEXR file 27 | film.set_destination_file('/path/to/output.exr') 28 | film.develop() 29 | 30 | # Write out a tonemapped JPG of the same rendering 31 | bmp = film.bitmap(raw=True) 32 | bmp.convert(Bitmap.PixelFormat.RGB, Struct.Type.UInt8, srgb_gamma=True).write('/path/to/output.jpg') 33 | 34 | # Get linear pixel values as a numpy array for further processing 35 | bmp_linear_rgb = bmp.convert(Bitmap.PixelFormat.RGB, Struct.Type.Float32, srgb_gamma=False) 36 | image_np = np.array(bmp_linear_rgb) 37 | print(image_np.shape) 38 | -------------------------------------------------------------------------------- /docs/examples/05_bsdf_eval/bsdf_eval.py: -------------------------------------------------------------------------------- 1 | import enoki as ek 2 | import mitsuba 3 | 4 | # Set the desired mitsuba variant 5 | mitsuba.set_variant('packet_rgb') 6 | 7 | from mitsuba.core import Float, Vector3f 8 | from mitsuba.core.xml import load_string 9 | from mitsuba.render import SurfaceInteraction3f, BSDFContext 10 | 11 | 12 | def sph_dir(theta, phi): 13 | """ Map spherical to Euclidean coordinates """ 14 | st, ct = ek.sincos(theta) 15 | sp, cp = ek.sincos(phi) 16 | return Vector3f(cp * st, sp * st, ct) 17 | 18 | 19 | # Load desired BSDF plugin 20 | bsdf = load_string(""" 21 | 22 | 23 | """) 24 | 25 | # Create a (dummy) surface interaction to use for the evaluation 26 | si = SurfaceInteraction3f() 27 | 28 | # Specify an incident direction with 45 degrees elevation 29 | si.wi = sph_dir(ek.pi * 45 / 180, 0.0) 30 | 31 | # Create grid in spherical coordinates and map it onto the sphere 32 | res = 300 33 | theta_o, phi_o = ek.meshgrid( 34 | ek.linspace(Float, 0, ek.pi, res), 35 | ek.linspace(Float, 0, 2 * ek.pi, 2 * res) 36 | ) 37 | wo = sph_dir(theta_o, phi_o) 38 | 39 | # Evaluate the whole array (18000 directions) at once 40 | values = bsdf.eval(BSDFContext(), si, wo) 41 | 42 | import numpy as np 43 | import matplotlib.pyplot as plt 44 | from mpl_toolkits.axes_grid1 import make_axes_locatable 45 | 46 | # Extract red channel of BRDF values and reshape into 2D grid 47 | values_r = np.array(values)[:, 0] 48 | values_r = values_r.reshape(2 * res, res).T 49 | 50 | # Plot values for spherical coordinates 51 | fig, ax = plt.subplots(figsize=(12, 7)) 52 | 53 | im = ax.imshow(values_r, extent=[0, 2 * np.pi, np.pi, 0], 54 | cmap='jet', interpolation='bicubic') 55 | 56 | ax.set_xlabel(r'$\phi_o$', size=14) 57 | ax.set_xticks([0, np.pi, 2 * np.pi]) 58 | ax.set_xticklabels(['0', '$\\pi$', '$2\\pi$']) 59 | ax.set_ylabel(r'$\theta_o$', size=14) 60 | ax.set_yticks([0, np.pi / 2, np.pi]) 61 | ax.set_yticklabels(['0', '$\\pi/2$', '$\\pi$']) 62 | 63 | divider = make_axes_locatable(ax) 64 | cax = divider.append_axes("right", size="3%", pad=0.05) 65 | plt.colorbar(im, cax=cax) 66 | 67 | fig.savefig("bsdf_eval.jpg", dpi=150, bbox_inches='tight', pad_inches=0) 68 | plt.show() 69 | -------------------------------------------------------------------------------- /docs/examples/10_inverse_rendering/forward_diff.py: -------------------------------------------------------------------------------- 1 | # Simple forward differentiation example: show how a perturbation 2 | # of a single scene parameter changes the rendered image. 3 | 4 | import enoki as ek 5 | import mitsuba 6 | mitsuba.set_variant('gpu_autodiff_rgb') 7 | 8 | from mitsuba.core import Thread, Float 9 | from mitsuba.core.xml import load_file 10 | from mitsuba.python.util import traverse 11 | from mitsuba.python.autodiff import render, write_bitmap 12 | 13 | # Load the Cornell Box 14 | Thread.thread().file_resolver().append('cbox') 15 | scene = load_file('cbox/cbox.xml') 16 | 17 | # Find differentiable scene parameters 18 | params = traverse(scene) 19 | 20 | # Keep track of derivatives with respect to one parameter 21 | param_0 = params['red.reflectance.value'] 22 | ek.set_requires_gradient(param_0) 23 | 24 | # Differentiable simulation 25 | image = render(scene, spp=32) 26 | 27 | # Assign the gradient [1, 1, 1] to the 'red.reflectance.value' input 28 | ek.set_gradient(param_0, [1, 1, 1], backward=False) 29 | 30 | # Forward-propagate previously assigned gradients 31 | Float.forward() 32 | 33 | # The gradients have been propagated to the output image 34 | image_grad = ek.gradient(image) 35 | 36 | # .. write them to a PNG file 37 | crop_size = scene.sensors()[0].film().crop_size() 38 | fname = 'out.png' 39 | write_bitmap(fname, image_grad, crop_size) 40 | print('Wrote forward differentiation image to: {}'.format(fname)) 41 | -------------------------------------------------------------------------------- /docs/examples/10_inverse_rendering/invert_cbox.py: -------------------------------------------------------------------------------- 1 | # Simple inverse rendering example: render a cornell box reference image, 2 | # then replace one of the scene parameters and try to recover it using 3 | # differentiable rendering and gradient-based optimization. 4 | 5 | import enoki as ek 6 | import mitsuba 7 | mitsuba.set_variant('gpu_autodiff_rgb') 8 | 9 | from mitsuba.core import Thread, Color3f 10 | from mitsuba.core.xml import load_file 11 | from mitsuba.python.util import traverse 12 | from mitsuba.python.autodiff import render, write_bitmap, Adam 13 | import time 14 | 15 | # Load the Cornell Box 16 | Thread.thread().file_resolver().append('cbox') 17 | scene = load_file('cbox/cbox.xml') 18 | 19 | # Find differentiable scene parameters 20 | params = traverse(scene) 21 | 22 | # Discard all parameters except for one we want to differentiate 23 | params.keep(['red.reflectance.value']) 24 | 25 | # Print the current value and keep a backup copy 26 | param_ref = Color3f(params['red.reflectance.value']) 27 | print(param_ref) 28 | 29 | # Render a reference image (no derivatives used yet) 30 | image_ref = render(scene, spp=8) 31 | crop_size = scene.sensors()[0].film().crop_size() 32 | write_bitmap('out_ref.png', image_ref, crop_size) 33 | 34 | # Change the left wall into a bright white surface 35 | params['red.reflectance.value'] = [.9, .9, .9] 36 | params.update() 37 | 38 | # Construct an Adam optimizer that will adjust the parameters 'params' 39 | opt = Adam(params, lr=.2) 40 | 41 | time_a = time.time() 42 | 43 | iterations = 100 44 | for it in range(iterations): 45 | # Perform a differentiable rendering of the scene 46 | image = render(scene, optimizer=opt, unbiased=True, spp=1) 47 | write_bitmap('out_%03i.png' % it, image, crop_size) 48 | 49 | # Objective: MSE between 'image' and 'image_ref' 50 | ob_val = ek.hsum(ek.sqr(image - image_ref)) / len(image) 51 | 52 | # Back-propagate errors to input parameters 53 | ek.backward(ob_val) 54 | 55 | # Optimizer: take a gradient step 56 | opt.step() 57 | 58 | # Compare iterate against ground-truth value 59 | err_ref = ek.hsum(ek.sqr(param_ref - params['red.reflectance.value'])) 60 | print('Iteration %03i: error=%g' % (it, err_ref[0]), end='\r') 61 | 62 | time_b = time.time() 63 | 64 | print() 65 | print('%f ms per iteration' % (((time_b - time_a) * 1000) / iterations)) 66 | -------------------------------------------------------------------------------- /docs/exts/sphinxtr/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Jeff Terrace 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 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 14 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 23 | DAMAGE. 24 | -------------------------------------------------------------------------------- /docs/exts/sphinxtr/fix_equation_ref.py: -------------------------------------------------------------------------------- 1 | """ 2 | Fixes equation references from Sphinx math domain 3 | from Equation (1) to Equation 1, which is what they 4 | should be. Must be before sphinx.ext.math* in 5 | extensions list. 6 | """ 7 | 8 | from docutils import nodes 9 | import sphinx.ext.mathbase 10 | from sphinx.ext.mathbase import displaymath, eqref 11 | 12 | def number_equations(app, doctree, docname): 13 | num = 0 14 | numbers = {} 15 | for node in doctree.traverse(displaymath): 16 | if node['label'] is not None: 17 | num += 1 18 | node['number'] = num 19 | numbers[node['label']] = num 20 | else: 21 | node['number'] = None 22 | for node in doctree.traverse(eqref): 23 | if node['target'] not in numbers: 24 | continue 25 | num = '%d' % numbers[node['target']] 26 | node[0] = nodes.Text(num, num) 27 | 28 | sphinx.ext.mathbase.number_equations = number_equations 29 | 30 | def setup(app): 31 | pass 32 | -------------------------------------------------------------------------------- /docs/exts/sphinxtr/numsec.py: -------------------------------------------------------------------------------- 1 | """ 2 | Changes section references to be the section number 3 | instead of the title of the section. 4 | """ 5 | 6 | from docutils import nodes 7 | import sphinx.domains.std 8 | 9 | class CustomStandardDomain(sphinx.domains.std.StandardDomain): 10 | 11 | def __init__(self, env): 12 | env.settings['footnote_references'] = 'superscript' 13 | sphinx.domains.std.StandardDomain.__init__(self, env) 14 | 15 | def resolve_xref(self, env, fromdocname, builder, 16 | typ, target, node, contnode): 17 | res = super(CustomStandardDomain, self).resolve_xref(env, fromdocname, builder, 18 | typ, target, node, contnode) 19 | 20 | if res is None: 21 | return res 22 | 23 | if typ == 'ref' and not node['refexplicit']: 24 | docname, labelid, sectname = self.data['labels'].get(target, ('','','')) 25 | res['refdocname'] = docname 26 | 27 | return res 28 | 29 | def doctree_resolved(app, doctree, docname): 30 | secnums = app.builder.env.toc_secnumbers 31 | for node in doctree.traverse(nodes.reference): 32 | if 'refdocname' in node: 33 | refdocname = node['refdocname'] 34 | if refdocname in secnums: 35 | secnum = secnums[refdocname] 36 | toclist = app.builder.env.tocs[refdocname] 37 | for child in node.traverse(): 38 | if isinstance(child, nodes.Text): 39 | text = child.astext() 40 | anchorname = None 41 | for refnode in toclist.traverse(nodes.reference): 42 | if refnode.astext() == text: 43 | anchorname = refnode['anchorname'] 44 | if anchorname is None: 45 | continue 46 | linktext = '.'.join(map(str, secnum[anchorname])) 47 | child.parent.replace(child, nodes.Text(linktext)) 48 | 49 | def setup(app): 50 | app.override_domain(CustomStandardDomain) 51 | app.connect('doctree-resolved', doctree_resolved) 52 | -------------------------------------------------------------------------------- /docs/exts/sphinxtr/singlehtml_toc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Fixes the table of contents in singlehtml mode so that section titles have the 3 | correct section number in front. 4 | """ 5 | 6 | from docutils import nodes 7 | from sphinx import addnodes 8 | 9 | def stringize_secnum(secnum): 10 | return '.'.join(map(str, secnum)) 11 | 12 | def doctree_resolved(app, doctree, fromdocname): 13 | if app.builder.name == 'singlehtml': 14 | secnums = app.builder.env.toc_secnumbers 15 | for filenode in doctree.traverse(addnodes.start_of_file): 16 | docname = filenode['docname'] 17 | if docname not in secnums: 18 | continue 19 | 20 | doc_secnums = secnums[docname] 21 | first_title_node = filenode.next_node(nodes.title) 22 | if first_title_node is not None and '' in doc_secnums: 23 | file_secnum = stringize_secnum(doc_secnums['']) 24 | title_text_node = first_title_node.children[0] 25 | newtext = file_secnum + ' ' + title_text_node.astext() 26 | first_title_node.replace(title_text_node, nodes.Text(newtext)) 27 | 28 | for section_node in filenode.traverse(nodes.section): 29 | for id in section_node['ids']: 30 | if '#' + id in doc_secnums: 31 | subsection_num = stringize_secnum(doc_secnums['#' + id]) 32 | first_title_node = section_node.next_node(nodes.title) 33 | if first_title_node is not None: 34 | title_text_node = first_title_node.children[0] 35 | newtext = subsection_num + ' ' + title_text_node.astext() 36 | first_title_node.replace(title_text_node, nodes.Text(newtext)) 37 | 38 | def setup(app): 39 | app.connect('doctree-resolved', doctree_resolved) 40 | -------------------------------------------------------------------------------- /docs/exts/sphinxtr/singletext.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | from os import path 3 | 4 | from docutils.io import StringOutput 5 | 6 | from sphinx.util.console import bold, darkgreen, brown 7 | from sphinx.util.nodes import inline_all_toctrees 8 | from sphinx.util.osutil import ensuredir, os_path 9 | from sphinx.builders.text import TextBuilder 10 | from sphinx.writers.text import TextWriter, TextTranslator 11 | 12 | class SingleFileTextTranslator(TextTranslator): 13 | def visit_start_of_file(self, node): 14 | pass 15 | def depart_start_of_file(self, node): 16 | pass 17 | 18 | class SingleFileTextWriter(TextWriter): 19 | def translate(self): 20 | visitor = SingleFileTextTranslator(self.document, self.builder) 21 | self.document.walkabout(visitor) 22 | self.output = visitor.body 23 | 24 | class SingleFileTextBuilder(TextBuilder): 25 | """ 26 | A TextBuilder subclass that puts the whole document tree in one text file. 27 | """ 28 | name = 'singletext' 29 | copysource = False 30 | 31 | def get_outdated_docs(self): 32 | return 'all documents' 33 | 34 | def assemble_doctree(self): 35 | master = self.config.master_doc 36 | tree = self.env.get_doctree(master) 37 | tree = inline_all_toctrees( 38 | self, set(), master, tree, darkgreen, [master]) 39 | tree['docname'] = master 40 | self.env.resolve_references(tree, master, self) 41 | return tree 42 | 43 | def prepare_writing(self, docnames): 44 | self.writer = SingleFileTextWriter(self) 45 | 46 | def write(self, *ignored): 47 | docnames = self.env.all_docs 48 | 49 | self.info(bold('preparing documents... '), nonl=True) 50 | self.prepare_writing(docnames) 51 | self.info('done') 52 | 53 | self.info(bold('assembling single document... '), nonl=True) 54 | doctree = self.assemble_doctree() 55 | self.info() 56 | self.info(bold('writing... '), nonl=True) 57 | self.write_doc(self.config.master_doc, doctree) 58 | self.info('done') 59 | 60 | def setup(app): 61 | app.add_builder(SingleFileTextBuilder) 62 | -------------------------------------------------------------------------------- /docs/images/bsdf_eval.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/docs/images/bsdf_eval.jpg -------------------------------------------------------------------------------- /docs/images/chi2_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/docs/images/chi2_example.png -------------------------------------------------------------------------------- /docs/images/integrator_path_figure.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/docs/images/integrator_path_figure.pdf -------------------------------------------------------------------------------- /docs/images/integrator_path_figure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/docs/images/integrator_path_figure.png -------------------------------------------------------------------------------- /docs/images/logo_plain.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/docs/images/logo_plain.pdf -------------------------------------------------------------------------------- /docs/images/logo_plain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/docs/images/logo_plain.png -------------------------------------------------------------------------------- /docs/images/mitsuba-logo-white-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/docs/images/mitsuba-logo-white-bg.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Mitsuba 2: A Retargetable Forward and Inverse Renderer 2 | ====================================================== 3 | 4 | .. only:: not latex 5 | 6 | .. image:: images/mitsuba-logo-white-bg.png 7 | :width: 75% 8 | :align: center 9 | 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | :caption: Overview 14 | 15 | src/getting_started/intro 16 | src/getting_started/cloning 17 | src/getting_started/variants 18 | src/getting_started/compiling 19 | src/getting_started/file_format 20 | src/getting_started/differences 21 | src/getting_started/faq 22 | release_notes.rst 23 | 24 | .. toctree:: 25 | :maxdepth: 1 26 | :caption: Python interface 27 | 28 | src/python_interface/intro 29 | src/python_interface/parsing_xml 30 | src/python_interface/rendering_scene 31 | src/python_interface/bsdf_eval 32 | 33 | 34 | .. toctree:: 35 | :maxdepth: 1 36 | :caption: Inverse rendering 37 | 38 | src/inverse_rendering/intro 39 | src/inverse_rendering/diff_render 40 | src/inverse_rendering/advanced 41 | src/inverse_rendering/pytorch 42 | 43 | 44 | .. toctree:: 45 | :maxdepth: 1 46 | :caption: Developer guide 47 | :titlesonly: 48 | 49 | src/developer_guide/intro 50 | src/developer_guide/variants_cpp 51 | src/developer_guide/writing_plugin 52 | src/developer_guide/testing 53 | src/developer_guide/debugging 54 | src/developer_guide/building_documentation 55 | src/developer_guide/polarization 56 | 57 | 58 | .. toctree:: 59 | :maxdepth: 1 60 | :caption: Advanced topics 61 | 62 | src/advanced_topics/custom_plugins 63 | 64 | .. toctree:: 65 | :maxdepth: 1 66 | :caption: Plugin reference 67 | :titlesonly: 68 | 69 | src/plugin_reference/intro 70 | generated/plugins 71 | 72 | .. toctree:: 73 | :maxdepth: 1 74 | :caption: API reference 75 | 76 | src/api_reference/intro 77 | generated/core_api 78 | generated/render_api 79 | generated/python_api 80 | 81 | .. toctree:: 82 | :maxdepth: 1 83 | :caption: Miscellaneous 84 | 85 | zz_bibliography 86 | -------------------------------------------------------------------------------- /docs/issue_template.md: -------------------------------------------------------------------------------- 1 | *Please add one of the following label to your issue's title, and delete this section:* 2 | 3 | - [🐛 bug report] 4 | - [🔨 compilation issue] 5 | - [✨ feature request] 6 | - [❔ other question] 7 | 8 | ## Summary 9 | 10 | *Please enter a brief description of your issue <- [remove this]* 11 | 12 | ## System configuration 13 | 14 | *For bug report, please enter information regarding your system configuration<- [remove this]* 15 | 16 | - Platform: ... 17 | - Compiler: ... 18 | - Python version: ... 19 | - Mitsuba 2 version: ... 20 | - Compiled variants: 21 | * `scalar_rgb` 22 | * ... 23 | 24 | ## Description 25 | 26 | *Please enter the full description of your issue <- [remove this]* 27 | 28 | ## Steps to reproduce 29 | 30 | *For bug report, please enumerate the steps to follow in order to reproduce the issue <- [remove this]* 31 | 32 | 1. ... 33 | 2. ... 34 | -------------------------------------------------------------------------------- /docs/pull_request_template.md: -------------------------------------------------------------------------------- 1 | *Please add the labels (e.g. bug, feature, ..) corresponding to this PR* 2 | 3 | ## Description 4 | 5 | *Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.* 6 | 7 | Fixes # (issue) 8 | 9 | ## Testing 10 | 11 | *Please describe the tests that you added to verify your changes.* 12 | 13 | ## Checklist: 14 | 15 | *Please make sure to complete this checklist before requesting a review.* 16 | 17 | - [ ] My code follows the [style guidelines](https://mitsuba2.readthedocs.io/en/latest/src/developer_guide/intro.html#introduction) of this project 18 | - [ ] My changes generate no new warnings 19 | - [ ] My code also compiles for `gpu_*` and `packet_*` variants. If you can't test this, please leave below 20 | - [ ] I have commented my code 21 | - [ ] I have made corresponding changes to the documentation 22 | - [ ] I have added tests that prove my fix is effective or that my feature works 23 | - [ ] I cleaned the commit history and removed any "Merge" commits 24 | - [ ] I give permission that the Mitsuba 2 project may redistribute my contributions under the terms of its [license](https://github.com/mitsuba-renderer/mitsuba2/blob/master/LICENSE) -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=2.4 2 | guzzle_sphinx_theme 3 | pygments-mathematica 4 | sphinxcontrib-bibtex 5 | pybtex 6 | -------------------------------------------------------------------------------- /docs/src/api_reference/intro.rst: -------------------------------------------------------------------------------- 1 | .. _sec-api: 2 | 3 | Introduction 4 | ============ 5 | 6 | This API reference documentation was automatically generated using the `Autodoc 7 | `_ Sphinx 8 | extension. 9 | 10 | Autodoc automatically processes the documentation of Mitsuba's Python bindings, 11 | hence all C++ function and class signatures are documented through their Python 12 | counterparts. Mitsuba's bindings mimic the C++ API as closely as possible, 13 | hence this documentation should still prove valuable even for C++ developers. 14 | 15 | The documentation is split into three-submodules: 16 | 17 | - :ref:`sec-api-core`: Core functionality that is not directly related to rendering algorithms. 18 | - :ref:`sec-api-render`: Interfaces of components like rendering algorithms, sensors, emitters, textures, participating media, etc. 19 | - :ref:`sec-api-python`: Higher-level functionality that is written in Python: infrastructure for automatic differentiation, testing (Chi^2 test), etc. 20 | 21 | .. warning:: 22 | 23 | The documentation was extracted from the ``scalar_rgb`` variant of the 24 | renderer. Other variants will use different types in many function and 25 | attribute signatures. Such changes should mostly be "obvious" from the 26 | context. For example, ``scalar_spectral`` will replace all RGB types by 27 | color spectra, and ``gpu_rgb`` will replace scalar floating point values 28 | with Enoki CUDA arrays, etc. 29 | -------------------------------------------------------------------------------- /docs/src/developer_guide/building_documentation.rst: -------------------------------------------------------------------------------- 1 | Building the documentation 2 | ========================== 3 | 4 | The Mitsuba documentation is generated using `Sphinx 5 | `_. It takes several steps to build the 6 | documentation from scratch, but only the last step will be necessary in most 7 | cases. 8 | 9 | From the ``build`` folder, one can run the following commands: 10 | 11 | ``ninja docstrings``: 12 | Generate the :file:`include/python/docstr.h` file from the C++ headers. 13 | This file is used by pybind11 to add docstrings to the Python bindings. 14 | 15 | .. note:: 16 | 17 | This step is only necessary if documentation/comments have changed in 18 | the header files (in which case the python bindings will need to be 19 | built again after calling this). 20 | 21 | ``ninja mkdoc-api``: 22 | Extract documentation (classes, functions, of both Python & C++ portions) 23 | of the ``scalar_rgb`` variant using `Autodoc 24 | `_ to 25 | generate generate API reference documentation (stored in 26 | ``extracted_rst_api.rst``, ``core_api.rst``, ``render_api.rst`` and 27 | ``python_api.rst``). 28 | 29 | .. note:: 30 | 31 | This step is only necessary if documentation/comments have changed in 32 | the header files. 33 | 34 | ``ninja mkdoc``: 35 | Generate the final HTML documentation including the extracted API and 36 | plugin documentation. The output will be stored in the ``html`` 37 | subdirectory. 38 | -------------------------------------------------------------------------------- /docs/src/developer_guide/intro.rst: -------------------------------------------------------------------------------- 1 | .. _sec-devguide: 2 | 3 | Introduction 4 | ============ 5 | 6 | New developers will want to begin by *thoroughly* reading the documentation of 7 | `Enoki `_ before looking at 8 | any Mitsuba code. Enoki is a template library for vector and matrix arithmetic 9 | that constitutes the foundation of Mitsuba 2. It also drives the code 10 | transformations that enable systematic vectorization and automatic 11 | differentiation of the renderer. 12 | 13 | Mitsuba 2 is a completely new codebase, and existing Mitsuba 0.6 plugins will 14 | require significant changes to be compatible with the architecture of the new 15 | system. Apart from differences in the overall architecture, a superficial 16 | change is that Mitsuba 2 code uses an ``underscore_case`` naming convention for 17 | function names and variables (in contrast to Mitsuba 0.4, which used 18 | ``camelCase`` everywhere). We've essentially imported Python's `PEP 8 19 | `_ into the C++ side (which does not 20 | specify a recommended naming convention), ensuring that code that uses 21 | functionality from both languages looks natural. 22 | 23 | 24 | Code structure 25 | -------------- 26 | 27 | Mitsuba is split into 3 basic support libraries: 28 | 29 | * The core library (in :code:`src/libcore`) implements basic functionality such 30 | as cross-platform file and bitmap I/O, data structures, scheduling, as well 31 | as logging and plugin management. 32 | * The rendering library (in :code:`src/librender`) contains abstractions needed 33 | to load and represent scenes containing light sources, shapes, materials, and 34 | participating media. 35 | * The python library (in :code:`src/python`) contains components of the system 36 | that are written in Python, and which access Mitsuba through bindings. This 37 | includes statistical tests (Chi^2, etc.) and tooling for differentiable 38 | rendering. 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/src/getting_started/cloning.rst: -------------------------------------------------------------------------------- 1 | .. _sec-cloning: 2 | 3 | Cloning the repository 4 | ====================== 5 | 6 | Mitsuba depends on several external dependencies, and its repository directly 7 | refers to specific versions of them using a Git feature called *submodules*. 8 | Cloning Mitsuba's repository will recursively fetch these dependencies, which 9 | are subsequently compiled using a single unified build system. This 10 | dramatically reduces the number steps needed to set up the renderer compared to 11 | previous versions of Mitsuba. 12 | 13 | For all of this to work out properly, you will have to specify the 14 | ``--recursive`` flag when cloning the repository: 15 | 16 | .. code-block:: bash 17 | 18 | git clone --recursive https://github.com/mitsuba-renderer/mitsuba2 19 | 20 | If you already cloned the repository and forgot to specify this flag, it's 21 | possible to fix the repository in retrospect using the following command: 22 | 23 | .. code-block:: bash 24 | 25 | git submodule update --init --recursive 26 | 27 | Staying up-to-date 28 | ------------------ 29 | 30 | Unfortunately, pulling from the main repository won't automatically keep the 31 | submodules in sync, which can lead to various problems. The following command 32 | installs a git alias named ``pullall`` that automates these two steps. 33 | 34 | .. code-block:: bash 35 | 36 | git config --global alias.pullall '!f(){ git pull "$@" && git submodule update --init --recursive; }; f' 37 | 38 | Afterwards, simply write 39 | 40 | .. code-block:: bash 41 | 42 | git pullall 43 | 44 | to fetch the latest version of Mitsuba 2. 45 | -------------------------------------------------------------------------------- /docs/src/plugin_reference/intro.rst: -------------------------------------------------------------------------------- 1 | .. _sec-plugins: 2 | 3 | Plugin reference 4 | ================ 5 | 6 | The following subsections describe the available Mitsuba 2 plugins, usually along with example 7 | renderings and a description of what each parameter does. They are separated into subsections 8 | covering textures, surface scattering models, etc. 9 | 10 | The documentation of a plugin always starts with a table similar to the one below: 11 | 12 | .. pluginparameters:: 13 | 14 | * - soft_rays 15 | - |bool| 16 | - Try not to damage objects in the scene by shooting softer rays (Default: false) 17 | * - dark_matter 18 | - |float| 19 | - Controls the proportionate amount of dark matter present in the scene. (Default: 0.42) 20 | * - (Nested plugin) 21 | - :paramtype:`integrator` 22 | - A nested integrator which does the actual hard work 23 | 24 | Suppose this hypothetical plugin is an integrator named ``amazing``. Then, based on this 25 | description, it can be instantiated from an XML scene file using a custom configuration such as: 26 | 27 | .. code-block:: xml 28 | 29 | 30 | 31 | 32 | 33 | 34 | In some cases, plugins also indicate that they accept nested plugins as input arguments. These can 35 | either be *named* or *unnamed*. If the ``amazing`` integrator also accepted the following two parameters: 36 | 37 | .. pluginparameters:: 38 | 39 | 40 | * - (Nested plugin) 41 | - :paramtype:`integrator` 42 | - A nested integrator which does the actual hard work 43 | * - puppies 44 | - :paramtype:`texture` 45 | - This must be used to supply a cute picture of puppies 46 | 47 | then it can be instantiated e.g. as follows: 48 | 49 | .. code-block:: xml 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs/src/plugin_reference/section_emitter.rst: -------------------------------------------------------------------------------- 1 | .. _sec-emitters: 2 | 3 | Emitters 4 | ======== 5 | 6 | .. image:: ../../resources/data/docs/images/emitter/emitter_overview.jpg 7 | :width: 70% 8 | :align: center 9 | 10 | Schematic overview of the emitters in Mitsuba 2. The arrows indicate 11 | the directional distribution of light. 12 | 13 | Mitsuba 2 supports a number of different emitters/light sources, which can be 14 | classified into two main categories: emitters which are located somewhere within the scene, and emitters that surround the scene to simulate a distant environment. 15 | 16 | Generally, light sources are specified as children of the ```` element; for instance, 17 | the following snippet instantiates a point light emitter that illuminates a sphere: 18 | 19 | .. code-block:: xml 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | An exception to this are area lights, which turn a geometric object into a light source. 31 | These are specified as children of the corresponding ```` element: 32 | 33 | .. code-block:: xml 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/src/plugin_reference/section_film.rst: -------------------------------------------------------------------------------- 1 | .. _sec-films: 2 | 3 | Films 4 | ===== 5 | 6 | A film defines how conducted measurements are stored and converted into the final 7 | output file that is written to disk at the end of the rendering process. 8 | 9 | In the XML scene description language, a normal film configuration might look 10 | as follows: 11 | 12 | .. code-block:: xml 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | The ```` plugin should be instantiated nested inside a ```` 34 | declaration. Note how the output filename is never specified---it is automatically 35 | inferred from the scene filename and can be manually overridden by passing the 36 | configuration parameter ``-o`` to the ``mitsuba`` executable when rendering 37 | from the command line. 38 | -------------------------------------------------------------------------------- /docs/src/plugin_reference/section_phase.rst: -------------------------------------------------------------------------------- 1 | .. _sec-phase: 2 | 3 | Phase functions 4 | =============== 5 | 6 | This section contains a description of all implemented medium scattering models, 7 | which are also known as phase functions. These are very similar in principle to 8 | surface scattering models (or BSDFs), and essentially describe where light 9 | travels after hitting a particle within the medium. Currently, only the most 10 | commonly used models for smoke, fog, and other homogeneous media are implemented. 11 | -------------------------------------------------------------------------------- /docs/src/plugin_reference/section_rfilter.rst: -------------------------------------------------------------------------------- 1 | .. _sec-rfilters: 2 | 3 | Reconstruction filters 4 | ====================== 5 | 6 | Image reconstruction filters are responsible for converting a series of radiance samples generated 7 | jointly by the sampler and integrator into the final output image that will be written to disk at 8 | the end of a rendering process. This section gives a brief overview of the reconstruction filters 9 | that are available in Mitsuba. There is no universally superior filter, and the final choice depends 10 | on a trade-off between sharpness, ringing, and aliasing, and computational efficiency. 11 | 12 | Desireable properties of a reconstruction filter are that it sharply captures all of the details 13 | that are displayable at the requested image resolution, while avoiding aliasing and ringing. 14 | Aliasing is the incorrect leakage of high-frequency into low-frequency detail, and ringing denotes 15 | oscillation artifacts near discontinuities, such as a light-shadow transiton. -------------------------------------------------------------------------------- /docs/src/plugin_reference/section_sampler.rst: -------------------------------------------------------------------------------- 1 | .. _sec-samplers: 2 | 3 | Samplers 4 | ======== 5 | 6 | When rendering an image, Mitsuba 2 has to solve a high-dimensional integration problem that involves 7 | the geometry, materials, lights, and sensors that make up the scene. Because of the mathematical 8 | complexity of these integrals, it is generally impossible to solve them analytically — instead, they 9 | are solved numerically by evaluating the function to be integrated at a large number of different 10 | positions referred to as samples. Sample generators are an essential ingredient to this process: 11 | they produce points in a (hypothetical) infinite dimensional hypercube :math:`[0, 1]^{\infty}` 12 | that constitute the canonical representation of these samples. 13 | 14 | To do its work, a rendering algorithm, or integrator, will send many queries to the sample 15 | generator. Generally, it will request subsequent 1D or 2D components of this infinite-dimensional 16 | *point* and map them into a more convenient space (for instance, positions on surfaces). This allows 17 | it to construct light paths to eventually evaluate the flow of light through the scene. 18 | -------------------------------------------------------------------------------- /docs/src/plugin_reference/section_sensor.rst: -------------------------------------------------------------------------------- 1 | .. _sec-sensors: 2 | 3 | Sensors 4 | ======= 5 | 6 | In Mitsuba 2, *sensors*, along with a *film*, are responsible for recording 7 | radiance measurements in some usable format. 8 | 9 | In the XML scene description language, a sensor declaration looks as follows: 10 | 11 | .. code-block:: xml 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | In other words, the ``sensor`` declaration is a child element of the ```` 30 | (the particular position in the scene file does not play a role). Nested within 31 | the sensor declaration is a sampler instance (see :ref:`Samplers `) 32 | and a film instance (see :ref:`Films `). 33 | 34 | Sensors in Mitsuba 2 are *right-handed*. Any number of rotations and translations 35 | can be applied to them without changing this property. By default, they are located 36 | at the origin and oriented in such a way that in the rendered image, :math:`+X` 37 | points left, :math:`+Y` points upwards, and :math:`+Z` points along the viewing 38 | direction. 39 | Left-handed sensors are also supported. To switch the handedness, flip any one 40 | of the axes, e.g. by passing a scale transform like ```` to the 41 | sensor's :monosp:`to_world` parameter. -------------------------------------------------------------------------------- /docs/src/plugin_reference/section_shape.rst: -------------------------------------------------------------------------------- 1 | .. _sec-shapes: 2 | 3 | Shapes 4 | ====== 5 | 6 | This section presents an overview of the shape plugins that are released along with the renderer. 7 | 8 | In Mitsuba 2, shapes define surfaces that mark transitions between different types of materials. For 9 | instance, a shape could describe a boundary between air and a solid object, such as a piece of rock. 10 | Alternatively, a shape can mark the beginning of a region of space that isn’t solid at all, but 11 | rather contains a participating medium, such as smoke or steam. Finally, a shape can be used to 12 | create an object that emits light on its own. 13 | 14 | Shapes are usually declared along with a surface scattering model named *BSDF* (see the :ref:`respective section `). This BSDF characterizes what happens at the surface. In the XML scene description language, this might look like the following: 15 | 16 | .. code-block:: xml 17 | 18 | 19 | 20 | .. shape parameters .. 21 | 22 | 23 | .. bsdf parameters .. 24 | 25 | 26 | 31 | 32 | 33 | 34 | The following subsections discuss the available shape types in greater detail. -------------------------------------------------------------------------------- /docs/zz_bibliography.rst: -------------------------------------------------------------------------------- 1 | .. only:: html or text 2 | 3 | Bibliography 4 | ============ 5 | 6 | .. bibliography:: references.bib -------------------------------------------------------------------------------- /ext/rgb2spec/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2.0) 2 | project(rgb2spec) 3 | 4 | if (POLICY CMP0068) 5 | cmake_policy(SET CMP0068 NEW) 6 | endif() 7 | 8 | # Set a default build configuration (Release) 9 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 10 | message(STATUS "Setting build type to 'Release' as none was specified.") 11 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) 12 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" 13 | "MinSizeRel" "RelWithDebInfo") 14 | endif() 15 | 16 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|Intel") 17 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 18 | endif() 19 | 20 | 21 | if (TARGET tbb) 22 | add_definitions(-DRGB2SPEC_USE_TBB=1) 23 | include_directories(${TBB_INCLUDE_DIRS}) 24 | else () 25 | include(FindOpenMP) 26 | if(OPENMP_FOUND) 27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 28 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 29 | endif() 30 | endif() 31 | 32 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 33 | 34 | add_executable(rgb2spec_opt rgb2spec_opt.cpp) 35 | add_library(rgb2spec STATIC rgb2spec.c) 36 | 37 | if (TARGET tbb) 38 | target_link_libraries(rgb2spec_opt PRIVATE tbb) 39 | 40 | add_custom_command(TARGET rgb2spec_opt POST_BUILD 41 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 42 | $ 43 | $ 44 | ) 45 | endif() 46 | 47 | add_custom_command( 48 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/srgb.coeff 49 | DEPENDS rgb2spec_opt 50 | COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=.:$ENV{LD_LIBRARY_PATH} 51 | $ 64 ${CMAKE_CURRENT_BINARY_DIR}/srgb.coeff 52 | ) 53 | 54 | add_custom_target( 55 | rgb2spec_opt_run 56 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/srgb.coeff 57 | ) 58 | -------------------------------------------------------------------------------- /ext/rgb2spec/rgb2spec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(__SSE4_2__) || defined(__AVX__) 6 | # include 7 | #endif 8 | 9 | #ifdef __cplusplus 10 | extern "C" 11 | { 12 | #endif 13 | 14 | /// How many polynomial coefficients? 15 | #define RGB2SPEC_N_COEFFS 3 16 | 17 | /// Underlying representation 18 | typedef struct { 19 | uint32_t res; 20 | float *scale; 21 | float *data; 22 | } RGB2Spec; 23 | 24 | /// Load a RGB2Spec model from disk 25 | RGB2Spec *rgb2spec_load(const char *filename); 26 | 27 | /// Release all memory associated with a RGB2Spec model 28 | void rgb2spec_free(RGB2Spec *model); 29 | 30 | /// Convert an RGB value into a RGB2Spec coefficient representation 31 | void rgb2spec_fetch(RGB2Spec *model, float rgb[3], float out[RGB2SPEC_N_COEFFS]); 32 | 33 | /// Evaluate the model for a given wavelength 34 | float rgb2spec_eval_precise(float coeff[RGB2SPEC_N_COEFFS], float lambda); 35 | 36 | /// Evaluate the model for a given wavelength (fast, with recip. square root) 37 | float rgb2spec_eval_fast(float coeff[RGB2SPEC_N_COEFFS], float lambda); 38 | 39 | #if defined(__SSE4_2__) 40 | /// SSE 4.2 version -- evaluates 4 wavelengths at once 41 | __m128 rgb2spec_eval_sse(float coeff[RGB2SPEC_N_COEFFS], __m128 lambda); 42 | #endif 43 | 44 | #if defined(__AVX__) 45 | /// AVX version -- evaluates 8 wavelengths at once 46 | __m256 rgb2spec_eval_avx(float coeff[RGB2SPEC_N_COEFFS], __m256 lambda); 47 | #endif 48 | 49 | #if defined(__AVX512F__) 50 | /// AVX512 version -- evaluates 16 wavelengths at once 51 | __m512 rgb2spec_eval_avx512(float coeff[RGB2SPEC_N_COEFFS], __m512 lambda); 52 | #endif 53 | 54 | #ifdef __cplusplus 55 | } // extern "C" 56 | #endif 57 | -------------------------------------------------------------------------------- /include/mitsuba/core/dstream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | /** \brief \ref Stream implementation that never writes to disk, but keeps track 9 | * of the size of the content being written. 10 | * It can be used, for example, to measure the precise amount of memory needed 11 | * to store serialized content. 12 | */ 13 | class MTS_EXPORT_CORE DummyStream : public Stream { 14 | public: 15 | DummyStream(); 16 | 17 | /** \brief Closes the stream. 18 | * No further read or write operations are permitted. 19 | * 20 | * This function is idempotent. 21 | * It may be called automatically by the destructor. 22 | */ 23 | virtual void close() override; 24 | 25 | /// Whether the stream is closed (no read or write are then permitted). 26 | virtual bool is_closed() const override; 27 | 28 | // ========================================================================= 29 | //! @{ \name Implementation of the Stream interface 30 | // ========================================================================= 31 | 32 | virtual void read(void *, size_t) override; 33 | virtual void write(const void *, size_t size) override; 34 | virtual void seek(size_t pos) override; 35 | virtual void truncate(size_t size) override; 36 | virtual size_t tell() const override; 37 | virtual size_t size() const override; 38 | virtual void flush() override; 39 | virtual bool can_write() const override; 40 | virtual bool can_read() const override; 41 | 42 | //! @} 43 | // ========================================================================= 44 | 45 | MTS_DECLARE_CLASS() 46 | protected: 47 | 48 | /// Protected destructor. 49 | virtual ~DummyStream() = default; 50 | 51 | private: 52 | /// Size of all data written to the stream 53 | size_t m_size; 54 | /** \brief Current position in the "virtual" stream (even though nothing 55 | * is ever written, we need to maintain consistent positioning). 56 | */ 57 | size_t m_pos; 58 | /// Whether the stream has been closed. 59 | bool m_is_closed; 60 | 61 | }; 62 | 63 | NAMESPACE_END(mitsuba) 64 | -------------------------------------------------------------------------------- /include/mitsuba/core/jit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(ENOKI_X86_64) 6 | # if defined(__GNUG__) && !defined(__clang__) 7 | # pragma GCC diagnostic push 8 | # pragma GCC diagnostic ignored "-Wbool-operation" 9 | # pragma GCC diagnostic ignored "-Wbool-compare" 10 | # elif defined(_MSC_VER) 11 | # pragma warning(push) 12 | # pragma warning(disable : 4804) // warning C4804: '~': unsafe use of type 'bool' in operation 13 | # pragma warning(disable : 4245) // warning C4245: 'argument': conversion from 'int' to 'uint32_t', signed/unsigned mismatch 14 | # endif 15 | 16 | # include 17 | 18 | # if defined(__GNUG__) && !defined(__clang__) 19 | # pragma GCC diagnostic pop 20 | # elif defined(_MSC_VER) 21 | # pragma warning(pop) 22 | # endif 23 | #endif 24 | 25 | #include 26 | 27 | NAMESPACE_BEGIN(mitsuba) 28 | 29 | struct MTS_EXPORT_CORE Jit { 30 | std::mutex mutex; 31 | #if defined(ENOKI_X86_64) 32 | asmjit::JitRuntime runtime; 33 | #endif 34 | 35 | /** 36 | * \brief Statically initialize the JIT runtime 37 | * 38 | * This function also does a runtime-check to ensure that the host 39 | * processor supports all instruction sets which were selected at compile 40 | * time. If not, the application is terminated via ``abort()``. 41 | */ 42 | static void static_initialization(); 43 | 44 | /// Release all memory used by JIT-compiled routines 45 | static void static_shutdown(); 46 | 47 | static Jit *get_instance(); 48 | 49 | private: 50 | Jit(); 51 | Jit(const Jit &) = delete; 52 | }; 53 | 54 | NAMESPACE_END(mitsuba) 55 | -------------------------------------------------------------------------------- /include/mitsuba/core/mmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | /** 8 | * \brief Basic cross-platform abstraction for memory mapped files 9 | * 10 | * \remark The Python API has one additional constructor 11 | * MemoryMappedFile(filename, array), which creates a new 12 | * file, maps it into memory, and copies the array contents. 13 | */ 14 | class MTS_EXPORT_CORE MemoryMappedFile : public Object { 15 | public: 16 | /// Create a new memory-mapped file of the specified size 17 | MemoryMappedFile(const fs::path &filename, size_t size); 18 | 19 | /// Map the specified file into memory 20 | MemoryMappedFile(const fs::path &filename, bool write = false); 21 | 22 | /// Return a pointer to the file contents in memory 23 | void *data(); 24 | 25 | /// Return a pointer to the file contents in memory (const version) 26 | const void *data() const; 27 | 28 | /// Return the size of the mapped region 29 | size_t size() const; 30 | 31 | /** 32 | * \brief Resize the memory-mapped file 33 | * 34 | * This involves remapping the file, which will 35 | * generally change the pointer obtained via data() 36 | */ 37 | void resize(size_t size); 38 | 39 | /// Return the associated filename 40 | const fs::path &filename() const; 41 | 42 | /// Return whether the mapped memory region can be modified 43 | bool can_write() const; 44 | 45 | /// Return a string representation 46 | std::string to_string() const override; 47 | 48 | /** 49 | * \brief Create a temporary memory-mapped file 50 | * 51 | * \remark When closing the mapping, the file is automatically deleted. 52 | * Mitsuba additionally informs the OS that any outstanding changes 53 | * that haven't yet been written to disk can be discarded 54 | * (Linux/OSX only). 55 | */ 56 | static ref create_temporary(size_t size); 57 | 58 | MTS_DECLARE_CLASS() 59 | protected: 60 | /// Internal constructor 61 | MemoryMappedFile(); 62 | 63 | /// Release all resources 64 | virtual ~MemoryMappedFile(); 65 | 66 | private: 67 | struct MemoryMappedFilePrivate; 68 | std::unique_ptr d; 69 | }; 70 | 71 | NAMESPACE_END(mitsuba) 72 | -------------------------------------------------------------------------------- /include/mitsuba/core/progress.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | /** 9 | * \brief General-purpose progress reporter 10 | * 11 | * This class is used to track the progress of various operations that might 12 | * take longer than a second or so. It provides interactive feedback when 13 | * Mitsuba is run on the console, via the OpenGL GUI, or in Jupyter Notebook. 14 | */ 15 | class MTS_EXPORT_CORE ProgressReporter : public Object { 16 | public: 17 | /** 18 | * \brief Construct a new progress reporter. 19 | * \param label 20 | * An identifying name for the operation taking place (e.g. "Rendering") 21 | * \param ptr 22 | * Custom pointer payload to be delivered as part of progress messages 23 | */ 24 | ProgressReporter(const std::string &label, void *payload = nullptr); 25 | 26 | /// Update the progress to \c progress (which should be in the range [0, 1]) 27 | void update(float progress); 28 | 29 | MTS_DECLARE_CLASS() 30 | protected: 31 | ~ProgressReporter(); 32 | 33 | protected: 34 | Timer m_timer; 35 | std::string m_label; 36 | std::string m_line; 37 | size_t m_bar_start; 38 | size_t m_bar_size; 39 | size_t m_last_update; 40 | float m_last_progress; 41 | void *m_payload; 42 | }; 43 | 44 | NAMESPACE_END(mitsuba) 45 | -------------------------------------------------------------------------------- /include/mitsuba/core/simd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #if defined(MTS_ENABLE_OPTIX) 8 | # include 9 | # include 10 | #endif 11 | 12 | NAMESPACE_BEGIN(mitsuba) 13 | 14 | /// Convenience function which computes an array size/type suffix (like '2u' or '3fP') 15 | template std::string type_suffix() { 16 | using B = scalar_t; 17 | 18 | std::string id = std::to_string(array_size_v); 19 | 20 | if (std::is_floating_point_v) { 21 | if (std::is_same_v) { 22 | id += 'h'; 23 | } else { 24 | if constexpr (is_float_v) 25 | id += std::is_same_v ? 'f' : 'd'; 26 | else 27 | id += std::is_same_v ? 'f' : 's'; 28 | } 29 | } else { 30 | if (std::is_signed_v) 31 | id += 'i'; 32 | else 33 | id += 'u'; 34 | } 35 | 36 | if (is_static_array_v>) 37 | id += 'P'; 38 | else if (is_diff_array_v>) 39 | id += 'D'; 40 | else if (is_cuda_array_v>) 41 | id += 'C'; 42 | else if (is_dynamic_array_v>) 43 | id += 'X'; 44 | 45 | return id; 46 | } 47 | 48 | /// Round an integer to a multiple of the current packet size 49 | template 50 | inline size_t round_to_packet_size(size_t size) { 51 | constexpr size_t PacketSize = Float::Size; 52 | return (size + PacketSize - 1) / PacketSize * PacketSize; 53 | } 54 | 55 | NAMESPACE_END(mitsuba) 56 | -------------------------------------------------------------------------------- /include/mitsuba/core/tensor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | NAMESPACE_BEGIN(mitsuba) 8 | 9 | /** 10 | * \brief Simple exchange format for tensor data of arbitrary rank and size 11 | * 12 | * This class provides convenient memory-mapped read-only access to tensor 13 | * data, usually exported from NumPy. 14 | */ 15 | class MTS_EXPORT_CORE TensorFile : public MemoryMappedFile { 16 | public: 17 | 18 | /// Information about the specified field 19 | struct Field { 20 | /// Data type (uint32, float, ...) of the tensor 21 | Struct::Type dtype; 22 | 23 | /// Offset within the file 24 | size_t offset; 25 | 26 | /// Specifies both rank and size along each dimension 27 | std::vector shape; 28 | 29 | /// Const pointer to the start of the tensor 30 | const void *data; 31 | }; 32 | 33 | /// Map the specified file into memory 34 | TensorFile(const fs::path &filename); 35 | 36 | /// Does the file contain a field of the specified name? 37 | bool has_field(const std::string &name) const; 38 | 39 | /// Return a data structure with information about the specified field 40 | const Field &field(const std::string &name) const; 41 | 42 | /// Return a human-readable summary 43 | std::string to_string() const override; 44 | 45 | MTS_DECLARE_CLASS() 46 | protected: 47 | /// Destructor 48 | ~TensorFile(); 49 | 50 | private: 51 | std::unordered_map m_fields; 52 | }; 53 | 54 | NAMESPACE_END(mitsuba) 55 | -------------------------------------------------------------------------------- /include/mitsuba/core/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | class Timer { 9 | public: 10 | using Unit = std::chrono::milliseconds; 11 | 12 | Timer() { 13 | start = std::chrono::system_clock::now(); 14 | } 15 | 16 | size_t value() const { 17 | auto now = std::chrono::system_clock::now(); 18 | auto duration = std::chrono::duration_cast(now - start); 19 | return (size_t) duration.count(); 20 | } 21 | 22 | size_t reset() { 23 | auto now = std::chrono::system_clock::now(); 24 | auto duration = std::chrono::duration_cast(now - start); 25 | start = now; 26 | return (size_t) duration.count(); 27 | } 28 | 29 | void begin_stage(const std::string &name) { 30 | reset(); 31 | std::cout << name << " .. "; 32 | std::cout.flush(); 33 | } 34 | 35 | void end_stage(const std::string &str = "") { 36 | std::cout << "done. (took " << util::time_string(value()); 37 | if (!str.empty()) 38 | std::cout << ", " << str; 39 | std::cout << ")" << std::endl; 40 | } 41 | private: 42 | std::chrono::system_clock::time_point start; 43 | }; 44 | 45 | NAMESPACE_END(mitsuba) 46 | -------------------------------------------------------------------------------- /include/mitsuba/core/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | NAMESPACE_BEGIN(mitsuba) 10 | NAMESPACE_BEGIN(util) 11 | 12 | #if defined(__WINDOWS__) 13 | /// Return a std::string version of GetLastError() 14 | extern std::string MTS_EXPORT_CORE last_error(); 15 | #endif 16 | 17 | /// Determine the number of available CPU cores (including virtual cores) 18 | extern MTS_EXPORT_CORE int core_count(); 19 | 20 | /** 21 | * \brief Convert a time difference (in seconds) to a string representation 22 | * \param time Time difference in (fractional) sections 23 | * \param precise When set to true, a higher-precision string representation 24 | * is generated. 25 | */ 26 | extern MTS_EXPORT_CORE std::string time_string(float time, bool precise = false); 27 | 28 | /// Turn a memory size into a human-readable string 29 | extern MTS_EXPORT_CORE std::string mem_string(size_t size, bool precise = false); 30 | 31 | /// Returns 'true' if the application is running inside a debugger 32 | extern MTS_EXPORT_CORE bool detect_debugger(); 33 | 34 | /// Generate a trap instruction if running in a debugger; otherwise, return. 35 | extern MTS_EXPORT_CORE void trap_debugger(); 36 | 37 | /// Return the absolute path to libmitsuba-core.dylib/so/dll 38 | extern MTS_EXPORT_CORE fs::path library_path(); 39 | 40 | /// Determine the width of the terminal window that is used to run Mitsuba 41 | extern MTS_EXPORT_CORE int terminal_width(); 42 | 43 | /// Return human-readable information about the Mitsuba build 44 | extern MTS_EXPORT_CORE std::string info_build(int thread_count); 45 | 46 | /// Return human-readable information about the version 47 | extern MTS_EXPORT_CORE std::string info_copyright(); 48 | 49 | /// Return human-readable information about the enabled processor features 50 | extern MTS_EXPORT_CORE std::string info_features(); 51 | 52 | NAMESPACE_END(util) 53 | NAMESPACE_END(mitsuba) 54 | -------------------------------------------------------------------------------- /include/mitsuba/mitsuba.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mitsuba 2: A Retargetable Forward and Inverse Renderer 3 | Copyright 2019, Realistic Graphics Lab, EPFL. 4 | 5 | All rights reserved. Use of this source code is governed by a 6 | BSD-style license that can be found in the LICENSE.txt file. 7 | */ 8 | 9 | #pragma once 10 | 11 | #define MTS_VERSION_MAJOR 2 12 | #define MTS_VERSION_MINOR 2 13 | #define MTS_VERSION_PATCH 1 14 | 15 | #define MTS_STRINGIFY(x) #x 16 | #define MTS_TOSTRING(x) MTS_STRINGIFY(x) 17 | 18 | /// Current release of Mitsuba 19 | #define MTS_VERSION \ 20 | MTS_TOSTRING(MTS_VERSION_MAJOR) "." \ 21 | MTS_TOSTRING(MTS_VERSION_MINOR) "." \ 22 | MTS_TOSTRING(MTS_VERSION_PATCH) 23 | 24 | /// Year of the current release 25 | #define MTS_YEAR "2020" 26 | 27 | /// Authors list 28 | #define MTS_AUTHORS "Realistic Graphics Lab, EPFL" 29 | 30 | #include 31 | #include 32 | #include 33 | -------------------------------------------------------------------------------- /include/mitsuba/render/optix/bbox.cuh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __CUDACC__ 4 | # include 5 | #endif 6 | 7 | namespace optix { 8 | struct BoundingBox3f { 9 | BoundingBox3f() = default; 10 | #ifndef __CUDACC__ 11 | BoundingBox3f(const mitsuba::BoundingBox> &b) { 12 | min[0] = b.min[0]; min[1] = b.min[1]; min[2] = b.min[2]; 13 | max[0] = b.max[0]; max[1] = b.max[1]; max[2] = b.max[2]; 14 | } 15 | #endif 16 | float min[3]; 17 | float max[3]; 18 | }; 19 | } // end namespace optix 20 | -------------------------------------------------------------------------------- /include/mitsuba/render/optix/math.cuh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __CUDACC__ 4 | #define DEVICE __forceinline__ __device__ 5 | 6 | template 7 | DEVICE T sqr(const T& x) { 8 | return x * x; 9 | } 10 | 11 | DEVICE bool solve_quadratic(float a, float b, float c, float& x0, float&x1) { 12 | bool linear_case = (a == 0.f); 13 | // For linear eq, we require b != 0 14 | if (linear_case && b == 0.f) 15 | return false; 16 | 17 | x0 = x1 = -c / b; 18 | float discrim = fmaf(b, b, -4.f * a * c); 19 | 20 | // Check if the quadratic eq is solvable 21 | if (!linear_case && discrim < 0.f) 22 | return false; 23 | 24 | /* Numerically stable version of (-b (+/-) sqrt_discrim) / (2 * a) 25 | * 26 | * Based on the observation that one solution is always 27 | * accurate while the other is not. Finds the solution of 28 | * greater magnitude which does not suffer from loss of 29 | * precision and then uses the identity x1 * x2 = c / a 30 | */ 31 | float temp = -0.5f * (b + copysign(sqrt(discrim), b)); 32 | 33 | float x0p = temp / a, 34 | x1p = c / temp; 35 | 36 | // Order the results so that x0 < x1 37 | float x0m = min(x0p, x1p), 38 | x1m = max(x0p, x1p); 39 | 40 | x0 = (linear_case ? x0 : x0m); 41 | x1 = (linear_case ? x0 : x1m); 42 | 43 | return true; 44 | } 45 | #endif -------------------------------------------------------------------------------- /include/mitsuba/render/optix/ray.cuh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __CUDACC__ 3 | 4 | # include 5 | #include 6 | 7 | namespace optix { 8 | 9 | struct Ray3f { 10 | Vector3f o; ///< Ray origin 11 | Vector3f d; ///< Ray direction 12 | Vector3f d_rcp; ///< Componentwise reciprocals of the ray direction 13 | float mint; ///< Minimum position on the ray segment 14 | float maxt; ///< Maximum position on the ray segment 15 | float time; ///< Time value associated with this ray 16 | 17 | DEVICE Ray3f() {} 18 | 19 | /// Construct a new ray (o, d) with bounds 20 | DEVICE Ray3f(const Vector3f &o, const Vector3f &d, float mint, float maxt, float time) 21 | : o(o), d(d), d_rcp(frcp(d)), mint(mint), maxt(maxt), time(time) { } 22 | 23 | /// Return the position of a point along the ray 24 | DEVICE Vector3f operator() (float t) const { return fmaf(t, d, o); } 25 | 26 | /// Update the reciprocal ray directions after changing 'd' 27 | DEVICE void update() { d_rcp = frcp(d); } 28 | }; 29 | 30 | /// Returns the current ray in instance space (based on the current instance transformation) 31 | DEVICE Ray3f get_ray() { 32 | Ray3f ray; 33 | ray.o = make_vector3f(optixTransformPointFromWorldToObjectSpace(optixGetWorldRayOrigin())); 34 | ray.d = make_vector3f(optixTransformVectorFromWorldToObjectSpace(optixGetWorldRayDirection())); 35 | ray.mint = optixGetRayTmin(); 36 | ray.maxt = optixGetRayTmax(); 37 | ray.time = optixGetRayTime(); 38 | ray.update(); 39 | return ray; 40 | } 41 | } // end namespace optix 42 | #endif // defined __CUDACC__ -------------------------------------------------------------------------------- /include/mitsuba/render/srgb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | template 9 | MTS_INLINE Spectrum srgb_model_eval(const Array3f &coeff, 10 | const wavelength_t &wavelengths) { 11 | static_assert(!is_polarized_v, "srgb_model_eval(): requires unpolarized spectrum type!"); 12 | 13 | if constexpr (is_spectral_v) { 14 | Spectrum v = fmadd(fmadd(coeff.x(), wavelengths, coeff.y()), wavelengths, coeff.z()); 15 | 16 | return select( 17 | enoki::isinf(coeff.z()), fmadd(sign(coeff.z()), .5f, .5f), 18 | max(0.f, fmadd(.5f * v, rsqrt(fmadd(v, v, 1.f)), .5f)) 19 | ); 20 | } else { 21 | Throw("srgb_model_eval(): invoked for a non-spectral color type!"); 22 | } 23 | } 24 | 25 | template 26 | MTS_INLINE value_t srgb_model_mean(const Array3f &coeff) { 27 | using Float = value_t; 28 | using Vec = Array; 29 | 30 | Vec lambda = linspace(MTS_WAVELENGTH_MIN, MTS_WAVELENGTH_MAX); 31 | Vec v = fmadd(fmadd(coeff.x(), lambda, coeff.y()), lambda, coeff.z()); 32 | Vec result = select(enoki::isinf(coeff.z()), fmadd(sign(coeff.z()), .5f, .5f), 33 | max(0.f, fmadd(.5f * v, rsqrt(fmadd(v, v, 1.f)), .5f))); 34 | return hmean(result); 35 | } 36 | 37 | /** 38 | * Look up the model coefficients for a sRGB color value 39 | * @param c An sRGB color value where all components are in [0, 1]. 40 | * @return Coefficients for use with \ref srgb_model_eval 41 | */ 42 | MTS_EXPORT_RENDER Array srgb_model_fetch(const Color &); 43 | 44 | /// Sanity check: convert the coefficients back to sRGB 45 | // MTS_EXPORT_RENDER Color srgb_model_eval_rgb(const Array &); 46 | 47 | NAMESPACE_END(mitsuba) 48 | -------------------------------------------------------------------------------- /include/mitsuba/render/volume_texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | NAMESPACE_BEGIN(mitsuba) 10 | 11 | /// Holds metadata about a volume, e.g. when loaded from a Mitsuba binary volume file. 12 | struct VolumeMetadata { 13 | using Float = float; 14 | MTS_IMPORT_CORE_TYPES() 15 | 16 | std::string filename; 17 | uint8_t version; 18 | int32_t data_type; 19 | Vector3i shape; 20 | size_t channel_count; 21 | BoundingBox3f bbox; 22 | Transform4f transform; 23 | 24 | double mean = 0.; 25 | float max; 26 | }; 27 | 28 | NAMESPACE_END(mitsuba) 29 | -------------------------------------------------------------------------------- /include/mitsuba/ui/fwd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | namespace ng = nanogui; 9 | 10 | class MitsubaViewer; 11 | class GUITexture; 12 | 13 | NAMESPACE_END(mitsuba) 14 | -------------------------------------------------------------------------------- /include/mitsuba/ui/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | /** 9 | * \brief Defines an abstraction for textures that works with 10 | * OpenGL, OpenGL ES, and Metal. 11 | * 12 | * Wraps nanogui::Texture and adds a new constructor for creating 13 | * textures from \ref mitsuba::Bitmap instances. 14 | */ 15 | class MTS_EXPORT_UI GPUTexture : public nanogui::Texture { 16 | public: 17 | using Base = nanogui::Texture; 18 | using Base::Base; 19 | 20 | GPUTexture(const Bitmap *bitmap, 21 | InterpolationMode min_interpolation_mode = InterpolationMode::Bilinear, 22 | InterpolationMode mag_interpolation_mode = InterpolationMode::Bilinear, 23 | WrapMode wrap_mode = WrapMode::ClampToEdge); 24 | 25 | protected: 26 | virtual ~GPUTexture(); 27 | }; 28 | 29 | NAMESPACE_END(mitsuba) 30 | -------------------------------------------------------------------------------- /include/mitsuba/ui/viewer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | namespace ng = nanogui; 9 | 10 | /** 11 | * \brief Main class of the Mitsuba user interface 12 | */ 13 | class MTS_EXPORT_UI MitsubaViewer : public ng::Screen { 14 | public: 15 | struct Tab; 16 | 17 | /// Create a new viewer interface 18 | MitsubaViewer(); 19 | 20 | /// Append an empty tab 21 | Tab *append_tab(const std::string &caption); 22 | 23 | /// Load content (a scene or an image) into a tab 24 | void load(Tab *tab, const fs::path &scene); 25 | 26 | using ng::Screen::perform_layout; 27 | virtual void perform_layout(NVGcontext* ctx) override; 28 | virtual bool keyboard_event(int key, int scancode, int action, int modifiers) override; 29 | 30 | protected: 31 | void close_tab_impl(Tab *tab); 32 | 33 | protected: 34 | ng::ref m_btn_play, m_btn_stop, m_btn_reload; 35 | ng::ref m_btn_menu, m_btn_settings; 36 | ng::ref m_contents, m_progress_panel; 37 | ng::ref m_progress_bar; 38 | ng::ref m_tab_widget; 39 | ng::ref m_view; 40 | std::vector m_tabs; 41 | }; 42 | 43 | NAMESPACE_END(mitsuba) 44 | -------------------------------------------------------------------------------- /resources/FindSphinx.cmake: -------------------------------------------------------------------------------- 1 | find_program(SPHINX_EXECUTABLE NAMES sphinx-build 2 | HINTS 3 | $ENV{SPHINX_DIR} 4 | PATH_SUFFIXES bin 5 | DOC "Sphinx documentation generator" 6 | ) 7 | 8 | include(FindPackageHandleStandardArgs) 9 | 10 | find_package_handle_standard_args(Sphinx DEFAULT_MSG 11 | SPHINX_EXECUTABLE 12 | ) 13 | 14 | mark_as_advanced(SPHINX_EXECUTABLE) 15 | -------------------------------------------------------------------------------- /resources/check-style.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Script to check include/test code for common code style errors. 4 | # 5 | # This script currently checks for 6 | # 7 | # 1. use of tabs instead of spaces 8 | # 2. trailing spaces 9 | # 3. missing space between keyword and parenthesis, e.g.: for(, if(, while( 10 | # 4. opening brace on its own line. It should always be on the same line as the 11 | # if/while/for/do statment. 12 | # 5. Missing space between right parenthesis and brace, e.g. 'for (...){' 13 | # 14 | # Invoke as: resources/check-style.sh 15 | # 16 | 17 | if [ -n "$BASH_VERSION" ]; then 18 | shopt -s globstar 19 | fi 20 | 21 | errors=0 22 | IFS=$'\n' 23 | found= 24 | # The mt=41 sets a red background for matched tabs: 25 | exec 3< <(GREP_COLORS='mt=41' grep $'\t' include/**/*.h src/**/*.{cpp,py} -rn --color=always) 26 | while read -u 3 f; do 27 | if [ -z "$found" ]; then 28 | echo -e '\e[31m\e[01mError: found tabs instead of spaces in the following files:\e[0m' 29 | found=1 30 | errors=1 31 | fi 32 | 33 | echo " $f" 34 | done 35 | 36 | found= 37 | # The mt=41 sets a red background for matched trailing spaces 38 | exec 3< <(GREP_COLORS='mt=41' grep '\s\+$' include/**/*.h src/**/*.{cpp,py} -rn --color=always) 39 | while read -u 3 f; do 40 | if [ -z "$found" ]; then 41 | echo -e '\e[31m\e[01mError: found trailing spaces in the following files:\e[0m' 42 | found=1 43 | errors=1 44 | fi 45 | 46 | echo " $f" 47 | done 48 | 49 | found= 50 | exec 3< <(GREP_COLORS='mt=41' grep '^\s*{\s*$' include/**/*.h src/**/*.cpp -rn --color=always) 51 | while read -u 3 f; do 52 | if [ -z "$found" ]; then 53 | echo -e '\e[31m\e[01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files: \e[0m' 54 | found=1 55 | errors=1 56 | fi 57 | 58 | echo " $f" 59 | done 60 | 61 | found= 62 | exec 3< <(grep '\<\(if\|catch\|for\|while\)(\|){' include/**/*.h src/**/*.{cpp,py} -rn --color=always) 63 | while read -u 3 line; do 64 | if [ -z "$found" ]; then 65 | echo -e '\e[31m\e[01mError: found the following coding style problems:\e[0m' 66 | found=1 67 | errors=1 68 | fi 69 | 70 | echo " $line" 71 | done 72 | 73 | exit $errors 74 | -------------------------------------------------------------------------------- /resources/mitsuba-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/resources/mitsuba-logo.png -------------------------------------------------------------------------------- /resources/ptx/Makefile: -------------------------------------------------------------------------------- 1 | mts_include_folder = ../../include 2 | mts_shape_folder = ../../src/shapes/optix 3 | mts_optix_main = ../../src/librender/optix/optix_rt.cu 4 | 5 | all: optix_rt.ptx 6 | 7 | optix_rt.ptx: $(mts_optix_main) $(mts_include_folder)/* $(mts_shape_folder)/* 8 | nvcc $(mts_optix_main) \ 9 | -I $(mts_include_folder) -I $(mts_shape_folder) -I /opt/optix-7.0.0/include/ \ 10 | -O3 -gencode arch=compute_61,code=compute_61 --ptx 11 | 12 | clean: 13 | rm -f optix_rt.ptx 14 | -------------------------------------------------------------------------------- /setpath.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM *************************************************************** 4 | REM * This script adds Mitsuba to the current path on Windows. 5 | REM * It assumes that Mitsuba is either compiled within the 6 | REM * source tree or within a subdirectory named 'build'. 7 | REM *************************************************************** 8 | 9 | set MITSUBA_DIR=%~dp0 10 | set MITSUBA_DIR=%MITSUBA_DIR:~0,-1% 11 | set PATH=%PATH%;%MITSUBA_DIR%\dist;%MITSUBA_DIR%\build\dist 12 | set PYTHONPATH=%PYTHONPATH%;%MITSUBA_DIR%\dist\python;%MITSUBA_DIR%\build\dist\python 13 | -------------------------------------------------------------------------------- /setpath.ps1: -------------------------------------------------------------------------------- 1 | 2 | # *************************************************************** 3 | # * This script adds Mitsuba to the current path on Windows. 4 | # * It assumes that Mitsuba is either compiled within the 5 | # * source tree or within a subdirectory named 'build'. 6 | # *************************************************************** 7 | 8 | $env:MITSUBA_DIR=Get-Location 9 | $env:PATH=$env:PATH + ";" + $env:MITSUBA_DIR + "\dist;" + $env:MITSUBA_DIR + "\build\dist" 10 | $env:PYTHONPATH=$env:PYTHONPATH + ";" +$env:MITSUBA_DIR + "\dist\python;" + $env:MITSUBA_DIR + "\build\dist\python" 11 | -------------------------------------------------------------------------------- /setpath.sh: -------------------------------------------------------------------------------- 1 | # 2 | # This script adds Mitsuba to the current path. 3 | # It works with both Bash and Zsh and assumes that Mitsuba 4 | # is compiled within the source tree or a subdirectory 5 | # named 'build'. 6 | # 7 | # NOTE: this script must be sourced and not run, i.e. 8 | # . setpath.sh for Bash 9 | # source setpath.sh for Zsh or Bash 10 | # 11 | 12 | if [[ "$#" -ge "1" ]]; then 13 | BUILD_DIR="$1" 14 | else 15 | BUILD_DIR="build" 16 | fi 17 | 18 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 19 | echo "The setpath.sh script must be sourced, not executed. In other words, run\n" 20 | echo "$ source setpath.sh\n" 21 | exit 0 22 | fi 23 | 24 | if [ "$BASH_VERSION" ]; then 25 | MITSUBA_DIR=$(dirname "$BASH_SOURCE") 26 | export MITSUBA_DIR=$(builtin cd "$MITSUBA_DIR"; builtin pwd) 27 | elif [ "$ZSH_VERSION" ]; then 28 | export MITSUBA_DIR=$(dirname "$0:A") 29 | fi 30 | 31 | export PYTHONPATH="$MITSUBA_DIR/dist/python:$MITSUBA_DIR/$BUILD_DIR/dist/python:$PYTHONPATH" 32 | export PATH="$MITSUBA_DIR/dist:$MITSUBA_DIR/$BUILD_DIR/dist:$PATH" 33 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [tool:pytest] 2 | minversion = 3.0 3 | norecursedirs = ext ext_build build build-debug CMakeFiles dist include .git 4 | python_paths = dist dist/python 5 | 6 | 7 | [pycodestyle] 8 | # E402: module level import not at top of file (needed for Mitsuba variants) 9 | # W503/W504: line break occurred before/after a binary operator 10 | ignore = E402,W503,W504 11 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (MTS_ENABLE_PYTHON) 2 | # Python bindings (need to include it first so that some required CMake 3 | # functions are defined). 4 | include("cmake/libpython.cmake") 5 | endif() 6 | 7 | # Mitsuba support libraries 8 | add_subdirectory(libcore) 9 | add_subdirectory(librender) 10 | if (MTS_ENABLE_GUI) 11 | add_subdirectory(libui) 12 | endif() 13 | 14 | # Mitsuba executables 15 | add_subdirectory(mitsuba) 16 | 17 | if (MTS_ENABLE_GUI) 18 | add_subdirectory(mtsgui) 19 | endif() 20 | 21 | # # Plugins 22 | add_subdirectory(bsdfs) 23 | add_subdirectory(emitters) 24 | add_subdirectory(films) 25 | add_subdirectory(integrators) 26 | add_subdirectory(media) 27 | add_subdirectory(phase) 28 | add_subdirectory(rfilters) 29 | add_subdirectory(samplers) 30 | add_subdirectory(sensors) 31 | add_subdirectory(shapes) 32 | add_subdirectory(spectra) 33 | add_subdirectory(textures) 34 | 35 | if (MTS_ENABLE_PYTHON) 36 | add_subdirectory(python) 37 | 38 | # Test support 39 | include("cmake/tests.cmake") 40 | endif() 41 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/__init__.py -------------------------------------------------------------------------------- /src/bsdfs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "bsdfs") 2 | 3 | add_plugin(blendbsdf blendbsdf.cpp) 4 | add_plugin(bumpmap bumpmap.cpp) 5 | add_plugin(conductor conductor.cpp) 6 | add_plugin(dielectric dielectric.cpp) 7 | add_plugin(diffuse diffuse.cpp) 8 | add_plugin(mask mask.cpp) 9 | add_plugin(measured measured.cpp) 10 | add_plugin(normalmap normalmap.cpp) 11 | add_plugin(null null.cpp) 12 | add_plugin(plastic plastic.cpp) 13 | add_plugin(roughconductor roughconductor.cpp) 14 | add_plugin(roughdielectric roughdielectric.cpp) 15 | add_plugin(roughplastic roughplastic.cpp) 16 | add_plugin(thindielectric thindielectric.cpp) 17 | add_plugin(twosided twosided.cpp) 18 | add_plugin(polarizer polarizer.cpp) 19 | add_plugin(retarder retarder.cpp) 20 | add_plugin(circular circular.cpp) 21 | add_plugin(measured_polarized measured_polarized.cpp) 22 | add_plugin(pplastic pplastic.cpp) 23 | 24 | # Register the test directory 25 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 26 | -------------------------------------------------------------------------------- /src/bsdfs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/bsdfs/__init__.py -------------------------------------------------------------------------------- /src/bsdfs/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/bsdfs/tests/__init__.py -------------------------------------------------------------------------------- /src/bsdfs/tests/test_diffuse.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | 5 | def test01_create(variant_scalar_rgb): 6 | from mitsuba.render import BSDFFlags 7 | from mitsuba.core.xml import load_string 8 | 9 | b = load_string("") 10 | assert b is not None 11 | assert b.component_count() == 1 12 | assert b.flags(0) == BSDFFlags.DiffuseReflection | BSDFFlags.FrontSide 13 | assert b.flags() == b.flags(0) 14 | 15 | 16 | def test02_eval_pdf(variant_scalar_rgb): 17 | from mitsuba.core import Frame3f 18 | from mitsuba.render import BSDFContext, BSDFFlags, SurfaceInteraction3f 19 | from mitsuba.core.xml import load_string 20 | 21 | bsdf = load_string("") 22 | 23 | si = SurfaceInteraction3f() 24 | si.p = [0, 0, 0] 25 | si.n = [0, 0, 1] 26 | si.wi = [0, 0, 1] 27 | si.sh_frame = Frame3f(si.n) 28 | 29 | ctx = BSDFContext() 30 | 31 | for i in range(20): 32 | theta = i / 19.0 * (ek.pi / 2) 33 | wo = [ek.sin(theta), 0, ek.cos(theta)] 34 | 35 | v_pdf = bsdf.pdf(ctx, si, wo=wo) 36 | v_eval = bsdf.eval(ctx, si, wo=wo)[0] 37 | assert ek.allclose(v_pdf, wo[2] / ek.pi) 38 | assert ek.allclose(v_eval, 0.5 * wo[2] / ek.pi) 39 | 40 | 41 | def test03_chi2(variant_packet_rgb): 42 | from mitsuba.python.chi2 import BSDFAdapter, ChiSquareTest, SphericalDomain 43 | 44 | sample_func, pdf_func = BSDFAdapter("diffuse", '') 45 | 46 | chi2 = ChiSquareTest( 47 | domain=SphericalDomain(), 48 | sample_func=sample_func, 49 | pdf_func=pdf_func, 50 | sample_dim=3 51 | ) 52 | 53 | assert chi2.run() 54 | -------------------------------------------------------------------------------- /src/bsdfs/tests/test_measured_polarized.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | import numpy as np 5 | 6 | from mitsuba.python.test.util import fresolver_append_path 7 | 8 | @fresolver_append_path 9 | def test01_evaluation(variant_scalar_spectral_polarized): 10 | from mitsuba.core import Vector3f, Frame3f 11 | from mitsuba.core.xml import load_dict 12 | from mitsuba.render import BSDFContext, SurfaceInteraction3f 13 | 14 | # Here we load a small example pBSDF file and evaluate the BSDF for a fixed 15 | # incident and outgoing position. Any future changes to polarization frames 16 | # or table interpolation should keep the values below unchanged. 17 | # 18 | # For convenience, we use a file where the usual resolution of parameters 19 | # (phi_d x theta_d x theta_h x wavlengths x Mueller mat. ) was significantly 20 | # downsampled from (361 x 91 x 91 x 5 x 4 x 4) to (22 x 9 x 9 x 5 x 4 x 4). 21 | 22 | bsdf = load_dict({'type': 'measured_polarized', 23 | 'filename': 'resources/data/tests/pbsdf/spectralon_lowres.pbsdf'}) 24 | 25 | phi_i = 30 * ek.pi/180 26 | theta_i = 10 * ek.pi/180 27 | wi = Vector3f([ek.sin(theta_i)*ek.cos(phi_i), 28 | ek.sin(theta_i)*ek.sin(phi_i), 29 | ek.cos(theta_i)]) 30 | 31 | ctx = BSDFContext() 32 | si = SurfaceInteraction3f() 33 | si.p = [0, 0, 0] 34 | si.wi = wi 35 | si.sh_frame = Frame3f([0, 0, 1]) 36 | si.wavelengths = [500, 500, 500, 500] 37 | 38 | phi_o = 200 * ek.pi/180 39 | theta_o = 3 * ek.pi/180 40 | wi = Vector3f([ek.sin(theta_o)*ek.cos(phi_o), 41 | ek.sin(theta_o)*ek.sin(phi_o), 42 | ek.cos(theta_o)]) 43 | 44 | value = bsdf.eval(ctx, si, wi) 45 | value = np.array(value)[0,:,:] # Extract Mueller matrix for one wavelength 46 | 47 | ref = [[ 0.17119151, -0.00223141, 0.00754681, 0.00010021], 48 | [-0.00393003, 0.00427623, -0.00117126, -0.00310079], 49 | [-0.00424358, 0.00312945, -0.01219576, 0.00086167], 50 | [ 0.00099006, -0.00345963, -0.00285343, -0.00205485]] 51 | assert ek.allclose(ref, value, rtol=1e-4) 52 | -------------------------------------------------------------------------------- /src/bsdfs/tests/test_pplastic.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import mitsuba 3 | import enoki as ek 4 | 5 | from mitsuba.python.chi2 import ChiSquareTest, BSDFAdapter, SphericalDomain 6 | 7 | def test01_chi2_smooth(variant_packet_rgb): 8 | xml = """ 9 | 10 | """ 11 | wi = ek.normalize([1.0, 1.0, 1.0]) 12 | sample_func, pdf_func = BSDFAdapter("pplastic", xml, wi=wi) 13 | 14 | chi2 = ChiSquareTest( 15 | domain=SphericalDomain(), 16 | sample_func=sample_func, 17 | pdf_func=pdf_func, 18 | sample_dim=3, 19 | res=201 20 | ) 21 | 22 | assert chi2.run() 23 | 24 | 25 | def test02_chi2_rough(variant_packet_rgb): 26 | xml = """ 27 | 28 | """ 29 | wi = ek.normalize([1.0, 1.0, 1.0]) 30 | sample_func, pdf_func = BSDFAdapter("pplastic", xml, wi=wi) 31 | 32 | chi2 = ChiSquareTest( 33 | domain=SphericalDomain(), 34 | sample_func=sample_func, 35 | pdf_func=pdf_func, 36 | sample_dim=3, 37 | ) 38 | 39 | assert chi2.run() 40 | -------------------------------------------------------------------------------- /src/bsdfs/tests/test_rough_plastic.py: -------------------------------------------------------------------------------- 1 | from mitsuba.python.chi2 import ChiSquareTest, BSDFAdapter, SphericalDomain 2 | 3 | 4 | def test01_chi2_smooth(variant_packet_rgb): 5 | xml = """ 6 | 7 | """ 8 | sample_func, pdf_func = BSDFAdapter("roughplastic", xml) 9 | 10 | chi2 = ChiSquareTest( 11 | domain=SphericalDomain(), 12 | sample_func=sample_func, 13 | pdf_func=pdf_func, 14 | sample_dim=3, 15 | res=201 16 | ) 17 | 18 | assert chi2.run() 19 | 20 | 21 | def test02_chi2_rough(variant_packet_rgb): 22 | xml = """ 23 | 24 | """ 25 | sample_func, pdf_func = BSDFAdapter("roughplastic", xml) 26 | 27 | chi2 = ChiSquareTest( 28 | domain=SphericalDomain(), 29 | sample_func=sample_func, 30 | pdf_func=pdf_func, 31 | sample_dim=3, 32 | ) 33 | 34 | assert chi2.run() 35 | -------------------------------------------------------------------------------- /src/cmake/FindSphinx.cmake: -------------------------------------------------------------------------------- 1 | find_program(SPHINX_EXECUTABLE NAMES sphinx-build 2 | HINTS 3 | $ENV{SPHINX_DIR} 4 | PATH_SUFFIXES bin 5 | DOC "Sphinx documentation generator" 6 | ) 7 | 8 | include(FindPackageHandleStandardArgs) 9 | 10 | find_package_handle_standard_args(Sphinx DEFAULT_MSG 11 | SPHINX_EXECUTABLE 12 | ) 13 | 14 | mark_as_advanced(SPHINX_EXECUTABLE) 15 | -------------------------------------------------------------------------------- /src/cmake/docstrings.cmake: -------------------------------------------------------------------------------- 1 | # Automatic generation of docstrings for the Python bindings from the C++ headers. 2 | 3 | # Compute compilation flags for 'docstrings' target, which extracts docstrings from the C++ header files 4 | if (NOT WIN32) 5 | string(REPLACE " " ";" MKDOC_CXX_FLAGS_LIST ${CMAKE_CXX_FLAGS}) 6 | get_property(MKDOC_INCLUDE_DIRECTORIES DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) 7 | get_property(MKDOC_COMPILE_DEFINITIONS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS) 8 | 9 | foreach (value ${MKDOC_INCLUDE_DIRECTORIES}) 10 | list(APPEND MKDOC_CXX_FLAGS_LIST -I${value}) 11 | endforeach() 12 | list(APPEND MKDOC_CXX_FLAGS_LIST -I${ZMQ_INCLUDE_DIR}) 13 | 14 | foreach (value ${MKDOC_COMPILE_DEFINITIONS}) 15 | list(APPEND MKDOC_CXX_FLAGS_LIST -D${value}) 16 | endforeach() 17 | 18 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 19 | execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=include 20 | ERROR_QUIET OUTPUT_VARIABLE CLANG_RESOURCE_DIR) 21 | get_filename_component(CLANG_RESOURCE_DIR ${CLANG_RESOURCE_DIR} DIRECTORY) 22 | if (CLANG_RESOURCE_DIR) 23 | list(APPEND MKDOC_CXX_FLAGS_LIST "-resource-dir=${CLANG_RESOURCE_DIR}") 24 | endif() 25 | endif() 26 | 27 | list(REMOVE_ITEM MKDOC_CXX_FLAGS_LIST "-fno-fat-lto-objects") 28 | list(REMOVE_ITEM MKDOC_CXX_FLAGS_LIST "-flto") 29 | 30 | add_custom_target(docstrings USES_TERMINAL COMMAND 31 | python3 ${PROJECT_SOURCE_DIR}/ext/pybind11/tools/mkdoc.py 32 | ${MKDOC_CXX_FLAGS_LIST} -DNANOGUI_USE_OPENGL 33 | -Wno-pragma-once-outside-header 34 | -ferror-limit=100000 35 | `find ${PROJECT_SOURCE_DIR}/include/mitsuba/core -name '*.h' ! -name fwd.h -print` 36 | `find ${PROJECT_SOURCE_DIR}/include/mitsuba/render -name '*.h' ! -name fwd.h -print` 37 | `find ${PROJECT_SOURCE_DIR}/include/mitsuba/ui -name '*.h' ! -name fwd.h -print` 38 | > ${PROJECT_SOURCE_DIR}/include/mitsuba/python/docstr.h) 39 | endif() 40 | -------------------------------------------------------------------------------- /src/cmake/tests.cmake: -------------------------------------------------------------------------------- 1 | # Test support in mitsuba using pytest and pytest-xdist 2 | 3 | # Make sure pytest and the pytest-xdist plugin are both found or produce a fatal error 4 | if (NOT MITSUBA_PYTEST_FOUND) 5 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pytest --version --noconftest OUTPUT_QUIET ERROR_QUIET 6 | RESULT_VARIABLE MITSUBA_EXEC_PYTHON_ERR_1) 7 | if (MITSUBA_EXEC_PYTHON_ERR_1) 8 | message(WARNING "Running the tests requires pytest. Please install it manually: \n$ ${PYTHON_EXECUTABLE} -m pip install pytest pytest-xdist") 9 | else() 10 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pytest --version -n 1 --noconftest OUTPUT_QUIET ERROR_QUIET 11 | RESULT_VARIABLE MITSUBA_EXEC_PYTHON_ERR_2) 12 | if (NOT WIN32 AND MITSUBA_EXEC_PYTHON_ERR_2) 13 | message(WARNING "Running the tests requires pytest-xdist. Please install it manually: \n$ ${PYTHON_EXECUTABLE} -m pip install pytest-xdist") 14 | else() 15 | set(MITSUBA_PYTEST_FOUND TRUE CACHE INTERNAL "") 16 | endif() 17 | endif() 18 | endif() 19 | 20 | include(ProcessorCount) 21 | ProcessorCount(PROC_COUNT) 22 | 23 | # Don't use more than 8 processors to run tests 24 | if (PROC_COUNT EQUAL 0) 25 | set(PROC_COUNT auto) 26 | elseif (PROC_COUNT GREATER 8) 27 | set(PROC_COUNT 8) 28 | endif() 29 | 30 | if (WIN32) 31 | # pytest-xdist does not seem to run reliably on windows 32 | set(PYTEST_XDIST "-v") 33 | else() 34 | set(PYTEST_XDIST "-n ${PROC_COUNT}") 35 | endif() 36 | 37 | if (MITSUBA_PYTEST_FOUND) 38 | # A single command to compile and run the tests 39 | add_custom_target(pytest 40 | COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH}:${CMAKE_BINARY_DIR}/dist" "PYTHONPATH=${CMAKE_BINARY_DIR}/dist/python" ${PYTHON_EXECUTABLE} -m pytest ${PYTEST_XDIST} -rws ${MITSUBA_TEST_DIRECTORIES} 41 | DEPENDS mitsuba-python python-copy dist-copy 42 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dist 43 | USES_TERMINAL 44 | ) 45 | endif() 46 | -------------------------------------------------------------------------------- /src/emitters/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "emitters") 2 | 3 | add_plugin(area area.cpp) 4 | add_plugin(point point.cpp) 5 | add_plugin(constant constant.cpp) 6 | add_plugin(envmap envmap.cpp) 7 | add_plugin(directional directional.cpp) 8 | add_plugin(spot spot.cpp) 9 | add_plugin(projector projector.cpp) 10 | 11 | # Register the test directory 12 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 13 | -------------------------------------------------------------------------------- /src/emitters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/emitters/__init__.py -------------------------------------------------------------------------------- /src/emitters/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/emitters/tests/__init__.py -------------------------------------------------------------------------------- /src/emitters/tests/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/emitters/tests/data/__init__.py -------------------------------------------------------------------------------- /src/emitters/tests/data/triangle.ply: -------------------------------------------------------------------------------- 1 | ply 2 | format ascii 1.0 3 | comment this file contains a triangle 4 | element vertex 3 5 | property float x 6 | property float y 7 | property float z 8 | element face 1 9 | property list uchar int vertex_index 10 | end_header 11 | 0 0 0 12 | 0 0 1 13 | 0 1 0 14 | 3 0 1 2 15 | -------------------------------------------------------------------------------- /src/films/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "films") 2 | 3 | add_plugin(hdrfilm hdrfilm.cpp) 4 | 5 | # Register the test directory 6 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 7 | -------------------------------------------------------------------------------- /src/integrators/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "integrators") 2 | 3 | add_plugin(depth depth.cpp) 4 | add_plugin(direct direct.cpp) 5 | add_plugin(path path.cpp) 6 | add_plugin(aov aov.cpp) 7 | add_plugin(stokes stokes.cpp) 8 | add_plugin(moment moment.cpp) 9 | add_plugin(volpath volpath.cpp) 10 | add_plugin(volpathmis volpathmis.cpp) 11 | 12 | # Register the test directory 13 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 14 | -------------------------------------------------------------------------------- /src/integrators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/integrators/__init__.py -------------------------------------------------------------------------------- /src/integrators/depth.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(mitsuba) 5 | 6 | /** 7 | * \brief Example of one an extremely simple type of integrator that is also 8 | * helpful for debugging: returns the distance from the camera to the closest 9 | * intersected object, or 0 if no intersection was found. 10 | */ 11 | template 12 | class DepthIntegrator final : public SamplingIntegrator { 13 | public: 14 | MTS_IMPORT_BASE(SamplingIntegrator) 15 | MTS_IMPORT_TYPES(Scene, Sampler, Medium) 16 | 17 | DepthIntegrator(const Properties &props) : Base(props) { } 18 | 19 | std::pair sample(const Scene *scene, 20 | Sampler * /* sampler */, 21 | const RayDifferential3f &ray, 22 | const Medium * /* medium */, 23 | Float * /* aovs */, 24 | Mask active) const override { 25 | MTS_MASKED_FUNCTION(ProfilerPhase::SamplingIntegratorSample, active); 26 | 27 | SurfaceInteraction3f si = scene->ray_intersect(ray, active); 28 | 29 | return { 30 | select(si.is_valid(), si.t, 0.f), 31 | si.is_valid() 32 | }; 33 | } 34 | 35 | MTS_DECLARE_CLASS() 36 | }; 37 | 38 | MTS_IMPLEMENT_CLASS_VARIANT(DepthIntegrator, SamplingIntegrator) 39 | MTS_EXPORT_PLUGIN(DepthIntegrator, "Depth integrator"); 40 | NAMESPACE_END(mitsuba) 41 | -------------------------------------------------------------------------------- /src/integrators/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/integrators/tests/__init__.py -------------------------------------------------------------------------------- /src/libcore/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/libcore/__init__.py -------------------------------------------------------------------------------- /src/libcore/dstream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NAMESPACE_BEGIN(mitsuba) 4 | 5 | DummyStream::DummyStream() 6 | : Stream(), m_size(0), m_pos(0), m_is_closed(false) { } 7 | 8 | void DummyStream::close() { m_is_closed = true; }; 9 | 10 | bool DummyStream::is_closed() const { return m_is_closed; }; 11 | 12 | void DummyStream::read(void *, size_t) { 13 | /// Always throws, since DummyStream is write-only. 14 | Throw("DummyStream does not support reading."); 15 | } 16 | 17 | void DummyStream::write(const void *, size_t size) { 18 | /// Does not actually write anything, only updates the stream's position and size. 19 | if (is_closed()) 20 | Throw("Attempted to write to a closed stream: %s", to_string()); 21 | 22 | m_size = std::max(m_size, m_pos + size); 23 | m_pos += size; 24 | } 25 | 26 | void DummyStream::seek(size_t pos) { 27 | /* Updates the current position in the stream. 28 | Even though the DummyStream doesn't write anywhere, position is 29 | taken into account to accurately compute the size of the stream. */ 30 | m_pos = pos; 31 | } 32 | 33 | void DummyStream::truncate(size_t size) { 34 | /* Simply sets the current size of the stream. 35 | The position is updated to min(old_position, size). */ 36 | m_size = size; // No underlying data, so there's nothing else to do. 37 | m_pos = std::min(m_pos, size); 38 | } 39 | 40 | size_t DummyStream::tell() const { return m_pos; } 41 | size_t DummyStream::size() const { return m_size; } 42 | void DummyStream::flush() { /* Nothing to do */ } 43 | bool DummyStream::can_write() const { return !is_closed(); } 44 | bool DummyStream::can_read() const { return false; } 45 | 46 | MTS_IMPLEMENT_CLASS(DummyStream, Stream) 47 | 48 | NAMESPACE_END(mitsuba) 49 | -------------------------------------------------------------------------------- /src/libcore/formatter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | NAMESPACE_BEGIN(mitsuba) 8 | 9 | DefaultFormatter::DefaultFormatter() 10 | : m_has_date(true), m_has_log_level(true), m_has_thread(true), 11 | m_has_class(true) { } 12 | 13 | std::string DefaultFormatter::format(LogLevel level, const Class *class_, 14 | const Thread *thread, const char *file, int line, 15 | const std::string &msg) { 16 | std::ostringstream oss; 17 | std::istringstream iss(msg); 18 | char buffer[128]; 19 | std::string msg_line; 20 | time_t time_ = std::time(nullptr); 21 | strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S ", std::localtime(&time_)); 22 | int line_idx = 0; 23 | 24 | while (std::getline(iss, msg_line) || line_idx == 0) { 25 | if (line_idx > 0) 26 | oss << '\n'; 27 | 28 | /* Date/Time */ 29 | if (m_has_date) 30 | oss << buffer; 31 | 32 | /* Log level */ 33 | if (m_has_log_level) { 34 | switch (level) { 35 | case Trace: oss << "TRACE "; break; 36 | case Debug: oss << "DEBUG "; break; 37 | case Info: oss << "INFO "; break; 38 | case Warn: oss << "WARN "; break; 39 | case Error: oss << "ERROR "; break; 40 | default: oss << "CUSTM "; break; 41 | } 42 | } 43 | 44 | /* Thread */ 45 | if (thread && m_has_thread) { 46 | oss << thread->name(); 47 | 48 | for (int i=0; i<(6 - (int) thread->name().size()); i++) 49 | oss << ' '; 50 | } 51 | 52 | /* Class */ 53 | if (m_has_class) { 54 | if (class_) 55 | oss << "[" << class_->name() << "] "; 56 | else if (line != -1 && file) 57 | oss << "[" << fs::path(file).filename() << ":" << line << "] "; 58 | } 59 | 60 | /* Message */ 61 | oss << msg_line; 62 | line_idx++; 63 | } 64 | 65 | return oss.str(); 66 | } 67 | 68 | MTS_IMPLEMENT_CLASS(Formatter, Object) 69 | MTS_IMPLEMENT_CLASS(DefaultFormatter, Formatter) 70 | 71 | NAMESPACE_END(mitsuba) 72 | -------------------------------------------------------------------------------- /src/libcore/fresolver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | FileResolver::FileResolver() : Object() { 8 | m_paths.push_back(fs::current_path()); 9 | } 10 | 11 | FileResolver::FileResolver(const FileResolver &fr) 12 | : Object(), m_paths(fr.m_paths) { } 13 | 14 | void FileResolver::erase(const fs::path &p) { 15 | m_paths.erase(std::remove(m_paths.begin(), m_paths.end(), p), m_paths.end()); 16 | } 17 | 18 | bool FileResolver::contains(const fs::path &p) const { 19 | return std::find(m_paths.begin(), m_paths.end(), p) != m_paths.end(); 20 | } 21 | 22 | fs::path FileResolver::resolve(const fs::path &path) const { 23 | if (!path.is_absolute()) { 24 | for (auto const &base : m_paths) { 25 | fs::path combined = base / path; 26 | if (fs::exists(combined)) 27 | return combined; 28 | } 29 | } 30 | return path; 31 | } 32 | 33 | std::string FileResolver::to_string() const { 34 | std::ostringstream oss; 35 | oss << "FileResolver[" << std::endl; 36 | for (size_t i = 0; i < m_paths.size(); ++i) { 37 | oss << " \"" << m_paths[i] << "\""; 38 | if (i + 1 < m_paths.size()) 39 | oss << ","; 40 | oss << std::endl; 41 | } 42 | oss << "]"; 43 | return oss.str(); 44 | } 45 | 46 | MTS_IMPLEMENT_CLASS(FileResolver, Object) 47 | 48 | NAMESPACE_END(mitsuba) 49 | -------------------------------------------------------------------------------- /src/libcore/jit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(mitsuba) 5 | 6 | static Jit *jit = nullptr; 7 | 8 | Jit::Jit() { } 9 | Jit *Jit::get_instance() { return jit; } 10 | 11 | void Jit::static_initialization() { 12 | #if defined(ENOKI_X86_64) 13 | jit = new Jit(); 14 | #endif 15 | } 16 | 17 | void Jit::static_shutdown() { 18 | #if defined(ENOKI_X86_64) 19 | delete jit; 20 | jit = nullptr; 21 | #endif 22 | } 23 | 24 | NAMESPACE_END(mitsuba) 25 | -------------------------------------------------------------------------------- /src/libcore/object.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | NAMESPACE_BEGIN(mitsuba) 8 | 9 | void Object::dec_ref(bool dealloc) const noexcept { 10 | --m_ref_count; 11 | if (m_ref_count == 0 && dealloc) { 12 | delete this; 13 | } else if (m_ref_count < 0) { 14 | fprintf(stderr, "Internal error: Object reference count < 0!\n"); 15 | abort(); 16 | } 17 | } 18 | 19 | std::vector> Object::expand() const { 20 | return { }; 21 | } 22 | 23 | void Object::traverse(TraversalCallback * /*callback*/) { } 24 | 25 | void Object::parameters_changed(const std::vector &/*keys*/) { } 26 | 27 | std::string Object::id() const { return std::string(); } 28 | 29 | std::string Object::to_string() const { 30 | std::ostringstream oss; 31 | oss << class_()->name() << "[" << (void *) this << "]"; 32 | return oss.str(); 33 | } 34 | 35 | Object::~Object() { } 36 | 37 | std::ostream& operator<<(std::ostream &os, const Object *object) { 38 | os << ((object != nullptr) ? object->to_string() : "nullptr"); 39 | return os; 40 | } 41 | 42 | MTS_IMPLEMENT_CLASS(Object,) 43 | NAMESPACE_END(mitsuba) 44 | -------------------------------------------------------------------------------- /src/libcore/python/appender.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Trampoline for derived types implemented in Python 5 | class PyAppender : public Appender { 6 | public: 7 | using Appender::Appender; 8 | 9 | virtual void append(LogLevel level, const std::string &text) override { 10 | PYBIND11_OVERLOAD_PURE( 11 | void, // Return value 12 | Appender, // Parent class 13 | append, // Function 14 | level, text // Arguments 15 | ); 16 | } 17 | 18 | virtual void log_progress(float progress, const std::string &name, 19 | const std::string &formatted, 20 | const std::string &eta, const void *ptr) override { 21 | PYBIND11_OVERLOAD_PURE( 22 | void, // Return value 23 | Appender, // Parent class 24 | log_progress, // Function 25 | progress, name, formatted, eta, ptr // Arguments 26 | ); 27 | } 28 | }; 29 | 30 | MTS_PY_EXPORT(Appender) { 31 | py::enum_(m, "LogLevel", D(LogLevel)) 32 | .value("Trace", Trace, D(LogLevel, Trace)) 33 | .value("Debug", Debug, D(LogLevel, Debug)) 34 | .value("Info", Info, D(LogLevel, Info)) 35 | .value("Warn", Warn, D(LogLevel, Warn)) 36 | .value("Error", Error, D(LogLevel, Error)); 37 | 38 | MTS_PY_TRAMPOLINE_CLASS(PyAppender, Appender, Object) 39 | .def(py::init<>()) 40 | .def_method(Appender, append, "level"_a, "text"_a) 41 | .def_method(Appender, log_progress, "progress"_a, "name"_a, 42 | "formatted"_a, "eta"_a, "ptr"_a = py::none()); 43 | 44 | MTS_PY_CLASS(StreamAppender, Appender) 45 | .def(py::init(), D(StreamAppender, StreamAppender)) 46 | .def_method(StreamAppender, logs_to_file) 47 | .def_method(StreamAppender, read_log); 48 | } 49 | -------------------------------------------------------------------------------- /src/libcore/python/argparser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(ArgParser) { 5 | py::class_ argp(m, "ArgParser", D(ArgParser)); 6 | py::class_ argpa(argp, "Arg", D(ArgParser, Arg)); 7 | 8 | argp.def(py::init<>()) 9 | .def("add", (const ArgParser::Arg * (ArgParser::*) (const std::string &, bool)) 10 | &ArgParser::add, "prefix"_a, "extra"_a = false, 11 | py::return_value_policy::reference_internal, 12 | D(ArgParser, add, 2)) 13 | .def("add", (const ArgParser::Arg * (ArgParser::*) (const std::vector &, bool)) 14 | &ArgParser::add, "prefixes"_a, "extra"_a = false, 15 | py::return_value_policy::reference_internal, 16 | D(ArgParser, add)) 17 | .def("parse", [](ArgParser &a, std::vector args) { 18 | std::unique_ptr args_(new const char *[args.size()]); 19 | for (size_t i = 0; i 2 | #include 3 | 4 | MTS_PY_EXPORT(atomic) { 5 | using AtomicFloat = mitsuba::AtomicFloat<>; 6 | 7 | py::class_(m, "AtomicFloat", D(AtomicFloat)) 8 | .def(py::init(), D(AtomicFloat, AtomicFloat)) 9 | .def(py::self += float(), D(AtomicFloat, operator, iadd)) 10 | .def(py::self -= float(), D(AtomicFloat, operator, isub)) 11 | .def(py::self *= float(), D(AtomicFloat, operator, imul)) 12 | .def(py::self /= float(), D(AtomicFloat, operator, idiv)) 13 | .def("__float__", [](const AtomicFloat &af) { return (float) af; }, 14 | D(AtomicFloat, operator, T0)); 15 | } 16 | -------------------------------------------------------------------------------- /src/libcore/python/bsphere_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MTS_PY_EXPORT(BoundingSphere) { 6 | MTS_PY_IMPORT_TYPES_DYNAMIC() 7 | 8 | MTS_PY_CHECK_ALIAS(BoundingSphere3f, "BoundingSphere3f") { 9 | py::class_(m, "BoundingSphere3f", D(BoundingSphere)) 10 | .def(py::init<>(), D(BoundingSphere, BoundingSphere)) 11 | .def(py::init(), D(BoundingSphere, BoundingSphere, 2)) 12 | .def(py::init()) 13 | .def("empty", &BoundingSphere3f::empty, D(BoundingSphere, empty)) 14 | .def("contains", 15 | [](const BoundingSphere3f &self, const Point3f &p, bool strict) { 16 | return strict ? self.template contains(p) 17 | : self.template contains(p); 18 | }, D(BoundingSphere, contains), "p"_a, "strict"_a = false) 19 | .def("expand", &BoundingSphere3f::expand, D(BoundingSphere, expand)) 20 | .def("ray_intersect", 21 | [](const BoundingSphere3f &self, const Ray3f &ray) { 22 | return self.ray_intersect(ray); 23 | }, D(BoundingSphere, ray_intersect), "ray"_a) 24 | .def(py::self == py::self) 25 | .def(py::self != py::self) 26 | .def_readwrite("center", &BoundingSphere3f::center) 27 | .def_readwrite("radius", &BoundingSphere3f::radius) 28 | .def_repr(BoundingSphere3f); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/libcore/python/cast.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using Caster = py::object(*)(mitsuba::Object *); 5 | 6 | static std::vector casters; 7 | 8 | py::object cast_object(Object *o) { 9 | for (auto &caster : casters) { 10 | py::object po = ((Caster) caster)(o); 11 | if (po) 12 | return po; 13 | } 14 | return py::cast(o); 15 | } 16 | 17 | MTS_PY_EXPORT(Cast) { 18 | m.attr("casters") = py::cast((void *) &casters); 19 | m.attr("cast_object") = py::cast((void *) &cast_object); 20 | } 21 | -------------------------------------------------------------------------------- /src/libcore/python/formatter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Trampoline for derived types implemented in Python 6 | class PyFormatter : public Formatter { 7 | public: 8 | using Formatter::Formatter; 9 | 10 | virtual std::string format(LogLevel level, const Class *theClass, 11 | const Thread *thread, const char *file, int line, 12 | const std::string &msg) override { 13 | PYBIND11_OVERLOAD_PURE( 14 | std::string, // Return value 15 | Formatter, // Parent class 16 | format, // Function 17 | level, theClass, thread, file, line, msg // Arguments 18 | ); 19 | } 20 | }; 21 | 22 | MTS_PY_EXPORT(Formatter) { 23 | MTS_PY_TRAMPOLINE_CLASS(PyFormatter, Formatter, Object) 24 | .def(py::init<>()) 25 | .def_method(Formatter, format, "level"_a, "theClass"_a, 26 | "thread"_a, "file"_a, "line"_a, 27 | "msg"_a); 28 | 29 | MTS_PY_CLASS(DefaultFormatter, Formatter) 30 | .def(py::init<>()) 31 | .def_method(DefaultFormatter, has_date) 32 | .def_method(DefaultFormatter, set_has_date) 33 | .def_method(DefaultFormatter, has_thread) 34 | .def_method(DefaultFormatter, set_has_thread) 35 | .def_method(DefaultFormatter, has_log_level) 36 | .def_method(DefaultFormatter, set_has_log_level) 37 | .def_method(DefaultFormatter, has_class) 38 | .def_method(DefaultFormatter, set_has_class); 39 | } 40 | -------------------------------------------------------------------------------- /src/libcore/python/frame_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(Frame) { 5 | MTS_PY_IMPORT_TYPES_DYNAMIC() 6 | MTS_PY_CHECK_ALIAS(Frame3f, "Frame3f") { 7 | auto f = py::class_(m, "Frame3f", D(Frame)) 8 | .def(py::init<>(), D(Frame, Frame)) 9 | .def(py::init(), "Copy constructor") 10 | .def(py::init(), D(Frame, Frame, 3)) 11 | .def(py::init(), D(Frame, Frame, 4)) 12 | .def(py::self == py::self, D(Frame, operator_eq)) 13 | .def(py::self != py::self, D(Frame, operator_ne)) 14 | .def("to_local", &Frame3f::to_local, "v"_a, D(Frame, to_local)) 15 | .def("to_world", &Frame3f::to_world, "v"_a, D(Frame, to_world)) 16 | .def_static("cos_theta", &Frame3f::cos_theta, "v"_a, D(Frame, cos_theta)) 17 | .def_static("cos_theta_2", &Frame3f::cos_theta_2, "v"_a, D(Frame, cos_theta_2)) 18 | .def_static("sin_theta", &Frame3f::sin_theta, "v"_a, D(Frame, sin_theta)) 19 | .def_static("sin_theta_2", &Frame3f::sin_theta_2, "v"_a, D(Frame, sin_theta_2)) 20 | .def_static("tan_theta", &Frame3f::tan_theta, "v"_a, D(Frame, tan_theta)) 21 | .def_static("tan_theta_2", &Frame3f::tan_theta_2, "v"_a, D(Frame, tan_theta_2)) 22 | .def_static("sin_phi", &Frame3f::sin_phi, "v"_a, D(Frame, sin_phi)) 23 | .def_static("sin_phi_2", &Frame3f::sin_phi_2, "v"_a, D(Frame, sin_phi_2)) 24 | .def_static("cos_phi", &Frame3f::cos_phi, "v"_a, D(Frame, cos_phi)) 25 | .def_static("cos_phi_2", &Frame3f::cos_phi_2, "v"_a, D(Frame, cos_phi_2)) 26 | .def_static("sincos_phi", &Frame3f::sincos_phi, "v"_a, D(Frame, sincos_phi)) 27 | .def_static("sincos_phi_2", &Frame3f::sincos_phi_2, "v"_a, D(Frame, sincos_phi_2)) 28 | 29 | .def_field(Frame3f, s) 30 | .def_field(Frame3f, t) 31 | .def_field(Frame3f, n) 32 | .def_repr(Frame3f); 33 | bind_slicing_operators(f); 34 | } 35 | } -------------------------------------------------------------------------------- /src/libcore/python/fresolver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(FileResolver) { 5 | MTS_PY_CLASS(FileResolver, Object) 6 | .def(py::init<>(), D(FileResolver, FileResolver)) 7 | .def(py::init(), D(FileResolver, FileResolver, 2)) 8 | .def("__len__", &FileResolver::size, D(FileResolver, size)) 9 | .def("__iter__", [](const FileResolver &fr) { return py::make_iterator(fr.begin(), fr.end()); }, 10 | py::keep_alive<0, 1>()) 11 | .def("__delitem__", [](FileResolver &fr, size_t i) { 12 | if (i >= fr.size()) 13 | throw py::index_error(); 14 | fr.erase(fr.begin() + i); 15 | }) 16 | .def("__getitem__", [](const FileResolver &fr, size_t i) -> fs::path { 17 | if (i >= fr.size()) 18 | throw pybind11::index_error(); 19 | return fr[i]; 20 | }) 21 | .def("__setitem__", [](FileResolver &fr, size_t i, const fs::path &value) { 22 | if (i >= fr.size()) 23 | throw pybind11::index_error(); 24 | fr[i] = value; 25 | }) 26 | .def_method(FileResolver, resolve) 27 | .def_method(FileResolver, clear) 28 | .def_method(FileResolver, prepend) 29 | .def_method(FileResolver, append); 30 | } 31 | -------------------------------------------------------------------------------- /src/libcore/python/logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /// Submit a log message to the Mitusba logging system and tag it with the Python caller 7 | static void PyLog(LogLevel level, const std::string &msg) { 8 | PyFrameObject *frame = PyThreadState_Get()->frame; 9 | 10 | std::string name = 11 | py::cast(py::handle(frame->f_code->co_name)); 12 | std::string filename = 13 | py::cast(py::handle(frame->f_code->co_filename)); 14 | std::string fmt = "%s: %s"; 15 | int lineno = PyFrame_GetLineNumber(frame); 16 | 17 | if (!name.empty() && name[0] != '<') 18 | fmt.insert(2, "()"); 19 | 20 | Thread::thread()->logger()->log( 21 | level, nullptr /* class_ */, 22 | filename.c_str(), lineno, 23 | tfm::format(fmt.c_str(), name.c_str(), msg.c_str())); 24 | } 25 | 26 | MTS_PY_EXPORT(Logger) { 27 | MTS_PY_CLASS(Logger, Object) 28 | .def(py::init(), D(Logger, Logger)) 29 | .def_method(Logger, log_progress, "progress"_a, "name"_a, 30 | "formatted"_a, "eta"_a, "ptr"_a = py::none()) 31 | .def_method(Logger, set_log_level) 32 | .def_method(Logger, log_level) 33 | .def_method(Logger, set_error_level) 34 | .def_method(Logger, error_level) 35 | .def_method(Logger, add_appender, py::keep_alive<1, 2>()) 36 | .def_method(Logger, remove_appender) 37 | .def_method(Logger, clear_appenders) 38 | .def_method(Logger, appender_count) 39 | .def("appender", (Appender * (Logger::*)(size_t)) &Logger::appender, D(Logger, appender)) 40 | .def("formatter", (Formatter * (Logger::*)()) &Logger::formatter, D(Logger, formatter)) 41 | .def_method(Logger, set_formatter, py::keep_alive<1, 2>()) 42 | .def_method(Logger, read_log); 43 | 44 | m.def("Log", &PyLog, "level"_a, "msg"_a); 45 | } 46 | -------------------------------------------------------------------------------- /src/libcore/python/mmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | MTS_PY_EXPORT(MemoryMappedFile) { 7 | MTS_PY_CLASS(MemoryMappedFile, Object, py::buffer_protocol()) 8 | .def(py::init(), 9 | D(MemoryMappedFile, MemoryMappedFile), "filename"_a, "size"_a) 10 | .def(py::init(), 11 | D(MemoryMappedFile, MemoryMappedFile, 2), "filename"_a, "write"_a = false) 12 | .def(py::init([](const mitsuba::filesystem::path &p, py::array array) { 13 | size_t size = array.size() * array.itemsize(); 14 | auto m = new MemoryMappedFile(p, size); 15 | memcpy(m->data(), array.data(), size); 16 | return m; 17 | }), "filename"_a, "array"_a) 18 | .def("size", &MemoryMappedFile::size, D(MemoryMappedFile, size)) 19 | .def("data", py::overload_cast<>(&MemoryMappedFile::data, py::const_), D(MemoryMappedFile, data)) 20 | .def("resize", &MemoryMappedFile::resize, D(MemoryMappedFile, resize)) 21 | .def("filename", &MemoryMappedFile::filename, D(MemoryMappedFile, filename)) 22 | .def("can_write", &MemoryMappedFile::can_write, D(MemoryMappedFile, can_write)) 23 | .def_static("create_temporary", &MemoryMappedFile::create_temporary, D(MemoryMappedFile, create_temporary)) 24 | .def_buffer([](MemoryMappedFile &m) -> py::buffer_info { 25 | return py::buffer_info( 26 | m.data(), 27 | sizeof(uint8_t), 28 | py::format_descriptor::format(), 29 | 1, 30 | { (size_t) m.size() }, 31 | { sizeof(uint8_t) } 32 | ); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/libcore/python/qmc_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MTS_PY_EXPORT(qmc) { 6 | MTS_PY_IMPORT_TYPES() 7 | 8 | py::class_>(m, "RadicalInverse", D(RadicalInverse), 9 | py::module_local()) 10 | .def(py::init(), "max_base"_a = 8161, "scramble"_a = -1) 11 | .def("base", &RadicalInverse::base, D(RadicalInverse, base)) 12 | .def("bases", &RadicalInverse::bases, D(RadicalInverse, bases)) 13 | .def("scramble", &RadicalInverse::scramble, D(RadicalInverse, scramble)) 14 | .def("eval", vectorize(&RadicalInverse::eval), "base_index"_a, "index"_a, 15 | D(RadicalInverse, eval)) 16 | .def("eval_scrambled", vectorize(&RadicalInverse::eval_scrambled), "base_index"_a, 17 | "index"_a, D(RadicalInverse, eval_scrambled)) 18 | .def("permutation", 19 | [](py::object self, uint32_t index) { 20 | const RadicalInverse &s = py::cast(self); 21 | return py::array_t(s.base(index), s.permutation(index), self); 22 | }, 23 | D(RadicalInverse, permutation)) 24 | .def("inverse_permutation", &RadicalInverse::inverse_permutation, 25 | D(RadicalInverse, inverse_permutation)); 26 | 27 | m.def("radical_inverse_2", vectorize(radical_inverse_2), 28 | "index"_a, "scramble"_a, D(radical_inverse_2)); 29 | 30 | m.def("sobol_2", vectorize(sobol_2), 31 | "index"_a, "scramble"_a, D(sobol_2)); 32 | } 33 | -------------------------------------------------------------------------------- /src/libcore/python/quad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(quad) { 5 | py::module quad = m.def_submodule("quad", "Functions for numerical quadrature"); 6 | using FloatX = DynamicArray>; 7 | 8 | quad.def("gauss_legendre", &quad::gauss_legendre, "n"_a, D(quad, gauss_legendre)); 9 | quad.def("gauss_lobatto", &quad::gauss_lobatto, "n"_a, D(quad, gauss_lobatto)); 10 | quad.def("composite_simpson", &quad::composite_simpson, "n"_a, 11 | D(quad, composite_simpson)); 12 | quad.def("composite_simpson_38", &quad::composite_simpson_38, "n"_a, 13 | D(quad, composite_simpson_38)); 14 | } 15 | -------------------------------------------------------------------------------- /src/libcore/python/random_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(sample_tea) { 5 | MTS_PY_IMPORT_TYPES() 6 | m.def("sample_tea_32", 7 | vectorize(sample_tea_32), 8 | "v0"_a, "v1"_a, "rounds"_a = 4, D(sample_tea_32)); 9 | 10 | m.def("sample_tea_float32", 11 | vectorize(sample_tea_float32), 12 | "v0"_a, "v1"_a, "rounds"_a = 4, D(sample_tea_float32)); 13 | 14 | m.def("sample_tea_float64", 15 | vectorize(sample_tea_float64), 16 | "v0"_a, "v1"_a, "rounds"_a = 4, D(sample_tea_float64)); 17 | 18 | m.attr("sample_tea_float") = m.attr( 19 | sizeof(Float) != sizeof(Float64) ? "sample_tea_float32" : "sample_tea_float64"); 20 | 21 | m.def("permute", 22 | vectorize(permute), 23 | "value"_a, "sample_count"_a, "seed"_a, "rounds"_a = 4, D(permute)); 24 | 25 | m.def("permute_kensler", 26 | vectorize(permute_kensler), 27 | "i"_a, "l"_a, "p"_a, "active"_a = true, D(permute_kensler)); 28 | } -------------------------------------------------------------------------------- /src/libcore/python/ray_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(Ray) { 5 | MTS_PY_IMPORT_TYPES_DYNAMIC() 6 | 7 | auto ray = py::class_(m, "Ray3f", D(Ray)) 8 | .def(py::init<>(), "Create an unitialized ray") 9 | .def(py::init(), "Copy constructor", "other"_a) 10 | .def(py::init(), 11 | D(Ray, Ray, 5), "o"_a, "d"_a, "time"_a, "wavelengths"_a) 12 | .def(py::init(), 13 | D(Ray, Ray, 6), "o"_a, "d"_a, "mint"_a, "maxt"_a, "time"_a, "wavelengths"_a) 14 | .def(py::init(), 15 | D(Ray, Ray, 7), "other"_a, "mint"_a, "maxt"_a) 16 | .def("update", &Ray3f::update, D(Ray, update)) 17 | .def("__call__", &Ray3f::operator(), D(Ray, operator, call), "t"_a) 18 | .def_field(Ray3f, o, D(Ray, o)) 19 | .def_field(Ray3f, d, D(Ray, d)) 20 | .def_field(Ray3f, d_rcp, D(Ray, d_rcp)) 21 | .def_field(Ray3f, mint, D(Ray, mint)) 22 | .def_field(Ray3f, maxt, D(Ray, maxt)) 23 | .def_field(Ray3f, time, D(Ray, time)) 24 | .def_field(Ray3f, wavelengths, D(Ray, wavelengths)) 25 | .def_repr(Ray3f); 26 | 27 | using ScalarSpectrum = scalar_spectrum_t; 28 | bind_slicing_operators>(ray); 29 | 30 | py::class_(m, "RayDifferential3f", D(RayDifferential)) 31 | .def(py::init(), "ray"_a) 32 | .def(py::init(), 33 | "Initialize without differentials.", 34 | "o"_a, "d"_a, "time"_a, "wavelengths"_a) 35 | .def("scale_differential", &RayDifferential3f::scale_differential, 36 | "amount"_a, D(RayDifferential, scale_differential)) 37 | .def_field(RayDifferential3f, o_x, D(RayDifferential, o_x)) 38 | .def_field(RayDifferential3f, o_y, D(RayDifferential, o_y)) 39 | .def_field(RayDifferential3f, d_x, D(RayDifferential, d_x)) 40 | .def_field(RayDifferential3f, d_y, D(RayDifferential, d_y)) 41 | .def_field(RayDifferential3f, has_differentials, D(RayDifferential, has_differentials)); 42 | } 43 | -------------------------------------------------------------------------------- /src/libcore/python/rfilter_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(rfilter) { 5 | MTS_PY_IMPORT_TYPES(ReconstructionFilter) 6 | 7 | MTS_PY_CLASS(ReconstructionFilter, Object) 8 | .def_method(ReconstructionFilter, border_size) 9 | .def_method(ReconstructionFilter, radius) 10 | .def("eval", 11 | vectorize(&ReconstructionFilter::eval), 12 | D(ReconstructionFilter, eval), "x"_a, "active"_a = true) 13 | .def("eval_discretized", 14 | vectorize(&ReconstructionFilter::eval_discretized), 15 | D(ReconstructionFilter, eval_discretized), "x"_a, "active"_a = true) 16 | ; 17 | } 18 | -------------------------------------------------------------------------------- /src/libcore/python/util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(util) { 5 | auto util = m.def_submodule("util", "Miscellaneous utility routines"); 6 | 7 | util.def_method(util, core_count) 8 | .def_method(util, time_string, "time"_a, "precise"_a = false) 9 | .def_method(util, mem_string, "size"_a, "precise"_a = false) 10 | .def_method(util, trap_debugger); 11 | } 12 | -------------------------------------------------------------------------------- /src/libcore/python/vector_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MTS_PY_EXPORT(vector) { 6 | MTS_PY_IMPORT_TYPES() 7 | m.def("coordinate_system", 8 | vectorize(&coordinate_system>), 9 | "n"_a, D(coordinate_system)); 10 | } 11 | -------------------------------------------------------------------------------- /src/libcore/rfilter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(mitsuba) 5 | 6 | MTS_VARIANT ReconstructionFilter::ReconstructionFilter(const Properties &/*props*/) { } 7 | MTS_VARIANT ReconstructionFilter::~ReconstructionFilter() { } 8 | 9 | MTS_VARIANT void ReconstructionFilter::init_discretization() { 10 | Assert(m_radius > 0); 11 | m_values.resize(MTS_FILTER_RESOLUTION + 1); 12 | 13 | // Evaluate and store the filter values 14 | for (size_t i = 0; i < MTS_FILTER_RESOLUTION; ++i) 15 | m_values[i] = scalar_cast(hmax(eval((m_radius * i) / MTS_FILTER_RESOLUTION))); 16 | 17 | m_values[MTS_FILTER_RESOLUTION] = 0; 18 | m_scale_factor = MTS_FILTER_RESOLUTION / m_radius; 19 | m_border_size = (int) std::ceil(m_radius - .5f - 2.f * math::RayEpsilon); 20 | } 21 | 22 | std::ostream &operator<<(std::ostream &os, const FilterBoundaryCondition &value) { 23 | switch (value) { 24 | case FilterBoundaryCondition::Clamp: os << "clamp"; break; 25 | case FilterBoundaryCondition::Repeat: os << "repeat"; break; 26 | case FilterBoundaryCondition::Mirror: os << "mirror"; break; 27 | case FilterBoundaryCondition::Zero: os << "zero"; break; 28 | case FilterBoundaryCondition::One: os << "one"; break; 29 | default: os << "invalid"; break; 30 | } 31 | return os; 32 | } 33 | 34 | MTS_IMPLEMENT_CLASS_VARIANT(ReconstructionFilter, Object, "rfilter") 35 | MTS_INSTANTIATE_CLASS(ReconstructionFilter) 36 | NAMESPACE_END(mitsuba) 37 | -------------------------------------------------------------------------------- /src/libcore/string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(mitsuba) 5 | NAMESPACE_BEGIN(string) 6 | 7 | std::vector tokenize(const std::string &string, 8 | const std::string &delim, 9 | bool include_empty) { 10 | std::string::size_type last_pos = 0, pos = string.find_first_of(delim, last_pos); 11 | std::vector tokens; 12 | 13 | while (last_pos != std::string::npos) { 14 | std::string substr = string.substr(last_pos, pos - last_pos); 15 | if (!substr.empty() || include_empty) 16 | tokens.push_back(std::move(substr)); 17 | last_pos = pos; 18 | if (last_pos != std::string::npos) { 19 | last_pos += 1; 20 | pos = string.find_first_of(delim, last_pos); 21 | } 22 | } 23 | 24 | return tokens; 25 | } 26 | 27 | std::string indent(const std::string &string, size_t amount) { 28 | std::string result; 29 | result.reserve(string.size()); 30 | for (size_t i = 0; i < string.length(); ++i) { 31 | char ch = string[i]; 32 | result += ch; 33 | if (ch == '\n') { 34 | for (size_t j = 0; j < amount; ++j) 35 | result += ' '; 36 | } 37 | } 38 | return result; 39 | } 40 | 41 | std::string indent(const Object *value, size_t amount) { 42 | std::string string = value == nullptr ? std::string("nullptr") 43 | : value->to_string(); 44 | return indent(string, amount); 45 | } 46 | 47 | std::string trim(const std::string &s, const std::string &whitespace) { 48 | auto it1 = s.find_first_not_of(whitespace); 49 | if (it1 == std::string::npos) 50 | return ""; 51 | auto it2 = s.find_last_not_of(whitespace); 52 | return s.substr(it1, it2 - it1 + 1); 53 | } 54 | 55 | bool contains(const std::vector &keys, const std::string &key) { 56 | for (auto& k: keys) 57 | if (k == key) 58 | return true; 59 | return false; 60 | } 61 | 62 | NAMESPACE_END(string) 63 | NAMESPACE_END(mitsuba) 64 | -------------------------------------------------------------------------------- /src/libcore/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/libcore/tests/__init__.py -------------------------------------------------------------------------------- /src/libcore/tests/test_argparser.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import mitsuba 3 | 4 | 5 | def test01_short_args(variant_scalar_rgb): 6 | from mitsuba.core import ArgParser 7 | ap = ArgParser() 8 | a = ap.add("-a") 9 | b = ap.add("-b") 10 | c = ap.add("-c") 11 | d = ap.add("-d") 12 | ap.parse(['mitsuba', '-aba', '-d']) 13 | 14 | assert bool(a) 15 | assert a.count() == 2 16 | assert bool(b) 17 | assert b.count() == 1 18 | assert not bool(c) 19 | assert c.count() == 0 20 | assert bool(d) 21 | assert d.count() == 1 22 | assert ap.executable_name() == "mitsuba" 23 | 24 | 25 | def test02_parameter_value(variant_scalar_rgb): 26 | from mitsuba.core import ArgParser 27 | ap = ArgParser() 28 | a = ap.add("-a", True) 29 | ap.parse(['mitsuba', '-a', 'abc', '-axyz']) 30 | assert bool(a) 31 | assert a.count() == 2 32 | assert a.as_string() == 'abc' 33 | with pytest.raises(RuntimeError): 34 | a.as_int() 35 | assert a.next().as_string() == 'xyz' 36 | 37 | 38 | def test03_parameter_missing(variant_scalar_rgb): 39 | from mitsuba.core import ArgParser 40 | ap = ArgParser() 41 | ap.add("-a", True) 42 | with pytest.raises(RuntimeError): 43 | ap.parse(['mitsuba', '-a']) 44 | 45 | 46 | def test04_parameter_float_and_extra_args(variant_scalar_rgb): 47 | from mitsuba.core import ArgParser 48 | ap = ArgParser() 49 | f = ap.add("-f", True) 50 | other = ap.add("", True) 51 | ap.parse(['mitsuba', 'other', '-f0.25', 'arguments']) 52 | assert bool(f) 53 | assert f.as_float() == 0.25 54 | assert bool(other) 55 | assert other.count() == 2 56 | 57 | 58 | def test05_long_parameters_failure(variant_scalar_rgb): 59 | from mitsuba.core import ArgParser 60 | ap = ArgParser() 61 | i = ap.add("--int", True) 62 | with pytest.raises(RuntimeError): 63 | ap.parse(['mitsuba', '--int1']) 64 | with pytest.raises(RuntimeError): 65 | ap.parse(['mitsuba', '--int']) 66 | ap.parse(['mitsuba', '--int', '34']) 67 | assert i.as_int() == 34 68 | -------------------------------------------------------------------------------- /src/libcore/tests/test_atomic.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | 3 | import pytest 4 | import mitsuba 5 | 6 | 7 | def test01_add(variant_scalar_rgb): 8 | from mitsuba.core import AtomicFloat 9 | 10 | f = AtomicFloat(5) 11 | threads = [] 12 | 13 | def increment(f): 14 | for i in range(10): 15 | f += 1 16 | 17 | for j in range(0, 10): 18 | threads.append(Thread(target=increment, args=(f,))) 19 | for t in threads: 20 | t.start() 21 | for t in threads: 22 | t.join() 23 | assert float(f) == 105 24 | -------------------------------------------------------------------------------- /src/libcore/tests/test_bsphere.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import mitsuba 3 | 4 | 5 | def test01_basics(variant_scalar_rgb): 6 | from mitsuba.core import BoundingSphere3f as BSphere 7 | 8 | bsphere1 = BSphere() 9 | bsphere2 = BSphere([0, 1, 2], 1) 10 | 11 | assert str(bsphere1) == "BoundingSphere3f[empty]" 12 | assert str(bsphere2) == "BoundingSphere3f[\n center = [0, 1, 2],\n radius = 1\n]" 13 | assert bsphere1.radius == 0 14 | assert (bsphere1.center == [0, 0, 0]) 15 | assert bsphere2.radius == 1 16 | assert (bsphere2.center == [0, 1, 2]) 17 | assert bsphere1 != bsphere2 18 | assert bsphere2 == bsphere2 19 | assert bsphere1.empty() 20 | assert not bsphere2.empty() 21 | bsphere1.expand([0, 1, 0]) 22 | assert not bsphere1.empty() 23 | assert bsphere1.contains([0, 0, 1]) 24 | assert not bsphere1.contains([0, 0, 1], strict=True) 25 | -------------------------------------------------------------------------------- /src/libcore/tests/test_logger.py: -------------------------------------------------------------------------------- 1 | import enoki as ek 2 | import pytest 3 | import mitsuba 4 | 5 | 6 | def test01_custom(variant_scalar_rgb): 7 | from mitsuba.core import Thread, Appender, Formatter, Log, LogLevel 8 | 9 | # Install a custom formatter and appender and process a log message 10 | messages = [] 11 | 12 | logger = Thread.thread().logger() 13 | formatter = logger.formatter() 14 | appenders = [] 15 | while logger.appender_count() > 0: 16 | app = logger.appender(0) 17 | appenders.append(app) 18 | logger.remove_appender(app) 19 | 20 | try: 21 | class MyFormatter(Formatter): 22 | def format(self, level, theClass, thread, filename, line, msg): 23 | return "%i: class=%s, thread=%s, text=%s, filename=%s, ' \ 24 | 'line=%i" % (level, str(theClass), thread.name(), msg, 25 | filename, line) 26 | 27 | class MyAppender(Appender): 28 | def append(self, level, text): 29 | messages.append(text) 30 | 31 | logger.set_formatter(MyFormatter()) 32 | logger.add_appender(MyAppender()) 33 | 34 | Log(LogLevel.Info, "This is a test message") 35 | assert len(messages) == 1 36 | assert messages[0].startswith( 37 | '200: class=None, thread=main, text=test01_custom(): This is a' 38 | ' test message, filename=') 39 | finally: 40 | logger.clear_appenders() 41 | for app in appenders: 42 | logger.add_appender(app) 43 | logger.set_formatter(formatter) 44 | -------------------------------------------------------------------------------- /src/libcore/tests/test_mmap.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | 4 | import mitsuba 5 | mitsuba.set_variant('scalar_rgb') 6 | from mitsuba.core import MemoryMappedFile 7 | 8 | def test01_open_read_only(tmpdir): 9 | tmp_file = os.path.join(str(tmpdir), "mmap_test") 10 | with open(tmp_file, "w") as f: 11 | f.write('hello!') 12 | mmap = MemoryMappedFile(tmp_file) 13 | assert mmap.size() == 6 14 | assert not mmap.can_write() 15 | array_view = np.array(mmap, copy=False) 16 | assert np.all(array_view == np.array('hello!', 'c').view(np.uint8)) 17 | del array_view 18 | del mmap 19 | os.remove(tmp_file) 20 | 21 | 22 | def test02_open_read_write(tmpdir): 23 | tmp_file = os.path.join(str(tmpdir), "mmap_test") 24 | with open(tmp_file, "w") as f: 25 | f.write('hello!') 26 | mmap = MemoryMappedFile(tmp_file, write=True) 27 | assert mmap.size() == 6 28 | assert mmap.can_write() 29 | array_view = np.array(mmap, copy=False) 30 | array_view[1] = ord('a') 31 | with open(tmp_file, "r") as f: 32 | assert f.readline() == 'hallo!' 33 | del array_view 34 | del mmap 35 | os.remove(tmp_file) 36 | 37 | 38 | def test03_create_resize(tmpdir): 39 | tmp_file = os.path.join(str(tmpdir), "mmap_test") 40 | mmap = MemoryMappedFile(tmp_file, 8192) 41 | assert mmap.size() == 8192 42 | assert mmap.can_write() 43 | array_view = np.array(mmap, copy=False).view(np.uint32) 44 | array_view[:] = np.arange(2048, dtype=np.uint32) 45 | del array_view 46 | del mmap 47 | mmap = MemoryMappedFile(tmp_file, write=True) 48 | array_view = np.array(mmap, copy=False).view(np.uint32) 49 | assert np.all(array_view == np.arange(2048, dtype=np.uint32)) 50 | del array_view 51 | mmap.resize(4096) 52 | assert mmap.size() == 4096 53 | assert mmap.can_write() 54 | array_view = np.array(mmap, copy=False).view(np.uint32) 55 | assert np.all(array_view[:] == np.arange(1024, dtype=np.uint32)) 56 | del array_view 57 | assert os.path.getsize(tmp_file) == 4096 58 | del mmap 59 | os.remove(tmp_file) 60 | 61 | 62 | def test04_create_temp(): 63 | mmap = MemoryMappedFile.create_temporary(123) 64 | fname = str(mmap.filename()) 65 | assert os.path.exists(fname) 66 | assert mmap.size() == 123 67 | assert mmap.can_write() 68 | del mmap 69 | assert not os.path.exists(fname) 70 | -------------------------------------------------------------------------------- /src/libcore/tests/test_python.py: -------------------------------------------------------------------------------- 1 | import enoki as ek 2 | import pytest 3 | import mitsuba 4 | 5 | def test01_set_variants(): 6 | for v in mitsuba.variants(): 7 | mitsuba.set_variant(v) -------------------------------------------------------------------------------- /src/libcore/tests/test_quad.py: -------------------------------------------------------------------------------- 1 | import enoki as ek 2 | from enoki.dynamic import Float32 as Float 3 | import pytest 4 | import mitsuba 5 | 6 | 7 | def test01_gauss_lobatto(variant_scalar_rgb): 8 | from mitsuba.core.quad import gauss_lobatto 9 | 10 | assert ek.allclose(gauss_lobatto(2), [[-1, 1], [1.0, 1.0]]) 11 | assert ek.allclose(gauss_lobatto(3), [[-1, 0, 1], [1.0/3.0, 4.0/3.0, 1.0/3.0]]) 12 | assert ek.allclose(gauss_lobatto(4), [[-1, -ek.sqrt(1.0/5.0), ek.sqrt(1.0/5.0), 1], [1.0/6.0, 5.0/6.0, 5.0/6.0, 1.0/6.0]]) 13 | assert ek.allclose(gauss_lobatto(5), [[-1, -ek.sqrt(3.0/7.0), 0, ek.sqrt(3.0/7.0), 1], [1.0/10.0, 49.0/90.0, 32.0/45.0, 49.0/90.0, 1.0/10.0]]) 14 | 15 | 16 | def test02_gauss_legendre(variant_scalar_rgb): 17 | from mitsuba.core.quad import gauss_legendre 18 | 19 | assert ek.allclose(gauss_legendre(1), [[0], [2]]) 20 | assert ek.allclose(gauss_legendre(2), [[-ek.sqrt(1.0/3.0), ek.sqrt(1.0/3.0)], [1, 1]]) 21 | assert ek.allclose(gauss_legendre(3), [[-ek.sqrt(3.0/5.0), 0, ek.sqrt(3.0/5.0)], [5.0/9.0, 8.0/9.0, 5.0/9.0]]) 22 | assert ek.allclose(gauss_legendre(4), [[-0.861136, -0.339981, 0.339981, 0.861136, ], [0.347855, 0.652145, 0.652145, 0.347855]]) 23 | 24 | 25 | def test03_composite_simpson(variant_scalar_rgb): 26 | from mitsuba.core.quad import composite_simpson 27 | 28 | assert ek.allclose(composite_simpson(3), [ek.linspace(Float, -1, 1, 3), [1.0/3.0, 4.0/3.0, 1.0/3.0]]) 29 | assert ek.allclose(composite_simpson(5), [ek.linspace(Float, -1, 1, 5), [.5/3.0, 2/3.0, 1/3.0, 2/3.0, .5/3.0]]) 30 | 31 | 32 | def test04_composite_simpson_38(variant_scalar_rgb): 33 | from mitsuba.core.quad import composite_simpson_38 34 | 35 | assert ek.allclose(composite_simpson_38(4), [ek.linspace(Float, -1, 1, 4), [0.25, 0.75, 0.75, 0.25]]) 36 | assert ek.allclose(composite_simpson_38(7), [ek.linspace(Float, -1, 1, 7), [0.125, 0.375, 0.375, 0.25 , 0.375, 0.375, 0.125]], atol=1e-6) 37 | -------------------------------------------------------------------------------- /src/libcore/tests/test_util.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | import mitsuba 4 | 5 | 6 | def test01_time_string(variant_scalar_rgb): 7 | from mitsuba.core.util import time_string 8 | 9 | assert time_string(1, precise=True) == '1ms' 10 | assert time_string(2010, precise=True) == '2.01s' 11 | assert time_string(2 * 1000 * 60, precise=True) == '2m' 12 | assert time_string(2 * 1000 * 60 * 60, precise=True) == '2h' 13 | assert time_string(2 * 1000 * 60 * 60 * 24, precise=True) == '2d' 14 | assert time_string(2 * 1000 * 60 * 60 * 24 * 7, precise=True) == '2w' 15 | assert time_string(2 * 1000 * 60 * 60 * 24 * 7 * 52.1429, 16 | precise=True) == '2y' 17 | 18 | 19 | def test01_mem_string(variant_scalar_rgb): 20 | from mitsuba.core.util import mem_string 21 | 22 | assert mem_string(2, precise=True) == '2 B' 23 | assert mem_string(2 * 1024, precise=True) == '2 KiB' 24 | assert mem_string(2 * 1024 ** 2, precise=True) == '2 MiB' 25 | assert mem_string(2 * 1024 ** 3, precise=True) == '2 GiB' 26 | if sys.maxsize > 4*1024**3: 27 | assert mem_string(2 * 1024 ** 4, precise=True) == '2 TiB' 28 | assert mem_string(2 * 1024 ** 5, precise=True) == '2 PiB' 29 | assert mem_string(2 * 1024 ** 6, precise=True) == '2 EiB' 30 | -------------------------------------------------------------------------------- /src/libcore/tests/test_vector.py: -------------------------------------------------------------------------------- 1 | import enoki as ek 2 | from enoki.dynamic import Float32 as Float 3 | import pytest 4 | import mitsuba 5 | 6 | 7 | def test01_coordinate_system(variant_scalar_rgb): 8 | from mitsuba.core import coordinate_system, warp 9 | 10 | def branchless_onb(n): 11 | """ 12 | Building an Orthonormal Basis, Revisited 13 | Tom Duff, James Burgess, Per Christensen, Christophe Hery, 14 | Andrew Kensler, Max Liani, and Ryusuke Villemin 15 | """ 16 | sign = ek.copysign(1.0, n[2]) 17 | a = -1.0 / (sign + n[2]) 18 | b = n[0] * n[1] * a 19 | return ( 20 | [1.0 + sign * n[0] * n[0] * a, sign * b, -sign * n[0]], 21 | [b, sign + n[1] * n[1] * a, -n[1]] 22 | ) 23 | 24 | a = [0.70710678, -0. , -0.70710678] 25 | b = [-0., 1., 0.] 26 | assert ek.allclose( 27 | branchless_onb([ek.sqrt(0.5), 0, ek.sqrt(0.5)]), (a, b), atol=1e-6) 28 | assert ek.allclose( 29 | coordinate_system([ek.sqrt(0.5), 0, ek.sqrt(0.5)]), (a, b), atol=1e-6) 30 | 31 | for u in ek.linspace(Float, 0, 1, 10): 32 | for v in ek.linspace(Float, 0, 1, 10): 33 | n = warp.square_to_uniform_sphere([u, v]) 34 | s1, t1 = branchless_onb(n) 35 | s2, t2 = coordinate_system(n) 36 | assert ek.allclose(s1, s2, atol=1e-6) 37 | assert ek.allclose(t1, t2, atol=1e-6) 38 | -------------------------------------------------------------------------------- /src/librender/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/librender/__init__.py -------------------------------------------------------------------------------- /src/librender/emitter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | MTS_VARIANT Emitter::Emitter(const Properties &props) : Base(props) { } 8 | MTS_VARIANT Emitter::~Emitter() { } 9 | 10 | MTS_IMPLEMENT_CLASS_VARIANT(Emitter, Endpoint, "emitter") 11 | MTS_INSTANTIATE_CLASS(Emitter) 12 | NAMESPACE_END(mitsuba) 13 | -------------------------------------------------------------------------------- /src/librender/microfacet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NAMESPACE_BEGIN(mitsuba) 4 | 5 | MTS_INSTANTIATE_CLASS(MicrofacetDistribution) 6 | 7 | NAMESPACE_END(mitsuba) 8 | -------------------------------------------------------------------------------- /src/librender/phase.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | MTS_VARIANT 8 | PhaseFunction::PhaseFunction(const Properties &props) 9 | : m_flags(+PhaseFunctionFlags::None), m_id(props.id()) {} 10 | 11 | MTS_VARIANT PhaseFunction::~PhaseFunction() {} 12 | 13 | MTS_IMPLEMENT_CLASS_VARIANT(PhaseFunction, Object, "phase") 14 | MTS_INSTANTIATE_CLASS(PhaseFunction) 15 | NAMESPACE_END(mitsuba) 16 | -------------------------------------------------------------------------------- /src/librender/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | foreach (MTS_VARIANT ${MTS_VARIANTS}) 2 | string(REPLACE "|" ";" MTS_VARIANT ${MTS_VARIANT}) 3 | list(GET MTS_VARIANT 0 MTS_VARIANT_NAME) 4 | list(GET MTS_VARIANT 1 MTS_VARIANT_FLOAT) 5 | list(GET MTS_VARIANT 2 MTS_VARIANT_SPECTRUM) 6 | set(TARGET_NAME render_${MTS_VARIANT_NAME}_ext) 7 | 8 | if (MTS_VARIANT_FLOAT MATCHES "Packet") 9 | set(MTS_VARIANT_VECTORIZE 1) 10 | else() 11 | set(MTS_VARIANT_VECTORIZE 0) 12 | endif() 13 | 14 | add_mitsuba_python_library(${TARGET_NAME} 15 | main_v.cpp 16 | bsdf_v.cpp 17 | emitter_v.cpp 18 | endpoint_v.cpp 19 | film_v.cpp 20 | fresnel_v.cpp 21 | imageblock_v.cpp 22 | interaction_v.cpp 23 | integrator_v.cpp 24 | medium_v.cpp 25 | mueller_v.cpp 26 | microfacet_v.cpp 27 | phase_v.cpp 28 | records_v.cpp 29 | sampler_v.cpp 30 | scene_v.cpp 31 | sensor_v.cpp 32 | shape_v.cpp 33 | srgb_v.cpp 34 | texture_v.cpp 35 | ) 36 | 37 | target_compile_definitions(${TARGET_NAME}-obj PRIVATE 38 | "-DMTS_VARIANT_NAME=${MTS_VARIANT_NAME}" 39 | "-DMTS_VARIANT_FLOAT=${MTS_VARIANT_FLOAT}" 40 | "-DMTS_VARIANT_SPECTRUM=${MTS_VARIANT_SPECTRUM}" 41 | "-DMTS_VARIANT_VECTORIZE=${MTS_VARIANT_VECTORIZE}" 42 | ) 43 | 44 | target_link_libraries(${TARGET_NAME} PRIVATE mitsuba-core mitsuba-render tbb) 45 | 46 | if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64|AMD64") 47 | target_link_libraries(${TARGET_NAME} PRIVATE asmjit) 48 | endif() 49 | endforeach() 50 | 51 | add_mitsuba_python_library(render_ext 52 | emitter.cpp 53 | main.cpp 54 | bsdf.cpp 55 | interaction.cpp 56 | microfacet.cpp 57 | phase.cpp 58 | spiral.cpp 59 | ) 60 | 61 | target_link_libraries(render_ext PRIVATE mitsuba-core mitsuba-render tbb) 62 | -------------------------------------------------------------------------------- /src/librender/python/emitter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MTS_PY_EXPORT(EmitterExtras) { 6 | py::enum_(m, "EmitterFlags", D(EmitterFlags)) 7 | .def_value(EmitterFlags, None) 8 | .def_value(EmitterFlags, DeltaPosition) 9 | .def_value(EmitterFlags, DeltaDirection) 10 | .def_value(EmitterFlags, Infinite) 11 | .def_value(EmitterFlags, Surface) 12 | .def_value(EmitterFlags, SpatiallyVarying) 13 | .def_value(EmitterFlags, Delta) 14 | .def(py::self == py::self) 15 | .def(py::self | py::self) 16 | .def(int() | py::self) 17 | .def(py::self & py::self) 18 | .def(int() & py::self) 19 | .def(+py::self) 20 | .def(~py::self) 21 | .def("__pos__", [](const EmitterFlags &f) { 22 | return static_cast(f); 23 | }, py::is_operator()); 24 | } 25 | -------------------------------------------------------------------------------- /src/librender/python/endpoint_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | MTS_PY_EXPORT(Endpoint) { 12 | MTS_PY_IMPORT_TYPES() 13 | MTS_PY_CLASS(Endpoint, Object) 14 | .def("sample_ray", vectorize(&Endpoint::sample_ray), 15 | "time"_a, "sample1"_a, "sample2"_a, "sample3"_a, "active"_a = true, 16 | D(Endpoint, sample_ray)) 17 | .def("sample_direction", vectorize(&Endpoint::sample_direction), 18 | "it"_a, "sample"_a, "active"_a = true, D(Endpoint, sample_direction)) 19 | .def("pdf_direction", vectorize(&Endpoint::pdf_direction), 20 | "it"_a, "ds"_a, "active"_a = true, D(Endpoint, pdf_direction)) 21 | .def("eval", vectorize(&Endpoint::eval), 22 | "si"_a, "active"_a = true, D(Endpoint, eval)) 23 | .def_method(Endpoint, world_transform) 24 | .def_method(Endpoint, needs_sample_2) 25 | .def_method(Endpoint, needs_sample_3) 26 | .def("shape", py::overload_cast<>(&Endpoint::shape, py::const_), D(Endpoint, shape)) 27 | .def("medium", py::overload_cast<>(&Endpoint::medium, py::const_), D(Endpoint, medium)) 28 | .def_method(Endpoint, set_shape, "shape"_a) 29 | .def_method(Endpoint, set_medium, "medium"_a) 30 | .def_method(Endpoint, bbox); 31 | } 32 | -------------------------------------------------------------------------------- /src/librender/python/film_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | MTS_PY_EXPORT(Film) { 11 | MTS_PY_IMPORT_TYPES(Film) 12 | MTS_PY_CLASS(Film, Object) 13 | .def_method(Film, prepare, "channels"_a) 14 | .def_method(Film, put, "block"_a) 15 | .def_method(Film, set_destination_file, "filename"_a) 16 | .def("develop", py::overload_cast<>(&Film::develop)) 17 | .def("develop", py::overload_cast( 19 | &Film::develop, py::const_), 20 | "offset"_a, "size"_a, "target_offset"_a, "target"_a) 21 | .def_method(Film, destination_exists, "basename"_a) 22 | .def_method(Film, bitmap, "raw"_a = false) 23 | .def_method(Film, has_high_quality_edges) 24 | .def_method(Film, size) 25 | .def_method(Film, crop_size) 26 | .def_method(Film, crop_offset) 27 | .def_method(Film, set_crop_window) 28 | .def_method(Film, reconstruction_filter); 29 | } 30 | -------------------------------------------------------------------------------- /src/librender/python/fresnel_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(fresnel) { 5 | MTS_PY_IMPORT_TYPES() 6 | m.def("fresnel", 7 | vectorize(&fresnel), 8 | "cos_theta_i"_a, "eta"_a, D(fresnel)) 9 | .def("fresnel_conductor", 10 | vectorize(&fresnel_conductor), 11 | "cos_theta_i"_a, "eta"_a, D(fresnel_conductor)) 12 | .def("fresnel_polarized", 13 | vectorize(py::overload_cast>(&fresnel_polarized)), 14 | "cos_theta_i"_a, "eta"_a, D(fresnel_polarized, 2)) 15 | .def("reflect", 16 | vectorize(py::overload_cast(&reflect)), 17 | "wi"_a, D(reflect)) 18 | .def("reflect", 19 | vectorize(py::overload_cast(&reflect)), 20 | "wi"_a, "m"_a, D(reflect, 2)) 21 | .def("refract", 22 | vectorize( 23 | py::overload_cast(&refract)), 24 | "wi"_a, "cos_theta_t"_a, "eta_ti"_a, D(refract)) 25 | .def("refract", 26 | vectorize(py::overload_cast( 27 | &refract)), 28 | "wi"_a, "m"_a, "cos_theta_t"_a, "eta_ti"_a, D(refract, 2)); 29 | } 30 | -------------------------------------------------------------------------------- /src/librender/python/imageblock_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MTS_PY_EXPORT(ImageBlock) { 6 | MTS_PY_IMPORT_TYPES(ImageBlock, ReconstructionFilter) 7 | MTS_PY_CLASS(ImageBlock, Object) 8 | .def(py::init(), 10 | "size"_a, "channel_count"_a, "filter"_a = nullptr, 11 | "warn_negative"_a = true, "warn_invalid"_a = true, 12 | "border"_a = true, "normalize"_a = false) 13 | .def("put", py::overload_cast(&ImageBlock::put), 14 | D(ImageBlock, put), "block"_a) 15 | .def("put", vectorize(py::overload_cast &, const Spectrum &, const Float &, 17 | mask_t>(&ImageBlock::put)), 18 | "pos"_a, "wavelengths"_a, "value"_a, "alpha"_a = 1.f, "active"_a = true, 19 | D(ImageBlock, put, 2)) 20 | .def("put", 21 | [](ImageBlock &ib, const Point2f &pos, 22 | const std::vector &data, Mask mask) { 23 | if (data.size() != ib.channel_count()) 24 | throw std::runtime_error("Incompatible channel count!"); 25 | ib.put(pos, data.data(), mask); 26 | }, "pos"_a, "data"_a, "active"_a = true) 27 | .def_method(ImageBlock, clear) 28 | .def_method(ImageBlock, set_offset, "offset"_a) 29 | .def_method(ImageBlock, offset) 30 | .def_method(ImageBlock, size) 31 | .def_method(ImageBlock, width) 32 | .def_method(ImageBlock, height) 33 | .def_method(ImageBlock, warn_invalid) 34 | .def_method(ImageBlock, warn_negative) 35 | .def_method(ImageBlock, set_warn_invalid, "value"_a) 36 | .def_method(ImageBlock, set_warn_negative, "value"_a) 37 | .def_method(ImageBlock, border_size) 38 | .def_method(ImageBlock, channel_count) 39 | .def("data", py::overload_cast<>(&ImageBlock::data, py::const_), D(ImageBlock, data)); 40 | } 41 | -------------------------------------------------------------------------------- /src/librender/python/interaction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(HitComputeFlags) { 5 | py::enum_(m, "HitComputeFlags", py::arithmetic()) 6 | .def_value(HitComputeFlags, None) 7 | .def_value(HitComputeFlags, Minimal) 8 | .def_value(HitComputeFlags, UV) 9 | .def_value(HitComputeFlags, dPdUV) 10 | .def_value(HitComputeFlags, dNGdUV) 11 | .def_value(HitComputeFlags, dNSdUV) 12 | .def_value(HitComputeFlags, ShadingFrame) 13 | .def_value(HitComputeFlags, NonDifferentiable) 14 | .def_value(HitComputeFlags, All) 15 | .def_value(HitComputeFlags, AllNonDifferentiable) 16 | .def(py::self == py::self) 17 | .def(py::self | py::self) 18 | .def(py::self & py::self) 19 | .def(~py::self); 20 | } -------------------------------------------------------------------------------- /src/librender/python/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | MTS_PY_DECLARE(BSDFContext); 4 | MTS_PY_DECLARE(EmitterExtras); 5 | MTS_PY_DECLARE(HitComputeFlags); 6 | MTS_PY_DECLARE(MicrofacetType); 7 | MTS_PY_DECLARE(PhaseFunctionExtras); 8 | MTS_PY_DECLARE(Spiral); 9 | 10 | PYBIND11_MODULE(render_ext, m) { 11 | // Temporarily change the module name (for pydoc) 12 | m.attr("__name__") = "mitsuba.render"; 13 | 14 | MTS_PY_IMPORT(BSDFContext); 15 | MTS_PY_IMPORT(EmitterExtras); 16 | MTS_PY_IMPORT(HitComputeFlags); 17 | MTS_PY_IMPORT(MicrofacetType); 18 | MTS_PY_IMPORT(PhaseFunctionExtras); 19 | MTS_PY_IMPORT(Spiral); 20 | 21 | // Change module name back to correct value 22 | m.attr("__name__") = "mitsuba.render_ext"; 23 | } 24 | -------------------------------------------------------------------------------- /src/librender/python/microfacet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MTS_PY_EXPORT(MicrofacetType) { 6 | py::enum_(m, "MicrofacetType", D(MicrofacetType), py::arithmetic()) 7 | .def_value(MicrofacetType, Beckmann) 8 | .def_value(MicrofacetType, GGX); 9 | } 10 | -------------------------------------------------------------------------------- /src/librender/python/old/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | MTS_PY_DECLARE(autodiff); 4 | MTS_PY_DECLARE(Scene); 5 | MTS_PY_DECLARE(Shape); 6 | MTS_PY_DECLARE(ShapeKDTree); 7 | MTS_PY_DECLARE(Interaction); 8 | MTS_PY_DECLARE(SurfaceInteraction); 9 | MTS_PY_DECLARE(Endpoint); 10 | MTS_PY_DECLARE(Emitter); 11 | MTS_PY_DECLARE(Sensor); 12 | MTS_PY_DECLARE(BSDFSample); 13 | MTS_PY_DECLARE(BSDF); 14 | MTS_PY_DECLARE(ImageBlock); 15 | MTS_PY_DECLARE(Film); 16 | MTS_PY_DECLARE(Spiral); 17 | MTS_PY_DECLARE(Integrator); 18 | MTS_PY_DECLARE(Sampler); 19 | MTS_PY_DECLARE(Texture); 20 | MTS_PY_DECLARE(MicrofacetDistribution); 21 | MTS_PY_DECLARE(PositionSample); 22 | MTS_PY_DECLARE(DirectionSample); 23 | MTS_PY_DECLARE(fresnel); 24 | MTS_PY_DECLARE(srgb); 25 | MTS_PY_DECLARE(mueller); 26 | MTS_PY_DECLARE(Volume); 27 | 28 | PYBIND11_MODULE(mitsuba_render_ext, m_) { 29 | (void) m_; /* unused */ 30 | 31 | py::module m = py::module::import("mitsuba"); 32 | std::vector submodule_list; 33 | MTS_PY_DEF_SUBMODULE(submodule_list, render) 34 | 35 | // MTS_PY_IMPORT(autodiff); 36 | MTS_PY_IMPORT(Scene); 37 | MTS_PY_IMPORT(Shape); 38 | MTS_PY_IMPORT(ShapeKDTree); 39 | MTS_PY_IMPORT(Interaction); 40 | MTS_PY_IMPORT(SurfaceInteraction); 41 | MTS_PY_IMPORT(Endpoint); 42 | MTS_PY_IMPORT(Emitter); 43 | MTS_PY_IMPORT(Sensor); 44 | MTS_PY_IMPORT(BSDFSample); 45 | MTS_PY_IMPORT(BSDF); 46 | MTS_PY_IMPORT(ImageBlock); 47 | MTS_PY_IMPORT(Film); 48 | MTS_PY_IMPORT(Spiral); 49 | MTS_PY_IMPORT(Integrator); 50 | MTS_PY_IMPORT(Sampler); 51 | MTS_PY_IMPORT(Texture); 52 | MTS_PY_IMPORT(MicrofacetDistribution); 53 | MTS_PY_IMPORT(PositionSample); 54 | MTS_PY_IMPORT(DirectionSample); 55 | MTS_PY_IMPORT(fresnel); 56 | MTS_PY_IMPORT(srgb); 57 | MTS_PY_IMPORT(mueller); 58 | // MTS_PY_IMPORT(Volume); 59 | } 60 | -------------------------------------------------------------------------------- /src/librender/python/phase.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | 8 | MTS_PY_EXPORT(PhaseFunctionExtras) { 9 | py::enum_(m, "PhaseFunctionFlags", D(PhaseFunctionFlags)) 10 | .def_value(PhaseFunctionFlags, None) 11 | .def_value(PhaseFunctionFlags, Isotropic) 12 | .def_value(PhaseFunctionFlags, Anisotropic) 13 | .def_value(PhaseFunctionFlags, Microflake) 14 | .def(py::self == py::self) 15 | .def(py::self | py::self) 16 | .def(int() | py::self) 17 | .def(py::self & py::self) 18 | .def(int() & py::self) 19 | .def(+py::self) 20 | .def(~py::self) 21 | .def("__pos__", [](const PhaseFunctionFlags &f) { return static_cast(f); }, 22 | py::is_operator()); 23 | } -------------------------------------------------------------------------------- /src/librender/python/sampler_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(Sampler) { 5 | MTS_PY_IMPORT_TYPES(Sampler) 6 | MTS_PY_CLASS(Sampler, Object) 7 | .def_method(Sampler, clone) 8 | .def_method(Sampler, sample_count) 9 | .def_method(Sampler, wavefront_size) 10 | .def_method(Sampler, set_samples_per_wavefront, "samples_per_wavefront"_a) 11 | .def_method(Sampler, advance) 12 | .def("seed", vectorize(&Sampler::seed), 13 | "seed_offset"_a, "wavefront_size"_a = 1, D(Sampler, seed)) 14 | .def("next_1d", vectorize(&Sampler::next_1d), 15 | "active"_a = true, D(Sampler, next_1d)) 16 | .def("next_2d", vectorize(&Sampler::next_2d), 17 | "active"_a = true, D(Sampler, next_2d)); 18 | } 19 | -------------------------------------------------------------------------------- /src/librender/python/spiral.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(Spiral) { 5 | using Vector2i = typename Spiral::Vector2i; 6 | MTS_PY_CLASS(Spiral, Object) 7 | .def(py::init(), 8 | "size"_a, "offset"_a, "block_size"_a = MTS_BLOCK_SIZE, "passes"_a = 1, 9 | D(Spiral, Spiral)) 10 | .def_method(Spiral, max_block_size) 11 | .def_method(Spiral, block_count) 12 | .def_method(Spiral, reset) 13 | .def_method(Spiral, set_passes) 14 | .def_method(Spiral, next_block); 15 | } 16 | -------------------------------------------------------------------------------- /src/librender/python/srgb_v.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MTS_PY_EXPORT(srgb) { 5 | MTS_PY_IMPORT_TYPES() 6 | m.def("srgb_model_fetch", &srgb_model_fetch, D(srgb_model_fetch)) 7 | // .def("srgb_model_eval_rgb", &srgb_model_eval_rgb, D(srgb_model_eval_rgb)) 8 | .def("srgb_model_eval", 9 | vectorize(&srgb_model_eval, Array>), 10 | D(srgb_model_eval)) 11 | .def("srgb_model_mean", 12 | vectorize(&srgb_model_mean>), 13 | D(srgb_model_mean)) 14 | ; 15 | } 16 | -------------------------------------------------------------------------------- /src/librender/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/librender/tests/__init__.py -------------------------------------------------------------------------------- /src/librender/tests/data/triangle.ply: -------------------------------------------------------------------------------- 1 | ply 2 | format ascii 1.0 3 | comment this file contains a triangle 4 | element vertex 3 5 | property float x 6 | property float y 7 | property float z 8 | element face 1 9 | property list uchar int vertex_index 10 | end_header 11 | 0 0 0 12 | 0 0 1 13 | 0 1 0 14 | 3 0 1 2 15 | -------------------------------------------------------------------------------- /src/librender/tests/data/triangle_face_colors.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/librender/tests/data/triangle_face_colors.ply -------------------------------------------------------------------------------- /src/librender/tests/mesh_generation.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | 3 | from mitsuba.python.util import traverse 4 | 5 | def create_single_triangle(): 6 | from mitsuba.render import Mesh 7 | 8 | m = Mesh("tri", 3, 1) 9 | m.vertex_positions_buffer()[:] = [0.0, 0.0, 0.0, 1.0, 0.2, 0.0, 0.2, 1.0, 0.0] 10 | m.faces_buffer()[:] = [0, 1, 2] 11 | m.recompute_bbox() 12 | return m 13 | 14 | 15 | def create_regular_tetrahedron(): 16 | from mitsuba.render import Mesh 17 | 18 | m = Mesh("tetrahedron", 4, 4) 19 | params = traverse(m) 20 | m.vertex_positions_buffer()[:] = [0, 0, 0, 0.8, 0.8, 0, 0.8, 0, 0.8, 0, 0.8, 0.8] 21 | m.faces_buffer()[:] = [0, 1, 2, 1, 2, 3, 0, 2, 2, 1, 3, 3, 3, 1, 0] 22 | m.recompute_bbox() 23 | return m 24 | 25 | 26 | # Generate stairs in a 1x1x1 bbox, going up the Z axis along the X axis 27 | def create_stairs(num_steps): 28 | import numpy as np 29 | from mitsuba.render import Mesh 30 | 31 | size_step = 1.0 / num_steps 32 | 33 | m = Mesh("stairs", 4 * num_steps, 4 * num_steps - 2) 34 | params = traverse(m) 35 | 36 | v = np.zeros((4 * num_steps, 3)) 37 | f = np.zeros((4 * num_steps - 2, 3)) 38 | 39 | for i in range(num_steps): 40 | h = i * size_step 41 | s1 = i * size_step 42 | s2 = (i + 1) * size_step 43 | k = 4 * i 44 | 45 | v[k + 0] = [0.0, s1, h] 46 | v[k + 1] = [1.0, s1, h] 47 | v[k + 2] = [0.0, s2, h] 48 | v[k + 3] = [1.0, s2, h] 49 | 50 | f[k] = [k, k + 1, k + 2] 51 | f[k + 1] = [k + 1, k + 3, k + 2] 52 | if i < num_steps - 1: 53 | f[k + 2] = [k + 2, k + 3, k + 5] 54 | f[k + 3] = [k + 5, k + 4, k + 2] 55 | 56 | m.vertex_positions_buffer()[:] = v.reshape(-1) 57 | m.faces_buffer()[:] = f.reshape(-1) 58 | m.recompute_bbox() 59 | return m 60 | -------------------------------------------------------------------------------- /src/librender/tests/test_bsdf.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | import numpy as np 5 | 6 | 7 | def test01_ctx_construct(variant_scalar_rgb): 8 | from mitsuba.render import BSDFContext, BSDFFlags, TransportMode 9 | ctx = BSDFContext() 10 | assert ctx.type_mask == +BSDFFlags.All 11 | assert ctx.component == np.uint32(-1) 12 | assert ctx.mode == TransportMode.Radiance 13 | ctx.reverse() 14 | assert ctx.mode == TransportMode.Importance 15 | 16 | # By default, all components and types are queried 17 | assert ctx.is_enabled(BSDFFlags.DeltaTransmission) 18 | assert ctx.is_enabled(BSDFFlags.Delta, 1) 19 | assert ctx.is_enabled(BSDFFlags.DiffuseTransmission, 6) 20 | assert ctx.is_enabled(BSDFFlags.Glossy, 10) 21 | 22 | 23 | def test02_bs_construct(variant_scalar_rgb): 24 | from mitsuba.render import BSDFSample3f 25 | wo = [1, 0, 0] 26 | bs = BSDFSample3f(wo) 27 | assert ek.allclose(bs.wo, wo) 28 | assert ek.allclose(bs.pdf, 0.0) 29 | assert ek.allclose(bs.eta, 1.0) 30 | assert bs.sampled_type == 0 31 | -------------------------------------------------------------------------------- /src/libui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | ${NANOGUI_INCLUDE_DIRS} 3 | ${CMAKE_CURRENT_BINARY_DIR} 4 | ) 5 | 6 | add_definitions( 7 | ${NANOGUI_EXTRA_DEFS} 8 | ) 9 | 10 | # Glob up resource files 11 | file(GLOB resources "${PROJECT_SOURCE_DIR}/resources/*.png") 12 | 13 | # Concatenate resource files into a comma separated string 14 | string (REGEX REPLACE "([^\\]|^);" "\\1," resources_string "${resources}") 15 | string (REGEX REPLACE "[\\](.)" "\\1" resources_string "${resources_string}") 16 | 17 | # Create command line for running bin2c cmake script 18 | set(bin2c_cmdline 19 | -DOUTPUT_C=libui_resources.cpp 20 | -DOUTPUT_H=libui_resources.h 21 | "-DINPUT_FILES=${resources_string}" 22 | -P "${PROJECT_SOURCE_DIR}/ext/nanogui/resources/bin2c.cmake") 23 | 24 | # Run bin2c on resource files 25 | add_custom_command( 26 | OUTPUT libui_resources.cpp libui_resources.h 27 | COMMAND ${CMAKE_COMMAND} ARGS ${bin2c_cmdline} 28 | DEPENDS ${resources} 29 | COMMENT "Running bin2c" 30 | PRE_BUILD VERBATIM) 31 | 32 | set(INC_DIR "../../include/mitsuba/ui") 33 | 34 | add_library(mitsuba-ui-obj OBJECT 35 | texture.cpp ${INC_DIR}/texture.h 36 | viewer.cpp ${INC_DIR}/viewer.h 37 | libui_resources.cpp libui_resources.h 38 | ) 39 | 40 | add_library(mitsuba-ui SHARED $) 41 | set_property(TARGET mitsuba-ui-obj PROPERTY POSITION_INDEPENDENT_CODE ON) 42 | set_target_properties(mitsuba-ui-obj mitsuba-ui PROPERTIES FOLDER mitsuba-ui) 43 | target_compile_definitions(mitsuba-ui-obj PRIVATE -DMTS_BUILD_MODULE=MTS_MODULE_UI ${NANOGUI_EXTRA_DEFS}) 44 | 45 | # Link to Mitsuba core library 46 | target_link_libraries(mitsuba-ui PUBLIC mitsuba-core) 47 | 48 | # Link to NanoGUI 49 | target_link_libraries(mitsuba-ui PUBLIC nanogui) 50 | set_target_properties(mitsuba-ui PROPERTIES FOLDER mitsuba-ui) 51 | 52 | # Copy to 'dist' directory 53 | add_dist(mitsuba-ui nanogui) 54 | 55 | # # Python bindings 56 | #add_subdirectory(python) 57 | 58 | # Register the test directory 59 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 60 | -------------------------------------------------------------------------------- /src/libui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/libui/__init__.py -------------------------------------------------------------------------------- /src/libui/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_mitsuba_python_library(mitsuba_ui_ext ui 2 | main.cpp 3 | ) 4 | 5 | target_link_libraries(mitsuba_ui_ext PRIVATE mitsuba-core mitsuba-render mitsuba-ui) 6 | -------------------------------------------------------------------------------- /src/libui/python/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | PYBIND11_DECLARE_HOLDER_TYPE(T, nanogui::ref) 5 | 6 | MTS_PY_EXPORT(Texture) { 7 | MTS_PY_CHECK_ALIAS(mitsuba::GPUTexture, "Texture") { 8 | py::class_>(m, "Texture", D(Texture)) 9 | .def(py::init(), 11 | D(GPUTexture, GPUTexture),"bitmap"_a, 12 | "min_interpolation_mode"_a = nanogui::Texture::InterpolationMode::Bilinear, 13 | "mag_interpolation_mode"_a = nanogui::Texture::InterpolationMode::Bilinear, 14 | "wrap_mode"_a = nanogui::Texture::WrapMode::ClampToEdge); 15 | } 16 | } 17 | 18 | PYBIND11_MODULE(mitsuba_ui_ext, m_) { 19 | (void) m_; /* unused */ 20 | py::module::import("nanogui"); 21 | 22 | py::module m = py::module::import("mitsuba"); 23 | std::vector submodule_list; 24 | MTS_PY_DEF_SUBMODULE(submodule_list, ui) 25 | 26 | MTS_PY_IMPORT(Texture); 27 | } 28 | -------------------------------------------------------------------------------- /src/libui/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/libui/tests/__init__.py -------------------------------------------------------------------------------- /src/media/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "media") 2 | 3 | add_plugin(homogeneous homogeneous.cpp) 4 | add_plugin(heterogeneous heterogeneous.cpp) 5 | 6 | # Register the test directory 7 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 8 | -------------------------------------------------------------------------------- /src/media/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/media/tests/__init__.py -------------------------------------------------------------------------------- /src/mitsuba/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | ${TBB_INCLUDE_DIRS} 3 | ${ASMJIT_INCLUDE_DIRS} 4 | ) 5 | 6 | add_executable(mitsuba mitsuba.cpp) 7 | 8 | target_link_libraries(mitsuba PRIVATE mitsuba-core mitsuba-render tbb) 9 | 10 | if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64|AMD64") 11 | target_link_libraries(mitsuba PRIVATE asmjit) 12 | endif() 13 | 14 | add_dist(mitsuba) 15 | 16 | if (APPLE) 17 | set_target_properties(mitsuba PROPERTIES INSTALL_RPATH "@executable_path") 18 | endif() 19 | 20 | if (MSVC) 21 | set_property(TARGET mitsuba PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "$(SolutionDir)dist") 22 | endif() 23 | -------------------------------------------------------------------------------- /src/mtsgui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | ${TBB_INCLUDE_DIRS} 3 | ${ASMJIT_INCLUDE_DIRS} 4 | ${NANOGUI_INCLUDE_DIRS} 5 | ${CMAKE_CURRENT_BINARY_DIR} 6 | ) 7 | 8 | add_definitions( 9 | ${NANOGUI_EXTRA_DEFS} 10 | ) 11 | 12 | add_definitions(-DMTS_BRANCH="${GIT_BRANCH}") 13 | add_definitions(-DMTS_HASH="${GIT_COMMIT_HASH}") 14 | 15 | add_executable(mtsgui mtsgui.cpp) 16 | target_link_libraries(mtsgui PRIVATE mitsuba-core mitsuba-render mitsuba-ui tbb) 17 | 18 | if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64|AMD64") 19 | target_link_libraries(mtsgui PRIVATE asmjit) 20 | endif() 21 | 22 | add_dist(mtsgui) 23 | 24 | if (APPLE) 25 | set_target_properties(mtsgui PROPERTIES INSTALL_RPATH "@executable_path") 26 | endif() 27 | -------------------------------------------------------------------------------- /src/phase/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "phasefunctions") 2 | 3 | add_plugin(hg hg.cpp) 4 | add_plugin(isotropic isotropic.cpp) 5 | 6 | # Register the test directory 7 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 8 | -------------------------------------------------------------------------------- /src/phase/isotropic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | /**! 8 | 9 | .. _phase-isotropic: 10 | 11 | Isotropic phase function (:monosp:`isotropic`) 12 | ----------------------------------------------- 13 | 14 | This phase function simulates completely uniform scattering, 15 | where all directionality is lost after a single scattering 16 | interaction. It does not have any parameters. 17 | 18 | */ 19 | 20 | template 21 | class IsotropicPhaseFunction final : public PhaseFunction { 22 | public: 23 | MTS_IMPORT_BASE(PhaseFunction, m_flags) 24 | MTS_IMPORT_TYPES(PhaseFunctionContext) 25 | 26 | IsotropicPhaseFunction(const Properties & props) : Base(props) { 27 | m_flags = +PhaseFunctionFlags::Isotropic; 28 | } 29 | 30 | std::pair sample(const PhaseFunctionContext & /* ctx */, 31 | const MediumInteraction3f & /* mi */, const Point2f &sample, 32 | Mask active) const override { 33 | MTS_MASKED_FUNCTION(ProfilerPhase::PhaseFunctionSample, active); 34 | 35 | auto wo = warp::square_to_uniform_sphere(sample); 36 | auto pdf = warp::square_to_uniform_sphere_pdf(wo); 37 | return std::make_pair(wo, pdf); 38 | } 39 | 40 | Float eval(const PhaseFunctionContext & /* ctx */, const MediumInteraction3f & /* mi */, 41 | const Vector3f &wo, Mask active) const override { 42 | MTS_MASKED_FUNCTION(ProfilerPhase::PhaseFunctionEvaluate, active); 43 | return warp::square_to_uniform_sphere_pdf(wo); 44 | } 45 | 46 | std::string to_string() const override { return "IsotropicPhaseFunction[]"; } 47 | 48 | MTS_DECLARE_CLASS() 49 | private: 50 | }; 51 | 52 | MTS_IMPLEMENT_CLASS_VARIANT(IsotropicPhaseFunction, PhaseFunction) 53 | MTS_EXPORT_PLUGIN(IsotropicPhaseFunction, "Isotropic phase function") 54 | NAMESPACE_END(mitsuba) 55 | -------------------------------------------------------------------------------- /src/phase/tests/test_hg.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | from enoki.dynamic import Float32 as Float 5 | 6 | 7 | def test01_create(variant_scalar_rgb): 8 | from mitsuba.core.xml import load_string 9 | p = load_string(""" 10 | 11 | """) 12 | assert p is not None 13 | 14 | def test02_chi2(variant_packet_rgb): 15 | from mitsuba.python.chi2 import PhaseFunctionAdapter, ChiSquareTest, SphericalDomain 16 | from mitsuba.core import ScalarBoundingBox2f 17 | 18 | sample_func, pdf_func = PhaseFunctionAdapter("hg", '') 19 | 20 | chi2 = ChiSquareTest( 21 | domain = SphericalDomain(), 22 | sample_func = sample_func, 23 | pdf_func = pdf_func, 24 | sample_dim = 2 25 | ) 26 | 27 | result = chi2.run(0.1) 28 | chi2._dump_tables() 29 | assert result 30 | -------------------------------------------------------------------------------- /src/phase/tests/test_isotropic.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import mitsuba 4 | import pytest 5 | import enoki as ek 6 | from enoki.dynamic import Float32 as Float 7 | 8 | 9 | def test01_create(variant_scalar_rgb): 10 | from mitsuba.core.xml import load_string 11 | p = load_string("") 12 | assert p is not None 13 | 14 | 15 | def test02_eval(variant_scalar_rgb): 16 | from mitsuba.core.math import Pi 17 | from mitsuba.render import PhaseFunctionContext, MediumInteraction3f 18 | from mitsuba.core.xml import load_string 19 | 20 | p = load_string("") 21 | ctx = PhaseFunctionContext(None) 22 | mi = MediumInteraction3f() 23 | for theta in np.linspace(0, np.pi / 2, 4): 24 | for ph in np.linspace(0, np.pi, 4): 25 | wo = [np.sin(theta), 0, np.cos(theta)] 26 | v_eval = p.eval(ctx, mi, wo) 27 | assert np.allclose(v_eval, 1.0 / (4 * Pi)) 28 | 29 | def test03_chi2(variant_packet_rgb): 30 | from mitsuba.python.chi2 import PhaseFunctionAdapter, ChiSquareTest, SphericalDomain 31 | from mitsuba.core import ScalarBoundingBox2f 32 | 33 | sample_func, pdf_func = PhaseFunctionAdapter("isotropic", '') 34 | 35 | chi2 = ChiSquareTest( 36 | domain = SphericalDomain(), 37 | sample_func = sample_func, 38 | pdf_func = pdf_func, 39 | sample_dim = 2 40 | ) 41 | 42 | result = chi2.run(0.1) 43 | chi2._dump_tables() 44 | assert result 45 | -------------------------------------------------------------------------------- /src/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE PYTHON_FILES RELATIVE 2 | ${CMAKE_CURRENT_SOURCE_DIR} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/*.py 4 | ${CMAKE_CURRENT_SOURCE_DIR}/*.shader) 5 | 6 | foreach(FNAME ${PYTHON_FILES}) 7 | set(IN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FNAME}) 8 | set(OUT_FILE ${CMAKE_BINARY_DIR}/dist/python/mitsuba/${FNAME}) 9 | if (UNIX) 10 | add_custom_command( 11 | OUTPUT ${OUT_FILE} DEPENDS ${IN_FILE} 12 | COMMAND ${CMAKE_COMMAND} -E copy ${IN_FILE} ${OUT_FILE} && chmod a=r ${OUT_FILE}) 13 | else() 14 | add_custom_command( 15 | OUTPUT ${OUT_FILE} DEPENDS ${IN_FILE} 16 | COMMAND ${CMAKE_COMMAND} -E copy ${IN_FILE} ${OUT_FILE}) 17 | endif() 18 | set(PYTHON_OUTPUT ${PYTHON_OUTPUT} ${OUT_FILE}) 19 | endforeach() 20 | 21 | add_custom_target(python-copy ALL DEPENDS ${PYTHON_OUTPUT}) 22 | set_property(TARGET python-copy PROPERTY FOLDER misc) 23 | 24 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/dist/python/mitsuba) -------------------------------------------------------------------------------- /src/python/python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/python/python/__init__.py -------------------------------------------------------------------------------- /src/python/python/math.py: -------------------------------------------------------------------------------- 1 | import enoki as ek 2 | 3 | def rlgamma(a, x): 4 | 'Regularized lower incomplete gamma function based on CEPHES' 5 | 6 | eps = 1e-15 7 | big = 4.503599627370496e15 8 | biginv = 2.22044604925031308085e-16 9 | 10 | if a < 0 or x < 0: 11 | raise "out of range" 12 | 13 | if x == 0: 14 | return 0 15 | 16 | ax = (a * ek.log(x)) - x - ek.lgamma(a) 17 | 18 | if ax < -709.78271289338399: 19 | return 1.0 if a < x else 0.0 20 | 21 | if x <= 1 or x <= a: 22 | r2 = a 23 | c2 = 1 24 | ans2 = 1 25 | 26 | while True: 27 | r2 = r2 + 1 28 | c2 = c2 * x / r2 29 | ans2 += c2 30 | 31 | if c2 / ans2 <= eps: 32 | break 33 | 34 | return ek.exp(ax) * ans2 / a 35 | 36 | c = 0 37 | y = 1 - a 38 | z = x + y + 1 39 | p3 = 1 40 | q3 = x 41 | p2 = x + 1 42 | q2 = z * x 43 | ans = p2 / q2 44 | 45 | while True: 46 | c += 1 47 | y += 1 48 | z += 2 49 | yc = y * c 50 | p = (p2 * z) - (p3 * yc) 51 | q = (q2 * z) - (q3 * yc) 52 | 53 | if q != 0: 54 | nextans = p / q 55 | error = ek.abs((ans - nextans) / nextans) 56 | ans = nextans 57 | else: 58 | error = 1 59 | 60 | p3 = p2 61 | p2 = p 62 | q3 = q2 63 | q2 = q 64 | 65 | # normalize fraction when the numerator becomes large 66 | if ek.abs(p) > big: 67 | p3 *= biginv 68 | p2 *= biginv 69 | q3 *= biginv 70 | q2 *= biginv 71 | 72 | if error <= eps: 73 | break; 74 | 75 | return 1 - ek.exp(ax) * ans 76 | -------------------------------------------------------------------------------- /src/python/python/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/python/python/test/__init__.py -------------------------------------------------------------------------------- /src/rfilters/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "rfilters") 2 | 3 | add_plugin(box box.cpp) 4 | add_plugin(tent tent.cpp) 5 | add_plugin(lanczos lanczos.cpp) 6 | add_plugin(mitchell mitchell.cpp) 7 | add_plugin(catmullrom catmullrom.cpp) 8 | add_plugin(gaussian gaussian.cpp) 9 | 10 | # Register the test directory 11 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 12 | -------------------------------------------------------------------------------- /src/rfilters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/rfilters/__init__.py -------------------------------------------------------------------------------- /src/rfilters/box.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | /**! 8 | 9 | .. _rfilter-box: 10 | 11 | Box filter (:monosp:`box`) 12 | -------------------------- 13 | 14 | This is the fastest, but also about the worst possible reconstruction filter, 15 | since it is prone to severe aliasing. It is included mainly for completeness, 16 | though some rare situations may warrant its use. 17 | 18 | */ 19 | 20 | template 21 | class BoxFilter final : public ReconstructionFilter { 22 | public: 23 | MTS_IMPORT_BASE(ReconstructionFilter, init_discretization, m_radius) 24 | 25 | BoxFilter(const Properties &props) : Base(props) { 26 | /* Filter radius in pixels. A tiny epsilon is added, since some 27 | samplers (Hammersley and Halton in particular) place samples 28 | at positions like (0, 0). Without such an epsilon and rounding 29 | errors, samples may end up not contributing to any pixel. */ 30 | m_radius = props.float_("radius", .5f) + math::RayEpsilon; 31 | init_discretization(); 32 | } 33 | 34 | Float eval(Float x, mask_t /* active */) const override { 35 | return select(abs(x) <= m_radius, Float(1.f), Float(0.f)); 36 | } 37 | 38 | std::string to_string() const override { 39 | return tfm::format("BoxFilter[radius=%f]", m_radius); 40 | } 41 | 42 | MTS_DECLARE_CLASS() 43 | }; 44 | 45 | MTS_IMPLEMENT_CLASS_VARIANT(BoxFilter, ReconstructionFilter) 46 | MTS_EXPORT_PLUGIN(BoxFilter, "Box filter"); 47 | NAMESPACE_END(mitsuba) 48 | -------------------------------------------------------------------------------- /src/rfilters/catmullrom.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(mitsuba) 5 | 6 | /**! 7 | 8 | .. _rfilter-catmullrom: 9 | 10 | Catmull-Rom filter (:monosp:`catmullrom`) 11 | ----------------------------------------- 12 | 13 | Special version of the Mitchell-Netravali filter with constants B and C configured 14 | to match the Catmull-Rom spline. It usually does a better job at at preserving sharp 15 | features at the cost of more ringing. 16 | 17 | */ 18 | 19 | template 20 | class CatmullRomFilter final : public ReconstructionFilter { 21 | public: 22 | MTS_IMPORT_BASE(ReconstructionFilter, init_discretization, m_radius) 23 | 24 | CatmullRomFilter(const Properties &props) : Base(props) { 25 | m_radius = 2.f; 26 | init_discretization(); 27 | } 28 | 29 | Float eval(Float x, mask_t /* active */) const override { 30 | x = abs(x); 31 | 32 | Float x2 = sqr(x), x3 = x2*x, 33 | B = 0.f, C = .5f; 34 | 35 | Float result = (1.f / 6.f) * select( 36 | x < 1, 37 | (12.f - 9.f * B - 6.f * C) * x3 + 38 | (-18.f + 12.f * B + 6.f * C) * x2 + (6.f - 2.f * B), 39 | (-B - 6.f * C) * x3 + (6.f * B + 30.f * C) * x2 + 40 | (-12.f * B - 48.f * C) * x + (8.f * B + 24.f * C) 41 | ); 42 | 43 | return select(x < 2.f, result, 0.f); 44 | } 45 | 46 | std::string to_string() const override { 47 | return tfm::format("CatmullRomFilter[radius=%f]", m_radius); 48 | } 49 | 50 | MTS_DECLARE_CLASS() 51 | }; 52 | 53 | MTS_IMPLEMENT_CLASS_VARIANT(CatmullRomFilter, ReconstructionFilter) 54 | MTS_EXPORT_PLUGIN(CatmullRomFilter, "Catmull-Rom filter"); 55 | NAMESPACE_END(mitsuba) 56 | -------------------------------------------------------------------------------- /src/rfilters/gaussian.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | /**! 8 | 9 | .. _rfilter-gaussian: 10 | 11 | Gaussian filter (:monosp:`gaussian`) 12 | ------------------------------------ 13 | 14 | This is a windowed Gaussian filter with configurable standard deviation. 15 | It often produces pleasing results, and never suffers from ringing, but may 16 | occasionally introduce too much blurring. 17 | 18 | When no reconstruction filter is explicitly requested, this is the default 19 | choice in Mitsuba. 20 | 21 | Takes a standard deviation parameter (:paramtype:`stddev`) which is set to 0.5 22 | pixels be default. 23 | 24 | */ 25 | 26 | template 27 | class GaussianFilter final : public ReconstructionFilter { 28 | public: 29 | MTS_IMPORT_BASE(ReconstructionFilter, init_discretization, m_radius) 30 | MTS_IMPORT_TYPES() 31 | 32 | GaussianFilter(const Properties &props) : Base(props) { 33 | // Standard deviation 34 | m_stddev = props.float_("stddev", 0.5f); 35 | 36 | // Cut off after 4 standard deviations 37 | m_radius = 4 * m_stddev; 38 | 39 | m_alpha = -1.f / (2.f * m_stddev * m_stddev); 40 | m_bias = std::exp(m_alpha * sqr(m_radius)); 41 | 42 | init_discretization(); 43 | } 44 | 45 | Float eval(Float x, mask_t /* active */) const override { 46 | return max(0.f, exp(m_alpha * sqr(x)) - m_bias); 47 | } 48 | 49 | std::string to_string() const override { 50 | return tfm::format("GaussianFilter[stddev=%.2f, radius=%.2f]", m_stddev, m_radius); 51 | } 52 | 53 | MTS_DECLARE_CLASS() 54 | protected: 55 | ScalarFloat m_stddev, m_alpha, m_bias; 56 | }; 57 | 58 | MTS_IMPLEMENT_CLASS_VARIANT(GaussianFilter, ReconstructionFilter) 59 | MTS_EXPORT_PLUGIN(GaussianFilter, "Gaussian reconstruction filter"); 60 | NAMESPACE_END(mitsuba) 61 | -------------------------------------------------------------------------------- /src/rfilters/lanczos.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(mitsuba) 7 | 8 | /**! 9 | 10 | .. _rfilter-lanczos: 11 | 12 | Lanczos filter (:monosp:`lanczos`) 13 | ---------------------------------- 14 | 15 | This is a windowed version of the theoretically optimal low-pass filter. It 16 | is generally one of the best available filters in terms of producing sharp 17 | high-quality output. Its main disadvantage is that it produces strong ringing 18 | around discontinuities, which can become a serious problem when rendering 19 | bright objects with sharp edges (a directly visible light source will for 20 | instance have black fringing artifacts around it). This is also the 21 | computationally slowest reconstruction filter. 22 | 23 | This plugin has an :paramtype:`integer`-valued parameter named 24 | :paramtype:`lobes`, that sets the desired number of filter side-lobes. The 25 | higher, the closer the filter will approximate an optimal low-pass filter, but 26 | this also increases ringing. Values of 2 or 3 are common (3 is the default). 27 | 28 | */ 29 | template 30 | class LanczosSincFilter final : public ReconstructionFilter { 31 | public: 32 | MTS_IMPORT_BASE(ReconstructionFilter, init_discretization, m_radius) 33 | MTS_IMPORT_TYPES() 34 | 35 | LanczosSincFilter(const Properties &props) : Base(props) { 36 | m_radius = (ScalarFloat) props.int_("lobes", 3); 37 | init_discretization(); 38 | } 39 | 40 | Float eval(Float x, mask_t /* active */) const override { 41 | x = abs(x); 42 | 43 | Float x1 = math::Pi * x, 44 | x2 = x1 / m_radius, 45 | result = (sin(x1) * sin(x2)) / (x1 * x2); 46 | 47 | return select(x < math::Epsilon, 48 | 1.f, 49 | select(x > m_radius, 0.f, result)); 50 | } 51 | 52 | std::string to_string() const override { 53 | return tfm::format("LanczosSincFilter[lobes=%f]", m_radius); 54 | } 55 | 56 | MTS_DECLARE_CLASS() 57 | }; 58 | 59 | MTS_IMPLEMENT_CLASS_VARIANT(LanczosSincFilter, ReconstructionFilter) 60 | MTS_EXPORT_PLUGIN(LanczosSincFilter, "Lanczos Sinc filter") 61 | NAMESPACE_END(mitsuba) 62 | -------------------------------------------------------------------------------- /src/rfilters/mitchell.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | /**! 8 | 9 | .. _rfilter-mitchell: 10 | 11 | Mitchell filter (:monosp:`mitchell`) 12 | ------------------------------------ 13 | 14 | Separable cubic spline reconstruction filter by Mitchell and Netravali :cite:`MitchellNetravali88`. 15 | This is often a good compromise between sharpness and ringing. 16 | 17 | This plugin has two :paramtype:`float`-valued parameters :paramtype:`B` and 18 | :paramtype:`C` that correspond to the two parameters in the original paper. 19 | By default, these are set to the recommended value of :math:`1/3`, but can be 20 | tweaked if desired. 21 | 22 | */ 23 | 24 | template 25 | class MitchellNetravaliFilter final : public ReconstructionFilter { 26 | public: 27 | MTS_IMPORT_BASE(ReconstructionFilter, init_discretization, m_radius) 28 | MTS_IMPORT_TYPES() 29 | 30 | MitchellNetravaliFilter(const Properties &props) : Base(props) { 31 | // Filter radius 32 | m_radius = 2.f; 33 | // B parameter from the paper 34 | m_b = props.float_("B", 1.f / 3.f); 35 | // C parameter from the paper 36 | m_c = props.float_("C", 1.f / 3.f); 37 | init_discretization(); 38 | } 39 | 40 | Float eval(Float x, mask_t /* active */) const override { 41 | x = abs(x); 42 | Float x2 = sqr(x), x3 = x2*x; 43 | 44 | Float result = (1.f / 6.f) * select( 45 | x < 1, 46 | (12.f - 9.f * m_b - 6.f * m_c) * x3 + 47 | (-18.f + 12.f * m_b + 6.f * m_c) * x2 + (6.f - 2.f * m_b), 48 | (-m_b - 6.f * m_c) * x3 + (6.f * m_b + 30.f * m_c) * x2 + 49 | (-12.f * m_b - 48.f * m_c) * x + (8.f * m_b + 24.f * m_c) 50 | ); 51 | 52 | return select(x < 2.f, result, 0.f); 53 | } 54 | 55 | std::string to_string() const override { 56 | return tfm::format("MitchellNetravaliFilter[radius=%f, B=%f, C=%f]", m_radius, m_b, m_c); 57 | } 58 | 59 | MTS_DECLARE_CLASS() 60 | protected: 61 | ScalarFloat m_b, m_c; 62 | }; 63 | 64 | MTS_IMPLEMENT_CLASS_VARIANT(MitchellNetravaliFilter, ReconstructionFilter) 65 | MTS_EXPORT_PLUGIN(MitchellNetravaliFilter, "Mitchell-Netravali filter"); 66 | NAMESPACE_END(mitsuba) 67 | -------------------------------------------------------------------------------- /src/rfilters/tent.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(mitsuba) 5 | 6 | /**! 7 | 8 | .. _rfilter-tent: 9 | 10 | Tent filter (:monosp:`tent`) 11 | ---------------------------- 12 | 13 | Simple tent (triangular) filter. This reconstruction filter never suffers 14 | from ringing and usually causes less aliasing than a naive box filter. When 15 | rendering scenes with sharp brightness discontinuities, this may be useful; 16 | otherwise, negative-lobed filters may be preferable (e.g. Mitchell-Netravali 17 | or Lanczos Sinc). 18 | 19 | */ 20 | 21 | template 22 | class TentFilter final : public ReconstructionFilter { 23 | public: 24 | MTS_IMPORT_BASE(ReconstructionFilter, init_discretization, m_radius) 25 | MTS_IMPORT_TYPES() 26 | 27 | TentFilter(const Properties &props) : Base(props) { 28 | m_radius = 1.f; 29 | m_inv_radius = 1.f / m_radius; 30 | init_discretization(); 31 | } 32 | 33 | Float eval(Float x, mask_t /* active */) const override { 34 | return max(0.f, 1.f - abs(x * m_inv_radius)); 35 | } 36 | 37 | std::string to_string() const override { 38 | return tfm::format("TentFilter[radius=%f]", m_radius); 39 | } 40 | 41 | MTS_DECLARE_CLASS() 42 | private: 43 | ScalarFloat m_inv_radius; 44 | }; 45 | 46 | MTS_IMPLEMENT_CLASS_VARIANT(TentFilter, ReconstructionFilter) 47 | MTS_EXPORT_PLUGIN(TentFilter, "Tent filter"); 48 | NAMESPACE_END(mitsuba) 49 | -------------------------------------------------------------------------------- /src/rfilters/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/rfilters/tests/__init__.py -------------------------------------------------------------------------------- /src/samplers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "samplers") 2 | 3 | add_plugin(independent independent.cpp) 4 | add_plugin(stratified stratified.cpp) 5 | add_plugin(multijitter multijitter.cpp) 6 | add_plugin(orthogonal orthogonal.cpp) 7 | add_plugin(ldsampler ldsampler.cpp) 8 | 9 | # Register the test directory 10 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 11 | -------------------------------------------------------------------------------- /src/samplers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/samplers/__init__.py -------------------------------------------------------------------------------- /src/samplers/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/samplers/tests/__init__.py -------------------------------------------------------------------------------- /src/samplers/tests/test_independent.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | from enoki.dynamic import Float32 as Float 5 | 6 | 7 | @pytest.fixture(scope="module") 8 | def sampler(): 9 | return make_sampler() 10 | def make_sampler(sample_count=8): 11 | from mitsuba.core.xml import load_string 12 | s = load_string(""" 13 | 14 | """ % sample_count) 15 | assert s is not None 16 | return s 17 | 18 | 19 | def next_float(rng, shape = None): 20 | return rng.next_float32(shape=shape) if shape else rng.next_float32() 21 | 22 | 23 | def test01_construct(variant_scalar_rgb): 24 | s = make_sampler(sample_count=58) 25 | assert s.sample_count() == 58 26 | 27 | 28 | def test02_sample_vs_pcg32(variant_scalar_rgb, sampler): 29 | # IndependentSampler uses the default-constructed PCG if no seed is provided. 30 | ref = ek.scalar.PCG32() 31 | for i in range(10): 32 | assert ek.all(sampler.next_1d() == next_float(ref)) 33 | assert ek.all(sampler.next_2d() == [next_float(ref), next_float(ref)]) 34 | 35 | 36 | def test03_clone(variant_scalar_rgb, sampler): 37 | """After cloning, the new sampler should *not* yield the same values: it 38 | should automatically be seeded differently rather than copying the exact 39 | state of the original sampler.""" 40 | sampler2 = sampler.clone() 41 | for i in range(5): 42 | assert ek.all(sampler.next_1d() != sampler2.next_1d()) 43 | assert ek.all(sampler.next_2d() != sampler2.next_2d()) 44 | -------------------------------------------------------------------------------- /src/samplers/tests/test_ldsampler.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | import numpy as np 5 | 6 | from .utils import check_uniform_scalar_sampler, check_uniform_wavefront_sampler 7 | 8 | def test01_ldsampler_scalar(variant_scalar_rgb): 9 | from mitsuba.core import xml 10 | 11 | sampler = xml.load_dict({ 12 | "type" : "ldsampler", 13 | "sample_count" : 1024, 14 | }) 15 | 16 | check_uniform_scalar_sampler(sampler) 17 | 18 | 19 | def test02_ldsampler_wavefront(variant_gpu_rgb): 20 | from mitsuba.core import xml 21 | 22 | sampler = xml.load_dict({ 23 | "type" : "ldsampler", 24 | "sample_count" : 1024, 25 | }) 26 | 27 | check_uniform_wavefront_sampler(sampler) 28 | 29 | 30 | def test03_ldsampler_deterministic_values(variant_scalar_rgb): 31 | from mitsuba.core import xml 32 | 33 | sampler = xml.load_dict({ 34 | "type" : "ldsampler", 35 | "sample_count" : 1024, 36 | }) 37 | 38 | sampler.seed(0) 39 | 40 | values_1d_dim0 = [0.1091986894, 0.0291205644, 0.8533393144, 0.3924018144, 0.6511908769, 41 | 0.9900580644, 0.7781440019, 0.2771674394, 0.4353705644, 0.3601752519] 42 | 43 | values_2d_dim0 = [[0.09034, 0.975105], [0.332528, 0.576667], [0.521004, 0.88819], 44 | [0.00244939, 0.0629952], [0.182137, 0.367683], [0.679207, 0.296394], 45 | [0.0219806, 0.137214], [0.308113, 0.995612], [0.548348, 0.579597]] 46 | 47 | values_1d_dim1 = [0.934394001, 0.534003376, 0.258612751, 0.856269001, 0.279120564, 0.434394001, 48 | 0.235175251, 0.654120564, 0.811347126, 0.958808064] 49 | 50 | values_2d_dim1 = [[0.771981, 0.387214], [0.732918, 0.113776], [0.0708088, 0.791511], 51 | [0.966317, 0.849128], [0.959481, 0.199714], [0.392098, 0.985847], 52 | [0.767098, 0.860847], [0.989754, 0.98194], [0.0932697, 0.726081]] 53 | 54 | for v in values_1d_dim0: 55 | assert ek.allclose(sampler.next_1d(), v) 56 | 57 | for v in values_2d_dim0: 58 | assert ek.allclose(sampler.next_2d(), v) 59 | 60 | sampler.advance() 61 | 62 | for v in values_1d_dim1: 63 | assert ek.allclose(sampler.next_1d(), v) 64 | 65 | for v in values_2d_dim1: 66 | assert ek.allclose(sampler.next_2d(), v) 67 | -------------------------------------------------------------------------------- /src/samplers/tests/test_multijitter.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | import numpy as np 5 | 6 | from .utils import check_uniform_scalar_sampler, check_uniform_wavefront_sampler 7 | 8 | def test01_multijitter_scalar(variant_scalar_rgb): 9 | from mitsuba.core import xml 10 | 11 | sampler = xml.load_dict({ 12 | "type" : "multijitter", 13 | "sample_count" : 1024, 14 | }) 15 | 16 | check_uniform_scalar_sampler(sampler) 17 | 18 | 19 | def test02_multijitter_wavefront(variant_gpu_rgb): 20 | from mitsuba.core import xml 21 | 22 | sampler = xml.load_dict({ 23 | "type" : "multijitter", 24 | "sample_count" : 1024, 25 | }) 26 | 27 | check_uniform_wavefront_sampler(sampler) 28 | -------------------------------------------------------------------------------- /src/samplers/tests/test_orthogonal.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | import numpy as np 5 | 6 | from .utils import check_uniform_scalar_sampler, check_uniform_wavefront_sampler 7 | 8 | def test01_orthogonal_scalar(variant_scalar_rgb): 9 | from mitsuba.core import xml 10 | 11 | sampler = xml.load_dict({ 12 | "type" : "orthogonal", 13 | "sample_count" : 1369, 14 | }) 15 | 16 | check_uniform_scalar_sampler(sampler, res=4, atol=4.0) 17 | 18 | 19 | def test02_orthogonal_wavefront(variant_gpu_rgb): 20 | from mitsuba.core import xml 21 | 22 | sampler = xml.load_dict({ 23 | "type" : "orthogonal", 24 | "sample_count" : 1369, 25 | }) 26 | 27 | check_uniform_wavefront_sampler(sampler, atol=4.0) 28 | -------------------------------------------------------------------------------- /src/samplers/tests/test_stratified.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | import numpy as np 5 | 6 | from .utils import check_uniform_scalar_sampler, check_uniform_wavefront_sampler 7 | 8 | def test01_stratified_scalar(variant_scalar_rgb): 9 | from mitsuba.core import xml 10 | 11 | sampler = xml.load_dict({ 12 | "type" : "stratified", 13 | "sample_count" : 1024, 14 | }) 15 | 16 | check_uniform_scalar_sampler(sampler) 17 | 18 | 19 | def test02_stratified_wavefront(variant_gpu_rgb): 20 | from mitsuba.core import xml 21 | 22 | sampler = xml.load_dict({ 23 | "type" : "stratified", 24 | "sample_count" : 1024, 25 | }) 26 | 27 | check_uniform_wavefront_sampler(sampler) 28 | -------------------------------------------------------------------------------- /src/samplers/tests/utils.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | import numpy as np 5 | 6 | def check_uniform_scalar_sampler(sampler, res=16, atol=0.5): 7 | sampler.seed(0) 8 | sample_count = sampler.sample_count() 9 | 10 | hist_1d = np.zeros(res) 11 | hist_2d = np.zeros((res, res)) 12 | 13 | for i in range(sample_count): 14 | v_1d = sampler.next_1d() 15 | hist_1d[int(v_1d * res)] += 1 16 | v_2d = sampler.next_2d() 17 | hist_2d[int(v_2d.x * res), int(v_2d.y * res)] += 1 18 | sampler.advance() 19 | 20 | # print(hist_1d) 21 | # print(hist_2d) 22 | 23 | assert ek.allclose(hist_1d, float(sample_count) / res, atol=atol) 24 | assert ek.allclose(hist_2d, float(sample_count) / (res * res), atol=atol) 25 | 26 | 27 | def check_uniform_wavefront_sampler(sampler, res=16, atol=0.5): 28 | from mitsuba.core import Float, UInt32, UInt64, Vector2u 29 | 30 | sample_count = sampler.sample_count() 31 | sampler.set_samples_per_wavefront(sample_count) 32 | 33 | sampler.seed(0, sample_count) 34 | 35 | hist_1d = ek.zero(UInt32, res) 36 | hist_2d = ek.zero(UInt32, res * res) 37 | 38 | v_1d = ek.clamp(sampler.next_1d() * res, 0, res) 39 | ek.scatter_add( 40 | target=hist_1d, 41 | index=UInt32(v_1d), 42 | source=UInt32(1.0) 43 | ) 44 | 45 | v_2d = Vector2u(ek.clamp(sampler.next_2d() * res, 0, res)) 46 | ek.scatter_add( 47 | target=hist_2d, 48 | index=UInt32(v_2d.x * res + v_2d.y), 49 | source=UInt32(1.0) 50 | ) 51 | 52 | assert ek.allclose(Float(hist_1d), float(sample_count) / res, atol=atol) 53 | assert ek.allclose(Float(hist_2d), float(sample_count) / (res * res), atol=atol) 54 | -------------------------------------------------------------------------------- /src/sensors/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "sensors") 2 | 3 | add_plugin(perspective perspective.cpp) 4 | add_plugin(radiancemeter radiancemeter.cpp) 5 | add_plugin(thinlens thinlens.cpp) 6 | add_plugin(irradiancemeter irradiancemeter.cpp) 7 | 8 | # Register the test directory 9 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 10 | -------------------------------------------------------------------------------- /src/sensors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/sensors/__init__.py -------------------------------------------------------------------------------- /src/sensors/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/sensors/tests/__init__.py -------------------------------------------------------------------------------- /src/shapes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "shapes") 2 | 3 | add_plugin(obj obj.cpp) 4 | add_plugin(ply ply.cpp) 5 | add_plugin(blender blender.cpp) 6 | add_plugin(serialized serialized.cpp) 7 | 8 | add_plugin(cylinder cylinder.cpp) 9 | add_plugin(disk disk.cpp) 10 | add_plugin(rectangle rectangle.cpp) 11 | add_plugin(sphere sphere.cpp) 12 | 13 | add_plugin(shapegroup shapegroup.cpp) 14 | add_plugin(instance instance.cpp) 15 | 16 | if (MTS_ENABLE_EMBREE) 17 | target_link_libraries(sphere PRIVATE embree) 18 | target_link_libraries(instance PRIVATE embree) 19 | endif() 20 | 21 | # Register the test directory 22 | add_tests(${CMAKE_CURRENT_SOURCE_DIR}/tests) 23 | -------------------------------------------------------------------------------- /src/shapes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/shapes/__init__.py -------------------------------------------------------------------------------- /src/shapes/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitsuba-renderer/mitsuba2/62863cb3b58ab81ac5161908f39c2bfbf928cb9d/src/shapes/tests/__init__.py -------------------------------------------------------------------------------- /src/spectra/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "spectra") 2 | 3 | add_plugin(blackbody blackbody.cpp) 4 | add_plugin(uniform uniform.cpp) 5 | add_plugin(regular regular.cpp) 6 | add_plugin(irregular irregular.cpp) 7 | add_plugin(d65 d65.cpp) 8 | add_plugin(srgb srgb.cpp) 9 | add_plugin(srgb_d65 srgb_d65.cpp) 10 | -------------------------------------------------------------------------------- /src/spectra/tests/test_d65.py: -------------------------------------------------------------------------------- 1 | import mitsuba 2 | import pytest 3 | import enoki as ek 4 | 5 | 6 | def test01_chi2(variant_packet_spectral): 7 | from mitsuba.python.chi2 import SpectrumAdapter, ChiSquareTest, LineDomain 8 | 9 | sample_func, pdf_func = SpectrumAdapter( 10 | '') 11 | 12 | chi2 = ChiSquareTest( 13 | domain=LineDomain(bounds=[360.0, 830.0]), 14 | sample_func=sample_func, 15 | pdf_func=pdf_func, 16 | sample_dim=1 17 | ) 18 | 19 | assert chi2.run() 20 | -------------------------------------------------------------------------------- /src/spectra/tests/test_irregular.py: -------------------------------------------------------------------------------- 1 | # Only superficial testing here, main coverage achieved 2 | # via 'src/libcore/tests/test_distr.py' 3 | 4 | import mitsuba 5 | import pytest 6 | import enoki as ek 7 | 8 | 9 | @pytest.fixture() 10 | def obj(): 11 | return mitsuba.core.xml.load_string(''' 12 | 13 | 14 | 15 | ''') 16 | 17 | 18 | def test01_eval(variant_scalar_spectral, obj): 19 | from mitsuba.render import SurfaceInteraction3f 20 | 21 | si = SurfaceInteraction3f() 22 | 23 | values = [0, 1, 1.5, 2, .5, 0] 24 | for i in range(6): 25 | si.wavelengths = 450 + 50 * i 26 | assert ek.allclose(obj.eval(si), values[i]) 27 | assert ek.allclose(obj.pdf_spectrum(si), values[i] / 212.5) 28 | 29 | with pytest.raises(RuntimeError) as excinfo: 30 | obj.eval_1(si) 31 | assert 'not implemented' in str(excinfo.value) 32 | 33 | with pytest.raises(RuntimeError) as excinfo: 34 | obj.eval_3(si) 35 | assert 'not implemented' in str(excinfo.value) 36 | 37 | 38 | def test02_sample_spectrum(variant_scalar_spectral, obj): 39 | from mitsuba.render import SurfaceInteraction3f 40 | 41 | si = SurfaceInteraction3f() 42 | assert ek.allclose(obj.sample_spectrum(si, 0), [500, 212.5]) 43 | assert ek.allclose(obj.sample_spectrum(si, 1), [650, 212.5]) 44 | assert ek.allclose( 45 | obj.sample_spectrum(si, .5), 46 | [576.777, 212.5] 47 | ) 48 | -------------------------------------------------------------------------------- /src/spectra/tests/test_regular.py: -------------------------------------------------------------------------------- 1 | # Only superficial testing here, main coverage achieved 2 | # via 'src/libcore/tests/test_distr.py' 3 | 4 | import mitsuba 5 | import pytest 6 | import enoki as ek 7 | 8 | 9 | @pytest.fixture() 10 | def obj(): 11 | return mitsuba.core.xml.load_string(''' 12 | 13 | 14 | 15 | 16 | ''') 17 | 18 | 19 | def test01_eval(variant_scalar_spectral, obj): 20 | from mitsuba.render import SurfaceInteraction3f 21 | 22 | si = SurfaceInteraction3f() 23 | 24 | values = [0, 1, 1.5, 2, 0] 25 | for i in range(5): 26 | si.wavelengths = 450 + 50 * i 27 | assert ek.allclose(obj.eval(si), values[i]) 28 | assert ek.allclose(obj.pdf_spectrum(si), values[i] / 150.0) 29 | 30 | with pytest.raises(RuntimeError) as excinfo: 31 | obj.eval_1(si) 32 | assert 'not implemented' in str(excinfo.value) 33 | 34 | with pytest.raises(RuntimeError) as excinfo: 35 | obj.eval_3(si) 36 | assert 'not implemented' in str(excinfo.value) 37 | 38 | 39 | def test02_sample_spectrum(variant_scalar_spectral, obj): 40 | from mitsuba.render import SurfaceInteraction3f 41 | 42 | si = SurfaceInteraction3f() 43 | assert ek.allclose(obj.sample_spectrum(si, 0), [500, 150]) 44 | assert ek.allclose(obj.sample_spectrum(si, 1), [600, 150]) 45 | assert ek.allclose( 46 | obj.sample_spectrum(si, .5), 47 | [500 + 100 * (ek.sqrt(10) / 2 - 1), 150] 48 | ) 49 | -------------------------------------------------------------------------------- /src/textures/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MTS_PLUGIN_PREFIX "textures") 2 | 3 | add_plugin(bitmap bitmap.cpp) 4 | add_plugin(checkerboard checkerboard.cpp) 5 | add_plugin(constvolume constant3d.cpp) 6 | add_plugin(gridvolume grid3d.cpp) 7 | add_plugin(mesh_attribute mesh_attribute.cpp) 8 | -------------------------------------------------------------------------------- /src/textures/constant3d.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(mitsuba) 6 | 7 | template 8 | class ConstVolume final : public Volume { 9 | public: 10 | MTS_IMPORT_BASE(Volume, m_world_to_local) 11 | MTS_IMPORT_TYPES(Texture) 12 | 13 | ConstVolume(const Properties &props) : Base(props) { 14 | m_color = props.texture("color", 1.f); 15 | } 16 | 17 | UnpolarizedSpectrum eval(const Interaction3f &it, Mask active) const override { 18 | MTS_MASKED_FUNCTION(ProfilerPhase::TextureEvaluate, active); 19 | return eval_impl(it, active); 20 | } 21 | 22 | Float eval_1(const Interaction3f & /* it */, Mask /* active */) const override { 23 | return m_color->mean(); 24 | } 25 | 26 | 27 | MTS_INLINE auto eval_impl(const Interaction3f &it, const Mask &active) const { 28 | SurfaceInteraction3f si; 29 | si.uv = Point2f(0.f, 0.f); 30 | si.wavelengths = it.wavelengths; 31 | si.time = it.time; 32 | auto result = m_color->eval(si, active); 33 | return result; 34 | } 35 | 36 | 37 | ScalarFloat max() const override { NotImplementedError("max"); } 38 | 39 | void traverse(TraversalCallback *callback) override { 40 | callback->put_object("color", m_color.get()); 41 | } 42 | 43 | std::string to_string() const override { 44 | std::ostringstream oss; 45 | oss << "ConstVolume[" << std::endl 46 | << " world_to_local = " << m_world_to_local << "," << std::endl 47 | << " color = " << m_color << std::endl 48 | << "]"; 49 | return oss.str(); 50 | } 51 | 52 | MTS_DECLARE_CLASS() 53 | protected: 54 | ref m_color; 55 | }; 56 | 57 | MTS_IMPLEMENT_CLASS_VARIANT(ConstVolume, Volume) 58 | MTS_EXPORT_PLUGIN(ConstVolume, "Constant 3D texture") 59 | NAMESPACE_END(mitsuba) 60 | --------------------------------------------------------------------------------