├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── examples ├── 00_ocl_info.rs ├── 01_1_spheres.rs ├── 01_2_motion.rs ├── 02_shapes.rs ├── 03_materials.rs ├── 04_1_light_source.rs ├── 04_2_multiple_sources.rs ├── 05_indirect_lighting.rs └── 98_rocks.rs ├── ocl-src ├── filter │ ├── glare.h │ └── log.h ├── map │ ├── linear.h │ ├── scale.h │ └── shift.h ├── material │ ├── diffuse.h │ ├── luminous.h │ └── reflective.h ├── scene │ ├── background │ │ ├── constant.h │ │ └── gradient.h │ ├── list_scene.h │ └── target_list_scene.h ├── shape │ ├── cube.h │ └── sphere.h └── view │ └── proj_view.h └── src ├── filter ├── glare.rs ├── log.rs └── mod.rs ├── lib.rs ├── map ├── affine.rs ├── linear.rs ├── mod.rs ├── scale.rs └── shift.rs ├── material ├── diffuse.rs ├── luminous.rs ├── mod.rs └── reflective.rs ├── process ├── mod.rs ├── postproc.rs └── render.rs ├── scene ├── background │ ├── constant.rs │ ├── gradient.rs │ └── mod.rs ├── list_scene.rs ├── mod.rs └── target_list_scene.rs ├── shape ├── ellipsoid.rs ├── mod.rs ├── parallelepiped.rs ├── sphere.rs ├── unit_cube.rs └── unit_sphere.rs ├── source.rs └── view ├── mod.rs └── projection.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | __gen_* 5 | *.png 6 | /screenshots 7 | .vscode 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | os: linux 4 | dist: bionic 5 | rust: stable 6 | sudo: required 7 | 8 | before_install: 9 | - sudo apt-get update 10 | 11 | install: 12 | - sudo apt-get install -y ocl-icd-opencl-dev pocl-opencl-icd libpocl-dev libsdl2-dev 13 | 14 | script: 15 | - cargo build --all-targets 16 | - cargo run --example 00_ocl_info 17 | - cargo test --all-targets 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clay" 3 | version = "0.1.2" 4 | authors = ["Alexey Gerasev "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2018" 7 | 8 | description = "Fast, modular and extendable ray tracer powered by OpenCL" 9 | homepage = "https://clay-rs.github.io/" 10 | repository = "https://github.com/clay-rs/clay" 11 | 12 | keywords = ["ray-tracing", "opencl"] 13 | categories = ["graphics", "rendering", "multimedia"] 14 | 15 | readme = "README.md" 16 | build = "build.rs" 17 | 18 | [dependencies] 19 | ocl = "0.19" 20 | ocl-include = "0.3.3" 21 | nalgebra = "0.18" 22 | rand = "0.7" 23 | lazy_static = "1.3.0" 24 | regex = "1" 25 | uuid = { version = "0.7", features = ["v4"] } 26 | 27 | clay-core = "^0.1.3" 28 | #clay-core = { git = "https://github.com/clay-rs/clay-core.git", rev = "c6e3ce379d6a883917bd2f26d48336c2d679e8de" } 29 | #clay-core = { path = "../clay-core" } 30 | 31 | [build-dependencies] 32 | walkdir = "2" 33 | 34 | [dev-dependencies] 35 | clay-viewer = "^0.1.2" 36 | #clay-viewer = { git = "https://github.com/clay-rs/clay-viewer.git", rev = "651a10171ed978de6af76d074da40672af6ab7f0" } 37 | #clay-viewer = { path = "../clay-viewer" } 38 | 39 | clay-utils = "^0.1.1" 40 | #clay-utils = { git = "https://github.com/clay-rs/clay-utils.git", rev = "09b920798c9b3f08f8703910a62a5b26c6c19139" } 41 | #clay-utils = { path = "../clay-utils" } 42 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Alexey Gerasev 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clay 2 | 3 | [![Crates.io][crates_badge]][crates] 4 | [![Docs.rs][docs_badge]][docs] 5 | [![Travis CI][travis_badge]][travis] 6 | [![License][license_badge]][license] 7 | 8 | [crates_badge]: https://img.shields.io/crates/v/clay.svg 9 | [docs_badge]: https://docs.rs/clay/badge.svg 10 | [travis_badge]: https://api.travis-ci.org/clay-rs/clay.svg?branch=master 11 | [license_badge]: https://img.shields.io/crates/l/clay.svg 12 | 13 | [crates]: https://crates.io/crates/clay 14 | [docs]: https://docs.rs/clay 15 | [travis]: https://travis-ci.org/clay-rs/clay 16 | [license]: #license 17 | 18 | Flexible ray tracing engine written in Rust and OpenCL. 19 | 20 | ## About 21 | 22 | Clay ray tracing engine is: 23 | 24 | + **Fast** - because of the OpenCL, Clay is able to run its kernel code in massively 25 | parallel computing systems (e.g. GPUs), that makes it much faster than CPU-only analogs, 26 | and allows it to render images of sufficient quality even in real-time. 27 | 28 | + **Modular** - Clay is based on strict but flexible Rust trait system and type parametrization, 29 | that means you can assemble desired ray tracing pipeline from primitive building blocks. 30 | 31 | + **Extendable** - if desired functionality doesn't exist in Clay yet, you always can write 32 | it by yourself by implementing corresponding traits. Moreover, you can even write your own 33 | modules of OpenCL code to run on a GPU. (And make a pull request after that, if you want to.) 34 | 35 | 36 | This project is primarily aimed to be a convenient framework for experimenting with ray tracing, 37 | testing new techniques, making proof of concepts and other research activity in this field. 38 | 39 | The key principles of the project is modularity and extendability. 40 | The performance is also one of the primary goals, as long as it doesn't significantly reduce flexibility. 41 | 42 | You can find more information at the [Clay project website](https://clay-rs.github.io). 43 | 44 | ## License 45 | 46 | Licensed under either of 47 | 48 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 49 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 50 | 51 | at your option. 52 | 53 | ### Contribution 54 | 55 | Unless you explicitly state otherwise, any contribution intentionally submitted 56 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 57 | additional terms or conditions. 58 | 59 | ### Notes 60 | 61 | + *OpenCL and the OpenCL logo are trademarks of Apple Inc. used by permission by Khronos.* 62 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | fs::File, 4 | io::{Read, Write}, 5 | path::{Path, PathBuf}, 6 | }; 7 | use walkdir::WalkDir; 8 | 9 | 10 | fn main() { 11 | let ocl_src_dir = Path::new("ocl-src"); 12 | 13 | let mut files = Vec::new(); 14 | for entry in WalkDir::new(ocl_src_dir).into_iter().map(|e| e.unwrap()) { 15 | println!("cargo:rerun-if-changed={}", entry.path().display()); 16 | if entry.file_type().is_file() { 17 | files.push(entry.into_path()); 18 | } 19 | } 20 | let lines = files.into_iter().map(|path| { 21 | let mut content = String::new(); 22 | File::open(&path).unwrap().read_to_string(&mut content).unwrap(); 23 | format!( 24 | "\t(r#\"{}\"#, r###\"{}\"###),", 25 | path.strip_prefix(ocl_src_dir).unwrap().display(), 26 | content, 27 | ) 28 | }).collect::>(); 29 | let text = [ 30 | format!("const OCL_SRC_LIST: [(&'static str, &'static str); {}] = [", lines.len()), 31 | lines.join("\n"), 32 | "];".to_string(), 33 | ].join("\n"); 34 | 35 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 36 | let mut out_file = File::create(&out_dir.join("ocl_src_list.rs")).unwrap(); 37 | 38 | out_file.write_all(text.as_bytes()).unwrap(); 39 | } 40 | -------------------------------------------------------------------------------- /examples/00_ocl_info.rs: -------------------------------------------------------------------------------- 1 | use ocl::{self, Platform, Device}; 2 | 3 | 4 | fn print_info() -> ocl::Result<()> { 5 | println!("Available platforms:\n"); 6 | for p in Platform::list() { 7 | println!("Name: {}", p.name()?); 8 | println!("Version: {}", p.version()?); 9 | println!("Vendor: {}", p.vendor()?); 10 | println!("Devices:"); 11 | for d in Device::list_all(&p)? { 12 | println!(" {}", d.name()?); 13 | } 14 | println!(""); 15 | } 16 | Ok(()) 17 | } 18 | 19 | fn main() { 20 | match print_info() { 21 | Ok(()) => (), 22 | Err(err) => panic!("{}", err), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/01_1_spheres.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use ocl::{Platform, Device}; 3 | use nalgebra::{Vector3, Rotation3}; 4 | use clay::{ 5 | Context, 6 | shape::*, 7 | material::*, 8 | object::*, 9 | scene::{ListScene, GradientBackground as GradBg}, 10 | view::ProjectionView, 11 | process::{create_renderer, create_default_postproc}, 12 | }; 13 | use clay_viewer::{Window}; 14 | 15 | 16 | // Here we declare our object - a combination of 17 | // spherical shape and colored diffuse material 18 | type MyObject = Covered>; 19 | 20 | // Scene contains our objects and has gradient background 21 | type MyScene = ListScene; 22 | type MyView = ProjectionView; 23 | 24 | 25 | fn main() -> clay::Result<()> { 26 | // Select default OpenCL platform and device 27 | let platform = Platform::default(); 28 | let device = Device::first(platform)?; 29 | let context = Context::new(platform, device)?; 30 | 31 | // Dimensions of the window 32 | let dims = (1280, 800); 33 | 34 | // Initialize the scene 35 | let mut scene = ListScene::new(GradBg::new( 36 | Vector3::new(1.0, 1.0, 1.0), Vector3::new(0.0, 0.0, 0.0), 37 | Vector3::new(0.0, 0.0, 1.0), 38 | )); 39 | 40 | // Add two spheres to the scene 41 | scene.add( 42 | Sphere::new(0.75, Vector3::new(-0.75, 0.0, 0.0)) 43 | .cover(Diffuse {}.color_with(Vector3::new(0.4, 1.0, 0.4))) 44 | ); 45 | scene.add( 46 | Sphere::new(1.0, Vector3::new(1.0, 0.0, 0.0)) 47 | .cover(Diffuse {}.color_with(Vector3::new(0.4, 0.4, 1.0))) 48 | ); 49 | 50 | // Create view 51 | let view = ProjectionView::new( 52 | Vector3::new(0.25, -3.0, 0.0), 53 | Rotation3::face_towards(&-Vector3::new(0.0, 1.0, 0.0), &Vector3::z_axis()), 54 | ); 55 | 56 | // Create renderer and worker 57 | let renderer = create_renderer::().build(dims, scene, view)?; 58 | let (mut worker, _) = renderer.create_worker(&context)?; 59 | 60 | // Create dummy postprocessor 61 | let (mut postproc, _) = create_default_postproc().collect()? 62 | .build_default(&context, dims)?; 63 | 64 | // Create viewer window 65 | let mut window = Window::new(dims)?; 66 | 67 | // Repeatedly trace rays and collect statictics 68 | // that subsequently reduces image noise 69 | while !window.poll()? { 70 | // Render scene 71 | worker.run_for(Duration::from_millis(20))?; 72 | // Postprocess 73 | postproc.process_one(&worker.data().buffer())?; 74 | // Make image 75 | postproc.make_image()?; 76 | // Draw image on window 77 | window.draw(postproc.image())?; 78 | } 79 | 80 | Ok(()) 81 | } 82 | -------------------------------------------------------------------------------- /examples/01_2_motion.rs: -------------------------------------------------------------------------------- 1 | use std::{env, time::Duration}; 2 | use nalgebra::{Vector3, Rotation3}; 3 | use clay::{ 4 | prelude::*, 5 | shape::*, 6 | material::*, 7 | object::*, 8 | scene::{ListScene, GradientBackground as GradBg}, 9 | view::ProjectionView, 10 | process::{create_renderer, create_default_postproc}, 11 | }; 12 | use clay_viewer::{Window, Motion}; 13 | use clay_utils::{args, FrameCounter}; 14 | 15 | 16 | // Here we declare our object - a combination of 17 | // spherical shape and colored diffuse material 18 | type MyObject = Covered>; 19 | 20 | // Scene contains our objects and has gradient background 21 | type MyScene = ListScene; 22 | type MyView = ProjectionView; 23 | 24 | 25 | fn main() -> clay::Result<()> { 26 | // Parse args to select OpenCL platform 27 | let context = args::parse(env::args())?; 28 | 29 | // Dimensions of the window 30 | let dims = (1280, 800); 31 | 32 | // Initialize the scene 33 | let mut scene = ListScene::new(GradBg::new( 34 | Vector3::new(1.0, 1.0, 1.0), Vector3::new(0.0, 0.0, 0.0), 35 | Vector3::new(0.0, 0.0, 1.0), 36 | )); 37 | 38 | // Add two spheres to the scene 39 | scene.add( 40 | Sphere::new(0.75, Vector3::new(-0.75, 0.0, 0.0)) 41 | .cover(Diffuse {}.color_with(Vector3::new(0.4, 1.0, 0.4))) 42 | ); 43 | scene.add( 44 | Sphere::new(1.0, Vector3::new(1.0, 0.0, 0.0)) 45 | .cover(Diffuse {}.color_with(Vector3::new(0.4, 0.4, 1.0))) 46 | ); 47 | 48 | // Create view 49 | let view = ProjectionView::new( 50 | Vector3::new(0.25, -3.0, 0.0), 51 | Rotation3::face_towards(&-Vector3::new(0.0, 1.0, 0.0), &Vector3::z_axis()), 52 | ); 53 | 54 | // Create renderer and worker 55 | let mut renderer = create_renderer::().build(dims, scene, view)?; 56 | let (mut worker, _) = renderer.create_worker(&context)?; 57 | 58 | // Create dummy postprocessor 59 | let (mut postproc, _) = create_default_postproc().collect()? 60 | .build_default(&context, dims)?; 61 | 62 | // Create viewer window 63 | let mut window = Window::new(dims)?; 64 | // Capture mouse 65 | window.set_capture_mode(true); 66 | 67 | // Create motion controller 68 | let mut motion = Motion::new(renderer.view.pos, renderer.view.ori.clone()); 69 | 70 | // Structure for frame rate measurement (optional) 71 | let mut fcnt = FrameCounter::new_with_log(Duration::from_secs(2)); 72 | 73 | // Main loop - repeatedly update view and render 74 | while !window.poll_with_handler(&mut motion)? { 75 | // Render 76 | let n = worker.run_for(Duration::from_millis(20))?; 77 | 78 | // Postprocess 79 | postproc.process_one(&worker.data().buffer())?; 80 | postproc.make_image()?; 81 | 82 | // Draw image to Window 83 | window.draw(&postproc.image())?; 84 | 85 | // Measure frame duration 86 | let dt = window.step_frame(); 87 | 88 | // Check motion occured 89 | if motion.was_updated() { 90 | // Clear cumulative buffer 91 | worker.data_mut().buffer_mut().clear()?; 92 | 93 | // Move to a new location 94 | motion.step(dt); 95 | 96 | // Update view location 97 | renderer.view.update(motion.pos(), motion.ori()); 98 | renderer.view.fov = motion.fov; 99 | renderer.update_data(&context, worker.data_mut())?; 100 | } 101 | 102 | // Count and print frame rate 103 | fcnt.step_frame(dt, n); 104 | } 105 | 106 | Ok(()) 107 | } 108 | -------------------------------------------------------------------------------- /examples/02_shapes.rs: -------------------------------------------------------------------------------- 1 | use std::{env, time::Duration}; 2 | use nalgebra::{Vector3, Rotation3, Matrix3}; 3 | use clay::{ 4 | prelude::*, 5 | shape::*, 6 | material::*, 7 | object::*, 8 | scene::{ListScene, GradientBackground as GradBg}, 9 | view::ProjectionView, 10 | process::{create_renderer, create_default_postproc}, 11 | shape_select, 12 | }; 13 | use clay_viewer::{Window, Motion}; 14 | use clay_utils::{args, FrameCounter}; 15 | 16 | 17 | shape_select!(MyShape { 18 | P(TP=Parallelepiped), 19 | S(TS=Ellipsoid), 20 | }); 21 | 22 | // Here we declare our object - a combination of 23 | // spherical shape and colored diffuse material 24 | type MyObject = Covered>; 25 | 26 | // Scene contains our objects and has gradient background 27 | type MyScene = ListScene; 28 | type MyView = ProjectionView; 29 | 30 | 31 | fn main() -> clay::Result<()> { 32 | // Parse args to select OpenCL platform 33 | let context = args::parse(env::args())?; 34 | 35 | // Dimensions of the window 36 | let dims = (1280, 800); 37 | 38 | // Initialize the scene 39 | let mut scene = ListScene::new(GradBg::new( 40 | Vector3::new(1.0, 1.0, 1.0), Vector3::new(0.0, 0.0, 0.0), 41 | Vector3::new(0.0, 0.0, 1.0), 42 | )); 43 | 44 | scene.add( 45 | MyShape::from(Ellipsoid::new( 46 | Matrix3::from_diagonal(&Vector3::new(0.8, 0.7, 0.4)), 47 | Vector3::new(0.0, 0.0, 0.4), 48 | )) 49 | .cover(Diffuse {}.color_with(Vector3::new(0.9, 0.3, 0.3))) 50 | ); 51 | scene.add( 52 | MyShape::from(Ellipsoid::new( 53 | Matrix3::from_diagonal(&Vector3::new(0.4, 0.5, 0.2)), 54 | Vector3::new(0.0, 0.0, 1.0), 55 | )) 56 | .cover(Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))) 57 | ); 58 | scene.add( 59 | MyShape::from(Parallelepiped::new( 60 | 0.4/3.0f64.sqrt()*Rotation3::rotation_between( 61 | &Vector3::new(1.0, 1.0, 1.0), 62 | &Vector3::new(0.0, 0.0, 1.0), 63 | ).unwrap().matrix().clone(), 64 | Vector3::new(0.0, 0.0, 1.6), 65 | )) 66 | .cover(Diffuse {}.color_with(Vector3::new(0.9, 0.3, 0.3))) 67 | ); 68 | 69 | // Add ground 70 | scene.add( 71 | MyShape::from(Parallelepiped::new( 72 | Matrix3::from_diagonal(&Vector3::new(10.0, 10.0, 0.5)), 73 | Vector3::new(0.0, 0.0, -0.5), 74 | )) 75 | .cover(Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))) 76 | ); 77 | 78 | // Create view 79 | let view = ProjectionView::new( 80 | Vector3::new(2.0, 0.0, 1.0), 81 | Rotation3::face_towards(&-Vector3::new(-1.0, 0.0, 0.0), &Vector3::z_axis()), 82 | ); 83 | 84 | // Create renderer and worker 85 | let mut renderer = create_renderer::().build(dims, scene, view)?; 86 | let (mut worker, _) = renderer.create_worker(&context)?; 87 | 88 | // Create dummy postprocessor 89 | let (mut postproc, _) = create_default_postproc().collect()? 90 | .build_default(&context, dims)?; 91 | 92 | // Create viewer window 93 | let mut window = Window::new(dims)?; 94 | // Capture mouse 95 | window.set_capture_mode(true); 96 | 97 | // Create motion controller 98 | let mut motion = Motion::new(renderer.view.pos, renderer.view.ori.clone()); 99 | 100 | // Structure for frame rate measurement (optional) 101 | let mut fcnt = FrameCounter::new_with_log(Duration::from_secs(2)); 102 | 103 | // Main loop - repeatedly update view and render 104 | while !window.poll_with_handler(&mut motion)? { 105 | // Render 106 | let n = worker.run_for(Duration::from_millis(20))?; 107 | 108 | // Postprocess 109 | postproc.process_one(&worker.data().buffer())?; 110 | postproc.make_image()?; 111 | 112 | // Draw image to Window 113 | window.draw(&postproc.image())?; 114 | 115 | // Measure frame duration 116 | let dt = window.step_frame(); 117 | 118 | // Check motion occured 119 | if motion.was_updated() { 120 | // Clear cumulative buffer 121 | worker.data_mut().buffer_mut().clear()?; 122 | 123 | // Move to a new location 124 | motion.step(dt); 125 | 126 | // Update view location 127 | renderer.view.update(motion.pos(), motion.ori()); 128 | renderer.view.fov = motion.fov; 129 | renderer.update_data(&context, worker.data_mut())?; 130 | } 131 | 132 | // Count and print frame rate 133 | fcnt.step_frame(dt, n); 134 | } 135 | 136 | Ok(()) 137 | } 138 | -------------------------------------------------------------------------------- /examples/03_materials.rs: -------------------------------------------------------------------------------- 1 | use std::{env, time::Duration}; 2 | use nalgebra::{Vector3, Rotation3, Matrix3}; 3 | use clay::{ 4 | prelude::*, 5 | shape::*, 6 | material::*, 7 | object::*, 8 | scene::{ListScene, ConstantBackground as ConstBg}, 9 | view::ProjectionView, 10 | filter::*, 11 | process::{create_renderer, create_postproc}, 12 | shape_select, material_select, material_combine, 13 | }; 14 | use clay_viewer::{Window, Motion}; 15 | use clay_utils::{args, FrameCounter}; 16 | 17 | 18 | shape_select!(MyShape { 19 | P(TP=Parallelepiped), 20 | S(TS=Ellipsoid), 21 | }); 22 | 23 | material_combine!(Glossy { 24 | reflect: Reflective, 25 | diffuse: Colored, 26 | }); 27 | material_combine!(Glowing { 28 | diffuse: Reflective, 29 | reflect: Colored, 30 | luminous: Colored, 31 | }); 32 | material_select!(MyMaterial { 33 | D(TD=Colored), 34 | R(TR=Glossy), 35 | G(TG=Glowing), 36 | L(TL=Colored), 37 | }); 38 | 39 | // Here we declare our object - a combination of 40 | // spherical shape and colored diffuse material 41 | type MyObject = Covered; 42 | 43 | // Scene contains our objects and has gradient background 44 | type MyScene = ListScene; 45 | type MyView = ProjectionView; 46 | 47 | 48 | fn main() -> clay::Result<()> { 49 | // Parse args to select OpenCL platform 50 | let context = args::parse(env::args())?; 51 | 52 | // Dimensions of the window 53 | let dims = (1280, 800); 54 | 55 | // Initialize the scene 56 | let mut scene = MyScene::new(ConstBg::new(Vector3::new(0.0, 0.0, 0.0))); 57 | scene.set_max_depth(6); 58 | 59 | scene.add( 60 | MyShape::from(Parallelepiped::new( 61 | Matrix3::from_diagonal(&Vector3::new(0.8, 1.6, 0.8)), 62 | Vector3::new(-2.0, 0.0, 0.8), 63 | )) 64 | .cover(MyMaterial::from(Glossy::new( 65 | (0.95, Reflective {}), 66 | (0.05, Diffuse {}.color_with(Vector3::new(1.0, 1.0, 1.0))), 67 | ))) 68 | ); 69 | scene.add( 70 | MyShape::from(Ellipsoid::new( 71 | 0.4*Matrix3::identity(), 72 | Vector3::new(0.0, 1.0, 0.4), 73 | )) 74 | .cover(MyMaterial::from(Glossy::new( 75 | (0.2, Reflective {}), 76 | (0.8, Diffuse {}.color_with(Vector3::new(1.0, 0.01, 0.01))), 77 | ))) 78 | ); 79 | scene.add( 80 | MyShape::from(Ellipsoid::new( 81 | 0.3*Matrix3::identity(), 82 | Vector3::new(0.0, -1.0, 0.3), 83 | )) 84 | .cover(MyMaterial::from( 85 | Diffuse {}.color_with(Vector3::new(0.1, 0.01, 0.9)) 86 | )) 87 | ); 88 | scene.add( 89 | MyShape::from(Ellipsoid::new( 90 | 0.25*Matrix3::identity(), 91 | Vector3::new(0.0, 0.0, 0.25), 92 | )) 93 | .cover(MyMaterial::from( 94 | Luminous {}.color_with(20.0*Vector3::new(1.0, 1.0, 0.2)), 95 | )) 96 | ); 97 | scene.add( 98 | MyShape::from(Ellipsoid::new( 99 | 0.25*Matrix3::identity(), 100 | Vector3::new(1.0, 0.0, 0.25), 101 | )) 102 | .cover(MyMaterial::from(Glowing::new( 103 | (0.1, Reflective {}), 104 | (0.6, Diffuse {}.color_with(Vector3::new(1.0, 1.0, 1.0))), 105 | (0.3, Luminous {}.color_with(2.0*Vector3::new(0.01, 1.0, 0.01))), 106 | ))) 107 | ); 108 | 109 | scene.add( 110 | MyShape::from(Parallelepiped::new( 111 | Matrix3::from_diagonal(&Vector3::new(5.0, 5.0, 0.5)), 112 | Vector3::new(0.0, 0.0, -0.5), 113 | )) 114 | .cover(MyMaterial::from(Glossy::new( 115 | (0.1, Reflective {}), 116 | (0.9, Diffuse {}.color_with(Vector3::new(1.0, 1.0, 1.0))), 117 | ))) 118 | ); 119 | 120 | // Create view 121 | let view = ProjectionView::new( 122 | Vector3::new(2.0, -2.0, 2.0), 123 | Rotation3::face_towards(&-Vector3::new(-1.0, 0.8, -0.8), &Vector3::z_axis()), 124 | ); 125 | 126 | // Create renderer and worker 127 | let mut renderer = create_renderer::().build(dims, scene, view)?; 128 | let (mut worker, _) = renderer.create_worker(&context)?; 129 | 130 | // Create dummy postprocessor 131 | let (mut postproc, _) = create_postproc().collect()? 132 | .build(&context, dims, LogFilter::new(-4.0, 2.0))?; 133 | 134 | // Create viewer window 135 | let mut window = Window::new(dims)?; 136 | // Capture mouse 137 | window.set_capture_mode(true); 138 | 139 | // Create motion controller 140 | let mut motion = Motion::new(renderer.view.pos, renderer.view.ori.clone()); 141 | 142 | // Structure for frame rate measurement (optional) 143 | let mut fcnt = FrameCounter::new_with_log(Duration::from_secs(2)); 144 | 145 | // Main loop - repeatedly update view and render 146 | while !window.poll_with_handler(&mut motion)? { 147 | // Render 148 | let n = worker.run_for(Duration::from_millis(20))?; 149 | 150 | // Postprocess 151 | postproc.process_one(&worker.data().buffer())?; 152 | postproc.make_image()?; 153 | 154 | // Draw image to Window 155 | window.draw(&postproc.image())?; 156 | 157 | // Measure frame duration 158 | let dt = window.step_frame(); 159 | 160 | // Check motion occured 161 | if motion.was_updated() { 162 | // Clear cumulative buffer 163 | worker.data_mut().buffer_mut().clear()?; 164 | 165 | // Move to a new location 166 | motion.step(dt); 167 | 168 | // Update view location 169 | renderer.view.update(motion.pos(), motion.ori()); 170 | renderer.view.fov = motion.fov; 171 | renderer.update_data(&context, worker.data_mut())?; 172 | } 173 | 174 | // Count and print frame rate 175 | fcnt.step_frame(dt, n); 176 | } 177 | 178 | Ok(()) 179 | } 180 | -------------------------------------------------------------------------------- /examples/04_1_light_source.rs: -------------------------------------------------------------------------------- 1 | use std::{env, time::Duration}; 2 | use nalgebra::{Vector3, Rotation3, Matrix3}; 3 | use clay::{ 4 | prelude::*, 5 | shape::*, 6 | material::*, 7 | object::*, 8 | scene::{TargetListScene, GradientBackground as GradBg}, 9 | view::ProjectionView, 10 | process::{create_renderer, create_default_postproc}, 11 | shape_select, material_select, 12 | }; 13 | use clay_viewer::{Window, Motion}; 14 | use clay_utils::{args, FrameCounter}; 15 | 16 | 17 | shape_select!(MyShape { 18 | P(TP=Parallelepiped), 19 | S(TS=Ellipsoid), 20 | }); 21 | material_select!(MyMaterial { 22 | D(TD=Colored), 23 | L(TL=Colored), 24 | }); 25 | 26 | // Here we declare our object - a combination of 27 | // spherical shape and colored diffuse material 28 | type MyObject = Covered; 29 | 30 | // Scene contains our objects and has gradient background 31 | type MyScene = TargetListScene; 32 | type MyView = ProjectionView; 33 | 34 | 35 | fn main() -> clay::Result<()> { 36 | // Parse args to select OpenCL platform 37 | let context = args::parse(env::args())?; 38 | 39 | // Dimensions of the window 40 | let dims = (1280, 800); 41 | 42 | // Initialize the scene 43 | let mut scene = TargetListScene::new(GradBg::new( 44 | Vector3::new(0.1, 0.1, 0.2), Vector3::new(0.0, 0.0, 0.0), 45 | Vector3::new(0.0, 0.0, 1.0), 46 | )); 47 | scene.set_max_depth(4); 48 | 49 | // Add complex shape 50 | let mut shapes = Vec::new(); 51 | let size = 1.0; 52 | let fill = 0.5; 53 | for i in 0..4 { 54 | let (x, y) = (2.0*((i % 2) as f64) - 1.0, 2.0*((i / 2) as f64) - 1.0); 55 | shapes.push(Parallelepiped::new( 56 | Matrix3::from_diagonal(&Vector3::new(fill/3.0, fill/3.0, 1.0))*size, 57 | (Vector3::new(x, y, 0.0)*(1.0 - fill/3.0) + Vector3::new(0.0, 0.0, 1.0))*size, 58 | )); 59 | shapes.push(Parallelepiped::new( 60 | Matrix3::from_diagonal(&Vector3::new(fill, 3.0 - 2.0*fill, fill))*size/3.0, 61 | (Vector3::new(x, 0.0, y)*(1.0 - fill/3.0) + Vector3::new(0.0, 0.0, 1.0))*size, 62 | )); 63 | shapes.push(Parallelepiped::new( 64 | Matrix3::from_diagonal(&Vector3::new(3.0 - 2.0*fill, fill, fill))*size/3.0, 65 | (Vector3::new(0.0, x, y)*(1.0 - fill/3.0) + Vector3::new(0.0, 0.0, 1.0))*size, 66 | )); 67 | } 68 | for p in shapes { 69 | scene.add(MyShape::from(p).cover( 70 | MyMaterial::from(Diffuse {}.color_with(Vector3::new(0.3, 0.3, 0.9))) 71 | )); 72 | } 73 | 74 | // Add ground 75 | scene.add( 76 | MyShape::from(Parallelepiped::new( 77 | Matrix3::from_diagonal(&Vector3::new(10.0, 10.0, 0.5)), 78 | Vector3::new(0.0, 0.0, -0.5), 79 | )) 80 | .cover(MyMaterial::from(Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9)))) 81 | ); 82 | 83 | // Add light sources 84 | scene.add_targeted( 85 | MyShape::from(Ellipsoid::new( 86 | 1.0*Matrix3::identity(), 10.0*Vector3::new(4.0, 6.0, 8.0), 87 | )) 88 | .cover(MyMaterial::from(Luminous {}.color_with(2e4*Vector3::new(1.0, 1.0, 0.8)))) 89 | ); 90 | 91 | // Create view 92 | let view = ProjectionView::new( 93 | Vector3::new(2.0, 0.0, 1.0), 94 | Rotation3::face_towards(&-Vector3::new(-1.0, 0.0, 0.0), &Vector3::z_axis()), 95 | ); 96 | 97 | // Create renderer and worker 98 | let mut renderer = create_renderer::().build(dims, scene, view)?; 99 | let (mut worker, _) = renderer.create_worker(&context)?; 100 | 101 | // Create dummy postprocessor 102 | let (mut postproc, _) = create_default_postproc().collect()? 103 | .build_default(&context, dims)?; 104 | 105 | // Create viewer window 106 | let mut window = Window::new(dims)?; 107 | // Capture mouse 108 | window.set_capture_mode(true); 109 | 110 | // Create motion controller 111 | let mut motion = Motion::new(renderer.view.pos, renderer.view.ori.clone()); 112 | 113 | // Structure for frame rate measurement (optional) 114 | let mut fcnt = FrameCounter::new_with_log(Duration::from_secs(2)); 115 | 116 | // Main loop - repeatedly update view and render 117 | while !window.poll_with_handler(&mut motion)? { 118 | // Render 119 | let n = worker.run_for(Duration::from_millis(20))?; 120 | 121 | // Postprocess 122 | postproc.process_one(&worker.data().buffer())?; 123 | postproc.make_image()?; 124 | 125 | // Draw image to Window 126 | window.draw(&postproc.image())?; 127 | 128 | // Measure frame duration 129 | let dt = window.step_frame(); 130 | 131 | // Check motion occured 132 | if motion.was_updated() { 133 | // Clear cumulative buffer 134 | worker.data_mut().buffer_mut().clear()?; 135 | 136 | // Move to a new location 137 | motion.step(dt); 138 | 139 | // Update view location 140 | renderer.view.update(motion.pos(), motion.ori()); 141 | renderer.view.fov = motion.fov; 142 | renderer.update_data(&context, worker.data_mut())?; 143 | } 144 | 145 | // Count and print frame rate 146 | fcnt.step_frame(dt, n); 147 | } 148 | 149 | Ok(()) 150 | } 151 | -------------------------------------------------------------------------------- /examples/04_2_multiple_sources.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | io::Write, 4 | fs::{File, create_dir_all}, 5 | time::Duration, 6 | }; 7 | use nalgebra::{Vector3, Matrix3, Rotation3}; 8 | use clay::{ 9 | prelude::*, 10 | shape::*, 11 | material::*, 12 | object::*, 13 | scene::{TargetListScene, GradientBackground as GradBg}, 14 | view::ProjectionView, 15 | filter::{IdentityFilter}, 16 | process::{create_renderer, create_postproc}, 17 | shape_select, material_select, material_combine, 18 | }; 19 | use clay_viewer::{Window, Motion}; 20 | use clay_utils::{args, FrameCounter}; 21 | 22 | shape_select!(MyShape { 23 | Cube(TC=Parallelepiped), 24 | Sphere(TS=Ellipsoid), 25 | }); 26 | material_combine!(Glossy { 27 | reflect: Reflective, 28 | diffuse: Colored, 29 | }); 30 | material_select!(MyMaterial { 31 | Matte(TM=Colored), 32 | Glossy(TG=Glossy), 33 | Luminous(TC=Colored), 34 | }); 35 | type MyObject = Covered; 36 | type MyScene = TargetListScene; 37 | type MyView = ProjectionView; 38 | 39 | 40 | fn main() -> clay::Result<()> { 41 | // Parse args to select OpenCL platform 42 | let context = args::parse(env::args())?; 43 | let dims = (1280, 800); 44 | 45 | let mut scene = TargetListScene::new(GradBg::new( 46 | Vector3::new(0.2, 0.2, 0.4), Vector3::zeros(), 47 | Vector3::new(0.0, 0.0, 1.0), 48 | )); 49 | scene.add_targeted( 50 | MyShape::from(Parallelepiped::new( 51 | 0.25*Matrix3::identity(), 52 | Vector3::new(-2.0, 0.0, 5.0), 53 | )) 54 | .cover(MyMaterial::from( 55 | Luminous {}.color_with(100.0*Vector3::new(1.0, 1.0, 0.5)), 56 | )) 57 | ); 58 | scene.add_targeted( 59 | MyShape::from(Ellipsoid::new( 60 | 0.2*Matrix3::identity(), 61 | Vector3::new(0.0, -2.0, 2.5), 62 | )) 63 | .cover(MyMaterial::from( 64 | Luminous {}.color_with(100.0*Vector3::new(0.2, 0.2, 1.0)), 65 | )) 66 | ); 67 | scene.add( 68 | MyShape::from(Parallelepiped::new( 69 | Matrix3::from_diagonal(&Vector3::new(5.0, 5.0, 0.1)), 70 | Vector3::new(0.0, 0.0, -0.1), 71 | )) 72 | .cover(MyMaterial::from( 73 | Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9)), 74 | )) 75 | ); 76 | scene.add( 77 | MyShape::from(Parallelepiped::new( 78 | 0.3*Matrix3::identity(), 79 | Vector3::new(1.0, 0.0, 0.3), 80 | )) 81 | .cover(MyMaterial::from(Glossy::new( 82 | (0.2, Reflective {}), 83 | (0.8, Diffuse {}.color_with(Vector3::new(0.5, 0.5, 0.9))), 84 | ))) 85 | ); 86 | scene.add( 87 | MyShape::from(Ellipsoid::new( 88 | 0.5*Matrix3::identity(), 89 | Vector3::new(0.0, 0.0, 0.5), 90 | )) 91 | .cover(MyMaterial::from(Glossy::new( 92 | (0.1, Reflective {}), 93 | (0.9, Diffuse {}.color_with(Vector3::new(0.9, 0.5, 0.5))), 94 | ))) 95 | ); 96 | scene.add( 97 | MyShape::from(Ellipsoid::new( 98 | 0.4*Matrix3::identity(), 99 | Vector3::new(0.5, 1.0, 0.4), 100 | )) 101 | .cover(MyMaterial::from( 102 | Diffuse {}.color_with(Vector3::new(0.5, 0.9, 0.5)), 103 | )) 104 | ); 105 | 106 | let view = ProjectionView::new( 107 | Vector3::new(0.5, -2.0, 2.0), 108 | Rotation3::face_towards(&-Vector3::new(0.0, 1.0,-0.75), &Vector3::z_axis()), 109 | ); 110 | 111 | let mut renderer = create_renderer::().build(dims, scene, view)?; 112 | let postproc_builder = create_postproc::().collect()?; 113 | 114 | create_dir_all("./__gen_programs")?; 115 | for (name, prog) in [ 116 | ("render.c", &renderer.program()), 117 | ("filter.c", &postproc_builder.program()), 118 | ].iter() { 119 | File::create(&format!("__gen_programs/{}", name))? 120 | .write_all(prog.source().as_bytes())?; 121 | } 122 | 123 | let (mut worker, message) = renderer.create_worker(&context)?; 124 | if message.len() > 0 { 125 | println!("render build log:\n{}", message); 126 | } 127 | 128 | let (mut postproc, message) = postproc_builder.build(&context, dims, IdentityFilter::new())?; 129 | if message.len() > 0 { 130 | println!("filter build log:\n{}", message); 131 | } 132 | 133 | let mut window = Window::new(dims)?; 134 | window.set_capture_mode(true); 135 | 136 | let mut motion = Motion::new(renderer.view.pos, renderer.view.ori.clone()); 137 | 138 | // Structure for frame rate measurement (optional) 139 | let mut fcnt = FrameCounter::new_with_log(Duration::from_secs(2)); 140 | 141 | // Main loop - repeatedly update view and render 142 | while !window.poll_with_handler(&mut motion)? { 143 | // Render 144 | let n = worker.run_for(Duration::from_millis(20))?; 145 | 146 | // Postprocess 147 | postproc.process_one(&worker.data().buffer())?; 148 | postproc.make_image()?; 149 | 150 | // Draw image to Window 151 | window.draw(&postproc.image())?; 152 | 153 | // Measure frame duration 154 | let dt = window.step_frame(); 155 | 156 | // Check motion occured 157 | if motion.was_updated() { 158 | // Clear cumulative buffer 159 | worker.data_mut().buffer_mut().clear()?; 160 | 161 | // Move to a new location 162 | motion.step(dt); 163 | 164 | // Update view location 165 | renderer.view.update(motion.pos(), motion.ori()); 166 | renderer.view.fov = motion.fov; 167 | renderer.update_data(&context, worker.data_mut())?; 168 | } 169 | 170 | // Count and print frame rate 171 | fcnt.step_frame(dt, n); 172 | } 173 | 174 | Ok(()) 175 | } 176 | -------------------------------------------------------------------------------- /examples/05_indirect_lighting.rs: -------------------------------------------------------------------------------- 1 | use std::{env, time::Duration}; 2 | use nalgebra::{Vector3, Rotation3, Matrix3}; 3 | use clay::{ 4 | prelude::*, 5 | shape::*, 6 | material::*, 7 | object::*, 8 | scene::{TargetListScene, GradientBackground as GradBg}, 9 | view::ProjectionView, 10 | filter::*, 11 | process::{create_renderer, create_postproc}, 12 | shape_select, material_select, material_combine, 13 | }; 14 | use clay_viewer::{Window, Motion}; 15 | use clay_utils::{args, FrameCounter}; 16 | 17 | 18 | shape_select!(MyShape { 19 | P(TP=Parallelepiped), 20 | S(TS=Ellipsoid), 21 | }); 22 | material_combine!(Glossy { 23 | reflect: Reflective, 24 | diffuse: Colored, 25 | }); 26 | material_select!(MyMaterial { 27 | D(TD=Colored), 28 | R(TR=Reflective), 29 | G(TG=Glossy), 30 | L(TL=Colored), 31 | }); 32 | 33 | // Here we declare our object - a combination of 34 | // spherical shape and colored diffuse material 35 | type MyObject = Covered; 36 | 37 | // Scene contains our objects and has gradient background 38 | type MyScene = TargetListScene; 39 | type MyView = ProjectionView; 40 | 41 | 42 | fn main() -> clay::Result<()> { 43 | // Parse args to select OpenCL platform 44 | let context = args::parse(env::args())?; 45 | 46 | // Dimensions of the window 47 | let dims = (1280, 800); 48 | 49 | // Initialize the scene 50 | let mut scene = TargetListScene::new(GradBg::new( 51 | 10.0*Vector3::new(0.1, 0.1, 1.0), 10.0*Vector3::new(0.5, 0.5, 1.0), 52 | Vector3::new(0.0, 0.0, 1.0), 53 | )); 54 | scene.set_max_depth(8); 55 | scene.set_target_prob(0.1); 56 | 57 | // Add room 58 | let size = (3.0, 3.0, 1.5); // room parameters 59 | let (wpos, wsize) = (0.5, (1.6, 1.0)); // window parameters 60 | let thc = 0.05; // thickness 61 | let mut parts = Vec::new(); 62 | 63 | // ceil 64 | parts.push((Parallelepiped::new( 65 | Matrix3::from_diagonal(&Vector3::new(size.0 + 2.0*thc, size.1 + 2.0*thc, thc)), 66 | Vector3::new(0.0, 0.0, 2.0*size.2 + thc), 67 | ), Vector3::new(0.9, 0.9, 0.9))); 68 | // walls 69 | parts.push((Parallelepiped::new( 70 | Matrix3::from_diagonal(&Vector3::new(thc, size.1, size.2)), 71 | Vector3::new(-(size.0 + thc), 0.0, size.2), 72 | ), Vector3::new(0.4, 0.4, 1.0))); 73 | parts.push((Parallelepiped::new( 74 | Matrix3::from_diagonal(&Vector3::new(size.0 + 2.0*thc, thc, size.2)), 75 | Vector3::new(0.0, -(size.1 + thc), size.2), 76 | ), Vector3::new(0.4, 1.0, 0.4))); 77 | parts.push((Parallelepiped::new( 78 | Matrix3::from_diagonal(&Vector3::new(size.0 + 2.0*thc, thc, size.2)), 79 | Vector3::new(0.0, size.1 + thc, size.2), 80 | ), Vector3::new(1.0, 1.0, 0.4))); 81 | // last wall with window 82 | let mut wparts = Vec::new(); 83 | wparts.push(Parallelepiped::new( 84 | Matrix3::from_diagonal(&Vector3::new(thc, size.1, 0.5*wpos)), 85 | Vector3::new(size.0 + thc, 0.0, 0.5*wpos), 86 | )); 87 | wparts.push(Parallelepiped::new( 88 | Matrix3::from_diagonal(&Vector3::new(thc, size.1, size.2 - wsize.1 - 0.5*wpos)), 89 | Vector3::new(size.0 + thc, 0.0, size.2 + wsize.1 + 0.5*wpos), 90 | )); 91 | wparts.push(Parallelepiped::new( 92 | Matrix3::from_diagonal(&Vector3::new(thc, 0.5*(size.1 - wsize.0), wsize.1)), 93 | Vector3::new(size.0 + thc, 0.5*(size.1 + wsize.0), wpos + wsize.1), 94 | )); 95 | wparts.push(Parallelepiped::new( 96 | Matrix3::from_diagonal(&Vector3::new(thc, 0.5*(size.1 - wsize.0), wsize.1)), 97 | Vector3::new(size.0 + thc, -0.5*(size.1 + wsize.0), wpos + wsize.1), 98 | )); 99 | // window cross 100 | wparts.push(Parallelepiped::new( 101 | Matrix3::from_diagonal(&Vector3::new(thc, thc, wsize.1)), 102 | Vector3::new(size.0 + thc, 0.0, wpos + wsize.1), 103 | )); 104 | wparts.push(Parallelepiped::new( 105 | Matrix3::from_diagonal(&Vector3::new(thc, wsize.0, thc)), 106 | Vector3::new(size.0 + thc, 0.0, wpos + wsize.1), 107 | )); 108 | for wp in wparts { 109 | parts.push((wp, Vector3::new(0.9, 0.9, 0.9))); 110 | } 111 | for (s, c) in parts { 112 | scene.add(MyShape::from(s).cover(MyMaterial::from(Diffuse {}.color_with(c)))); 113 | } 114 | 115 | // floor 116 | scene.add(MyShape::from(Parallelepiped::new( 117 | Matrix3::from_diagonal(&Vector3::new(size.0 + 2.0*thc, size.1 + 2.0*thc, thc)), 118 | Vector3::new(0.0, 0.0, -thc), 119 | )).cover(MyMaterial::from(Glossy::new( 120 | (0.1, Reflective {}), 121 | (0.9, Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))), 122 | )))); 123 | 124 | let gap = 0.05; 125 | // Bed 126 | scene.add(MyShape::from(Parallelepiped::new( 127 | Matrix3::from_diagonal(&Vector3::new(0.8, 1.2, 0.3)), 128 | Vector3::new(-(size.0 - 0.8 - gap), size.1 - 1.2 - gap, 0.3 + gap), 129 | )).cover(MyMaterial::from(Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))))); 130 | // Shelf 131 | scene.add(MyShape::from(Parallelepiped::new( 132 | Matrix3::from_diagonal(&Vector3::new(0.8, 0.2, 1.0)), 133 | Vector3::new(-(size.0 - 0.8 - gap), -(size.1 - 0.2 - gap), 1.0), 134 | )).cover(MyMaterial::from(Glossy::new( 135 | (0.1, Reflective {}), 136 | (0.9, Diffuse {}.color_with(Vector3::new(1.0, 1.0, 1.0))), 137 | )))); 138 | // Mirror 139 | scene.add(MyShape::from(Parallelepiped::new( 140 | Matrix3::from_diagonal(&Vector3::new(1.0, 0.01, 0.8)), 141 | Vector3::new(0.0, size.1 - 0.01, 1.3), 142 | )).cover(MyMaterial::from(Reflective {}))); 143 | // Ball 144 | scene.add(MyShape::from(Ellipsoid::new( 145 | 0.4*Matrix3::identity(), 146 | Vector3::new(-(size.0 - 0.4 - gap), -0.2*(size.1 - 0.4), 0.4), 147 | )).cover(MyMaterial::from(Reflective {}))); 148 | // Table 149 | let tsize = (0.6, 0.6, 0.4); 150 | let tpos = (0.0, size.1 - tsize.1 - gap); 151 | let tw = 0.05; 152 | let mut tparts = Vec::new(); 153 | tparts.push(Parallelepiped::new( 154 | Matrix3::from_diagonal(&Vector3::new(tsize.0, tsize.1, tw)), 155 | Vector3::new(tpos.0, tpos.1, tsize.2 - tw), 156 | )); 157 | for i in 0..4 { 158 | let s = (2.0*((i/2) as f64 - 0.5), 2.0*((i%2) as f64 - 0.5)); 159 | tparts.push(Parallelepiped::new( 160 | Matrix3::from_diagonal(&Vector3::new(tw, tw, 0.5*(tsize.2 - 2.0*tw))), 161 | Vector3::new(tpos.0 + s.0*(tsize.0 - tw), tpos.1 + s.1*(tsize.1 - tw), 0.5*(tsize.2 - 2.0*tw)), 162 | )); 163 | } 164 | for tp in tparts { 165 | scene.add(MyShape::from(tp).cover(MyMaterial::from(Glossy::new( 166 | (0.1, Reflective {}), 167 | (0.9, Diffuse {}.color_with(Vector3::new(0.2, 0.1, 0.1))), 168 | )))); 169 | } 170 | 171 | // Add ground 172 | scene.add(MyShape::from(Parallelepiped::new( 173 | Matrix3::from_diagonal(&Vector3::new(100.0, 100.0, 0.5)), 174 | Vector3::new(0.0, 0.0, -0.5 - 2.0*thc), 175 | )).cover(MyMaterial::from(Diffuse {}.color_with(Vector3::new(0.5, 1.0, 0.3))))); 176 | 177 | // Add light source 178 | let dist = 1e4; 179 | let lrad = 2e-2*dist; 180 | scene.add_targeted(MyShape::from(Ellipsoid::new( 181 | lrad*Matrix3::identity(), dist*Vector3::new(1.0,-0.1, 0.2), 182 | )).cover(MyMaterial::from(Luminous {}.color_with(4e4*Vector3::new(1.0, 1.0, 0.6))))); 183 | 184 | // Create view 185 | let view = ProjectionView::new( 186 | Vector3::new(1.5,-2.0, 1.0), 187 | Rotation3::face_towards(&-Vector3::new(-0.85, 1.0,-0.1), &Vector3::z_axis()), 188 | ); 189 | 190 | // Create renderer and worker 191 | let mut renderer = create_renderer::().build(dims, scene, view)?; 192 | let (mut worker, _) = renderer.create_worker(&context)?; 193 | 194 | // Create dummy postprocessor 195 | let (mut postproc, _) = create_postproc().collect()? 196 | .build(&context, dims, LogFilter::new(-1.0, 1.5))?; 197 | 198 | // Create viewer window 199 | let mut window = Window::new(dims)?; 200 | // Capture mouse 201 | window.set_capture_mode(true); 202 | 203 | // Create motion controller 204 | let mut motion = Motion::new(renderer.view.pos, renderer.view.ori.clone()); 205 | 206 | // Structure for frame rate measurement (optional) 207 | let mut fcnt = FrameCounter::new_with_log(Duration::from_secs(2)); 208 | 209 | // Main loop - repeatedly update view and render 210 | while !window.poll_with_handler(&mut motion)? { 211 | // Render 212 | let n = worker.run_for(Duration::from_millis(20))?; 213 | 214 | // Postprocess 215 | postproc.process_one(&worker.data().buffer())?; 216 | postproc.make_image()?; 217 | 218 | // Draw image to Window 219 | window.draw(&postproc.image())?; 220 | 221 | // Measure frame duration 222 | let dt = window.step_frame(); 223 | 224 | // Check motion occured 225 | if motion.was_updated() { 226 | // Clear cumulative buffer 227 | worker.data_mut().buffer_mut().clear()?; 228 | 229 | // Move to a new location 230 | motion.step(dt); 231 | 232 | // Update view location 233 | renderer.view.update(motion.pos(), motion.ori()); 234 | renderer.view.fov = motion.fov; 235 | renderer.update_data(&context, worker.data_mut())?; 236 | } 237 | 238 | // Count and print frame rate 239 | fcnt.step_frame(dt, n); 240 | } 241 | 242 | Ok(()) 243 | } 244 | -------------------------------------------------------------------------------- /examples/98_rocks.rs: -------------------------------------------------------------------------------- 1 | use std::{env, time::Duration}; 2 | use nalgebra::{Vector3, Rotation3, Matrix3}; 3 | use clay::{ 4 | prelude::*, 5 | shape::*, 6 | material::*, 7 | object::*, 8 | scene::{TargetListScene, GradientBackground as GradBg}, 9 | view::ProjectionView, 10 | filter::*, 11 | process::{create_renderer, create_postproc}, 12 | shape_select, material_select, material_combine, 13 | }; 14 | use clay_viewer::{Window, Motion}; 15 | use clay_utils::{args, FrameCounter}; 16 | 17 | shape_select!(MyShape { 18 | P(TP=Parallelepiped), 19 | S(TS=Ellipsoid), 20 | }); 21 | material_combine!(Glossy { 22 | reflect: Reflective, 23 | diffuse: Colored, 24 | }); 25 | material_combine!(Glowing { 26 | reflect: Reflective, 27 | diffuse: Colored, 28 | }); 29 | material_select!(MyMaterial { 30 | D(TD=Colored), 31 | G(TG=Glossy), 32 | F(TF=Glowing), 33 | L(TL=Colored), 34 | }); 35 | 36 | // Here we declare our object - a combination of 37 | // spherical shape and colored diffuse material 38 | type MyObject = Covered; 39 | 40 | // Scene contains our objects and has gradient background 41 | type MyScene = TargetListScene; 42 | type MyView = ProjectionView; 43 | 44 | 45 | fn main() -> clay::Result<()> { 46 | // Parse args to select OpenCL platform 47 | let context = args::parse(env::args())?; 48 | 49 | // Dimensions of the window 50 | let dims = (1280, 800); 51 | 52 | // Initialize the scene 53 | let mut scene = TargetListScene::new(GradBg::new( 54 | Vector3::new(0.1, 0.1, 0.3), Vector3::new(0.0, 0.0, 0.0), 55 | Vector3::new(0.0, 0.0, 1.0), 56 | )); 57 | 58 | // Ground 59 | scene.add(MyShape::from(Parallelepiped::new( 60 | Matrix3::from_diagonal(&Vector3::new(1000.0, 1000.0, 0.5)), 61 | Vector3::new(0.0, 0.0, -0.5), 62 | )).cover(MyMaterial::from(Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))))); 63 | 64 | // Rocks 65 | let mut rocks = Vec::new(); 66 | 67 | rocks.push((30.0, (-60.0, 30.0), 3e3*Vector3::new(1.0, 0.4, 0.4))); 68 | rocks.push((100.0, (-200.0,-0.0), 2e3*Vector3::new(0.4, 0.4, 1.0))); 69 | rocks.push((15.0, (-30.0,-30.0), 3e3*Vector3::new(0.4, 1.0, 0.4))); 70 | 71 | let rot = Rotation3::rotation_between( 72 | &Vector3::new(1.0, 1.0, 1.0), 73 | &Vector3::new(0.0, 0.0, 1.0), 74 | ).unwrap().matrix().clone(); 75 | for (size, pos, color) in rocks { 76 | scene.add(MyShape::from(Parallelepiped::new( 77 | size*3.0f64.sqrt()/2.0*rot.clone(), 78 | Vector3::new(pos.0, pos.1, -0.5*size), 79 | )).cover(MyMaterial::from(Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))))); 80 | let tsize = 0.025*size; 81 | scene.add_targeted(MyShape::from(Parallelepiped::new( 82 | tsize*3.0f64.sqrt()/2.0*rot*Matrix3::from_diagonal(&Vector3::new(0.5, 0.5, 2.0)), 83 | Vector3::new(pos.0, pos.1, 1.2*size + 2.0*tsize), 84 | )).cover(MyMaterial::from(Luminous {}.color_with(color)))); 85 | } 86 | 87 | scene.add(MyShape::from(Ellipsoid::new( 88 | 0.25*Matrix3::identity(), 89 | Vector3::new(1.0, 0.0, 0.25), 90 | )).cover(MyMaterial::from(Luminous {}.color_with(1e1*Vector3::new(0.2, 1.0, 0.2))))); 91 | 92 | scene.add(MyShape::from(Ellipsoid::new( 93 | 0.4*Matrix3::identity(), 94 | Vector3::new(0.0, 1.0, 0.4), 95 | )).cover(MyMaterial::from(Glossy::new( 96 | (0.2, Reflective {}), 97 | (0.8, Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))), 98 | )))); 99 | 100 | scene.add(MyShape::from(Parallelepiped::new( 101 | 0.5*Matrix3::identity(), 102 | Vector3::new(-1.0, 0.0, 0.5), 103 | )).cover(MyMaterial::from(Glossy::new( 104 | (0.5, Reflective {}), 105 | (0.5, Diffuse {}.color_with(Vector3::new(0.9, 0.9, 0.9))), 106 | )))); 107 | 108 | // Create view 109 | let view = ProjectionView::new( 110 | Vector3::new(2.0, 0.0, 1.0), 111 | Rotation3::face_towards(&-Vector3::new(-1.0, 0.0, 0.0), &Vector3::z_axis()), 112 | ); 113 | 114 | // Create renderer and worker 115 | let mut renderer = create_renderer::().build(dims, scene, view)?; 116 | let (mut worker, _) = renderer.create_worker(&context)?; 117 | 118 | // Create dummy postprocessor 119 | let (mut postproc, _) = create_postproc().collect()? 120 | .build(&context, dims, LogFilter::new(-2.0, 1.0))?; 121 | 122 | // Create viewer window 123 | let mut window = Window::new(dims)?; 124 | // Capture mouse 125 | window.set_capture_mode(true); 126 | 127 | // Create motion controller 128 | let mut motion = Motion::new(renderer.view.pos, renderer.view.ori.clone()); 129 | 130 | // Structure for frame rate measurement (optional) 131 | let mut fcnt = FrameCounter::new_with_log(Duration::from_secs(2)); 132 | 133 | // Main loop - repeatedly update view and render 134 | while !window.poll_with_handler(&mut motion)? { 135 | // Render 136 | let n = worker.run_for(Duration::from_millis(20))?; 137 | 138 | // Postprocess 139 | postproc.process_one(&worker.data().buffer())?; 140 | postproc.make_image()?; 141 | 142 | // Draw image to Window 143 | window.draw(&postproc.image())?; 144 | 145 | // Measure frame duration 146 | let dt = window.step_frame(); 147 | 148 | // Check motion occured 149 | if motion.was_updated() { 150 | // Clear cumulative buffer 151 | worker.data_mut().buffer_mut().clear()?; 152 | 153 | // Move to a new location 154 | motion.step(dt); 155 | 156 | // Update view location 157 | renderer.view.update(motion.pos(), motion.ori()); 158 | renderer.view.fov = motion.fov; 159 | renderer.update_data(&context, worker.data_mut())?; 160 | } 161 | 162 | // Count and print frame rate 163 | fcnt.step_frame(dt, n); 164 | } 165 | 166 | Ok(()) 167 | } 168 | -------------------------------------------------------------------------------- /ocl-src/filter/glare.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GLARE_FILTER_ARGS_DEF \ 4 | float glare_strength 5 | 6 | #define GLARE_FILTER_ARGS \ 7 | glare_strength 8 | 9 | float3 glare_filter_apply( 10 | int2 pos, int2 size, 11 | __global const float *buffer, 12 | GLARE_FILTER_ARGS_DEF 13 | ) { 14 | int i; 15 | 16 | float3 addition = (float3)(0.0f); 17 | for (i = 0; i < pos.x; ++i) { 18 | addition += vload3(i + pos.y*size.x, buffer)/(pos.x - i); 19 | } 20 | for (i = pos.x + 1; i < size.x; ++i) { 21 | addition += vload3(i + pos.y*size.x, buffer)/(i - pos.x); 22 | } 23 | 24 | for (i = 0; i < pos.y; ++i) { 25 | addition += vload3(pos.x + i*size.x, buffer)/(pos.y - i); 26 | } 27 | for (i = pos.y + 1; i < size.y; ++i) { 28 | addition += vload3(pos.x + i*size.x, buffer)/(i - pos.y); 29 | } 30 | 31 | addition *= glare_strength; 32 | 33 | return vload3(pos.x + pos.y*size.x, buffer) + addition; 34 | } 35 | -------------------------------------------------------------------------------- /ocl-src/filter/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LOG_FILTER_ARGS_DEF \ 4 | float log_lower, \ 5 | float log_upper 6 | 7 | #define LOG_FILTER_ARGS \ 8 | log_lower, \ 9 | log_upper 10 | 11 | float3 log_filter_apply( 12 | int2 pos, int2 size, 13 | __global const float *buffer, 14 | LOG_FILTER_ARGS_DEF 15 | ) { 16 | return (log(vload3(pos.x + pos.y*size.x, buffer)) - log_lower)/(log_upper - log_lower); 17 | } 18 | -------------------------------------------------------------------------------- /ocl-src/map/linear.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | MAP_RET linear_rel(MAP_ARGS_DEF) { 8 | matrix3 linear = matrix3_load(fbuf); 9 | return matrix3_dot(linear, v); 10 | } 11 | 12 | MAP_RET linear_abs(MAP_ARGS_DEF) { 13 | return linear_rel(MAP_ARGS); 14 | } 15 | 16 | MAP_RET linear_rel_inv(MAP_ARGS_DEF) { 17 | matrix3 inverse = matrix3_load(fbuf + 9); 18 | return matrix3_dot(inverse, v); 19 | } 20 | 21 | MAP_RET linear_abs_inv(MAP_ARGS_DEF) { 22 | return linear_rel_inv(MAP_ARGS); 23 | } 24 | 25 | MAP_RET linear_norm(MAP_ARGS_DEF) { 26 | matrix3 inverse = matrix3_load(fbuf + 9); 27 | return matrix3_dot(matrix3_transpose(inverse), v); 28 | } 29 | -------------------------------------------------------------------------------- /ocl-src/map/scale.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | MAP_RET scale_rel(MAP_ARGS_DEF) { 7 | return v*fbuf[0]; 8 | } 9 | 10 | MAP_RET scale_abs(MAP_ARGS_DEF) { 11 | return scale_rel(MAP_ARGS); 12 | } 13 | 14 | MAP_RET scale_rel_inv(MAP_ARGS_DEF) { 15 | return v/fbuf[0]; 16 | } 17 | 18 | MAP_RET scale_abs_inv(MAP_ARGS_DEF) { 19 | return scale_rel_inv(MAP_ARGS); 20 | } 21 | 22 | MAP_RET scale_norm(MAP_ARGS_DEF) { 23 | return v; 24 | } 25 | -------------------------------------------------------------------------------- /ocl-src/map/shift.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef float3 Shift; 6 | 7 | Shift shift_load(__global const int *ibuf, __global const float *fbuf) { 8 | return vload3(0, fbuf); 9 | } 10 | 11 | MAP_RET shift_rel(MAP_ARGS_DEF) { 12 | return v; 13 | } 14 | 15 | MAP_RET shift_abs(MAP_ARGS_DEF) { 16 | return v + shift_load(ibuf, fbuf); 17 | } 18 | 19 | MAP_RET shift_rel_inv(MAP_ARGS_DEF) { 20 | return v; 21 | } 22 | 23 | MAP_RET shift_abs_inv(MAP_ARGS_DEF) { 24 | return v - shift_load(ibuf, fbuf); 25 | } 26 | 27 | MAP_RET shift_norm(MAP_ARGS_DEF) { 28 | return v; 29 | } 30 | -------------------------------------------------------------------------------- /ocl-src/material/diffuse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | #define DIFFUSE_COSINE 10 | 11 | MATERIAL_BOUNCE_RET diffuse_bounce( 12 | MATERIAL_BOUNCE_ARGS_DEF 13 | ) { 14 | new_ray->start = pos; 15 | 16 | if (!directed) { 17 | float3 rand_dir = 18 | #ifdef DIFFUSE_COSINE 19 | random_hemisphere_cosine(seed); 20 | #else 21 | random_hemisphere(seed); 22 | #endif // DIFFUSE_COSINE 23 | 24 | matrix3 basis = { .z = norm }; 25 | complement(basis.z, &basis.x, &basis.y); 26 | new_ray->dir = matrix3_dot(matrix3_transpose(basis), rand_dir); 27 | new_ray->color = 28 | #ifndef DIFFUSE_COSINE 29 | 2.0f*rand_dir.z* 30 | #endif // DIFFUSE_COSINE 31 | ray.color; 32 | } else { 33 | float cos_theta = dot(dir, norm); 34 | if (cos_theta < 0.0f) { 35 | return false; 36 | } 37 | new_ray->dir = dir; 38 | new_ray->color = 2.0f*cos_theta*size*ray.color; 39 | } 40 | 41 | new_ray->history = ray.history | RAY_DIFFUSE; 42 | 43 | return true; 44 | } 45 | -------------------------------------------------------------------------------- /ocl-src/material/luminous.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | MATERIAL_BOUNCE_RET luminous_bounce( 7 | MATERIAL_BOUNCE_ARGS_DEF 8 | ) { 9 | *color += ray.color; 10 | return false; 11 | } 12 | -------------------------------------------------------------------------------- /ocl-src/material/reflective.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | MATERIAL_BOUNCE_RET reflective_bounce( 7 | MATERIAL_BOUNCE_ARGS_DEF 8 | ) { 9 | if (directed) { 10 | return false; 11 | } 12 | new_ray->start = pos; 13 | new_ray->dir = ray.dir - 2.0f*norm*dot(norm, ray.dir); 14 | new_ray->color = ray.color; 15 | return true; 16 | } 17 | -------------------------------------------------------------------------------- /ocl-src/scene/background/constant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define BACKGROUND_ARGS_DEF \ 4 | float3 bg_color 5 | 6 | #define BACKGROUND_ARGS \ 7 | bg_color 8 | 9 | float3 __background( 10 | Ray ray, 11 | BACKGROUND_ARGS_DEF 12 | ) { 13 | return ray.color*bg_color; 14 | } 15 | -------------------------------------------------------------------------------- /ocl-src/scene/background/gradient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define BACKGROUND_ARGS_DEF \ 4 | float3 bg_top, float3 bg_bottom, float3 bg_dir 5 | 6 | #define BACKGROUND_ARGS \ 7 | bg_top, bg_bottom, bg_dir 8 | 9 | float3 __background( 10 | Ray ray, 11 | BACKGROUND_ARGS_DEF 12 | ) { 13 | float z = 0.5f*(dot(ray.dir, bg_dir) + 1.0f); 14 | return ray.color*(z*bg_top + (1.0f - z)*bg_bottom); 15 | } 16 | -------------------------------------------------------------------------------- /ocl-src/scene/list_scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | #define SCENE_ARGS_DEF \ 7 | __global const int *object_buffer_int, \ 8 | __global const float *object_buffer_float, \ 9 | int objects_count, \ 10 | int max_depth, \ 11 | \ 12 | BACKGROUND_ARGS_DEF 13 | 14 | #define SCENE_ARGS \ 15 | object_buffer_int, \ 16 | object_buffer_float, \ 17 | objects_count, \ 18 | max_depth, \ 19 | \ 20 | BACKGROUND_ARGS 21 | 22 | 23 | bool scene_trace( 24 | uint *seed, 25 | Ray ray, 26 | Ray *new_ray, 27 | float3 *color, 28 | SCENE_ARGS_DEF 29 | ) { 30 | int hit_idx = -1; 31 | float hit_enter = INFINITY; 32 | float hit_exit = 0.0f; 33 | float3 hit_norm; 34 | 35 | int i = 0; 36 | for (i = 0; i < objects_count; ++i) { 37 | float enter, exit; 38 | float3 norm; 39 | 40 | if (ray.origin == i) { 41 | continue; 42 | } 43 | 44 | __global const int *ibuf = object_buffer_int + OBJECT_SIZE_INT*i; 45 | __global const float *fbuf = object_buffer_float + OBJECT_SIZE_FLOAT*i; 46 | if (__object_hit(seed, ray, ibuf, fbuf, &enter, &exit, &norm)) { 47 | if (enter < hit_enter) { 48 | hit_enter = enter; 49 | hit_exit = exit; 50 | hit_norm = norm; 51 | hit_idx = i; 52 | } 53 | } 54 | } 55 | 56 | if (hit_idx >= 0) { 57 | float3 hit_pos = ray.start + ray.dir*hit_enter; 58 | 59 | __global const int *ibuf = object_buffer_int + OBJECT_SIZE_INT*hit_idx; 60 | __global const float *fbuf = object_buffer_float + OBJECT_SIZE_FLOAT*hit_idx; 61 | if(__object_bounce( 62 | seed, ray, hit_pos, hit_norm, 63 | false, (float3)(0.0f), 0.0f, 64 | ibuf, fbuf, new_ray, color 65 | )) { 66 | new_ray->origin = hit_idx; 67 | return true; 68 | } 69 | return false; 70 | } 71 | 72 | // Background 73 | *color += __background(ray, BACKGROUND_ARGS); 74 | return false; 75 | } 76 | 77 | float3 __scene_trace( 78 | uint *seed, 79 | Ray ray, 80 | SCENE_ARGS_DEF 81 | ) { 82 | float3 color = (float3)(0.0f); 83 | int i = 0; 84 | Ray current_ray = ray; 85 | for (i = 0; i < max_depth; ++i) { 86 | Ray next_ray = ray_new(); 87 | bool bounce = scene_trace(seed, current_ray, &next_ray, &color, SCENE_ARGS); 88 | if (!bounce) { 89 | break; 90 | } 91 | current_ray = next_ray; 92 | } 93 | return color; 94 | } 95 | -------------------------------------------------------------------------------- /ocl-src/scene/target_list_scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | #define SCENE_ARGS_DEF \ 7 | __global const int *object_buffer_int, \ 8 | __global const float *object_buffer_float, \ 9 | int objects_count, \ 10 | \ 11 | __global const int *target_buffer_int, \ 12 | __global const float *target_buffer_float, \ 13 | int targets_count, \ 14 | \ 15 | int max_depth, \ 16 | float target_prob, \ 17 | \ 18 | BACKGROUND_ARGS_DEF 19 | 20 | #define SCENE_ARGS \ 21 | object_buffer_int, \ 22 | object_buffer_float, \ 23 | objects_count, \ 24 | \ 25 | target_buffer_int, \ 26 | target_buffer_float, \ 27 | targets_count, \ 28 | \ 29 | max_depth, \ 30 | target_prob, \ 31 | \ 32 | BACKGROUND_ARGS 33 | 34 | #define TARGET_THRESHOLD 0.1f 35 | 36 | #define OBJ_DI 1 37 | #define OBJ_DF 0 38 | #define TAR_DI 1 39 | #define TAR_DF 1 40 | 41 | 42 | bool scene_trace( 43 | uint *seed, 44 | Ray ray, 45 | Ray *new_ray, 46 | float3 *color, 47 | SCENE_ARGS_DEF 48 | ) { 49 | int hit_idx = -1; 50 | int tar_idx = -1; 51 | float hit_enter = INFINITY; 52 | float hit_exit = 0.0f; 53 | float3 hit_norm; 54 | __global const int *oibuf = 0; 55 | __global const float *ofbuf = 0; 56 | 57 | int i = 0; 58 | for (i = 0; i < objects_count; ++i) { 59 | float enter, exit; 60 | float3 norm; 61 | 62 | if (ray.origin == i) { 63 | continue; 64 | } 65 | 66 | __global const int *ibuf = object_buffer_int + OBJECT_SIZE_INT*i; 67 | __global const float *fbuf = object_buffer_float + OBJECT_SIZE_FLOAT*i; 68 | if (__object_hit( 69 | seed, ray, 70 | ibuf + OBJ_DI, fbuf + OBJ_DF, 71 | &enter, &exit, &norm 72 | )) { 73 | if (enter < hit_enter) { 74 | hit_enter = enter; 75 | hit_exit = exit; 76 | hit_norm = norm; 77 | hit_idx = i; 78 | tar_idx = ibuf[0]; 79 | oibuf = ibuf; 80 | ofbuf = fbuf; 81 | } 82 | } 83 | } 84 | 85 | if (hit_idx >= 0) { 86 | if (ray.history & RAY_TARGETED) { 87 | if (ray.target != hit_idx) { 88 | return false; 89 | } 90 | } else if (ray.history & RAY_DIFFUSE) { 91 | if (ray.target != tar_idx) { 92 | return false; 93 | } 94 | } 95 | 96 | float3 hit_pos = ray.start + ray.dir*hit_enter; 97 | 98 | // Sample target 99 | int target = -1; 100 | bool directed = false; 101 | float target_size = 0.0f; 102 | float3 target_dir = (float3)(0.0f); 103 | if (random_uniform(seed) < target_prob) { 104 | int target_idx = floor(random_uniform(seed)*targets_count); 105 | __global const int *tibuf = target_buffer_int + TARGET_SIZE_INT*target_idx; 106 | __global const float *tfbuf = target_buffer_float + TARGET_SIZE_FLOAT*target_idx; 107 | 108 | //float brightness = tfbuf[0]; 109 | target = tibuf[0]; 110 | target_size = __target_sample( 111 | seed, hit_pos, 112 | tibuf + TAR_DI, tfbuf + TAR_DF, 113 | &target_dir 114 | ); 115 | directed = true; 116 | } 117 | 118 | // Bounce from material 119 | bool bounce = __object_bounce( 120 | seed, ray, hit_pos, hit_norm, 121 | directed, target_dir, target_size, 122 | oibuf + OBJ_DI, ofbuf + OBJ_DF, new_ray, color 123 | ); 124 | if (bounce && !(ray.history & RAY_TARGETED)) { 125 | new_ray->origin = hit_idx; 126 | if (directed) { 127 | new_ray->target = target; 128 | new_ray->history |= RAY_TARGETED; 129 | new_ray->color *= targets_count/target_prob; 130 | } else { 131 | new_ray->color *= 1.0f/(1.0f - target_prob); 132 | } 133 | return true; 134 | } else { 135 | return false; 136 | } 137 | } else { 138 | // Background 139 | *color += __background(ray, BACKGROUND_ARGS); 140 | return false; 141 | } 142 | } 143 | 144 | float3 __scene_trace( 145 | uint *seed, 146 | Ray ray, 147 | SCENE_ARGS_DEF 148 | ) { 149 | float3 color = (float3)(0.0f); 150 | Ray current_ray = ray; 151 | int i = 0; 152 | for (i = 0; i < max_depth; ++i) { 153 | Ray next_ray = ray_new(); 154 | next_ray.history = current_ray.history; 155 | bool bounce = scene_trace(seed, current_ray, &next_ray, &color, SCENE_ARGS); 156 | if (!bounce) { 157 | break; 158 | } 159 | current_ray = next_ray; 160 | } 161 | return color; 162 | } 163 | -------------------------------------------------------------------------------- /ocl-src/shape/cube.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | float _cube_hit_nearest(float3 near, float3 *norm) { 7 | bool xy = near.x > near.y; 8 | bool yz = near.y > near.z; 9 | bool xz = near.x > near.z; 10 | float dist = 0.0f; 11 | if (xy && xz) { 12 | dist = near.x; 13 | norm->x = 1.0f; 14 | } else if (yz) { 15 | dist = near.y; 16 | norm->y = 1.0f; 17 | } else { 18 | dist = near.z; 19 | norm->z = 1.0f; 20 | } 21 | return dist; 22 | } 23 | 24 | 25 | SHAPE_HIT_RET cube_hit( 26 | SHAPE_HIT_ARGS_DEF 27 | ) { 28 | const float3 cmax = (float3)(1.0f); 29 | const float3 cmin = (float3)(-1.0f); 30 | 31 | float3 inv_dir = 1.0f/ray.dir; 32 | 33 | float3 vmin = (cmin - ray.start)*inv_dir; 34 | float3 vmax = (cmax - ray.start)*inv_dir; 35 | 36 | float3 near = min(vmin, vmax); 37 | float3 far = max(vmin, vmax); 38 | 39 | float3 norm_in = (float3)(0.0f); 40 | float dist_in = _cube_hit_nearest(near, &norm_in); 41 | norm_in *= -sign(ray.dir); 42 | 43 | float3 norm_out = (float3)(0.0f); 44 | float dist_out = -_cube_hit_nearest(-far, &norm_out); 45 | norm_out *= sign(ray.dir); 46 | 47 | if (dist_in < 0.0f || dist_in > dist_out) { 48 | return false; 49 | } 50 | 51 | *enter = dist_in; 52 | *exit = dist_out; 53 | *norm = norm_in; 54 | return true; 55 | } 56 | -------------------------------------------------------------------------------- /ocl-src/shape/sphere.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | SHAPE_HIT_RET unit_sphere_hit( 12 | SHAPE_HIT_ARGS_DEF 13 | ) { 14 | // t^2 - 2*b*t + c = 0 15 | float b = -dot(ray.dir, ray.start); 16 | float c = dot(ray.start, ray.start) - 1.0f; 17 | float d = b*b - c; 18 | if (d < 0.0f) { 19 | return false; 20 | } 21 | d = sqrt(d); 22 | float e = b - d; 23 | if (e < 0.0f) { 24 | return false; 25 | } 26 | *enter = e; 27 | *exit = b + d; 28 | *norm = ray.start + ray.dir*e; 29 | return true; 30 | } 31 | 32 | TARGET_SAMPLE_RET sphere_target_sample( 33 | TARGET_SAMPLE_ARGS_DEF 34 | ) { 35 | float rad = fbuf[0]; 36 | float3 spos = vload3(0, fbuf + 1); 37 | 38 | float3 sdir = spos - pos; 39 | float len2 = dot(sdir, sdir); 40 | 41 | float sin_alpha_2 = (rad*rad)/len2; 42 | if (sin_alpha_2 >= 1.0f) { 43 | *dir = random_sphere(seed); 44 | return 2.0f; 45 | } 46 | float cos_alpha = sqrt(1.0f - sin_alpha_2); 47 | 48 | sdir /= sqrt(len2); 49 | float3 rand_dir = random_sphere_cap(seed, cos_alpha); 50 | matrix3 basis = { .z = sdir }; 51 | complement(basis.z, &basis.x, &basis.y); 52 | *dir = matrix3_dot(matrix3_transpose(basis), rand_dir); 53 | 54 | return 1.0f - cos_alpha; 55 | } 56 | -------------------------------------------------------------------------------- /ocl-src/view/proj_view.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | typedef struct { 6 | float3 pos; 7 | float16 ori; 8 | } View; 9 | 10 | #define VIEW_ARGS_DEF \ 11 | float3 view_pos, \ 12 | float16 view_map, \ 13 | float fov 14 | 15 | #define VIEW_ARGS \ 16 | view_pos, \ 17 | view_map,\ 18 | fov 19 | 20 | 21 | float2 ptos(int2 pos, int2 size) { 22 | float2 p = convert_float2(pos) - 0.5f*convert_float2(size); 23 | p.y = -p.y; 24 | return p/(float)size.y; 25 | } 26 | 27 | float2 ptos_rand(uint *seed, int2 pos, int2 size) { 28 | float2 p = convert_float2(pos) - 0.5f*convert_float2(size); 29 | p.y = -p.y; 30 | p += (float2)(random_uniform(seed), random_uniform(seed)) - 0.5f; 31 | return p/(float)size.y; 32 | } 33 | 34 | 35 | Ray __view_emit( 36 | uint *seed, 37 | int2 pos, 38 | int2 size, 39 | VIEW_ARGS_DEF 40 | ) { 41 | float2 v = ptos_rand(seed, pos, size); 42 | Ray ray = ray_new(); 43 | ray.start = view_pos; 44 | ray.dir = normalize(v.x*view_map.s012 + v.y*view_map.s456 - 1.0f/fov*view_map.s89a); 45 | ray.color = (float3)(1.0f, 1.0f, 1.0f); 46 | return ray; 47 | } 48 | -------------------------------------------------------------------------------- /src/filter/glare.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use ocl::{self, builders::KernelBuilder}; 3 | use crate::{Push, filter::Filter}; 4 | 5 | 6 | /// Glare blur. 7 | /// 8 | /// Creates horizontal and vertical stripes around bright objects. 9 | pub struct GlareFilter { 10 | strength: f64, 11 | } 12 | 13 | impl GlareFilter { 14 | pub fn new(strength: f64) -> Self { 15 | Self { strength } 16 | } 17 | } 18 | 19 | impl Filter for GlareFilter { 20 | fn inst_name() -> String { 21 | "glare_filter".to_string() 22 | } 23 | fn source(_: &mut HashSet) -> String { 24 | "#include ".to_string() 25 | } 26 | } 27 | 28 | impl Push for GlareFilter { 29 | fn args_count() -> usize { 30 | 1 31 | } 32 | fn args_def(kb: &mut KernelBuilder) { 33 | kb.arg(&0f32); 34 | } 35 | fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> { 36 | k.set_arg(i + 0, &(self.strength as f32)) 37 | .map_err(|e| e.into()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/filter/log.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use ocl::{self, builders::KernelBuilder}; 3 | use crate::{Push, filter::Filter}; 4 | 5 | /// Logarithmic filter. 6 | /// 7 | /// Useful for scenes with wide dynamic range. 8 | pub struct LogFilter { 9 | lower: f64, 10 | upper: f64, 11 | } 12 | 13 | impl LogFilter { 14 | pub fn new(lower: f64, upper: f64) -> Self { 15 | Self { lower, upper } 16 | } 17 | } 18 | 19 | impl Filter for LogFilter { 20 | fn inst_name() -> String { 21 | "log_filter".to_string() 22 | } 23 | fn source(_: &mut HashSet) -> String { 24 | "#include ".to_string() 25 | } 26 | } 27 | 28 | impl Push for LogFilter { 29 | fn args_count() -> usize { 30 | 2 31 | } 32 | fn args_def(kb: &mut KernelBuilder) { 33 | kb.arg(&0f32); 34 | kb.arg(&0f32); 35 | } 36 | fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> { 37 | k.set_arg(i + 0, &(self.lower as f32))?; 38 | k.set_arg(i + 1, &(self.upper as f32))?; 39 | Ok(()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/filter/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::core::filter::*; 2 | 3 | mod glare; 4 | pub use glare::*; 5 | 6 | mod log; 7 | pub use log::*; 8 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Fast, modular and extendable Monte-Carlo ray tracing engine written in Rust and OpenCL. 2 | //! 3 | //! You can find more information at the [Clay project website](https://clay-rs.github.io). 4 | 5 | 6 | /// Reexport `clay-core` 7 | pub use clay_core as core; 8 | 9 | /// Mappings in render space. 10 | pub mod map; 11 | /// Shape of an object. 12 | pub mod shape; 13 | /// Material of an object. 14 | pub mod material; 15 | 16 | /// Scene to be rendered. 17 | pub mod scene; 18 | /// View of the scene. 19 | pub mod view; 20 | 21 | /// Filter for rendered image postprocessing. 22 | pub mod filter; 23 | /// Functionality for rendering pipeline. 24 | pub mod process; 25 | /// Loading the device OpenCL source code. 26 | pub mod source; 27 | 28 | /// Reexport of the basic traits. 29 | pub mod prelude { 30 | pub use crate::core::prelude::*; 31 | } 32 | 33 | pub use clay_core::{Error, Result}; 34 | 35 | pub use clay_core::{ 36 | object, 37 | context, 38 | buffer, 39 | }; 40 | 41 | pub use prelude::*; 42 | pub use context::*; 43 | pub use source::*; 44 | 45 | pub use clay_core::{ 46 | instance_select, 47 | shape_select, 48 | material_select, 49 | object_select, 50 | material_combine 51 | }; 52 | -------------------------------------------------------------------------------- /src/map/affine.rs: -------------------------------------------------------------------------------- 1 | use crate::map::*; 2 | 3 | /// Affine transformation. 4 | pub type Affine = Chain; 5 | -------------------------------------------------------------------------------- /src/map/linear.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use nalgebra::{Matrix3}; 3 | use crate::{prelude::*, map::*}; 4 | 5 | 6 | /// Linear transformation represented by a matrix. 7 | pub struct Linear(pub Matrix3); 8 | 9 | impl From> for Linear { 10 | fn from(x: Matrix3) -> Self { 11 | Linear(x) 12 | } 13 | } 14 | 15 | impl Map for Linear {} 16 | 17 | impl Instance for Linear { 18 | fn source(_: &mut HashSet) -> String { 19 | "#include ".to_string() 20 | } 21 | fn inst_name() -> String { 22 | "linear".to_string() 23 | } 24 | } 25 | 26 | impl Pack for Linear { 27 | fn size_int() -> usize { 28 | 2*Matrix3::::size_int() 29 | } 30 | fn size_float() -> usize { 31 | 2*Matrix3::::size_float() 32 | } 33 | fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) { 34 | let inverse = self.0.try_inverse().unwrap(); 35 | Packer::new(buffer_int, buffer_float) 36 | .pack(&self.0) 37 | .pack(&inverse); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/map/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::core::map::*; 2 | 3 | 4 | mod shift; 5 | pub use shift::*; 6 | mod scale; 7 | pub use scale::*; 8 | mod linear; 9 | pub use linear::*; 10 | mod affine; 11 | pub use affine::*; 12 | -------------------------------------------------------------------------------- /src/map/scale.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use crate::{prelude::*, map::*}; 3 | 4 | 5 | /// Isotropic scaling. 6 | pub struct Scale(pub f64); 7 | 8 | impl From for Scale { 9 | fn from(x: f64) -> Self { 10 | Scale(x) 11 | } 12 | } 13 | 14 | impl Map for Scale {} 15 | 16 | impl Instance for Scale { 17 | fn source(_: &mut HashSet) -> String { 18 | "#include ".to_string() 19 | } 20 | fn inst_name() -> String { 21 | "scale".to_string() 22 | } 23 | } 24 | 25 | impl Pack for Scale { 26 | fn size_int() -> usize { 27 | 0 28 | } 29 | fn size_float() -> usize { 30 | 1 31 | } 32 | fn pack_to(&self, _buffer_int: &mut [i32], buffer_float: &mut [f32]) { 33 | buffer_float.pack(&self.0); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/map/shift.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use nalgebra::{Vector3}; 3 | use crate::{prelude::*, map::*}; 4 | 5 | 6 | /// Shift along a specific vector. 7 | pub struct Shift(pub Vector3); 8 | 9 | impl From> for Shift { 10 | fn from(x: Vector3) -> Self { 11 | Shift(x) 12 | } 13 | } 14 | 15 | impl Map for Shift {} 16 | 17 | impl Instance for Shift { 18 | fn source(_: &mut HashSet) -> String { 19 | "#include ".to_string() 20 | } 21 | fn inst_name() -> String { 22 | "shift".to_string() 23 | } 24 | } 25 | 26 | impl Pack for Shift { 27 | fn size_int() -> usize { 28 | Vector3::::size_int() 29 | } 30 | fn size_float() -> usize { 31 | Vector3::::size_float() 32 | } 33 | fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) { 34 | self.0.pack_to(buffer_int, buffer_float); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/material/diffuse.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use crate::{prelude::*, material::*}; 3 | 4 | 5 | /// Diffuse material. 6 | /// 7 | /// The light scatters in any direction in the half-space 8 | /// proportional to the cosine of an angle betwen the normal of the surface. 9 | #[derive(Clone, Debug, Default)] 10 | pub struct Diffuse {} 11 | 12 | impl Material for Diffuse { 13 | fn brightness(&self) -> f64 { 14 | 0.0 15 | } 16 | } 17 | 18 | impl Instance for Diffuse { 19 | fn source(_: &mut HashSet) -> String { 20 | "#include ".to_string() 21 | } 22 | fn inst_name() -> String { 23 | "diffuse".to_string() 24 | } 25 | } 26 | 27 | impl Pack for Diffuse { 28 | fn size_int() -> usize { 0 } 29 | fn size_float() -> usize { 0 } 30 | fn pack_to(&self, _buffer_int: &mut [i32], _buffer_float: &mut [f32]) {} 31 | } 32 | -------------------------------------------------------------------------------- /src/material/luminous.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use crate::{prelude::*, material::*}; 3 | 4 | 5 | /// Luminous material. 6 | /// 7 | /// Returns the white color (to specify color use `Colored<_>` adapter) 8 | /// and doesn't produce secondary ray. 9 | #[derive(Clone, Debug, Default)] 10 | pub struct Luminous {} 11 | 12 | impl Material for Luminous { 13 | fn brightness(&self) -> f64 { 14 | 1.0 15 | } 16 | } 17 | 18 | impl Instance for Luminous { 19 | fn source(_: &mut HashSet) -> String { 20 | "#include ".to_string() 21 | } 22 | fn inst_name() -> String { 23 | "luminous".to_string() 24 | } 25 | } 26 | 27 | impl Pack for Luminous { 28 | fn size_int() -> usize { 0 } 29 | fn size_float() -> usize { 0 } 30 | fn pack_to(&self, _buffer_int: &mut [i32], _buffer_float: &mut [f32]) {} 31 | } 32 | -------------------------------------------------------------------------------- /src/material/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::core::material::*; 2 | 3 | mod reflective; 4 | pub use reflective::*; 5 | mod diffuse; 6 | pub use diffuse::*; 7 | mod luminous; 8 | pub use luminous::*; 9 | -------------------------------------------------------------------------------- /src/material/reflective.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use crate::{prelude::*, material::*}; 3 | 4 | 5 | /// Reflective material. 6 | /// 7 | /// Produces secondary ray in the same direction but with inverse normal component. 8 | #[derive(Clone, Debug, Default)] 9 | pub struct Reflective {} 10 | 11 | impl Material for Reflective { 12 | fn brightness(&self) -> f64 { 13 | 0.0 14 | } 15 | } 16 | 17 | impl Instance for Reflective { 18 | fn source(_: &mut HashSet) -> String { 19 | "#include ".to_string() 20 | } 21 | fn inst_name() -> String { 22 | "reflective".to_string() 23 | } 24 | } 25 | 26 | impl Pack for Reflective { 27 | fn size_int() -> usize { 0 } 28 | fn size_float() -> usize { 0 } 29 | fn pack_to(&self, _buffer_int: &mut [i32], _buffer_float: &mut [f32]) {} 30 | } 31 | -------------------------------------------------------------------------------- /src/process/mod.rs: -------------------------------------------------------------------------------- 1 | mod render; 2 | pub use render::*; 3 | mod postproc; 4 | pub use postproc::*; 5 | -------------------------------------------------------------------------------- /src/process/postproc.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | filter::{Filter, IdentityFilter}, 3 | }; 4 | pub use crate::core::process::{ 5 | PostprocBuilder, PostprocCollector, Postproc, 6 | }; 7 | 8 | /// Creates postrocessor with already included device source from `clay` and `clay-core`. 9 | pub fn create_postproc() -> PostprocCollector { 10 | let mut collector = crate::core::process::create_postproc::(); 11 | collector.add_hook(crate::source()); 12 | collector 13 | } 14 | 15 | /// Creates postrocessor with identity filter. 16 | pub fn create_default_postproc() -> PostprocCollector { 17 | create_postproc::() 18 | } 19 | -------------------------------------------------------------------------------- /src/process/render.rs: -------------------------------------------------------------------------------- 1 | use crate::{scene::Scene, view::View}; 2 | pub use crate::core::process::{ 3 | RendererBuilder, Renderer, 4 | RenderWorker, RenderData, 5 | }; 6 | 7 | /// Creates renderer with already included device source from `clay` and `clay-core`. 8 | pub fn create_renderer() -> RendererBuilder { 9 | let mut builder = crate::core::process::create_renderer::(); 10 | builder.add_hook(crate::source()); 11 | builder 12 | } 13 | -------------------------------------------------------------------------------- /src/scene/background/constant.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use nalgebra::{Vector3}; 3 | use ocl::{self, prm, builders::KernelBuilder}; 4 | use crate::{prelude::*, Context, scene::Background}; 5 | 6 | 7 | /// Background of constant color. 8 | #[derive(Debug, Clone)] 9 | pub struct ConstantBackground { 10 | pub color: Vector3, 11 | } 12 | 13 | impl ConstantBackground { 14 | pub fn new(color: Vector3) -> Self { 15 | Self { color } 16 | } 17 | } 18 | 19 | impl Background for ConstantBackground { 20 | fn source(_: &mut HashSet) -> String { 21 | "#include ".to_string() 22 | } 23 | } 24 | 25 | impl Store for ConstantBackground { 26 | type Data = Self; 27 | fn create_data(&self, _context: &Context) -> clay_core::Result { 28 | Ok(self.clone()) 29 | } 30 | fn update_data(&self, _context: &Context, data: &mut Self::Data) -> clay_core::Result<()> { 31 | *data = self.clone(); 32 | Ok(()) 33 | } 34 | } 35 | 36 | impl Push for ConstantBackground { 37 | fn args_def(kb: &mut KernelBuilder) { 38 | kb.arg(prm::Float3::zero()); 39 | } 40 | fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> { 41 | let c = self.color.map(|d| d as f32); 42 | k.set_arg(i, &prm::Float3::new(c[0], c[1], c[2]))?; 43 | Ok(()) 44 | } 45 | fn args_count() -> usize { 46 | 1 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/scene/background/gradient.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use nalgebra::{Vector3}; 3 | use ocl::{self, prm, builders::KernelBuilder}; 4 | use crate::{prelude::*, Context, scene::Background}; 5 | 6 | 7 | /// Background with color gradient along the given direction. 8 | #[derive(Debug, Clone)] 9 | pub struct GradientBackground { 10 | pub front: Vector3, 11 | pub back: Vector3, 12 | pub dir: Vector3, 13 | } 14 | 15 | impl GradientBackground { 16 | pub fn new(front: Vector3, back: Vector3, dir: Vector3) -> Self { 17 | Self { front, back, dir } 18 | } 19 | } 20 | 21 | impl Background for GradientBackground { 22 | fn source(_: &mut HashSet) -> String { 23 | "#include ".to_string() 24 | } 25 | } 26 | 27 | impl Store for GradientBackground { 28 | type Data = Self; 29 | fn create_data(&self, _context: &Context) -> clay_core::Result { 30 | Ok(self.clone()) 31 | } 32 | fn update_data(&self, _context: &Context, data: &mut Self::Data) -> clay_core::Result<()> { 33 | *data = self.clone(); 34 | Ok(()) 35 | } 36 | } 37 | 38 | impl Push for GradientBackground { 39 | fn args_def(kb: &mut KernelBuilder) { 40 | kb 41 | .arg(prm::Float3::zero()) // front color 42 | .arg(prm::Float3::zero()) // back color 43 | .arg(prm::Float3::zero()); // gradient direction 44 | } 45 | fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> { 46 | let (fc, bc) = (self.front.map(|d| d as f32), self.back.map(|d| d as f32)); 47 | let d = self.dir.map(|d| d as f32); 48 | k.set_arg(i + 0, &prm::Float3::new(fc[0], fc[1], fc[2]))?; 49 | k.set_arg(i + 1, &prm::Float3::new(bc[0], bc[1], bc[2]))?; 50 | k.set_arg(i + 2, &prm::Float3::new(d[0], d[1], d[2]))?; 51 | Ok(()) 52 | } 53 | fn args_count() -> usize { 54 | 3 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/scene/background/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::core::scene::Background; 2 | 3 | mod constant; 4 | pub use constant::*; 5 | mod gradient; 6 | pub use gradient::*; 7 | -------------------------------------------------------------------------------- /src/scene/list_scene.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use ocl::{ 3 | self, 4 | builders::KernelBuilder, 5 | }; 6 | use uuid::Uuid; 7 | use crate::{ 8 | prelude::*, 9 | object::*, 10 | scene::{Scene, Background}, 11 | Context, 12 | buffer::InstanceBuffer, 13 | }; 14 | 15 | 16 | /// Scene with linear complexity of object search. 17 | pub struct ListScene { 18 | objects: Vec, 19 | uuid: Uuid, 20 | background: B, 21 | max_depth: usize, 22 | } 23 | 24 | impl ListScene { 25 | pub fn new(background: B) -> Self { 26 | Self { objects: Vec::new(), background, uuid: Uuid::new_v4(), max_depth: 4 } 27 | } 28 | 29 | pub fn add(&mut self, object: O) { 30 | self.objects.push(object); 31 | self.uuid = Uuid::new_v4(); 32 | } 33 | 34 | pub fn background(&self) -> &B { 35 | &self.background 36 | } 37 | pub fn background_mut(&mut self) -> &mut B { 38 | &mut self.background 39 | } 40 | 41 | pub fn max_depth(&self) -> usize { 42 | self.max_depth 43 | } 44 | pub fn set_max_depth(&mut self, max_depth: usize) { 45 | self.max_depth = max_depth; 46 | } 47 | } 48 | 49 | impl Scene for ListScene { 50 | fn source(cache: &mut HashSet) -> String { 51 | [ 52 | O::source(cache), 53 | B::source(cache), 54 | ObjectClass::methods().into_iter().map(|method| { 55 | format!( 56 | "#define __object_{} {}_{}", 57 | method, O::inst_name(), method, 58 | ) 59 | }).collect::>().join("\n"), 60 | format!("#define OBJECT_SIZE_INT {}", O::size_int()), 61 | format!("#define OBJECT_SIZE_FLOAT {}", O::size_float()), 62 | "#include ".to_string(), 63 | ] 64 | .join("\n") 65 | } 66 | } 67 | 68 | pub struct ListSceneData { 69 | buffer: InstanceBuffer, 70 | background: B::Data, 71 | uuid: Uuid, 72 | max_depth: usize, 73 | } 74 | 75 | impl Store for ListScene { 76 | type Data = ListSceneData; 77 | fn create_data(&self, context: &Context) -> clay_core::Result { 78 | Ok(ListSceneData { 79 | buffer: InstanceBuffer::new(context, self.objects.iter())?, 80 | background: self.background.create_data(context)?, 81 | uuid: self.uuid, max_depth: self.max_depth, 82 | }) 83 | } 84 | fn update_data(&self, context: &Context, data: &mut Self::Data) -> clay_core::Result<()> { 85 | if self.uuid != data.uuid { 86 | *data = self.create_data(context)?; 87 | } else { 88 | data.max_depth = self.max_depth; 89 | self.background.update_data(context, &mut data.background)?; 90 | } 91 | Ok(()) 92 | } 93 | } 94 | 95 | impl Push for ListSceneData { 96 | fn args_def(kb: &mut KernelBuilder) { 97 | InstanceBuffer::::args_def(kb); 98 | kb.arg(0i32); 99 | B::Data::args_def(kb); 100 | } 101 | fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> { 102 | let mut j = i; 103 | self.buffer.args_set(j, k)?; 104 | j += InstanceBuffer::::args_count(); 105 | k.set_arg(j, &(self.max_depth as i32))?; 106 | j += 1; 107 | self.background.args_set(j, k) 108 | } 109 | fn args_count() -> usize { 110 | InstanceBuffer::::args_count() + 111 | 1 + 112 | B::Data::args_count() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/scene/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::core::scene::*; 2 | 3 | mod list_scene; 4 | pub use list_scene::*; 5 | mod target_list_scene; 6 | pub use target_list_scene::*; 7 | 8 | mod background; 9 | pub use background::*; 10 | -------------------------------------------------------------------------------- /src/scene/target_list_scene.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | rc::Rc, 3 | cell::Cell, 4 | collections::HashSet, 5 | }; 6 | use ocl::{ 7 | self, 8 | builders::KernelBuilder, 9 | }; 10 | use uuid::Uuid; 11 | use crate::{ 12 | prelude::*, 13 | shape::*, 14 | object::*, 15 | scene::{Scene, Background}, 16 | Context, 17 | buffer::InstanceBuffer, 18 | }; 19 | 20 | 21 | struct TargetData { 22 | object_index: usize, 23 | brightness: f64, 24 | target: Rc, 25 | } 26 | 27 | impl Pack for TargetData { 28 | fn size_int() -> usize { 29 | 1 + T::size_int() 30 | } 31 | fn size_float() -> usize { 32 | 1 + T::size_float() 33 | } 34 | fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) { 35 | buffer_int.pack(&(self.object_index as i32)); 36 | buffer_float.pack(&(self.brightness as f32)); 37 | self.target.pack_to( 38 | &mut buffer_int[1..], 39 | &mut buffer_float[1..], 40 | ); 41 | } 42 | } 43 | 44 | struct ObjectData { 45 | target_index: Option, 46 | object: Rc, 47 | } 48 | 49 | impl Pack for ObjectData { 50 | fn size_int() -> usize { 51 | 1 + O::size_int() 52 | } 53 | fn size_float() -> usize { 54 | O::size_float() 55 | } 56 | fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) { 57 | buffer_int.pack(&(match self.target_index { 58 | Some(ti) => ti as i32, 59 | None => -1i32, 60 | })); 61 | self.object.pack_to( 62 | &mut buffer_int[1..], 63 | buffer_float, 64 | ); 65 | } 66 | } 67 | 68 | 69 | type Element = (O, Option<(T, f64)>); 70 | 71 | 72 | /// Scene with linear complexity and importance sampling for bright objects. 73 | pub struct TargetListScene, T: Target, B: Background> { 74 | elements: Cell>>, 75 | background: B, 76 | uuid: Uuid, 77 | max_depth: usize, 78 | target_prob: f64, 79 | } 80 | 81 | impl, T: Target, B: Background> TargetListScene { 82 | pub fn new(background: B) -> Self { 83 | Self { 84 | elements: Cell::new(Vec::new()), background, 85 | uuid: Uuid::new_v4(), max_depth: 4, 86 | target_prob: 0.5, 87 | } 88 | } 89 | pub fn add(&mut self, object: O) { 90 | self.elements.get_mut().push((object, None)); 91 | self.uuid = Uuid::new_v4(); 92 | } 93 | pub fn add_targeted(&mut self, object: O) { 94 | let target_opt = object.target(); 95 | self.elements.get_mut().push((object, target_opt)); 96 | self.uuid = Uuid::new_v4(); 97 | } 98 | 99 | pub fn background(&self) -> &B { 100 | &self.background 101 | } 102 | pub fn background_mut(&mut self) -> &mut B { 103 | &mut self.background 104 | } 105 | 106 | pub fn max_depth(&self) -> usize { 107 | self.max_depth 108 | } 109 | pub fn set_max_depth(&mut self, max_depth: usize) { 110 | self.max_depth = max_depth; 111 | } 112 | 113 | pub fn target_prob(&self) -> f64 { 114 | self.target_prob 115 | } 116 | pub fn set_target_prob(&mut self, target_prob: f64) { 117 | self.target_prob = target_prob; 118 | } 119 | } 120 | 121 | pub struct TargetListSceneData, T: Target, B: Background> { 122 | object_buffer: InstanceBuffer>, 123 | target_buffer: InstanceBuffer>, 124 | background: B::Data, 125 | uuid: Uuid, 126 | max_depth: usize, 127 | target_prob: f64, 128 | } 129 | 130 | impl, T: Target, B: Background> Scene for TargetListScene { 131 | fn source(cache: &mut HashSet) -> String { 132 | [ 133 | O::source(cache), 134 | T::source(cache), 135 | B::source(cache), 136 | ObjectClass::methods().into_iter().map(|method| { 137 | format!( 138 | "#define __object_{} {}_{}", 139 | method, O::inst_name(), method, 140 | ) 141 | }).collect::>().join("\n"), 142 | TargetClass::methods().into_iter().map(|method| { 143 | format!( 144 | "#define __target_{} {}_{}", 145 | method, T::inst_name(), method, 146 | ) 147 | }).collect::>().join("\n"), 148 | format!("#define OBJECT_SIZE_INT {}", ObjectData::::size_int()), 149 | format!("#define OBJECT_SIZE_FLOAT {}", ObjectData::::size_float()), 150 | format!("#define TARGET_SIZE_INT {}", TargetData::::size_int()), 151 | format!("#define TARGET_SIZE_FLOAT {}", TargetData::::size_float()), 152 | "#include ".to_string(), 153 | ] 154 | .join("\n") 155 | } 156 | } 157 | 158 | impl, T: Target, B: Background> Store for TargetListScene { 159 | type Data = TargetListSceneData; 160 | fn create_data(&self, context: &Context) -> clay_core::Result { 161 | let elems = self.elements.replace(Vec::new()) 162 | .into_iter().map(|(o, to)| { 163 | (Rc::new(o), to.map(|t| (Rc::new(t.0), t.1))) 164 | }).collect::>(); 165 | 166 | let mut objects = Vec::new(); 167 | let mut targets = Vec::new(); 168 | for (i, (object, target_opt)) in elems.iter().enumerate() { 169 | match target_opt { 170 | Some((target, brightness)) => { 171 | objects.push(ObjectData { 172 | target_index: Some(targets.len()), 173 | object: object.clone(), 174 | }); 175 | targets.push(TargetData { 176 | object_index: i, 177 | target: target.clone(), 178 | brightness: *brightness, 179 | }); 180 | }, 181 | None => { 182 | objects.push(ObjectData { 183 | target_index: None, 184 | object: object.clone(), 185 | }); 186 | } 187 | } 188 | } 189 | 190 | let res = InstanceBuffer::new(context, objects.iter()) 191 | .and_then(|ob| InstanceBuffer::new(context, targets.iter()).map(|tb| (ob, tb))); 192 | let _ = (objects, targets); 193 | 194 | assert_eq!(self.elements.replace( 195 | elems.into_iter().map(|(o, to)| { 196 | ( 197 | Rc::try_unwrap(o).map_err(|_| "Rc still exists somewhere").unwrap(), 198 | to.map(|t| ( 199 | Rc::try_unwrap(t.0).map_err(|_| "Rc still exists somewhere").unwrap(), 200 | t.1, 201 | )), 202 | ) 203 | }).collect::>() 204 | ).len(), 0); 205 | 206 | let (object_buffer, target_buffer) = res?; 207 | 208 | Ok(Self::Data { 209 | object_buffer, target_buffer, 210 | background: self.background.create_data(context)?, 211 | uuid: self.uuid, 212 | max_depth: self.max_depth, target_prob: self.target_prob, 213 | }) 214 | } 215 | fn update_data(&self, context: &Context, data: &mut Self::Data) -> clay_core::Result<()> { 216 | if self.uuid != data.uuid { 217 | *data = self.create_data(context)?; 218 | } else { 219 | data.max_depth = self.max_depth; 220 | data.target_prob = self.target_prob; 221 | self.background.update_data(context, &mut data.background)?; 222 | } 223 | Ok(()) 224 | } 225 | } 226 | 227 | impl, T: Target, B: Background> Push for TargetListSceneData { 228 | fn args_def(kb: &mut KernelBuilder) { 229 | InstanceBuffer::>::args_def(kb); 230 | InstanceBuffer::>::args_def(kb); 231 | kb.arg(0i32); 232 | kb.arg(0f32); 233 | B::Data::args_def(kb); 234 | } 235 | fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> { 236 | let mut j = i; 237 | self.object_buffer.args_set(j, k)?; 238 | j += InstanceBuffer::>::args_count(); 239 | self.target_buffer.args_set(j, k)?; 240 | j += InstanceBuffer::>::args_count(); 241 | k.set_arg(j + 0, &(self.max_depth as i32))?; 242 | k.set_arg(j + 1, &(self.target_prob as f32))?; 243 | j += 2; 244 | self.background.args_set(j, k) 245 | } 246 | fn args_count() -> usize { 247 | InstanceBuffer::>::args_count() + 248 | InstanceBuffer::>::args_count() + 249 | 2 + 250 | B::Data::args_count() 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/shape/ellipsoid.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use nalgebra::{Vector3, Matrix3, linalg::SVD}; 3 | use crate::{ 4 | prelude::*, 5 | map::*, 6 | shape::*, 7 | }; 8 | 9 | 10 | type EllipsoidBase = ShapeMapper; 11 | /// Ellipsoid shape defined by affine transform on unit sphere. 12 | pub struct Ellipsoid(pub EllipsoidBase); 13 | 14 | impl Ellipsoid { 15 | pub fn new(ori: Matrix3, pos: Vector3) -> Self { 16 | Self::from(UnitSphere::new().map(Linear::from(ori).chain(Shift::from(pos)))) 17 | } 18 | } 19 | impl From for Ellipsoid { 20 | fn from(base: EllipsoidBase) -> Self { 21 | Self(base) 22 | } 23 | } 24 | 25 | impl Shape for Ellipsoid {} 26 | 27 | impl Instance for Ellipsoid { 28 | fn source(cache: &mut HashSet) -> String { EllipsoidBase::source(cache) } 29 | fn inst_name() -> String { EllipsoidBase::inst_name() } 30 | } 31 | 32 | impl Pack for Ellipsoid { 33 | fn size_int() -> usize { EllipsoidBase::size_int() } 34 | fn size_float() -> usize { EllipsoidBase::size_float() } 35 | fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) { 36 | self.0.pack_to(buffer_int, buffer_float); 37 | } 38 | } 39 | 40 | impl Bounded for Ellipsoid { 41 | fn bound(&self) -> Option { 42 | let rad = SVD::new( 43 | self.0.map.first.0, 44 | false, false, 45 | ) 46 | .singular_values.as_slice().iter() 47 | .fold(std::f64::NAN, |a, b| f64::max(a, *b)); 48 | Some(Sphere::new(rad, self.0.map.second.0)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/shape/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::core::shape::*; 2 | 3 | mod unit_sphere; 4 | pub use unit_sphere::*; 5 | mod sphere; 6 | pub use sphere::*; 7 | mod ellipsoid; 8 | pub use ellipsoid::*; 9 | 10 | mod unit_cube; 11 | pub use unit_cube::*; 12 | mod parallelepiped; 13 | pub use parallelepiped::*; 14 | -------------------------------------------------------------------------------- /src/shape/parallelepiped.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use nalgebra::{Vector3, Matrix3}; 3 | use crate::{ 4 | prelude::*, 5 | map::*, 6 | shape::*, 7 | }; 8 | 9 | 10 | type ParallelepipedBase = ShapeMapper; 11 | /// Parallelepiped shape defined by affine transform on unit cube. 12 | pub struct Parallelepiped(pub ParallelepipedBase); 13 | 14 | impl Parallelepiped { 15 | pub fn new(ori: Matrix3, pos: Vector3) -> Self { 16 | Self::from(UnitCube::new().map(Linear::from(ori).chain(Shift::from(pos)))) 17 | } 18 | } 19 | impl From for Parallelepiped { 20 | fn from(base: ParallelepipedBase) -> Self { 21 | Self(base) 22 | } 23 | } 24 | 25 | impl Shape for Parallelepiped {} 26 | impl Instance for Parallelepiped { 27 | fn source(cache: &mut HashSet) -> String { 28 | ParallelepipedBase::source(cache) 29 | } 30 | fn inst_name() -> String { 31 | ParallelepipedBase::inst_name() 32 | } 33 | } 34 | impl Pack for Parallelepiped { 35 | fn size_int() -> usize { ParallelepipedBase::size_int() } 36 | fn size_float() -> usize { ParallelepipedBase::size_float() } 37 | fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) { 38 | self.0.pack_to(buffer_int, buffer_float); 39 | } 40 | } 41 | 42 | impl Bounded for Parallelepiped { 43 | fn bound(&self) -> Option { 44 | let pos = self.0.map.second.0; 45 | let ori = self.0.map.first.0; 46 | let basis = ori.transpose(); 47 | let mut rad = 0.0; 48 | for i in 0..8 { 49 | let mut data = [0.0; 3]; 50 | for j in 0..3 { 51 | data[j] = 1.0 - 2.0*(((i << j) & 1) as f64); 52 | } 53 | let len = (basis*Vector3::from_column_slice(&data)).norm(); 54 | if len > rad { 55 | rad = len; 56 | } 57 | } 58 | Some(Sphere::new(rad, pos)) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/shape/sphere.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use nalgebra::{Vector3}; 3 | use crate::{ 4 | prelude::*, 5 | map::*, 6 | shape::*, 7 | }; 8 | 9 | 10 | type SphereBase = ShapeMapper>; 11 | /// Shape obtained by scaling and shifting the unit sphere. 12 | pub struct Sphere(pub SphereBase); 13 | 14 | impl Sphere { 15 | pub fn new(rad: f64, pos: Vector3) -> Self { 16 | Self::from(UnitSphere::new().map(Scale::from(rad).chain(Shift::from(pos)))) 17 | } 18 | } 19 | impl From for Sphere { 20 | fn from(base: SphereBase) -> Self { 21 | Self(base) 22 | } 23 | } 24 | 25 | impl Shape for Sphere {} 26 | 27 | impl Instance for Sphere { 28 | fn source(cache: &mut HashSet) -> String { SphereBase::source(cache) } 29 | fn inst_name() -> String { SphereBase::inst_name() } 30 | } 31 | 32 | impl Pack for Sphere { 33 | fn size_int() -> usize { SphereBase::size_int() } 34 | fn size_float() -> usize { SphereBase::size_float() } 35 | fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) { 36 | self.0.pack_to(buffer_int, buffer_float); 37 | } 38 | } 39 | 40 | impl Bound for Sphere {} 41 | impl Instance for Sphere { 42 | fn source(cache: &mut HashSet) -> String { UnitSphere::source(cache) } 43 | fn inst_name() -> String { "sphere".to_string() } 44 | } 45 | 46 | impl Target for Sphere {} 47 | impl Instance for Sphere { 48 | fn source(cache: &mut HashSet) -> String { UnitSphere::source(cache) } 49 | fn inst_name() -> String { "sphere_target".to_string() } 50 | } 51 | -------------------------------------------------------------------------------- /src/shape/unit_cube.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use crate::{ 3 | prelude::*, 4 | shape::*, 5 | }; 6 | 7 | 8 | /// Unit cube - centered at the origin and of edge length two. 9 | /// 10 | /// This shape could be transformed to an arbitrary parallelepiped 11 | /// by combining with the affine transform (*see `Shape::map()`*). 12 | #[derive(Clone, Debug, Default)] 13 | pub struct UnitCube {} 14 | 15 | impl UnitCube { 16 | /// Creates new unit cube 17 | pub fn new() -> Self { 18 | Self {} 19 | } 20 | } 21 | 22 | impl Shape for UnitCube {} 23 | 24 | impl Instance for UnitCube { 25 | fn source(_: &mut HashSet) -> String { 26 | "#include ".to_string() 27 | } 28 | fn inst_name() -> String { 29 | "cube".to_string() 30 | } 31 | } 32 | 33 | impl Pack for UnitCube { 34 | fn size_int() -> usize { 0 } 35 | fn size_float() -> usize { 0 } 36 | fn pack_to(&self, _buffer_int: &mut [i32], _buffer_float: &mut [f32]) {} 37 | } 38 | -------------------------------------------------------------------------------- /src/shape/unit_sphere.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use crate::{ 3 | prelude::*, 4 | shape::*, 5 | }; 6 | 7 | 8 | /// Unit sphere - of radius one and centered at the origin. 9 | /// 10 | /// This shape could be transformed to an arbitrary ellipsoid 11 | /// by combining with the affine transform (*see `Shape::map()`*). 12 | #[derive(Clone, Debug, Default)] 13 | pub struct UnitSphere {} 14 | 15 | impl UnitSphere { 16 | /// Creates new unit sphere 17 | pub fn new() -> Self { 18 | Self {} 19 | } 20 | fn source() -> String { 21 | "#include ".to_string() 22 | } 23 | } 24 | 25 | impl Shape for UnitSphere {} 26 | 27 | impl Instance for UnitSphere { 28 | fn source(_: &mut HashSet) -> String { Self::source() } 29 | fn inst_name() -> String { "unit_sphere".to_string() } 30 | } 31 | 32 | impl Pack for UnitSphere { 33 | fn size_int() -> usize { 0 } 34 | fn size_float() -> usize { 0 } 35 | fn pack_to(&self, _buffer_int: &mut [i32], _buffer_float: &mut [f32]) {} 36 | } 37 | -------------------------------------------------------------------------------- /src/source.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::Path, 3 | }; 4 | use ocl_include::{MemHook}; 5 | 6 | include!(concat!(env!("OUT_DIR"), "/ocl_src_list.rs")); 7 | 8 | /// OpenCL source code tree. 9 | pub fn source() -> MemHook { 10 | let mut hook = MemHook::new(); 11 | let pref = Path::new("clay"); 12 | for (name, content) in OCL_SRC_LIST.iter() { 13 | hook.add_file( 14 | &pref.join(name), 15 | content.to_string(), 16 | ).unwrap(); 17 | } 18 | hook 19 | } 20 | -------------------------------------------------------------------------------- /src/view/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::core::view::*; 2 | 3 | mod projection; 4 | pub use projection::*; 5 | -------------------------------------------------------------------------------- /src/view/projection.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use ocl::{self, prm, builders::KernelBuilder}; 3 | use nalgebra::{Vector3, Rotation3}; 4 | use crate::{prelude::*, Context, view::View}; 5 | 6 | 7 | /// Perspective projection view. 8 | #[derive(Debug, Clone)] 9 | pub struct ProjectionView { 10 | /// Position of the point of view. 11 | pub pos: Vector3, 12 | /// Orientation. 13 | pub ori: Rotation3, 14 | /// Field of view width. 15 | pub fov: f64, 16 | } 17 | 18 | impl ProjectionView { 19 | pub fn new(pos: Vector3, ori: Rotation3) -> Self { 20 | Self { pos, ori, fov: 1.0 } 21 | } 22 | 23 | pub fn update(&mut self, pos: Vector3, ori: Rotation3) { 24 | self.pos = pos; 25 | self.ori = ori; 26 | } 27 | } 28 | 29 | impl View for ProjectionView { 30 | fn source(_: &mut HashSet) -> String { 31 | "#include \n".to_string() 32 | } 33 | } 34 | 35 | impl Store for ProjectionView { 36 | type Data = Self; 37 | fn create_data(&self, _context: &Context) -> clay_core::Result { 38 | Ok(self.clone()) 39 | } 40 | fn update_data(&self, _context: &Context, data: &mut Self::Data) -> clay_core::Result<()> { 41 | *data = self.clone(); 42 | Ok(()) 43 | } 44 | } 45 | 46 | impl Push for ProjectionView { 47 | fn args_def(kb: &mut KernelBuilder) { 48 | kb 49 | .arg(prm::Float3::zero()) 50 | .arg(prm::Float16::zero()) 51 | .arg(0.0f32); 52 | } 53 | fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> { 54 | let mapf = self.ori.matrix().map(|x| x as f32); 55 | let mut map16 = [0f32; 16]; 56 | map16[0..3].copy_from_slice(&mapf.as_slice()[0..3]); 57 | map16[4..7].copy_from_slice(&mapf.as_slice()[3..6]); 58 | map16[8..11].copy_from_slice(&mapf.as_slice()[6..9]); 59 | 60 | let posf = self.pos.map(|x| x as f32); 61 | let mut pos3 = [0f32; 3]; 62 | pos3.copy_from_slice(posf.as_slice()); 63 | 64 | k.set_arg(i + 0, &prm::Float3::from(pos3))?; 65 | k.set_arg(i + 1, &prm::Float16::from(map16))?; 66 | k.set_arg(i + 2, &(self.fov as f32))?; 67 | 68 | Ok(()) 69 | } 70 | fn args_count() -> usize { 71 | 3 72 | } 73 | } 74 | --------------------------------------------------------------------------------