├── .clang-format ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── FindTriSYCL.cmake └── triSYCLConfig.cmake.in ├── doc ├── MovingSpheres.jpg ├── SmokeSphere.jpg ├── XilinxRT.jpg ├── XilinxRect.jpg ├── checker.jpg ├── materials.jpg └── pyramid.jpg ├── images ├── SYCL.png └── Xilinx.jpg ├── include ├── box.hpp ├── build_parameters.hpp ├── camera.hpp ├── constant_medium.hpp ├── hitable.hpp ├── material.hpp ├── ray.hpp ├── rectangle.hpp ├── render.hpp ├── rtweekend.hpp ├── sphere.hpp ├── sycl.hpp ├── texture.hpp ├── triangle.hpp ├── vec.hpp ├── visit.hpp └── xorshift.hpp └── src └── main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -1 4 | BreakInheritanceList: BeforeComma 5 | BreakConstructorInitializers: BeforeComma 6 | Cpp11BracedListStyle: false 7 | PointerAlignment: Left 8 | SpaceBeforeCpp11BracedList: true 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | option(USE_SINGLE_TASK "Use a SYCL executor that loops over pixel in one task instead of using a parallel_for(), better for FPGA" OFF) 4 | option(SANITIZE_THREADS "Activate thread sanitizer" OFF) 5 | set(SYCL_CXX_COMPILER "" CACHE STRING "Path to the SYCL compiler. Defaults to using triSYCL CPU implementation" ) 6 | # Use SYCL host device by default 7 | set(SYCL_DEVICE_TRIPLE "" CACHE STRING "Device triple to be used. only used with SYCL_CXX_COMPILER") 8 | set(SYCL_BACKEND_OPTIONS "" CACHE STRING "Options to pass to the backend compiler") 9 | 10 | if (NOT "${SYCL_CXX_COMPILER}" STREQUAL "") 11 | set(CMAKE_CXX_COMPILER "${SYCL_CXX_COMPILER}" CACHE FILEPATH ${SYCL_CXX_COMPILER} FORCE) 12 | endif() 13 | 14 | project(SYCL-path-tracer LANGUAGES CXX) 15 | set(CMAKE_CXX_STANDARD 20) 16 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | set(CMAKE_CXX_EXTENSIONS OFF) 18 | 19 | # Use triSYCL 20 | include(FindtriSYCL) 21 | 22 | if (NOT "${SYCL_CXX_COMPILER}" STREQUAL "") 23 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsycl -Xsycl-target-frontend -fno-exceptions") 24 | if (NOT "${SYCL_DEVICE_TRIPLE}" STREQUAL "") 25 | message(STATUS "targeting: ${SYCL_DEVICE_TRIPLE}") 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsycl-targets=${SYCL_DEVICE_TRIPLE}") 27 | endif() 28 | if (NOT "${SYCL_BACKEND_OPTIONS}" STREQUAL "") 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xsycl-target-backend \"${SYCL_BACKEND_OPTIONS}\"") 30 | endif() 31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_SYCL_COMPILER") 32 | endif() 33 | 34 | 35 | # Set a default build type if none was specified 36 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 37 | message(STATUS "Setting build type to Release as none was specified.") 38 | set(CMAKE_BUILD_TYPE "Release" CACHE 39 | STRING "Choose the type of build." FORCE) 40 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 41 | "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 42 | endif() 43 | 44 | if(NOT OUTPUT_WIDTH) 45 | message(STATUS "Setting output width to 800 as none was specified.") 46 | set(OUTPUT_WIDTH "800" CACHE 47 | STRING "Image width in pixel" FORCE) 48 | endif() 49 | 50 | if(NOT OUTPUT_HEIGHT) 51 | message(STATUS "Setting output height to 480 as none was specified.") 52 | set(OUTPUT_HEIGHT "480" CACHE 53 | STRING "Image height in pixel" FORCE) 54 | endif() 55 | 56 | 57 | set(SYCL_RT_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 58 | set(SYCL_RT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) 59 | 60 | add_executable(sycl-rt ${SYCL_RT_SRC_DIR}/main.cpp) 61 | 62 | target_include_directories(sycl-rt PRIVATE ${SYCL_RT_INCLUDE_DIR}) 63 | target_compile_definitions(sycl-rt PRIVATE OUTPUT_WIDTH=${OUTPUT_WIDTH}) 64 | target_compile_definitions(sycl-rt PRIVATE OUTPUT_HEIGHT=${OUTPUT_HEIGHT}) 65 | 66 | # This is a SYCL program 67 | if ("${SYCL_CXX_COMPILER}" STREQUAL "") 68 | add_sycl_to_target(sycl-rt) 69 | else() 70 | #target_include_directories(sycl-rt PRIVATE ${TRISYCL_INCLUDE_DIR}/) 71 | endif() 72 | 73 | # Use C+20 74 | target_compile_features(sycl-rt PRIVATE cxx_std_20) 75 | 76 | if (SANITIZE_THREADS) 77 | target_compile_options(sycl-rt PRIVATE 78 | -fno-omit-frame-pointer -fsanitize=thread) 79 | target_link_options(sycl-rt PRIVATE -fsanitize=thread) 80 | endif() 81 | # To use various code sanitizer: 82 | #target_compile_options(sycl-rt PRIVATE 83 | # -fno-omit-frame-pointer -fsanitize=address) 84 | #target_link_options(sycl-rt PRIVATE -fsanitize=address) 85 | #target_compile_options(sycl-rt PRIVATE 86 | # -fno-omit-frame-pointer -fsanitize=undefined) 87 | #target_link_options(sycl-rt PRIVATE -fsanitize=undefined) 88 | #target_compile_options(sycl-rt PRIVATE 89 | # -fno-omit-frame-pointer -fstack-check) 90 | #target_link_options(sycl-rt PRIVATE -fstack-check) 91 | 92 | 93 | if(USE_SINGLE_TASK) 94 | # On FPGA use a loop on image pixels instead of a parallel_for 95 | set_property(TARGET sycl-rt 96 | APPEND PROPERTY 97 | COMPILE_DEFINITIONS USE_SINGLE_TASK=) 98 | endif() 99 | 100 | message(STATUS "path_tracer USE_SINGLE_TASK: ${USE_SINGLE_TASK}") 101 | message(STATUS "path_tracer SANITIZE_THREADS: ${SANITIZE_THREADS}") 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Path tracer 2 | 3 | This is an experimental path tracer using C++20 and SYCL for 4 | acceleration on devices like FPGA or GPU by using a direct 5 | implementation. 6 | 7 | ![img](doc/SmokeSphere.jpg) 8 | 9 | This is using triSYCL for now but it might work even better on some 10 | other SYCL implementations. Contributions and optimizations welcome! 11 | 12 | The main focus here is to study how to replace classic features like pointers or 13 | dynamic polymorphism that does not work (well) on heterogeneous with 14 | more modern constructs such as `std::variant` and `std::visit`. 15 | 16 | ## Features 17 | 18 | - motion blur; 19 | - depth of field; 20 | - materials: 21 | - smoke; 22 | - textures; 23 | - Lambertian material; 24 | - dielectric material; 25 | - metallic roughness; 26 | - light; 27 | - geometry: 28 | - spheres; 29 | - triangles; 30 | - x/y/z-rectangles; 31 | - boxes; 32 | 33 | ## Required dependancies 34 | 35 | In addition to triSYCL, this project requires the following dependancies: 36 | 37 | - the [stb](https://github.com/nothings/stb) image manipulation library; 38 | 39 | On Linux, there is a good chance it can be installed with your package 40 | manager : 41 | 42 | On Ubuntu/Debian : 43 | 44 | ```sh 45 | sudo apt install libstb-dev 46 | ``` 47 | 48 | On Archlinux, install the [stb](https://aur.archlinux.org/packages/stb) package from AUR. 49 | 50 | ## Compiling 51 | 52 | Clone the reposity such as with: 53 | ```sh 54 | git clone git@github.com:triSYCL/path_tracer.git 55 | ``` 56 | 57 | Create a `build` directory for example inside the cloned repository 58 | and jump into it. 59 | 60 | From there, assuming you have the https://github.com/triSYCL/triSYCL 61 | repository somewhere, run: 62 | ```sh 63 | cmake .. -DCMAKE_MODULE_PATH=/triSYCL/cmake 64 | ``` 65 | 66 | The project defaults to a Release build configuration. 67 | If you wish to debug, configure your build settings as follow: 68 | 69 | ```sh 70 | cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MODULE_PATH=/triSYCL/cmake 71 | ``` 72 | 73 | It is also possible to build with https://github.com/triSYCL/sycl or https://github.com/intel/llvm/tree/sycl 74 | ```sh 75 | cmake .. --DCMAKE_MODULE_PATH=/triSYCL/cmake -DTRISYCL_OPENMP=OFF -DSYCL_CXX_COMPILER=/bin/clang++ -DSYCL_DEVICE_TRIPLE=fpga64_sw_emu 76 | # the triple fpga64_sw_emu is only available with https://github.com/triSYCL/sycl 77 | ``` 78 | 79 | The triSYCL cmake path and options are required for some cmake macros they define. 80 | 81 | For FPGA execution you might add `-DUSE_SINGLE_TASK=ON` on the 82 | previous `cmake` configuration to use a SYCL execution based on a 83 | `.single_task()` instead of `.parallel_for()`, probably more efficient 84 | on FPGA. 85 | 86 | Build the project with: 87 | ```sh 88 | cmake --build . --verbose --parallel `nproc` 89 | ``` 90 | This creates the executable. 91 | 92 | ## Running 93 | 94 | Now you can run the path tracer with: 95 | ```sh 96 | time ./sycl-rt 97 | ``` 98 | This results in the image `out.png` produced by the path tracer. 99 | 100 | 101 | ## Bibliography 102 | 103 | Some references that were tremendously useful in writing this project: 104 | 105 | 1. [Path tracing](https://en.wikipedia.org/wiki/Path_tracing) 106 | 107 | 2. [Ray Tracing in One Weekend - Peter 108 | Shirley](https://raytracing.github.io/books/RayTracingInOneWeekend.html) 109 | 110 | 3. [Ray Tracing: The Next Week - Peter 111 | Shirley](https://raytracing.github.io/books/RayTracingTheNextWeek.html) 112 | 113 | 4. [Ray-tracing in a Weekend with SYCL: Basic sphere tracing -- Georgi 114 | Mirazchiyski](https://www.codeplay.com/portal/blogs/2020/05/19/ray-tracing-in-a-weekend-with-sycl-basic-sphere-tracing.html) 115 | 116 | 5. [Ray-tracing in a Weekend with SYCL Part 2: Pixel sampling and 117 | Material tracing -- Georgi 118 | Mirazchiyski](https://www.codeplay.com/portal/blogs/2020/06/19/ray-tracing-in-a-weekend-with-sycl-part-2-pixel-sampling-and-material-tracing.html) 119 | 120 | 6. [CppCon 2018: Mateusz Pusz, “Effective replacement of dynamic 121 | polymorphism with 122 | std::variant”](https://www.youtube.com/watch?v=gKbORJtnVu8) 123 | 124 | 7. [Bartek's coding blog: Runtime Polymorphism with std::variant and 125 | std::visit](https://www.bfilipek.com/2020/04/variant-virtual-polymorphism.html) 126 | 127 | 8. [Intersection of a Ray/Segment with a 128 | Triangle](http://geomalgorithms.com/a06-_intersect-2.html) 129 | -------------------------------------------------------------------------------- /cmake/FindTriSYCL.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindTriSYCL 3 | #--------------- 4 | # 5 | # This file is distributed under the University of Illinois Open Source 6 | # License. See LICENSE.TXT for details. 7 | 8 | ######################### 9 | # FindTriSYCL.cmake 10 | ######################### 11 | # 12 | # Tools for finding and building with triSYCL. 13 | # 14 | # Requite CMake version 3.5 or higher 15 | 16 | cmake_minimum_required (VERSION 3.5) 17 | project(triSYCL CXX) # The name of the project (forward declare language) 18 | 19 | ####################### 20 | # set_target_cxx_std 21 | ####################### 22 | # 23 | # Sets the specified target to support the required C++ standard level. 24 | # 25 | # targetName : Name of the target to be set. 26 | # cxxStdYear : The year of the required C++ standard (e.g.: 17) 27 | # 28 | function(set_target_cxx_std targetName cxxStdYear) 29 | if(cxx_std_${cxxStdYear} IN_LIST CMAKE_CXX_COMPILE_FEATURES) 30 | set_property(TARGET ${targetName} 31 | PROPERTY 32 | INTERFACE_COMPILE_FEATURES cxx_std_${cxxStdYear}) 33 | else() 34 | if(MSVC) 35 | set(cxxStdFlag /std:c++${cxxStdYear}) 36 | else() 37 | set(cxxStdFlag -std=c++${cxxStdYear}) 38 | endif() 39 | include(CheckCXXCompilerFlag) 40 | check_cxx_compiler_flag(${cxxStdFlag} compilerHasCxxStdFlag) 41 | if(compilerHasCxxStdFlag) 42 | message(STATUS "CMake compile features not available for the current \ 43 | toolchain or CMake version, setting C++ standard level directly via compiler \ 44 | flags. Please be aware that setting flags doesn't check the actual C++${cxxStdYear} \ 45 | standard support provided by the underlying toolchain, e.g.: build may fail \ 46 | on unsupported features.") 47 | set_property(TARGET ${targetName} 48 | PROPERTY 49 | INTERFACE_COMPILE_OPTIONS ${cxxStdFlag}) 50 | else() 51 | message(WARNING "Compiler seems to be unable to accept ${cxxStdFlag}, 52 | build will probably fail. Please set CMAKE_CXX_FLAGS to some sensible value for \ 53 | your toolchain.") 54 | endif() 55 | endif() 56 | endfunction(set_target_cxx_std) 57 | 58 | add_library(_trisycl_cxxfeatures INTERFACE) 59 | add_library(triSYCL::cxxfeatures ALIAS _trisycl_cxxfeatures) 60 | 61 | # Check that a supported host compiler can be found 62 | if(CMAKE_COMPILER_IS_GNUCXX) 63 | # Require at least gcc 7 64 | if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7) 65 | message(FATAL_ERROR 66 | "host compiler - Not found! (gcc version must be at least 7)") 67 | else() 68 | message(STATUS "host compiler - gcc ${CMAKE_CXX_COMPILER_VERSION}") 69 | 70 | set_target_cxx_std(_trisycl_cxxfeatures 2a) 71 | target_compile_options(_trisycl_cxxfeatures 72 | INTERFACE 73 | # Turn on all warnings: 74 | -Wall 75 | -Wextra 76 | # Disable specific warnings: 77 | # warning: type qualifiers ignored on function return type 78 | -Wno-ignored-qualifiers 79 | # warning: comparison between signed and unsigned integer expressions 80 | -Wno-sign-compare 81 | # warning: ‘’ is deprecated 82 | -Wno-deprecated-declarations 83 | # warning: unused parameter ‘’ 84 | -Wno-unused-parameter 85 | # warning: ignoring attributes on template argument 86 | -Wno-ignored-attributes 87 | ) 88 | endif() 89 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 90 | # Require at least clang 7 91 | if (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 7) 92 | message(FATAL_ERROR 93 | "host compiler - Not found! (clang version must be at least 7)") 94 | else() 95 | message(STATUS "host compiler - clang ${CMAKE_CXX_COMPILER_VERSION}") 96 | 97 | set_target_cxx_std(_trisycl_cxxfeatures 2a) 98 | target_compile_options(_trisycl_cxxfeatures 99 | INTERFACE 100 | # Turn on all warnings 101 | -Wall 102 | -Wextra 103 | # Disable specific warnings: 104 | # warning: 'const' type qualifier on return type has no effect 105 | -Wno-ignored-qualifiers 106 | # warning: comparison between signed and unsigned integer expressions 107 | -Wno-sign-compare 108 | # warning: ‘’ is deprecated 109 | -Wno-deprecated-declarations 110 | # warning: unused parameter ‘’ 111 | -Wno-unused-parameter 112 | # warning: suggest braces around initialization of subobject 113 | -Wno-missing-braces 114 | # warning: unused variable '' 115 | -Wno-unused-variable 116 | # warning: instantiation of variable '' required here, 117 | # but no definition is available 118 | -Wno-undefined-var-template 119 | ) 120 | endif() 121 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 122 | # Change to /std:c++latest once Boost::funtional is fixed 123 | # (1.63.0 with toolset v141 not working) 124 | set_target_cxx_std(_trisycl_cxxfeatures 14) 125 | # Replace default Warning Level 3 with 4 (/Wall is pretty-much useless on MSVC 126 | # system headers are plagued with warnings) 127 | string(REGEX REPLACE "/W[0-9]" "/W4" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) 128 | 129 | target_compile_options(_trisycl_cxxfeatures 130 | INTERFACE 131 | # Disabling (default) Warning Level 3 output 132 | # warning C4996: Call to '' with parameters that may be 133 | # unsafe - this call relies on the caller to check that the passed values 134 | # are correct. 135 | /wd4996 136 | # warning C4267: '=': conversion from 'size_t' to 'int', possible loss of data 137 | /wd4267 138 | # warning C4244: '=': conversion from 'size_t' to 'double', 139 | # possible loss of data 140 | /wd4244 141 | # warning C4305: '': truncation from 'double' to 'float' 142 | /wd4305 143 | # warning C4101: '': unreferenced local variable 144 | /wd4101 145 | # warning C4700: uninitialized local variable '' used 146 | /wd4700 147 | # warning C4189: '': local variable is initialized but not referenced 148 | /wd4189 149 | # Disabling Warning Level 4 output 150 | # warning C4100: '': unreferenced formal parameter 151 | /wd4100 152 | # warning C4459: declaration of '' hides global declaration 153 | /wd4459 154 | # warning C4127: conditional expression is constant 155 | /wd4127 156 | # warning C4456: declaration of '' hides previous local declaration 157 | /wd4456 158 | ) 159 | else() 160 | message(WARNING 161 | "host compiler - Not found! (triSYCL supports GCC, Clang and MSVC)") 162 | endif() 163 | 164 | #triSYCL options 165 | option(TRISYCL_OPENMP "triSYCL multi-threading with OpenMP" ON) 166 | option(TRISYCL_TBB "triSYCL multi-threading with TBB" OFF) 167 | option(TRISYCL_OPENCL "triSYCL OpenCL interoperability mode" OFF) 168 | option(TRISYCL_NO_ASYNC "triSYCL use synchronous kernel execution" OFF) 169 | option(TRISYCL_DEBUG "triSYCL use debug mode" OFF) 170 | option(TRISYCL_DEBUG_STRUCTORS "triSYCL trace of object lifetimes" OFF) 171 | option(TRISYCL_TRACE_KERNEL "triSYCL trace of kernel execution" OFF) 172 | option(TRISYCL_INCLUDE_DIR "triSYCL include directory" ON) 173 | 174 | mark_as_advanced(TRISYCL_OPENMP) 175 | mark_as_advanced(TRISYCL_TBB) 176 | mark_as_advanced(TRISYCL_OPENCL) 177 | mark_as_advanced(TRISYCL_NO_ASYNC) 178 | mark_as_advanced(TRISYCL_DEBUG) 179 | mark_as_advanced(TRISYCL_DEBUG_STRUCTORS) 180 | mark_as_advanced(TRISYCL_TRACE_KERNEL) 181 | mark_as_advanced(TRISYCL_INCLUDE_DIR) 182 | 183 | #triSYCL definitions 184 | set(CL_SYCL_LANGUAGE_VERSION 121 CACHE STRING VERSION 185 | "Host language version to be used by triSYCL (default is: 121)") 186 | set(TRISYCL_CL_LANGUAGE_VERSION 121 CACHE STRING VERSION 187 | "Device language version to be used by triSYCL (default is: 121)") 188 | set(CMAKE_CXX_STANDARD 17) 189 | set(CXX_STANDARD_REQUIRED ON) 190 | 191 | 192 | if(NOT TRISYCL_INCLUDE_DIR) 193 | set(TRISYCL_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) 194 | endif() 195 | 196 | if(EXISTS ${TRISYCL_INCLUDE_DIR}) 197 | message(STATUS "Found triSYCL include directory: " ${TRISYCL_INCLUDE_DIR}) 198 | else() 199 | message(FATAL_ERROR "triSYCL include directory - no found! " 200 | ${TRISYCL_INCLUDE_DIR}) 201 | endif() 202 | 203 | 204 | # Find OpenCL package 205 | if(TRISYCL_OPENCL) 206 | find_package(OpenCL REQUIRED) 207 | if(UNIX) 208 | set(BOOST_COMPUTE_INCPATH /usr/include/compute CACHE PATH 209 | "Path to Boost.Compute headers (default is: /usr/include/compute)") 210 | endif(UNIX) 211 | endif() 212 | 213 | # Find OpenMP package 214 | if(TRISYCL_OPENMP) 215 | find_package(OpenMP REQUIRED) 216 | endif() 217 | 218 | # Find TBB package 219 | if(TRISYCL_TBB) 220 | find_package(TBB REQUIRED) 221 | endif() 222 | 223 | # Find Boost 224 | set(BOOST_REQUIRED_COMPONENTS chrono fiber log thread) 225 | 226 | if(TRISYCL_OPENCL) 227 | list(APPEND BOOST_REQUIRED_COMPONENTS filesystem) 228 | endif() 229 | find_package(Boost 1.65 REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) 230 | 231 | # If debug or trace we need boost log 232 | if(TRISYCL_DEBUG OR TRISYCL_DEBUG_STRUCTORS OR TRISYCL_TRACE_KERNEL) 233 | set(LOG_NEEDED ON) 234 | else() 235 | set(LOG_NEEDED OFF) 236 | endif() 237 | 238 | 239 | message(STATUS "triSYCL OpenMP: ${TRISYCL_OPENMP}") 240 | message(STATUS "triSYCL TBB: ${TRISYCL_TBB}") 241 | message(STATUS "triSYCL OpenCL: ${TRISYCL_OPENCL}") 242 | message(STATUS "triSYCL synchronous execution: ${TRISYCL_NO_ASYNC}") 243 | message(STATUS "triSYCL debug mode: ${TRISYCL_DEBUG}") 244 | message(STATUS "triSYCL object trace: ${TRISYCL_DEBUG_STRUCTORS}") 245 | message(STATUS "triSYCL kernel trace: ${TRISYCL_TRACE_KERNEL}") 246 | 247 | find_package(Threads REQUIRED) 248 | 249 | ####################### 250 | # add_sycl_to_target 251 | ####################### 252 | # 253 | # Sets the proper flags and includes for the target compilation. 254 | # 255 | # targetName : Name of the target to add a SYCL to. 256 | # 257 | function(add_sycl_to_target targetName) 258 | # Add include directories to the "#include <>" paths 259 | target_include_directories(${targetName} PUBLIC 260 | ${TRISYCL_INCLUDE_DIR} 261 | ${Boost_INCLUDE_DIRS} 262 | $<$:${OpenCL_INCLUDE_DIRS}> 263 | $<$:${BOOST_COMPUTE_INCPATH}>) 264 | 265 | # Link dependencies 266 | target_link_libraries(${targetName} PUBLIC 267 | triSYCL::cxxfeatures 268 | $<$:${OpenCL_LIBRARIES}> 269 | Threads::Threads 270 | $<$:Boost::log> 271 | Boost::chrono 272 | Boost::fiber 273 | Boost::thread 274 | $<$:Boost::filesystem> #Required by BOOST_COMPUTE_USE_OFFLINE_CACHE. 275 | ${GTKMM_LIBRARIES}) 276 | 277 | # Compile definitions 278 | target_compile_definitions(${targetName} PUBLIC 279 | $<$:TRISYCL_NO_ASYNC> 280 | $<$:TRISYCL_OPENCL> 281 | $<$:BOOST_COMPUTE_USE_OFFLINE_CACHE> 282 | $<$:TRISYCL_DEBUG> 283 | $<$:TRISYCL_DEBUG_STRUCTORS> 284 | $<$:TRISYCL_TRACE_KERNEL> 285 | $<$:BOOST_LOG_DYN_LINK>) 286 | 287 | # C++ and OpenMP requirements 288 | target_compile_options(${targetName} PUBLIC 289 | ${TRISYCL_COMPILE_OPTIONS} 290 | $<$:${OpenMP_CXX_FLAGS}>) 291 | 292 | if(${TRISYCL_OPENMP}) 293 | set_target_properties(${targetName} 294 | PROPERTIES 295 | LINK_FLAGS ${OpenMP_CXX_FLAGS}) 296 | endif(${TRISYCL_OPENMP}) 297 | 298 | # C++ and TBB requirements 299 | if(${TRISYCL_TBB}) 300 | target_compile_definitions(${targetName} PUBLIC -DTRISYCL_TBB) 301 | 302 | target_include_directories(${targetName} PUBLIC ${TBB_INCLUDE_DIR}) 303 | target_link_libraries(${targetName} PUBLIC ${TBB_LIBRARIES}) 304 | endif(${TRISYCL_TBB}) 305 | 306 | endfunction(add_sycl_to_target) -------------------------------------------------------------------------------- /cmake/triSYCLConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # https://stackoverflow.com/questions/47718485/install-and-export-interface-only-library-cmake 2 | 3 | @PACKAGE_INIT@ 4 | 5 | include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") 6 | 7 | check_required_components("@PROJECT_NAME@") 8 | 9 | # Set this for the EIGEN library 10 | set (TRISYCL_INCLUDE_DIR "@PACKAGE_TRISYCL_INCLUDE_DIR@") 11 | set (TRISYCL_FOUND 1) -------------------------------------------------------------------------------- /doc/MovingSpheres.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/doc/MovingSpheres.jpg -------------------------------------------------------------------------------- /doc/SmokeSphere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/doc/SmokeSphere.jpg -------------------------------------------------------------------------------- /doc/XilinxRT.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/doc/XilinxRT.jpg -------------------------------------------------------------------------------- /doc/XilinxRect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/doc/XilinxRect.jpg -------------------------------------------------------------------------------- /doc/checker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/doc/checker.jpg -------------------------------------------------------------------------------- /doc/materials.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/doc/materials.jpg -------------------------------------------------------------------------------- /doc/pyramid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/doc/pyramid.jpg -------------------------------------------------------------------------------- /images/SYCL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/images/SYCL.png -------------------------------------------------------------------------------- /images/Xilinx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triSYCL/path_tracer/88bdd77cfc1b8a9ff5afb553d5aaad62339db9dc/images/Xilinx.jpg -------------------------------------------------------------------------------- /include/box.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOX_HPP 2 | #define BOX_HPP 3 | 4 | #include "rectangle.hpp" 5 | #include "rtweekend.hpp" 6 | #include "visit.hpp" 7 | 8 | /// This class implements a axis aligned cuboid using 6 rectangles 9 | class box { 10 | public: 11 | box() = default; 12 | 13 | /// p0 = { x0, y0, z0 } and p1 = { x1, y1. z1 } 14 | /// where x0 <= x1, y0 <= y1 and z0 <= z1 15 | box(const point& p0, const point& p1, const material_t& mat_type) 16 | : box_min { p0 } 17 | , box_max { p1 } 18 | , material_type { mat_type } { 19 | /// Add six sides of the box based on box_min and box_max to sides 20 | sides[0] = xy_rect(p0.x(), p1.x(), p0.y(), p1.y(), p1.z(), mat_type); 21 | sides[1] = xy_rect(p0.x(), p1.x(), p0.y(), p1.y(), p0.z(), mat_type); 22 | sides[2] = xz_rect(p0.x(), p1.x(), p0.z(), p1.z(), p1.y(), mat_type); 23 | sides[3] = xz_rect(p0.x(), p1.x(), p0.z(), p1.z(), p0.y(), mat_type); 24 | sides[4] = yz_rect(p0.y(), p1.y(), p0.z(), p1.z(), p1.x(), mat_type); 25 | sides[5] = yz_rect(p0.y(), p1.y(), p0.z(), p1.z(), p0.x(), mat_type); 26 | } 27 | 28 | /// Compute ray interaction with the box 29 | bool hit(auto& ctx, const ray& r, real_t min, real_t max, hit_record& rec, 30 | material_t& hit_material_type) const { 31 | hit_record temp_rec; 32 | material_t temp_material_type; 33 | auto hit_anything = false; 34 | auto closest_so_far = max; 35 | // Checking if the ray hits any of the sides 36 | for (const auto& side : sides) { 37 | if (dev_visit( 38 | [&](auto&& arg) { 39 | return arg.hit(ctx, r, min, closest_so_far, temp_rec, 40 | temp_material_type); 41 | }, 42 | side)) { 43 | hit_anything = true; 44 | closest_so_far = temp_rec.t; 45 | rec = temp_rec; 46 | hit_material_type = temp_material_type; 47 | } 48 | } 49 | return hit_anything; 50 | } 51 | 52 | point box_min; 53 | point box_max; 54 | material_t material_type; 55 | std::array sides; 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/build_parameters.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BUILD_PARAMETERS_HPP 2 | #define BUILD_PARAMETERS_HPP 3 | namespace buildparams { 4 | 5 | #ifdef USE_SINGLE_TASK 6 | constexpr bool use_single_task = true; 7 | #else 8 | constexpr bool use_single_task = false; 9 | #endif 10 | 11 | #ifdef USE_SYCL_COMPILER 12 | constexpr bool use_sycl_compiler = USE_SYCL_COMPILER; 13 | #else 14 | constexpr bool use_sycl_compiler = false; 15 | #endif 16 | 17 | constexpr int output_width = OUTPUT_WIDTH; 18 | constexpr int output_height = OUTPUT_HEIGHT; 19 | } // namespace buildparams 20 | 21 | #endif // BUILD_PARAMETERS_HPP 22 | -------------------------------------------------------------------------------- /include/camera.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RT_SYCL_CAMERA_H 2 | #define RT_SYCL_CAMERA_H 3 | 4 | #include 5 | 6 | #include "ray.hpp" 7 | #include "rtweekend.hpp" 8 | 9 | /** Camera model 10 | 11 | This implements: 12 | 13 | - 14 | https://raytracing.github.io/books/RayTracingInOneWeekend.html#positionablecamera 15 | 16 | - 17 | https://raytracing.github.io/books/RayTracingInOneWeekend.html#defocusblur 18 | */ 19 | class camera { 20 | 21 | /// Position of the camera 22 | point origin; 23 | 24 | /// Lower left corner of the camera plane 25 | point lower_left_corner; 26 | 27 | /// Horizontal vector of the camera plane 28 | vec horizontal; 29 | 30 | /// Vertical vector of the camera plane 31 | vec vertical; 32 | 33 | /// Right axis of the camera plane 34 | vec u; 35 | 36 | /// Vertical axis of the camera plane 37 | vec v; 38 | 39 | /// Camera viewing direction 40 | vec w; 41 | 42 | /// Size of the lens simulating the depth-of-field 43 | real_t lens_radius; 44 | 45 | /// Shutter open and close times 46 | real_t time0, time1; 47 | 48 | public: 49 | /** Create a parameterized camera 50 | 51 | \param[in] look_from is the position of the camera 52 | 53 | \param[in] look_at is a point the camera is looking at 54 | 55 | \param[in] vup is the “view up” orientation for the 56 | camera. {0,1,0} means the usual vertical orientation 57 | 58 | \param[in] degree_vfov is the vertical field-of-view in degrees 59 | 60 | \param[in] aspect_ratio is the ratio between the camera image 61 | width and the camera image height 62 | 63 | \param[in] aperture is the lens aperture of the camera 64 | 65 | \param[in] focus_dist is the focus distance 66 | */ 67 | camera(const point& look_from, const point& look_at, const vec& vup, 68 | real_t degree_vfov, real_t aspect_ratio, real_t aperture, 69 | real_t focus_dist, real_t _time0 = 0, real_t _time1 = 0) 70 | : origin { look_from } { 71 | auto theta = degrees_to_radians(degree_vfov); 72 | auto h = sycl::tan(theta / 2); 73 | auto viewport_height = 2.0f * h; 74 | auto viewport_width = aspect_ratio * viewport_height; 75 | 76 | w = unit_vector(look_from - look_at); 77 | u = unit_vector(sycl::cross(vup, w)); 78 | v = sycl::cross(w, u); 79 | 80 | horizontal = focus_dist * viewport_width * u; 81 | vertical = focus_dist * viewport_height * v; 82 | lower_left_corner = origin - horizontal / 2 - vertical / 2 - focus_dist * w; 83 | 84 | lens_radius = aperture / 2; 85 | time0 = _time0; 86 | time1 = _time1; 87 | } 88 | 89 | /** Computes ray from camera passing through 90 | viewport local coordinates (s,t) based on viewport 91 | width, height and focus distance 92 | */ 93 | ray get_ray(real_t s, real_t t, LocalPseudoRNG& rng) const { 94 | vec rd = lens_radius * rng.in_unit_disk(); 95 | vec offset = u * rd.x() + v * rd.y(); 96 | return { origin + offset, 97 | lower_left_corner + s * horizontal + t * vertical - origin - 98 | offset, 99 | rng.float_t(time0, time1) }; 100 | } 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /include/constant_medium.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANT_MEDIUM_HPP 2 | #define CONSTANT_MEDIUM_HPP 3 | 4 | #include "box.hpp" 5 | #include "material.hpp" 6 | #include "sphere.hpp" 7 | #include "texture.hpp" 8 | #include "visit.hpp" 9 | 10 | using hittableVolume_t = std::variant; 11 | 12 | /** 13 | * A ray going through the volume can either make it all the way through 14 | * or be scattered at some point on or inside the volume. 15 | */ 16 | class constant_medium { 17 | public: 18 | constant_medium(const hittableVolume_t& b, real_t d, texture_t& a) 19 | : boundary { b } 20 | , neg_inv_density { -1 / d } 21 | , phase_function { isotropic_material { a } } {} 22 | 23 | constant_medium(const hittableVolume_t& b, real_t d, const color& a) 24 | : boundary { b } 25 | , neg_inv_density { -1 / d } 26 | , phase_function { isotropic_material { a } } {} 27 | 28 | bool hit(auto& ctx, const ray& r, real_t min, real_t max, hit_record& rec, 29 | material_t& hit_material_type) const { 30 | auto& rng = ctx.rng; 31 | hit_material_type = phase_function; 32 | material_t temp_material_type; 33 | hit_record rec1, rec2; 34 | if (!dev_visit( 35 | [&](auto&& arg) { 36 | return arg.hit(ctx, r, -infinity, infinity, rec1, 37 | temp_material_type); 38 | }, 39 | boundary)) { 40 | return false; 41 | } 42 | 43 | if (!dev_visit( 44 | [&](auto&& arg) { 45 | return arg.hit(ctx, r, rec1.t + 0.0001f, infinity, rec2, 46 | temp_material_type); 47 | }, 48 | boundary)) { 49 | return false; 50 | } 51 | 52 | if (rec1.t < min) 53 | rec1.t = min; 54 | if (rec2.t > max) 55 | rec2.t = max; 56 | if (rec1.t >= rec2.t) 57 | return false; 58 | if (rec1.t < 0) 59 | rec1.t = 0; 60 | 61 | const auto ray_length = sycl::length(r.direction()); 62 | /// Distance between the two hitpoints affect of probability 63 | /// of the ray hitting a smoke particle 64 | const auto distance_inside_boundary = (rec2.t - rec1.t) * ray_length; 65 | const auto hit_distance = neg_inv_density * sycl::log(rng.float_t()); 66 | 67 | /// With lower density, hit_distance has higher probabilty 68 | /// of being greater than distance_inside_boundary 69 | if (hit_distance > distance_inside_boundary) 70 | return false; 71 | 72 | rec.t = rec1.t + hit_distance / ray_length; 73 | rec.p = r.at(rec.t); 74 | 75 | rec.normal = vec { 1, 0, 0 }; // arbitrary 76 | rec.front_face = true; // also arbitrary 77 | return true; 78 | } 79 | 80 | hittableVolume_t boundary; 81 | real_t neg_inv_density; 82 | material_t phase_function; 83 | }; 84 | #endif 85 | -------------------------------------------------------------------------------- /include/hitable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITTABLE_H 2 | #define HITTABLE_H 3 | 4 | #include "ray.hpp" 5 | #include "rtweekend.hpp" 6 | #include "vec.hpp" 7 | 8 | class hit_record { 9 | public: 10 | float t; // 11 | point p; // hit point 12 | vec normal; // normal at hit point 13 | bool front_face; // to check if hit point is on the outer surface 14 | /*local coordinates for rectangles 15 | and mercator coordintes for spheres */ 16 | float u; 17 | float v; 18 | 19 | // To set if the hit point is on the front face 20 | void set_face_normal(const ray& r, const vec& outward_normal) { 21 | front_face = dot(r.direction(), outward_normal) < 0; 22 | normal = front_face ? outward_normal : vec {} - outward_normal; 23 | } 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/material.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RT_SYCL_MATERIAL_HPP 2 | #define RT_SYCL_MATERIAL_HPP 3 | 4 | #include 5 | 6 | #include "hitable.hpp" 7 | #include "texture.hpp" 8 | #include "vec.hpp" 9 | #include "visit.hpp" 10 | 11 | struct lambertian_material { 12 | lambertian_material() = default; 13 | lambertian_material(const color& a) 14 | : albedo { solid_texture { a } } {} 15 | lambertian_material(const texture_t& a) 16 | : albedo { a } {} 17 | 18 | bool scatter(auto& ctx, const ray& r_in, const hit_record& rec, 19 | color& attenuation, ray& scattered) const { 20 | auto& rng = ctx.rng; 21 | vec scatter_direction = rec.normal + rng.unit_vec(); 22 | scattered = ray(rec.p, scatter_direction, r_in.time()); 23 | // Attenuation of the ray hitting the object is modified based on the color 24 | // at hit point 25 | attenuation *= 26 | dev_visit([&](auto&& arg) { return arg.value(ctx, rec); }, albedo); 27 | return true; 28 | } 29 | color emitted(auto&, const hit_record& rec) { return color(0, 0, 0); } 30 | texture_t albedo; 31 | }; 32 | 33 | struct metal_material { 34 | metal_material() = default; 35 | metal_material(const color& a, float f) 36 | : albedo { a } 37 | , fuzz { std::clamp(f, 0.0f, 1.0f) } {} 38 | 39 | bool scatter(auto& ctx, const ray& r_in, const hit_record& rec, 40 | color& attenuation, ray& scattered) const { 41 | auto& rng = ctx.rng; 42 | vec reflected = reflect(unit_vector(r_in.direction()), rec.normal); 43 | scattered = ray(rec.p, reflected + fuzz * rng.in_unit_ball(), r_in.time()); 44 | // Attenuation of the ray hitting the object is modified based on the color 45 | // at hit point 46 | attenuation *= albedo; 47 | return (dot(scattered.direction(), rec.normal) > 0); 48 | } 49 | 50 | color emitted(auto&, const hit_record& rec) { return color(0, 0, 0); } 51 | color albedo; 52 | float fuzz; 53 | }; 54 | 55 | struct dielectric_material { 56 | dielectric_material() = default; 57 | dielectric_material(real_t ri, const color& albedo) 58 | : ref_idx { ri } 59 | , albedo { albedo } {} 60 | 61 | // Schlick's approximation for reflectance 62 | real_t reflectance(real_t cosine, real_t ref_idx) const { 63 | auto r0 = (1 - ref_idx) / (1 + ref_idx); 64 | r0 *= r0; 65 | return r0 + (1 - r0) * sycl::pow((1 - cosine), 5.0f); 66 | } 67 | 68 | bool scatter(auto& ctx, const ray& r_in, const hit_record& rec, 69 | color& attenuation, ray& scattered) const { 70 | // Attenuation of the ray hitting the object is modified based on the color 71 | // at hit point 72 | auto& rng = ctx.rng; 73 | attenuation *= albedo; 74 | float refraction_ratio = rec.front_face ? (1.0f / ref_idx) : ref_idx; 75 | vec unit_direction = unit_vector(r_in.direction()); 76 | float cos_theta = sycl::fmin(-sycl::dot(unit_direction, rec.normal), 1.0f); 77 | float sin_theta = sycl::sqrt(1.0f - cos_theta * cos_theta); 78 | bool cannot_refract = refraction_ratio * sin_theta > 1.0f; 79 | vec direction; 80 | if (cannot_refract || 81 | reflectance(cos_theta, refraction_ratio) > rng.float_t()) 82 | direction = reflect(unit_direction, rec.normal); 83 | else 84 | direction = refract(unit_direction, rec.normal, refraction_ratio); 85 | 86 | scattered = ray(rec.p, direction, r_in.time()); 87 | return true; 88 | } 89 | 90 | color emitted(auto&, const hit_record& rec) { return color(0, 0, 0); } 91 | // Refractive index of the glass 92 | real_t ref_idx; 93 | // Color of the glass 94 | color albedo; 95 | }; 96 | 97 | struct lightsource_material { 98 | lightsource_material() = default; 99 | lightsource_material(const texture_t& a) 100 | : emit { a } {} 101 | lightsource_material(const color& a) 102 | : emit { solid_texture { a } } {} 103 | 104 | template bool scatter(T&...) const { return false; } 105 | 106 | color emitted(auto& ctx, const hit_record& rec) { 107 | return dev_visit([&](auto&& arg) { return arg.value(ctx, rec); }, emit); 108 | } 109 | 110 | texture_t emit; 111 | }; 112 | 113 | struct isotropic_material { 114 | isotropic_material(const color& a) 115 | : albedo { solid_texture { a } } {} 116 | isotropic_material(texture_t& a) 117 | : albedo { a } {} 118 | 119 | bool scatter(auto& ctx, const ray& r_in, const hit_record& rec, 120 | color& attenuation, ray& scattered) const { 121 | auto& rng = ctx.rng; 122 | scattered = ray(rec.p, rng.in_unit_ball(), r_in.time()); 123 | attenuation *= 124 | dev_visit([&](auto&& arg) { return arg.value(ctx, rec); }, albedo); 125 | return true; 126 | } 127 | 128 | color emitted(auto&, const hit_record& rec) { return color(0, 0, 0); } 129 | 130 | texture_t albedo; 131 | }; 132 | 133 | using material_t = 134 | std::variant; 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /include/ray.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RT_SYCL_RAY_HPP 2 | #define RT_SYCL_RAY_HPP 3 | 4 | #include "vec.hpp" 5 | 6 | class ray { 7 | public: 8 | ray() = default; 9 | 10 | ray(const point& origin, const vec& direction, real_t time = 0.0f) 11 | : orig { origin } 12 | , dir { direction } 13 | , tm { time } {} 14 | 15 | point origin() const { return orig; } 16 | vec direction() const { return dir; } 17 | real_t time() const { return tm; } 18 | 19 | // returns point along the ray at distance t from ray's origin 20 | // the ray P(t) = Origin + t*direction 21 | point at(float t) const { return orig + t * dir; } 22 | 23 | public: 24 | // To store the origin and direction of the ray 25 | point orig; 26 | vec dir; 27 | real_t tm; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/rectangle.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RECT_HPP 2 | #define RECT_HPP 3 | 4 | #include "material.hpp" 5 | #include "ray.hpp" 6 | #include "rtweekend.hpp" 7 | #include "texture.hpp" 8 | #include "vec.hpp" 9 | 10 | /** The Following classes implement: 11 | 12 | - 13 | https://raytracing.github.io/books/RayTracingTheNextWeek.html#rectanglesandlights/creatingrectangleobjectsa 14 | */ 15 | 16 | class xy_rect { 17 | public: 18 | xy_rect() = default; 19 | 20 | /// x0 <= x1 and y0 <= y1 21 | xy_rect(real_t _x0, real_t _x1, real_t _y0, real_t _y1, real_t _k, 22 | const material_t& mat_type) 23 | : x0 { _x0 } 24 | , x1 { _x1 } 25 | , y0 { _y0 } 26 | , y1 { _y1 } 27 | , k { _k } 28 | , material_type { mat_type } {} 29 | 30 | /// Compute ray interaction with rectangle 31 | bool hit(auto&, const ray& r, real_t min, real_t max, hit_record& rec, 32 | material_t& hit_material_type) const { 33 | hit_material_type = material_type; 34 | 35 | auto t = (k - r.origin().z()) / r.direction().z(); 36 | if (t < min || t > max) 37 | return false; 38 | auto x = r.origin().x() + t * r.direction().x(); 39 | auto y = r.origin().y() + t * r.direction().y(); 40 | if (x < x0 || x > x1 || y < y0 || y > y1) 41 | return false; 42 | rec.u = (x - x0) / (x1 - x0); 43 | rec.v = (y - y0) / (y1 - y0); 44 | rec.t = t; 45 | rec.p = r.at(rec.t); 46 | vec outward_normal = vec(0, 0, 1); 47 | rec.set_face_normal(r, outward_normal); 48 | return true; 49 | } 50 | real_t x0, x1, y0, y1, k; 51 | material_t material_type; 52 | }; 53 | 54 | class xz_rect { 55 | public: 56 | xz_rect() = default; 57 | 58 | /// x0 <= x1 and z0 <= z1 59 | xz_rect(real_t _x0, real_t _x1, real_t _z0, real_t _z1, real_t _k, 60 | const material_t& mat_type) 61 | : x0 { _x0 } 62 | , x1 { _x1 } 63 | , z0 { _z0 } 64 | , z1 { _z1 } 65 | , k { _k } 66 | , material_type { mat_type } {} 67 | 68 | /// Compute ray interaction with rectangle 69 | bool hit(auto&, const ray& r, real_t min, real_t max, hit_record& rec, 70 | material_t& hit_material_type) const { 71 | hit_material_type = material_type; 72 | 73 | auto t = (k - r.origin().y()) / r.direction().y(); 74 | if (t < min || t > max) 75 | return false; 76 | auto x = r.origin().x() + t * r.direction().x(); 77 | auto z = r.origin().z() + t * r.direction().z(); 78 | if (x < x0 || x > x1 || z < z0 || z > z1) 79 | return false; 80 | rec.u = (x - x0) / (x1 - x0); 81 | rec.v = (z - z0) / (z1 - z0); 82 | rec.t = t; 83 | rec.p = r.at(rec.t); 84 | vec outward_normal = vec(0, 1, 0); 85 | rec.set_face_normal(r, outward_normal); 86 | return true; 87 | } 88 | real_t x0, x1, z0, z1, k; 89 | material_t material_type; 90 | }; 91 | 92 | class yz_rect { 93 | public: 94 | yz_rect() = default; 95 | 96 | /// y0 <= y1 and z0 <= z1 97 | yz_rect(real_t _y0, real_t _y1, real_t _z0, real_t _z1, real_t _k, 98 | const material_t& mat_type) 99 | : y0 { _y0 } 100 | , y1 { _y1 } 101 | , z0 { _z0 } 102 | , z1 { _z1 } 103 | , k { _k } 104 | , material_type { mat_type } {} 105 | 106 | /// Compute ray interaction with rectangle 107 | bool hit(auto&, const ray& r, real_t min, real_t max, hit_record& rec, 108 | material_t& hit_material_type) const { 109 | hit_material_type = material_type; 110 | 111 | auto t = (k - r.origin().x()) / r.direction().x(); 112 | if (t < min || t > max) 113 | return false; 114 | auto y = r.origin().y() + t * r.direction().y(); 115 | auto z = r.origin().z() + t * r.direction().z(); 116 | if (y < y0 || y > y1 || z < z0 || z > z1) 117 | return false; 118 | rec.u = (y - y0) / (y1 - y0); 119 | rec.v = (z - z0) / (z1 - z0); 120 | rec.t = t; 121 | rec.p = r.at(rec.t); 122 | vec outward_normal = vec(1, 0, 0); 123 | rec.set_face_normal(r, outward_normal); 124 | return true; 125 | } 126 | real_t y0, y1, z0, z1, k; 127 | material_t material_type; 128 | }; 129 | 130 | using rectangle_t = std::variant; 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /include/render.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "box.hpp" 7 | #include "build_parameters.hpp" 8 | #include "camera.hpp" 9 | #include "constant_medium.hpp" 10 | #include "hitable.hpp" 11 | #include "material.hpp" 12 | #include "ray.hpp" 13 | #include "rectangle.hpp" 14 | #include "rtweekend.hpp" 15 | #include "sphere.hpp" 16 | #include "sycl.hpp" 17 | #include "texture.hpp" 18 | #include "triangle.hpp" 19 | #include "vec.hpp" 20 | #include "visit.hpp" 21 | 22 | using hittable_t = 23 | std::variant; 24 | 25 | template 26 | inline auto render_pixel(auto& ctx, int x_coord, int y_coord, camera const& cam, 27 | auto& hittable_acc, auto fb_acc) { 28 | auto& rng = ctx.rng; 29 | auto get_color = [&](const ray& r) { 30 | auto hit_world = [&](const ray& r, hit_record& rec, 31 | material_t& material_type) { 32 | hit_record temp_rec; 33 | material_t temp_material_type; 34 | auto hit_anything = false; 35 | auto closest_so_far = infinity; 36 | // Checking if the ray hits any of the spheres 37 | for (auto i = 0; i < hittable_acc.get_count(); i++) { 38 | if (dev_visit( 39 | [&](auto&& arg) { 40 | return arg.hit(ctx, r, 0.001f, closest_so_far, temp_rec, 41 | temp_material_type); 42 | }, 43 | hittable_acc[i])) { 44 | hit_anything = true; 45 | closest_so_far = temp_rec.t; 46 | rec = temp_rec; 47 | material_type = temp_material_type; 48 | } 49 | } 50 | return hit_anything; 51 | }; 52 | 53 | ray cur_ray = r; 54 | color cur_attenuation { 1.0f, 1.0f, 1.0f }; 55 | ray scattered; 56 | color emitted; 57 | material_t material_type; 58 | for (auto i = 0; i < depth; i++) { 59 | hit_record rec; 60 | if (hit_world(cur_ray, rec, material_type)) { 61 | emitted = dev_visit([&](auto&& arg) { return arg.emitted(ctx, rec); }, 62 | material_type); 63 | if (dev_visit( 64 | [&](auto&& arg) { 65 | return arg.scatter(ctx, cur_ray, rec, cur_attenuation, 66 | scattered); 67 | }, 68 | material_type)) { 69 | // On hitting the object, the ray gets scattered 70 | cur_ray = scattered; 71 | } else { 72 | // Ray did not get scattered or reflected 73 | return emitted; 74 | } 75 | } else { 76 | /** 77 | If ray doesn't hit anything during iteration linearly blend white and 78 | blue color depending on the height of the y coordinate after scaling 79 | the ray direction to unit length. While -1.0f < y < 1.0f, hit_pt is 80 | between 0 and 1. This produces a blue to white gradient in the 81 | background 82 | */ 83 | vec unit_direction = unit_vector(cur_ray.direction()); 84 | auto hit_pt = 0.5f * (unit_direction.y() + 1.0f); 85 | color c = (1.0f - hit_pt) * color { 1.0f, 1.0f, 1.0f } + 86 | hit_pt * color { 0.5f, 0.7f, 1.0f }; 87 | return cur_attenuation * c; 88 | } 89 | } 90 | // If not returned within max_depth return black 91 | return color { 0.0f, 0.0f, 0.0f }; 92 | }; 93 | 94 | color final_color(0.0f, 0.0f, 0.0f); 95 | for (auto i = 0; i < samples; i++) { 96 | const auto u = (x_coord + rng.float_t()) / width; 97 | const auto v = (y_coord + rng.float_t()) / height; 98 | // u and v are points on the viewport 99 | ray r = cam.get_ray(u, v, rng); 100 | final_color += get_color(r); 101 | } 102 | final_color /= static_cast(samples); 103 | 104 | // Write final color to the frame buffer global memory 105 | fb_acc[y_coord][x_coord] = final_color; 106 | } 107 | 108 | struct PixelRender; 109 | 110 | template 111 | inline void executor(sycl::handler& cgh, camera const& cam_ptr, 112 | auto& hittable_acc, auto& fb_acc, auto& texture_acc) { 113 | if constexpr (buildparams::use_single_task) { 114 | cgh.single_task([=] { 115 | LocalPseudoRNG rng; 116 | task_context ctx { rng, texture_acc.get_pointer() }; 117 | for (int x_coord = 0; x_coord != width; ++x_coord) 118 | for (int y_coord = 0; y_coord != height; ++y_coord) { 119 | render_pixel( 120 | ctx, x_coord, y_coord, cam_ptr, hittable_acc, fb_acc); 121 | } 122 | }); 123 | } else { 124 | const auto global = sycl::range<2>(height, width); 125 | 126 | cgh.parallel_for(global, [=](sycl::item<2> item) { 127 | auto gid = item.get_id(); 128 | const auto x_coord = gid[1]; 129 | const auto y_coord = gid[0]; 130 | auto init_generator_state = 131 | std::hash {}(item.get_linear_id()); 132 | LocalPseudoRNG rng(init_generator_state); 133 | task_context ctx { rng, texture_acc.get_pointer() }; 134 | render_pixel( 135 | ctx, x_coord, y_coord, cam_ptr, hittable_acc, fb_acc); 136 | }); 137 | } 138 | } 139 | 140 | // Render function to call the render kernel 141 | template 142 | void render(sycl::queue& queue, sycl::buffer& frame_buf, 143 | std::vector& hittables, camera& cam) { 144 | auto constexpr depth = 50; 145 | const auto nb_hittable = hittables.size(); 146 | auto hittables_buf = sycl::buffer(hittables.data(), 147 | sycl::range<1>(nb_hittable)); 148 | auto texture_buf = image_texture::freeze(); 149 | 150 | // Submit command group on device 151 | queue.submit([&](sycl::handler& cgh) { 152 | auto fb_acc = frame_buf.get_access(cgh); 153 | auto hittables_acc = 154 | hittables_buf.get_access(cgh); 155 | auto texture_acc = texture_buf.get_access(cgh); 156 | 157 | executor(cgh, cam, hittables_acc, fb_acc, 158 | texture_acc); 159 | }); 160 | } 161 | -------------------------------------------------------------------------------- /include/rtweekend.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RT_SYCL_RTWEEKEND_HPP 2 | #define RT_SYCL_RTWEEKEND_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | // Constants 20 | 21 | constexpr float infinity = std::numeric_limits::infinity(); 22 | constexpr float pi = 3.1415926535897932385f; 23 | 24 | // type aliases for float3 - vec, point and color 25 | using point = sycl::float3; 26 | using color = sycl::float3; 27 | using vec = sycl::float3; 28 | 29 | // Utility Functions 30 | 31 | inline float degrees_to_radians(float degrees) { return degrees * pi / 180.0f; } 32 | 33 | class LocalPseudoRNG { 34 | public: 35 | inline LocalPseudoRNG(std::uint32_t init_state = xorshift<>::initial_state) 36 | : generator { init_state } {} 37 | 38 | // Returns a random float in 0.f 1. 39 | inline float float_t() { 40 | constexpr float scale = 1.f / (uint64_t { 1 } << 32); 41 | return generator() * scale; 42 | } 43 | 44 | // Returns a random float in min, max 45 | inline float float_t(float min, float max) { 46 | // TODO use FMA ? 47 | return min + (max - min) * float_t(); 48 | } 49 | 50 | // Returns a random vector with coordinates in 0.f 1. 51 | inline vec vec_t() { return { float_t(), float_t(), float_t() }; } 52 | 53 | // Returns a random vec with coordinates in min, max 54 | inline vec vec_t(float min, float max) { 55 | auto scale = max - min; 56 | return vec_t() * scale + min; 57 | } 58 | 59 | // Returns a random unit vector 60 | inline vec unit_vec() { 61 | auto x = float_t(-1.f, 1.f); 62 | auto maxy = sycl::sqrt(1 - x * x); 63 | auto y = float_t(-maxy, maxy); 64 | auto absz = sycl::sqrt(maxy * maxy - y * y); 65 | auto z = (float_t() > 0.5) ? absz : -absz; 66 | return vec(x, y, z); 67 | } 68 | 69 | // Returns a random vector in the unit ball of usual norm 70 | inline vec in_unit_ball() { 71 | // Polar coordinates r, theta, phi 72 | auto r = float_t(); 73 | auto theta = float_t(0, 2 * pi); 74 | auto phi = float_t(0, pi); 75 | 76 | auto plan_seed = r * sycl::sin(phi); 77 | auto z = r * sycl::cos(phi); 78 | 79 | return { plan_seed * sycl::cos(theta), plan_seed * sycl::sin(theta), z }; 80 | } 81 | 82 | // Return a random vector in the unit disk of usual norm in plane x, y 83 | inline vec in_unit_disk() { 84 | auto x = float_t(-1.f, 1.f); 85 | auto maxy = sycl::sqrt(1 - x * x); 86 | auto y = float_t(-maxy, maxy); 87 | return { x, y, 0.f }; 88 | } 89 | 90 | private: 91 | xorshift<> generator; 92 | }; 93 | 94 | /** 95 | @brief Used as a poorman's cooperative ersatz of device global variable 96 | The task context is (manually) passed through the call stack to all 97 | kernel callees 98 | */ 99 | struct task_context { 100 | LocalPseudoRNG rng; 101 | // See image_texture in texture.hpp for more details 102 | sycl::global_ptr texture_data; 103 | }; 104 | 105 | // Common Headers 106 | #include "ray.hpp" 107 | #include "vec.hpp" 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /include/sphere.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPHERE_H 2 | #define SPHERE_H 3 | 4 | #include "material.hpp" 5 | #include "ray.hpp" 6 | #include "rtweekend.hpp" 7 | #include "texture.hpp" 8 | #include "vec.hpp" 9 | 10 | /* Computes normalised values of theta and phi. The input vector p 11 | corresponds to a vector passing through the centre of the a sphere 12 | and the hipoint on the surface of the sphere */ 13 | std::pair mercator_coordinates(const vec& p) { 14 | // phi is the angle around the axis 15 | auto phi = sycl::atan2(p.z(), p.x()); 16 | // theta is the angle down from the pole 17 | auto theta = sycl::asin(p.y()); 18 | // theta and phi together constitute the spherical coordinates 19 | // phi is between -pi and pi , u is between 0 and 1 20 | auto u = 1 - (phi + pi) / (2 * pi); 21 | // theta is between -pi/2 and pi/2 , v is between 0 and 1 22 | auto v = (theta + pi / 2) / pi; 23 | return { u, v }; 24 | } 25 | 26 | class sphere { 27 | public: 28 | sphere() = default; 29 | 30 | sphere(const point& cen, real_t r, const material_t& mat_type) 31 | : center0 { cen } 32 | , center1 { cen } 33 | , radius { r } 34 | , time0 { 0 } 35 | , time1 { 0 } 36 | , material_type { mat_type } {} 37 | 38 | /// Simulates moving spheres from center0 to 39 | /// center1 between time0 and time1 40 | sphere(const point& cen0, const point& cen1, real_t _time0, real_t _time1, 41 | real_t r, const material_t& mat_type) 42 | : center0 { cen0 } 43 | , center1 { cen1 } 44 | , radius { r } 45 | , time0 { _time0 } 46 | , time1 { _time1 } 47 | , material_type { mat_type } {} 48 | 49 | /// Computes center of the sphere based on 50 | /// the time information stored in the ray 51 | point center(real_t time) const { 52 | if (time0 == time1) 53 | return center0; 54 | else 55 | return center0 + ((time - time0) / (time1 - time0)) * (center1 - center0); 56 | } 57 | 58 | /// Compute ray interaction with sphere 59 | bool hit(auto&, const ray& r, real_t min, real_t max, hit_record& rec, 60 | material_t& hit_material_type) const { 61 | hit_material_type = material_type; 62 | 63 | /*(P(t)-C).(P(t)-C)=r^2 64 | in the above sphere equation P(t) is the point on sphere hit by the ray 65 | (A+tb−C)⋅(A+tb−C)=r^2 66 | (t^2)b⋅b + 2tb⋅(A−C) + (A−C)⋅(A−C)−r^2 = 0 67 | There can 0 or 1 or 2 real roots*/ 68 | vec oc = r.origin() - center(r.time()); // oc = A-C 69 | auto a = sycl::dot(r.direction(), r.direction()); 70 | auto b = sycl::dot(oc, r.direction()); 71 | auto c = sycl::dot(oc, oc) - radius * radius; 72 | auto discriminant = b * b - a * c; 73 | // Real roots if discriminant is positive 74 | if (discriminant > 0) { 75 | // First root 76 | auto temp = (-b - sycl::sqrt(discriminant)) / a; 77 | if (temp < max && temp > min) { 78 | rec.t = temp; 79 | // Ray hits the sphere at p 80 | rec.p = r.at(rec.t); 81 | vec outward_normal = (rec.p - center(r.time())) / radius; 82 | // To set if hit point is on the front face and the outward normal in 83 | // rec 84 | rec.set_face_normal(r, outward_normal); 85 | /* Update u and v values in the hit record. Normal of a 86 | point is calculated as above. This vector is used to also 87 | used to get the mercator coordinates of the hitpoint.*/ 88 | std::tie(rec.u, rec.v) = mercator_coordinates(rec.normal); 89 | return true; 90 | } 91 | // Second root 92 | temp = (-b + sycl::sqrt(discriminant)) / a; 93 | if (temp < max && temp > min) { 94 | rec.t = temp; 95 | // Ray hits the sphere at p 96 | rec.p = r.at(rec.t); 97 | vec outward_normal = (rec.p - center(r.time())) / radius; 98 | rec.set_face_normal(r, outward_normal); 99 | // Update u and v values in the hit record 100 | std::tie(rec.u, rec.v) = mercator_coordinates(rec.normal); 101 | return true; 102 | } 103 | } 104 | // No real roots 105 | return false; 106 | } 107 | 108 | // Geometry properties 109 | point center0, center1; 110 | real_t radius; 111 | 112 | // Time of start and end of motion of the sphere 113 | real_t time0, time1; 114 | 115 | // Material properties 116 | material_t material_type; 117 | }; 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /include/sycl.hpp: -------------------------------------------------------------------------------- 1 | #ifdef USE_SYCL_COMPILER 2 | #include 3 | using namespace cl; 4 | #else 5 | #include 6 | #endif 7 | -------------------------------------------------------------------------------- /include/texture.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RT_SYCL_TEXTURE_HPP 2 | #define RT_SYCL_TEXTURE_HPP 3 | #include "hitable.hpp" 4 | #include "rtweekend.hpp" 5 | #include "vec.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "sycl.hpp" 13 | 14 | #define STB_IMAGE_IMPLEMENTATION 15 | #include 16 | 17 | // Solid texture consists of a single color 18 | struct solid_texture { 19 | solid_texture() = default; 20 | solid_texture(const color& c) 21 | : color_value { c } {} 22 | solid_texture(float red, float green, float blue) 23 | : solid_texture { color { red, green, blue } } {} 24 | // For solid texture, the color is same throughout the sphere 25 | color value(auto&, const hit_record&) const { return color_value; } 26 | 27 | private: 28 | color color_value; 29 | }; 30 | 31 | // Takes two solid_textures to create checker pattern 32 | struct checker_texture { 33 | checker_texture() = default; 34 | 35 | checker_texture(const solid_texture& x, const solid_texture& y) 36 | : odd { x } 37 | , even { y } {} 38 | checker_texture(const color& c1, const color& c2) 39 | : odd { solid_texture { c1 } } 40 | , even { solid_texture { c2 } } {} 41 | // Color value is different based on normalised spherical coordinates 42 | color value(auto& ctx, const hit_record& rec) const { 43 | auto sines = sycl::sin(10 * rec.p.x()) * sycl::sin(10 * rec.p.y()) * 44 | sycl::sin(10 * rec.p.z()); 45 | if (sines < 0) 46 | return odd.value(ctx, rec); 47 | else 48 | return even.value(ctx, rec); 49 | } 50 | solid_texture odd; 51 | solid_texture even; 52 | }; 53 | 54 | /** 55 | @brief A texture based on an image 56 | 57 | In order to be able to get the bitmap on the device without embedding it in 58 | the object, all image_texture textures are serialized in one vector. 59 | 60 | The offset of the texture in the vector is stored in the image_texture 61 | instance. 62 | 63 | When all the textures have been loaded, the freeze() method can be called to 64 | get a sycl::buffer that store this data. 65 | 66 | */ 67 | struct image_texture { 68 | private: 69 | static constexpr auto bytes_per_pixel = 3; 70 | // Vector in which all the textures are serialized 71 | static std::vector texture_data; 72 | static bool frozen; 73 | 74 | std::size_t width {}; 75 | std::size_t height {}; 76 | // offset of the first pixel in the texture vector 77 | std::size_t offset; 78 | 79 | /// The repetition rate of the image 80 | float cyclic_frequency { 1.f }; 81 | 82 | image_texture(std::size_t _width, std::size_t _height, std::size_t _offset, 83 | float _cyclic_frequency) 84 | : width { _width } 85 | , height { _height } 86 | , offset { _offset } 87 | , cyclic_frequency { _cyclic_frequency } {} 88 | 89 | public: 90 | /** Create a texture from an image file 91 | 92 | \param[in] file_name is the path name to the image file 93 | 94 | \param[in] cyclic_frequency is an optional repetition rate of 95 | the image in the texture 96 | */ 97 | static image_texture image_texture_factory(const char* file_name, 98 | float _cyclic_frequency = 1) { 99 | assert(!frozen); 100 | auto components_per_pixel = bytes_per_pixel; 101 | uint8_t* _data; 102 | int _w, _h; 103 | _data = 104 | stbi_load(file_name, &_w, &_h, &components_per_pixel, bytes_per_pixel); 105 | std::size_t _offset = 0; 106 | if (!_data) { 107 | std::cerr << "ERROR: Could not load texture image file '" << file_name 108 | << "'.\n" 109 | << stbi_failure_reason() << std::endl; 110 | _w = _h = 1; 111 | } else { 112 | auto size = bytes_per_pixel * _w * _h; 113 | _offset = texture_data.size() / 3; 114 | std::copy(_data, _data + size, std::back_inserter(texture_data)); 115 | } 116 | return image_texture(_w, _h, _offset, _cyclic_frequency); 117 | } 118 | 119 | /** 120 | @brief Get a sycl::buffer containing texture data. 121 | 122 | image_texture_factory should not be called after having called freeze 123 | 124 | @return sycl::buffer 125 | */ 126 | static sycl::buffer freeze() { 127 | assert(!frozen); 128 | frozen = true; 129 | return sycl::buffer { texture_data.data(), 130 | { texture_data.size() / 3, 3 } }; 131 | } 132 | 133 | /// Get the color for the texture at the given place 134 | /// \todo rename this value() to color() everywhere? 135 | color value(auto& ctx, const hit_record& rec) const { 136 | // If texture data is unavailable, return solid cyan 137 | // The image is repeated by the repetition factor 138 | 139 | std::size_t i = 140 | sycl::fmod(rec.u * cyclic_frequency, (float)1) * (width - 1); 141 | // The image frame buffer is going downwards, so flip the y axis 142 | std::size_t j = 143 | (1 - sycl::fmod(rec.v * cyclic_frequency, (float)1)) * (height - 1); 144 | std::size_t local_offset = j * width + i; 145 | std::size_t pix_idx = local_offset + offset; 146 | auto scale = 1.f / 255; 147 | auto& texture_data = ctx.texture_data; 148 | return { texture_data[pix_idx * 3] * scale, 149 | texture_data[pix_idx * 3 + 1] * scale, 150 | texture_data[pix_idx * 3 + 2] * scale }; 151 | } 152 | }; 153 | 154 | using texture_t = std::variant; 155 | 156 | // Start filled with the fallback texture (solid blue) for texture load error 157 | std::vector image_texture::texture_data { 0, 0, 1 }; 158 | bool image_texture::frozen = false; 159 | #endif 160 | -------------------------------------------------------------------------------- /include/triangle.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TRIANGLE_HPP 2 | #define TRIANGLE_HPP 3 | 4 | #include "material.hpp" 5 | #include "ray.hpp" 6 | #include "rtweekend.hpp" 7 | #include "texture.hpp" 8 | #include "vec.hpp" 9 | 10 | struct _triangle_coord { 11 | point v0, v1, v2; 12 | }; 13 | 14 | inline bool badouel_ray_triangle_intersec(const ray& r, 15 | _triangle_coord const& tri, 16 | real_t min, real_t max, 17 | hit_record& rec) { 18 | // Get triangle edge vectors and plane normal 19 | auto u = tri.v1 - tri.v0; 20 | auto v = tri.v2 - tri.v0; 21 | vec outward_normal = sycl::cross(u, v); 22 | 23 | auto w0 = r.origin() - tri.v0; 24 | auto a = -sycl::dot(outward_normal, w0); 25 | auto b = sycl::dot(outward_normal, r.direction()); 26 | 27 | // ray is parallel to triangle plane 28 | if (sycl::fabs(b) < 0.000001f) 29 | return false; 30 | 31 | // intersection point of ray with triangle 32 | real_t length = a / b; 33 | if (length < 0) 34 | return false; 35 | else if (length < min || length > max) 36 | return false; 37 | 38 | vec hit_pt = r.at(length); 39 | auto uu = sycl::dot(u, u); 40 | auto uv = sycl::dot(u, v); 41 | auto vv = sycl::dot(v, v); 42 | auto w = hit_pt - tri.v0; 43 | auto wu = sycl::dot(w, u); 44 | auto wv = sycl::dot(w, v); 45 | auto D = uv * uv - uu * vv; 46 | 47 | auto s = (uv * wv - vv * wu) / D; 48 | auto t = (uv * wu - uu * wv) / D; 49 | if (s < 0.0f || s > 1.0f || t < 0.0f || (s + t) > 1.0f) 50 | return false; 51 | 52 | rec.set_face_normal(r, outward_normal); 53 | rec.t = length; 54 | rec.p = hit_pt; 55 | return true; 56 | }; 57 | 58 | inline bool moller_trumbore_triangle_intersec(const ray& r, 59 | _triangle_coord const& tri, 60 | real_t min, real_t max, 61 | hit_record& rec) { 62 | constexpr auto epsilon = 0.0000001f; 63 | 64 | // Get triangle edge vectors and plane normal 65 | auto edge1 = tri.v1 - tri.v0; 66 | auto edge2 = tri.v2 - tri.v0; 67 | vec h = sycl::cross(r.direction(), edge2); 68 | auto a = sycl::dot(edge1, h); 69 | auto a_abs = sycl::fabs(a); 70 | 71 | if (a_abs < epsilon) 72 | return false; // This ray is parallel to this triangle 73 | 74 | auto a_pos = a > 0.f; 75 | 76 | auto s = r.origin() - tri.v0; 77 | auto u = sycl::dot(s, h); 78 | auto u_pos = u > 0.f; 79 | 80 | if ((u_pos xor a_pos) || sycl::fabs(u) > a_abs) 81 | return false; 82 | 83 | auto q = sycl::cross(s, edge1); 84 | auto v = sycl::dot(r.direction(), q); 85 | auto v_pos = v > 0.f; 86 | if ((v_pos xor a_pos) || (sycl::fabs(u + v) > a_abs)) 87 | return false; 88 | 89 | auto length = sycl::dot(edge2, q) / a; 90 | 91 | if (length < min || length > max) 92 | return false; 93 | 94 | vec hit_pt = r.at(length); 95 | 96 | rec.set_face_normal(r, sycl::cross(edge1, edge2)); 97 | rec.t = length; 98 | rec.p = hit_pt; 99 | return true; 100 | }; 101 | 102 | // A triangle based on 3 points 103 | template 104 | class _triangle : public _triangle_coord { 105 | public: 106 | _triangle() = default; 107 | _triangle(const point& _v0, const point& _v1, const point& _v2, 108 | const material_t& mat_type) 109 | : _triangle_coord { _v0, _v1, _v2 } 110 | , material_type { mat_type } {} 111 | 112 | /// Compute ray interaction with triangle 113 | bool hit(auto&, const ray& r, real_t min, real_t max, hit_record& rec, 114 | material_t& hit_material_type) const { 115 | hit_material_type = material_type; 116 | return IntersectionStrategy(r, *this, min, max, rec); 117 | } 118 | 119 | material_t material_type; 120 | }; 121 | 122 | using triangle = _triangle<>; 123 | #endif 124 | -------------------------------------------------------------------------------- /include/vec.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RT_SYCL_VEC_HPP 2 | #define RT_SYCL_VEC_HPP 3 | 4 | #include "rtweekend.hpp" 5 | #include 6 | #include 7 | 8 | using real_t = float; 9 | 10 | // vec Utility Functions 11 | inline float length_squared(const vec& v) { 12 | return sycl::fma(v.x(), v.x(), sycl::fma(v.y(), v.y(), v.z() * v.z())); 13 | } 14 | 15 | inline std::ostream& operator<<(std::ostream& out, const vec& v) { 16 | return out << v.x() << ' ' << v.y() << ' ' << v.z(); 17 | } 18 | 19 | // Missing operator from the SYCL specification for now 20 | vec operator-(const vec& u) { return vec(-u.x(), -u.y(), -u.z()); } 21 | 22 | // Compute a unit vector from a non-null vector 23 | inline vec unit_vector(const vec& v) { return v / sycl::length(v); } 24 | 25 | // Compute reflected ray's direction 26 | vec reflect(const vec& v, const vec& n) { return v - 2 * sycl::dot(v, n) * n; } 27 | 28 | // Computes refracted ray's direction based on refractive index 29 | vec refract(const vec& uv, const vec& n, float etai_over_etat) { 30 | auto cos_theta = sycl::fmin(-sycl::dot(uv, n), 1.0f); 31 | vec r_out_perp = etai_over_etat * (uv + cos_theta * n); 32 | vec r_out_parallel = 33 | -sycl::sqrt(sycl::fabs(1.0f - length_squared(r_out_perp))) * n; 34 | return r_out_perp + r_out_parallel; 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/visit.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VISIT_HPP 2 | #define VISIT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace detail { 9 | 10 | template struct variant_trait {}; 11 | 12 | template struct variant_trait> { 13 | using indexes = std::make_index_sequence; 14 | }; 15 | 16 | template 17 | [[noreturn]] inline ret_type 18 | visit_single_impl(Func&&, std::integer_sequence, Var&&) { 19 | assert(false && "unreachable"); 20 | __builtin_unreachable(); 21 | } 22 | 23 | template 25 | inline ret_type visit_single_impl(Func&& f, 26 | std::integer_sequence, 27 | Var&& var) { 28 | if (var.index() == First) 29 | return std::forward(f)(std::get(var)); 30 | return visit_single_impl(std::forward(f), 31 | std::integer_sequence {}, 32 | std::forward(var)); 33 | } 34 | 35 | template 36 | decltype(auto) visit_single(Func&& f, Var&& var) { 37 | assert((!var.valueless_by_exception())); 38 | using ret_type = 39 | std::invoke_result_t(std::declval()))>; 40 | return visit_single_impl( 41 | std::forward(f), 42 | typename variant_trait< 43 | std::remove_cv_t>>::indexes {}, 44 | std::forward(var)); 45 | } 46 | } // namespace detail 47 | 48 | /// dev_visit is std::visit implementation suitable to be used in device code. 49 | /// this version of visit doesn't use any function pointer but uses if series 50 | /// which will be turned into switch case by the optimizer. 51 | template 52 | auto dev_visit(Func&& f, Var&& var, Rest&&... rest) { 53 | if constexpr (sizeof...(Rest) == 0) 54 | return detail::visit_single(std::forward(f), std::forward(var)); 55 | else 56 | return detail::visit_single( 57 | [&](auto&& First) { 58 | return visit( 59 | [&](auto&&... Others) { 60 | std::forward(f)( 61 | std::forward(First), 62 | std::forward(Others)...); 63 | }, 64 | std::forward(rest)...); 65 | }, 66 | std::forward(var)); 67 | } 68 | 69 | #endif // VISIT_HPP 70 | -------------------------------------------------------------------------------- /include/xorshift.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XORSHIFT_HPP 2 | #define XORSHIFT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Xorshift implementation from [triSYCL](https://github.com/triSYCL/triSYCL) 9 | template struct xorshift { 10 | /// Bit size of the random generator data type 11 | static auto constexpr bit_size = Nbits; 12 | 13 | /// The default initial internal state of the pseudo random generator 14 | static auto constexpr initial_state = [] { 15 | if constexpr (bit_size == 32) { 16 | /* Default value for xor32 from Marsaglia, 17 | p. 4 Section "3 Application to Xorshift RNGs" */ 18 | return std::uint32_t { 2463534242 }; 19 | } else if constexpr (bit_size == 64) { 20 | /* Default value for xor32 from Marsaglia, 21 | p. 4 Section "3 Application to Xorshift RNGs" */ 22 | return std::uint64_t { 88172645463325252 }; 23 | } else if constexpr (bit_size == 128) { 24 | /// Default value for xor128 from Marsaglia, p. 5 Section "4 Summary" 25 | return std::array { 123456789, 362436069, 521288629, 26 | 88675123 }; 27 | } else 28 | return nullptr; 29 | }(); 30 | 31 | /// The type of the internal state of the generator 32 | using value_type = std::remove_const_t; 33 | 34 | /// The type of the result 35 | using result_type = value_type; 36 | 37 | static_assert(!std::is_same_v, 38 | "Bit size not implemented"); 39 | 40 | /// The internal state of the pseudo random generator 41 | value_type state = initial_state; 42 | 43 | /// The minimum returned value 44 | static auto constexpr min() { 45 | // It cannot return 0 46 | return std::numeric_limits::min() + 1; 47 | }; 48 | 49 | /// The maximum returned value 50 | static auto constexpr max() { 51 | return std::numeric_limits::max(); 52 | }; 53 | 54 | /** Initialize the internal state from a user-given value 55 | 56 | Do not use the value 0 or the output is always 0 57 | */ 58 | xorshift(const value_type& s) 59 | : state { s } {} 60 | 61 | xorshift() = default; 62 | 63 | /// Compute a new pseudo random integer 64 | const result_type& operator()() { 65 | if constexpr (bit_size == 32) { 66 | /* Pick the one of type "I" with best bit equidistribution from 67 | Panneton & L'Écuyer, Section "5.1 Equidistribution 68 | properties" 69 | 70 | X4 = (I + Ra )(I + Lb )(I + Rc ) 71 | */ 72 | state ^= state >> 7; 73 | state ^= state << 1; 74 | state ^= state >> 9; 75 | } else if constexpr (bit_size == 64) { 76 | /* The xor64 from Marsaglia, p. 4 Section "3 Application to 77 | Xorshift RNGs" */ 78 | state ^= state << 13; 79 | state ^= state >> 7; 80 | state ^= state << 17; 81 | } else if constexpr (bit_size == 128) { 82 | // The xor128 from Marsaglia, p. 5 Section "4 Summary" 83 | auto t = state[0] ^ (state[0] << 11); 84 | state[0] = state[1]; 85 | state[1] = state[2]; 86 | state[2] = state[3]; 87 | state[3] = state[3] ^ (state[3] >> 19) ^ (t ^ (t >> 8)); 88 | } else 89 | // Just to error in that case 90 | return nullptr; 91 | 92 | return state; 93 | } 94 | }; 95 | #endif // XORSHIFT_HPP 96 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "sycl.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define STB_IMAGE_WRITE_IMPLEMENTATION 12 | #include 13 | 14 | #include "render.hpp" 15 | 16 | // Function to save image data in ppm format 17 | void dump_image_ppm(int width, int height, auto& fb_data) { 18 | std::cout << "P3\n" << width << " " << height << "\n255\n"; 19 | for (int y = height - 1; y >= 0; y--) { 20 | for (int x = 0; x < width; x++) { 21 | auto pixel_index = y * width + x; 22 | int r = static_cast( 23 | 256 * std::clamp(sycl::sqrt(fb_data[pixel_index].x()), 0.0f, 0.999f)); 24 | int g = static_cast( 25 | 256 * std::clamp(sycl::sqrt(fb_data[pixel_index].y()), 0.0f, 0.999f)); 26 | int b = static_cast( 27 | 256 * std::clamp(sycl::sqrt(fb_data[pixel_index].z()), 0.0f, 0.999f)); 28 | std::cout << r << " " << g << " " << b << "\n"; 29 | } 30 | } 31 | } 32 | 33 | void save_image_png(int width, int height, sycl::buffer &fb) { 34 | constexpr unsigned num_channels = 3; 35 | auto fb_data = fb.get_access(); 36 | 37 | std::vector pixels; 38 | pixels.resize(width * height * num_channels); 39 | 40 | int index = 0; 41 | for (int j = height - 1; j >= 0; --j) { 42 | for (int i = 0; i < width; ++i) { 43 | auto input_index = j * width + i; 44 | int r = static_cast( 45 | 256 * std::clamp(sycl::sqrt(fb_data[j][i].x()), 0.0f, 0.999f)); 46 | int g = static_cast( 47 | 256 * std::clamp(sycl::sqrt(fb_data[j][i].y()), 0.0f, 0.999f)); 48 | int b = static_cast( 49 | 256 * std::clamp(sycl::sqrt(fb_data[j][i].z()), 0.0f, 0.999f)); 50 | 51 | pixels[index++] = r; 52 | pixels[index++] = g; 53 | pixels[index++] = b; 54 | } 55 | } 56 | 57 | stbi_write_png("out.png", width, height, num_channels, pixels.data(), 58 | width * num_channels); 59 | } 60 | 61 | int main() { 62 | // Frame buffer dimensions 63 | constexpr auto width = buildparams::output_width; 64 | constexpr auto height = buildparams::output_height; 65 | 66 | /// Graphical objects 67 | std::vector hittables; 68 | 69 | // Generating a checkered ground and some random spheres 70 | texture_t t = 71 | checker_texture(color { 0.2f, 0.3f, 0.1f }, color { 0.9f, 0.9f, 0.9f }); 72 | material_t m = lambertian_material(t); 73 | hittables.emplace_back(sphere(point { 0, -1000, 0 }, 1000, m)); 74 | t = checker_texture(color { 0.9f, 0.9f, 0.9f }, color { 0.4f, 0.2f, 0.1f }); 75 | 76 | LocalPseudoRNG rng; 77 | 78 | for (int a = -11; a < 11; a++) { 79 | for (int b = -11; b < 11; b++) { 80 | // Based on a random variable , the material type is chosen 81 | auto choose_mat = rng.float_t(); 82 | // Spheres are placed at a point randomly displaced from a,b 83 | point center(a + 0.9f * rng.float_t(), 0.2f, b + 0.9f * rng.float_t()); 84 | if (sycl::length((center - point(4, 0.2f, 0))) > 0.9f) { 85 | if (choose_mat < 0.4f) { 86 | // Lambertian 87 | auto albedo = rng.vec_t() * rng.vec_t(); 88 | hittables.emplace_back( 89 | sphere(center, 0.2f, lambertian_material(albedo))); 90 | } else if (choose_mat < 0.8f) { 91 | // Lambertian movig spheres 92 | auto albedo = rng.vec_t() * rng.vec_t(); 93 | auto center2 = center + point { 0, rng.float_t(0, 0.25f), 0 }; 94 | hittables.emplace_back(sphere(center, center2, 0.0f, 1.0f, 0.2f, 95 | lambertian_material(albedo))); 96 | } else if (choose_mat < 0.95f) { 97 | // metal 98 | auto albedo = rng.vec_t(0.5f, 1); 99 | auto fuzz = rng.float_t(0, 0.5f); 100 | hittables.emplace_back( 101 | sphere(center, 0.2f, metal_material(albedo, fuzz))); 102 | } else { 103 | // glass 104 | hittables.emplace_back( 105 | sphere(center, 0.2f, 106 | dielectric_material(1.5f, color { 1.0f, 1.0f, 1.0f }))); 107 | } 108 | } 109 | } 110 | } 111 | 112 | // Pyramid 113 | hittables.emplace_back( 114 | triangle(point { 6.5f, 0.0f, 1.30f }, point { 6.25f, 0.50f, 1.05f }, 115 | point { 6.5f, 0.0f, 0.80f }, 116 | lambertian_material(color(0.68f, 0.50f, 0.1f)))); 117 | hittables.emplace_back( 118 | triangle(point { 6.0f, 0.0f, 1.30f }, point { 6.25f, 0.50f, 1.05f }, 119 | point { 6.5f, 0.0f, 1.30f }, 120 | lambertian_material(color(0.89f, 0.73f, 0.29f)))); 121 | hittables.emplace_back(triangle( 122 | point { 6.5f, 0.0f, 0.80f }, point { 6.25f, 0.50f, 1.05f }, 123 | point { 6.0f, 0.0f, 0.80f }, lambertian_material(color(0.0f, 0.0f, 1)))); 124 | hittables.emplace_back(triangle( 125 | point { 6.0f, 0.0f, 0.80f }, point { 6.25f, 0.50f, 1.05f }, 126 | point { 6.0f, 0.0f, 1.30f }, lambertian_material(color(0.0f, 0.0f, 1)))); 127 | 128 | // Glowing ball 129 | hittables.emplace_back( 130 | sphere(point { 4, 1, 0 }, 0.2f, lightsource_material(color(10, 0, 10)))); 131 | 132 | // Four large spheres of metal, dielectric and Lambertian material types 133 | t = image_texture::image_texture_factory("../images/Xilinx.jpg"); 134 | hittables.emplace_back(xy_rect(2, 4, 0, 1, -1, lambertian_material(t))); 135 | hittables.emplace_back( 136 | sphere(point { 4, 1, 2.25f }, 1, lambertian_material(t))); 137 | hittables.emplace_back( 138 | sphere(point { 0, 1, 0 }, 1, 139 | dielectric_material(1.5f, color { 1.0f, 0.5f, 0.5f }))); 140 | hittables.emplace_back(sphere(point { -4, 1, 0 }, 1, 141 | lambertian_material(color(0.4f, 0.2f, 0.1f)))); 142 | hittables.emplace_back(sphere(point { 0, 1, -2.25f }, 1, 143 | metal_material(color(0.7f, 0.6f, 0.5f), 0.0f))); 144 | 145 | t = image_texture::image_texture_factory("../images/SYCL.png", 5); 146 | 147 | // // Add a sphere with a SYCL logo in the background 148 | hittables.emplace_back( 149 | sphere { point { -60, 3, 5 }, 4, lambertian_material { t } }); 150 | 151 | // Add a metallic monolith 152 | hittables.emplace_back( 153 | box { point { 6.5f, 0, -1.5f }, point { 7.0f, 3.0f, -1.0f }, 154 | metal_material { color { 0.7f, 0.6f, 0.5f }, 0.25f } }); 155 | 156 | // Add a smoke ball 157 | sphere smoke_sphere = 158 | sphere { point { 5, 1, 3.5f }, 1, 159 | lambertian_material { color { 0.75f, 0.75f, 0.75f } } }; 160 | hittables.emplace_back( 161 | constant_medium { smoke_sphere, 1, color { 1, 1, 1 } }); 162 | 163 | // SYCL queue 164 | sycl::queue myQueue; 165 | 166 | // Camera setup 167 | /// Position of the camera 168 | point look_from { 13, 3, 3 }; 169 | /// The center of the scene 170 | point look_at { 0, -1, 0 }; 171 | // Make the camera oriented upwards 172 | vec vup { 0, 1, 0 }; 173 | 174 | /// Vertical angle of view in degree 175 | real_t angle = 40; 176 | // Lens aperture. 0 if not depth-of-field 177 | real_t aperture = 0.04f; 178 | // Make the focus on the point we are looking at 179 | real_t focus_dist = length(look_at - look_from); 180 | camera cam { 181 | look_from, look_at, vup, angle, static_cast(width) / height, 182 | aperture, focus_dist, 0.0f, 1.0f 183 | }; 184 | 185 | // Sample per pixel 186 | constexpr auto samples = 100; 187 | 188 | // SYCL render kernel 189 | 190 | sycl::buffer fb(sycl::range<2>(height, width)); 191 | render(myQueue, fb, hittables, cam); 192 | 193 | // Save image to file 194 | save_image_png(width, height, fb); 195 | 196 | return 0; 197 | } 198 | --------------------------------------------------------------------------------