├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── prototype ├── Cargo.toml ├── data │ ├── models │ │ ├── CornellBox-Original.gltf │ │ ├── FlightHelmet │ │ │ ├── README.md │ │ │ ├── glTF │ │ │ │ ├── FlightHelmet.bin │ │ │ │ ├── FlightHelmet.gltf │ │ │ │ ├── FlightHelmet_baseColor.png │ │ │ │ ├── FlightHelmet_baseColor1.png │ │ │ │ ├── FlightHelmet_baseColor2.png │ │ │ │ ├── FlightHelmet_baseColor3.png │ │ │ │ ├── FlightHelmet_baseColor4.png │ │ │ │ ├── FlightHelmet_normal.png │ │ │ │ ├── FlightHelmet_normal1.png │ │ │ │ ├── FlightHelmet_normal2.png │ │ │ │ ├── FlightHelmet_normal3.png │ │ │ │ ├── FlightHelmet_normal4.png │ │ │ │ ├── FlightHelmet_occlusionRoughnessMetallic.png │ │ │ │ ├── FlightHelmet_occlusionRoughnessMetallic1.png │ │ │ │ ├── FlightHelmet_occlusionRoughnessMetallic2.png │ │ │ │ ├── FlightHelmet_occlusionRoughnessMetallic3.png │ │ │ │ └── FlightHelmet_occlusionRoughnessMetallic4.png │ │ │ └── screenshot │ │ │ │ └── screenshot.jpg │ │ ├── MetalRoughSpheresNoTextures │ │ │ └── glTF │ │ │ │ ├── MetalRoughSpheresNoTextures.bin │ │ │ │ └── MetalRoughSpheresNoTextures.gltf │ │ ├── Sponza │ │ │ ├── README.md │ │ │ ├── glTF │ │ │ │ ├── 10381718147657362067.jpg │ │ │ │ ├── 10388182081421875623.jpg │ │ │ │ ├── 11474523244911310074.jpg │ │ │ │ ├── 11490520546946913238.jpg │ │ │ │ ├── 11872827283454512094.jpg │ │ │ │ ├── 11968150294050148237.jpg │ │ │ │ ├── 1219024358953944284.jpg │ │ │ │ ├── 12501374198249454378.jpg │ │ │ │ ├── 13196865903111448057.jpg │ │ │ │ ├── 13824894030729245199.jpg │ │ │ │ ├── 13982482287905699490.jpg │ │ │ │ ├── 14118779221266351425.jpg │ │ │ │ ├── 14170708867020035030.jpg │ │ │ │ ├── 14267839433702832875.jpg │ │ │ │ ├── 14650633544276105767.jpg │ │ │ │ ├── 15295713303328085182.jpg │ │ │ │ ├── 15722799267630235092.jpg │ │ │ │ ├── 16275776544635328252.png │ │ │ │ ├── 16299174074766089871.jpg │ │ │ │ ├── 16885566240357350108.jpg │ │ │ │ ├── 17556969131407844942.jpg │ │ │ │ ├── 17876391417123941155.jpg │ │ │ │ ├── 2051777328469649772.jpg │ │ │ │ ├── 2185409758123873465.jpg │ │ │ │ ├── 2299742237651021498.jpg │ │ │ │ ├── 2374361008830720677.jpg │ │ │ │ ├── 2411100444841994089.jpg │ │ │ │ ├── 2775690330959970771.jpg │ │ │ │ ├── 2969916736137545357.jpg │ │ │ │ ├── 332936164838540657.jpg │ │ │ │ ├── 3371964815757888145.jpg │ │ │ │ ├── 3455394979645218238.jpg │ │ │ │ ├── 3628158980083700836.jpg │ │ │ │ ├── 3827035219084910048.jpg │ │ │ │ ├── 4477655471536070370.jpg │ │ │ │ ├── 4601176305987539675.jpg │ │ │ │ ├── 466164707995436622.jpg │ │ │ │ ├── 4675343432951571524.jpg │ │ │ │ ├── 4871783166746854860.jpg │ │ │ │ ├── 4910669866631290573.jpg │ │ │ │ ├── 4975155472559461469.jpg │ │ │ │ ├── 5061699253647017043.png │ │ │ │ ├── 5792855332885324923.jpg │ │ │ │ ├── 5823059166183034438.jpg │ │ │ │ ├── 6047387724914829168.jpg │ │ │ │ ├── 6151467286084645207.jpg │ │ │ │ ├── 6593109234861095314.jpg │ │ │ │ ├── 6667038893015345571.jpg │ │ │ │ ├── 6772804448157695701.jpg │ │ │ │ ├── 7056944414013900257.jpg │ │ │ │ ├── 715093869573992647.jpg │ │ │ │ ├── 7268504077753552595.jpg │ │ │ │ ├── 7441062115984513793.jpg │ │ │ │ ├── 755318871556304029.jpg │ │ │ │ ├── 759203620573749278.jpg │ │ │ │ ├── 7645212358685992005.jpg │ │ │ │ ├── 7815564343179553343.jpg │ │ │ │ ├── 8006627369776289000.png │ │ │ │ ├── 8051790464816141987.jpg │ │ │ │ ├── 8114461559286000061.jpg │ │ │ │ ├── 8481240838833932244.jpg │ │ │ │ ├── 8503262930880235456.jpg │ │ │ │ ├── 8747919177698443163.jpg │ │ │ │ ├── 8750083169368950601.jpg │ │ │ │ ├── 8773302468495022225.jpg │ │ │ │ ├── 8783994986360286082.jpg │ │ │ │ ├── 9288698199695299068.jpg │ │ │ │ ├── 9916269861720640319.jpg │ │ │ │ ├── Sponza.bin │ │ │ │ ├── Sponza.gltf │ │ │ │ └── white.png │ │ │ └── screenshot │ │ │ │ ├── large.jpg │ │ │ │ └── screenshot.jpg │ │ └── sphere.gltf │ └── rust.png └── src │ ├── lib.rs │ ├── main.rs │ ├── scenes.rs │ └── ui.rs └── utopian ├── Cargo.toml ├── data ├── models │ ├── sphere.gltf │ └── sphere.obj ├── printscreens │ ├── printscreen1.png │ ├── printscreen2.png │ ├── printscreen3.jpg │ └── printscreen4.jpg └── textures │ └── defaults │ ├── black_texture.png │ ├── checker.jpg │ ├── default_metallic_roughness.png │ ├── flat_normal_map.png │ └── white_texture.png ├── shaders ├── atmosphere │ ├── atmosphere.frag │ └── atmosphere.vert ├── blit │ └── blit.frag ├── common │ └── fullscreen.vert ├── deferred │ └── deferred.frag ├── forward │ ├── forward.frag │ └── forward.vert ├── gbuffer │ ├── gbuffer.frag │ └── gbuffer.vert ├── ibl │ ├── brdf_lut.frag │ ├── cubemap.frag │ ├── fullscreen_with_pushconst.vert │ ├── irradiance_filter.frag │ └── specular_filter.frag ├── include │ ├── atmosphere.glsl │ ├── bindless.glsl │ ├── brdf.glsl │ ├── fxaa.glsl │ ├── pbr_lighting.glsl │ ├── random.glsl │ ├── restir_sampling.glsl │ ├── shadow_mapping.glsl │ └── view.glsl ├── marching_cubes │ ├── marching_cubes.comp │ ├── noise.glsl │ ├── reset_counter.comp │ └── tables.glsl ├── pathtrace_reference │ ├── payload.glsl │ ├── reference.rchit │ ├── reference.rgen │ └── reference.rmiss ├── present │ └── present.frag ├── restir │ ├── initial_ris.rchit │ ├── initial_ris.rgen │ ├── initial_ris.rmiss │ ├── reset_reservoirs.comp │ ├── spatial_reuse.rchit │ ├── spatial_reuse.rgen │ ├── spatial_reuse.rmiss │ ├── temporal_reuse.rchit │ ├── temporal_reuse.rgen │ └── temporal_reuse.rmiss ├── rt_reflections │ ├── rt_reflections.rchit │ ├── rt_reflections.rgen │ └── rt_reflections.rmiss ├── rt_shadows │ ├── rt_shadows.rchit │ ├── rt_shadows.rgen │ └── rt_shadows.rmiss ├── shadow │ ├── shadow.frag │ └── shadow.vert └── ssao │ ├── blur.frag │ └── ssao.frag └── src ├── bindless.rs ├── buffer.rs ├── camera.rs ├── descriptor_set.rs ├── device.rs ├── directory_watcher.rs ├── fps_timer.rs ├── gltf_loader.rs ├── graph.rs ├── image.rs ├── input.rs ├── lib.rs ├── model_loader.rs ├── pass.rs ├── pipeline.rs ├── primitive.rs ├── profiler_backend.rs ├── raytracing.rs ├── render_utils.rs ├── renderer.rs ├── renderers ├── atmosphere.rs ├── deferred.rs ├── forward.rs ├── gbuffer.rs ├── ibl.rs ├── marching_cubes.rs ├── mod.rs ├── present.rs ├── rt_reflections.rs ├── rt_shadows.rs ├── shadow.rs └── ssao.rs ├── shader.rs ├── synch.rs ├── texture.rs └── vulkan_base.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "utopian", 4 | "prototype", 5 | ] 6 | resolver = "2" 7 | 8 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Axel Blackert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-renderer 2 | 3 | ![Image](utopian/data/printscreens/printscreen3.jpg) 4 | ![Image](utopian/data/printscreens/printscreen4.jpg) 5 | _Example renders using the path tracing mode_ 6 | 7 | Developed to learn Rust and to have a framework for experimenting with modern rendering techniques 8 | 9 | ## Features 10 | + GPU path tracer based on [Ray Tracing in One Weekend](https://raytracing.github.io/books/RayTracingInOneWeekend.html) 11 | + ReSTIR to improve sampling of analytical point lights 12 | + Basic PBR rendering 13 | + Bindless GPU resources 14 | + Experimental high level render graph 15 | + Vulkan using [ash](https://github.com/MaikKlein/ash) 16 | + SSAO, FXAA, IBL, shadow mapping 17 | + Raytraced shadows and reflections 18 | + Live shader recompilation 19 | + Shader reflection 20 | + Marching cubes demo 21 | 22 | ## Building 23 | 24 | You need [Rust](https://www.rust-lang.org/tools/install) for building 25 | 26 | Build and run the project with `cargo run --release` 27 | 28 | **Note**: requires a GPU with raytracing support, e.g Nvidia RTX series 29 | 30 | ## Controls 31 | 32 | - `1`, `2` - toggle between path tracing and rasterization 33 | - `W/A/S/D` - move the camera 34 | - `MOUSE + RMB` - rotate the camera 35 | - `Q` - toggle profiling window 36 | - `SPACE` - pause profiler 37 | -------------------------------------------------------------------------------- /prototype/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prototype" 3 | version = "0.0.3" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | utopian = { path = "../utopian" } 10 | 11 | winit = "0.26.0" 12 | ash = { version = "0.37.0", features = ["linked", "debug"]} 13 | vk-sync = { git = "https://github.com/simplerr/vk-sync-rs" } 14 | ash-window = "0.10" 15 | shaderc = "0.7.3" 16 | glam = "0.20.2" 17 | egui = "0.20.1" 18 | gpu-allocator = "0.18.0" 19 | gpu-profiler = { git = "https://github.com/simplerr/gpu-profiler.git", features = ["use-ash"] } 20 | egui-winit-ash-integration = { git = "https://github.com/simplerr/egui-winit-ash-integration" } 21 | egui-gizmo = { git = "https://github.com/simplerr/egui-gizmo.git" } 22 | rspirv-reflect = { git = "https://github.com/simplerr/rspirv-reflect.git" } # "0.6.0" 23 | puffin = { git = "https://github.com/simplerr/puffin.git" } 24 | puffin_egui = { git = "https://github.com/simplerr/puffin.git" } 25 | log = "0.4" 26 | simple_logger = "5.0.0" 27 | -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/README.md: -------------------------------------------------------------------------------- 1 | # Flight Helmet 2 | 3 | ## Screenshot 4 | 5 | ![screenshot](screenshot/screenshot.jpg) 6 | 7 | ## License Information 8 | 9 | Donated by Microsoft for glTF testing 10 | 11 | [![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)](http://creativecommons.org/publicdomain/zero/1.0/) 12 | To the extent possible under law, Microsoft has waived all copyright and related or neighboring rights to this asset. 13 | -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet.bin -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor1.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor2.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor3.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_baseColor4.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal1.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal2.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal3.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_normal4.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic1.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic2.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic3.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/glTF/FlightHelmet_occlusionRoughnessMetallic4.png -------------------------------------------------------------------------------- /prototype/data/models/FlightHelmet/screenshot/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/FlightHelmet/screenshot/screenshot.jpg -------------------------------------------------------------------------------- /prototype/data/models/MetalRoughSpheresNoTextures/glTF/MetalRoughSpheresNoTextures.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/MetalRoughSpheresNoTextures/glTF/MetalRoughSpheresNoTextures.bin -------------------------------------------------------------------------------- /prototype/data/models/Sponza/README.md: -------------------------------------------------------------------------------- 1 | # Sponza 2 | 3 | ## Screenshot 4 | 5 | Lights are shown here, they are not part of the model. 6 | 7 | ![screenshot](screenshot/large.jpg) 8 | 9 | ## Model notes 10 | 11 | Tangents have been computed using MikkTSpace, as the original OBJ model did not have them. 12 | I have manually inspected the normals, and it looks correct to me. 13 | Be aware that W is -1.0 for most of the tangent signs, you will need to handle tangent W for correct results. 14 | 15 | 16 | ## Sources 17 | 18 | ### https://www.cryengine.com/marketplace/ 19 | 20 | - https://www.cryengine.com/marketplace/product/CEMP-1102 21 | 22 | ### http://www.alexandre-pestana.com/pbr-textures-sponza/ 23 | 24 | - www.alexandre-pestana.com/downloads/SponzaPBR_Textures.rar 25 | 26 | I needed to resize some of the alpha mask textures to the 1024x1024 resolution used by the new texture pack, 27 | and merge in diffuse with alpha. 28 | I also repacked the separate metallic/roughness textures into the glTF layout (G - roughness, B - metallic). 29 | The images are also re-encoded as PNG instead of TGA. 30 | All the materials also had a constant diffuse factor of about 0.58. I assume it was supposed to be there, so I kept it. 31 | I also ran the vertices and indices through a mesh optimizer. 32 | 33 | ## Licensing notes 34 | 35 | Taken from copyright.txt in SponzaPBR\_Textures.rar 36 | 37 | ``` 38 | PBR textures for the Sponza model. 39 | For more informations: www.alexandre-pestana.com 40 | 41 | 42 | Original copyright: 43 | 44 | July 14, 2011 Morgan McGuire modified the model from Crytek's OBJ 45 | export to correct some small errors. He computed bump maps from the 46 | normal maps using normal2bump.cpp (since 48 | MTL files expect height bumps, not normals), put the "mask" textures 49 | into the alpha channel of the associated diffuse texture, cleaned up 50 | noise in the masks, created the missing gi_flag.tga texture, and 51 | removed the long untextured banner floating in the middle of the 52 | atrium that appears in the file but in none of the published images of 53 | the model. The banner is in banner.obj. 54 | 55 | 56 | 57 | http://www.crytek.com/cryengine/cryengine3/downloads 58 | 59 | 60 | Sponza Model 61 | August 19, 2010 62 | The Atrium Sponza Palace, Dubrovnik, is an elegant and improved model created by Frank Meinl. The original Sponza model was created by Marko Dabrovic in early 2002. Over the years, the Sponza Atrium scene has become one of the most popular 3D scenes for testing global illumination and radiosity due to it's specific architectural structure which is particularly complex for global illumination light. 63 | 64 | However, nowadays it is considered as a simple model, thus it was decided to crate a new model with highly improved appearance and scene complexity. It is donated to the public for radiosity and is represented in several different formats (3ds, Obj) for use with various commercial 3D applications and renderers. 65 | 66 | 67 | Screenshot from the I3D paper 68 | http://crytek.com/sites/default/files/20100301_lpv.pdf 69 | ``` 70 | -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/10381718147657362067.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/10381718147657362067.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/10388182081421875623.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/10388182081421875623.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/11474523244911310074.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/11474523244911310074.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/11490520546946913238.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/11490520546946913238.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/11872827283454512094.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/11872827283454512094.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/11968150294050148237.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/11968150294050148237.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/1219024358953944284.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/1219024358953944284.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/12501374198249454378.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/12501374198249454378.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/13196865903111448057.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/13196865903111448057.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/13824894030729245199.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/13824894030729245199.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/13982482287905699490.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/13982482287905699490.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/14118779221266351425.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/14118779221266351425.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/14170708867020035030.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/14170708867020035030.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/14267839433702832875.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/14267839433702832875.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/14650633544276105767.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/14650633544276105767.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/15295713303328085182.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/15295713303328085182.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/15722799267630235092.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/15722799267630235092.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/16275776544635328252.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/16275776544635328252.png -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/16299174074766089871.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/16299174074766089871.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/16885566240357350108.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/16885566240357350108.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/17556969131407844942.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/17556969131407844942.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/17876391417123941155.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/17876391417123941155.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/2051777328469649772.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/2051777328469649772.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/2185409758123873465.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/2185409758123873465.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/2299742237651021498.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/2299742237651021498.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/2374361008830720677.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/2374361008830720677.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/2411100444841994089.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/2411100444841994089.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/2775690330959970771.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/2775690330959970771.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/2969916736137545357.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/2969916736137545357.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/332936164838540657.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/332936164838540657.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/3371964815757888145.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/3371964815757888145.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/3455394979645218238.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/3455394979645218238.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/3628158980083700836.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/3628158980083700836.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/3827035219084910048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/3827035219084910048.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/4477655471536070370.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/4477655471536070370.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/4601176305987539675.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/4601176305987539675.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/466164707995436622.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/466164707995436622.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/4675343432951571524.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/4675343432951571524.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/4871783166746854860.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/4871783166746854860.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/4910669866631290573.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/4910669866631290573.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/4975155472559461469.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/4975155472559461469.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/5061699253647017043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/5061699253647017043.png -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/5792855332885324923.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/5792855332885324923.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/5823059166183034438.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/5823059166183034438.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/6047387724914829168.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/6047387724914829168.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/6151467286084645207.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/6151467286084645207.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/6593109234861095314.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/6593109234861095314.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/6667038893015345571.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/6667038893015345571.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/6772804448157695701.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/6772804448157695701.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/7056944414013900257.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/7056944414013900257.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/715093869573992647.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/715093869573992647.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/7268504077753552595.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/7268504077753552595.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/7441062115984513793.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/7441062115984513793.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/755318871556304029.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/755318871556304029.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/759203620573749278.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/759203620573749278.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/7645212358685992005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/7645212358685992005.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/7815564343179553343.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/7815564343179553343.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8006627369776289000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8006627369776289000.png -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8051790464816141987.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8051790464816141987.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8114461559286000061.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8114461559286000061.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8481240838833932244.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8481240838833932244.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8503262930880235456.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8503262930880235456.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8747919177698443163.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8747919177698443163.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8750083169368950601.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8750083169368950601.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8773302468495022225.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8773302468495022225.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/8783994986360286082.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/8783994986360286082.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/9288698199695299068.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/9288698199695299068.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/9916269861720640319.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/9916269861720640319.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/Sponza.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/Sponza.bin -------------------------------------------------------------------------------- /prototype/data/models/Sponza/glTF/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/glTF/white.png -------------------------------------------------------------------------------- /prototype/data/models/Sponza/screenshot/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/screenshot/large.jpg -------------------------------------------------------------------------------- /prototype/data/models/Sponza/screenshot/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/models/Sponza/screenshot/screenshot.jpg -------------------------------------------------------------------------------- /prototype/data/rust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/prototype/data/rust.png -------------------------------------------------------------------------------- /prototype/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod scenes; 2 | pub mod ui; 3 | 4 | pub use scenes::*; 5 | -------------------------------------------------------------------------------- /prototype/src/scenes.rs: -------------------------------------------------------------------------------- 1 | use glam::Vec3; 2 | 3 | pub fn create_scene( 4 | renderer: &mut utopian::Renderer, 5 | camera: &mut utopian::Camera, 6 | device: &utopian::Device, 7 | ) { 8 | let sphere = utopian::gltf_loader::load_gltf(device, "utopian/data/models/sphere.gltf"); 9 | 10 | renderer.add_model( 11 | device, 12 | sphere, 13 | glam::Mat4::from_translation(glam::Vec3::new(f32::MAX, f32::MAX, f32::MAX)), 14 | ); 15 | 16 | let num_lights = 10; 17 | for i in 0..num_lights { 18 | renderer.add_light( 19 | device, 20 | Vec3::new(((i / 30) * 20) as f32, 3.5, ((i % 30) * 20) as f32), 21 | Vec3::new(1.0, 1.0, 1.0), 22 | 1.0, 23 | ); 24 | } 25 | 26 | // create_cornell_box_scene(renderer, camera, device); 27 | // create_metal_rough_spheres(renderer, camera, device); 28 | create_sponza_scene(renderer, camera, device); 29 | // create_cube_scene(renderer, camera, device); 30 | } 31 | 32 | pub fn create_metal_rough_spheres( 33 | renderer: &mut utopian::Renderer, 34 | camera: &mut utopian::Camera, 35 | device: &utopian::Device, 36 | ) { 37 | camera.set_position_target( 38 | glam::Vec3::new(0.0, 0.9, 2.0), 39 | glam::Vec3::new(0.0, 0.5, 0.0), 40 | ); 41 | 42 | let spheres = utopian::gltf_loader::load_gltf( 43 | device, 44 | "prototype/data/models/MetalRoughSpheresNoTextures/glTF/MetalRoughSpheresNoTextures.gltf", 45 | ); 46 | 47 | renderer.add_model( 48 | device, 49 | spheres, 50 | glam::Mat4::from_scale_rotation_translation( 51 | glam::Vec3::new(1000.0, 1000.0, 1000.0), 52 | glam::Quat::from_rotation_y(std::f32::consts::PI / 2.0), 53 | glam::Vec3::new(-10.0, 15.0, 2.5), 54 | ), 55 | ); 56 | } 57 | 58 | pub fn create_cornell_box_scene( 59 | renderer: &mut utopian::Renderer, 60 | camera: &mut utopian::Camera, 61 | device: &utopian::Device, 62 | ) { 63 | camera.set_position_target( 64 | glam::Vec3::new(0.0, 0.9, 2.0), 65 | glam::Vec3::new(0.0, 0.5, 0.0), 66 | ); 67 | 68 | let cornell_box = 69 | utopian::gltf_loader::load_gltf(device, "prototype/data/models/CornellBox-Original.gltf"); 70 | 71 | let flight_helmet = utopian::gltf_loader::load_gltf( 72 | device, 73 | "prototype/data/models/FlightHelmet/glTF/FlightHelmet.gltf", 74 | ); 75 | 76 | renderer.add_model( 77 | device, 78 | cornell_box, 79 | glam::Mat4::from_translation(glam::Vec3::new(0.0, 0.0, 0.0)), 80 | ); 81 | 82 | let mut light = utopian::ModelLoader::load_cube(device); 83 | light.meshes[0].material.material_type = utopian::gltf_loader::MaterialType::DiffuseLight; 84 | 85 | renderer.add_model( 86 | device, 87 | light, 88 | glam::Mat4::from_scale_rotation_translation( 89 | glam::Vec3::new(0.50, 0.05, 0.35), 90 | glam::Quat::IDENTITY, 91 | glam::Vec3::new(0.0, 1.95, 0.0), 92 | ), 93 | ); 94 | 95 | renderer.add_model( 96 | device, 97 | flight_helmet, 98 | glam::Mat4::from_translation(glam::Vec3::new(-0.33, 0.4, 0.3)), 99 | ); 100 | } 101 | 102 | pub fn create_sponza_scene( 103 | renderer: &mut utopian::Renderer, 104 | camera: &mut utopian::Camera, 105 | device: &utopian::Device, 106 | ) { 107 | camera.set_position_target( 108 | glam::Vec3::new(-10.28, 2.10, -0.18), 109 | glam::Vec3::new(0.0, 0.5, 0.0), 110 | ); 111 | 112 | let sponza = 113 | utopian::gltf_loader::load_gltf(device, "prototype/data/models/Sponza/glTF/Sponza.gltf"); 114 | 115 | let mut metal_sphere = 116 | utopian::gltf_loader::load_gltf(device, "prototype/data/models/sphere.gltf"); 117 | metal_sphere.meshes[0].material.material_type = utopian::gltf_loader::MaterialType::Metal; 118 | let mut dielectric_sphere = 119 | utopian::gltf_loader::load_gltf(device, "prototype/data/models/sphere.gltf"); 120 | dielectric_sphere.meshes[0].material.material_type = 121 | utopian::gltf_loader::MaterialType::Dielectric; 122 | dielectric_sphere.meshes[0].material.material_property = 1.5; 123 | 124 | renderer.add_model( 125 | device, 126 | sponza, 127 | glam::Mat4::from_translation(glam::Vec3::new(0.0, 0.0, 0.0)), 128 | ); 129 | 130 | let size = 0.6; 131 | renderer.add_model( 132 | device, 133 | metal_sphere, 134 | glam::Mat4::from_scale_rotation_translation( 135 | glam::Vec3::new(size, size, size), 136 | glam::Quat::IDENTITY, 137 | glam::Vec3::new(-3.0, 2.65, 0.7), 138 | ), 139 | ); 140 | 141 | renderer.add_model( 142 | device, 143 | dielectric_sphere, 144 | glam::Mat4::from_scale_rotation_translation( 145 | glam::Vec3::new(size, size, size), 146 | glam::Quat::IDENTITY, 147 | glam::Vec3::new(-3.0, 0.65, 0.7), 148 | ), 149 | ); 150 | } 151 | 152 | pub fn create_cube_scene( 153 | renderer: &mut utopian::Renderer, 154 | camera: &mut utopian::Camera, 155 | device: &utopian::Device, 156 | ) { 157 | camera.set_position_target( 158 | glam::Vec3::new(-2.5, 3.0, -2.5), 159 | glam::Vec3::new(10.0, 1.0, 10.0), 160 | ); 161 | 162 | let model = utopian::model_loader::ModelLoader::load_cube(device); 163 | 164 | renderer.add_model( 165 | device, 166 | model, 167 | glam::Mat4::from_scale_rotation_translation( 168 | glam::Vec3::new(10000.0, 0.1, 10000.0), 169 | glam::Quat::IDENTITY, 170 | glam::Vec3::new(0.0, 0.0, 0.0), 171 | ), 172 | ); 173 | 174 | for x in 0..30 { 175 | for z in 0..10 { 176 | let model = utopian::model_loader::ModelLoader::load_cube(device); 177 | 178 | renderer.add_model( 179 | device, 180 | model, 181 | glam::Mat4::from_scale_rotation_translation( 182 | glam::Vec3::new(1.0, 2.0, 1.0), 183 | glam::Quat::IDENTITY, 184 | glam::Vec3::new(x as f32 * 2.0, 0.0, z as f32 * 2.0), 185 | ), 186 | ); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /prototype/src/ui.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use std::sync::Arc; 3 | use std::sync::Mutex; 4 | 5 | pub struct Ui { 6 | pub egui_integration: 7 | egui_winit_ash_integration::Integration>>, 8 | } 9 | 10 | impl Ui { 11 | pub fn new( 12 | width: u32, 13 | height: u32, 14 | device: &utopian::Device, 15 | swapchain_loader: &ash::extensions::khr::Swapchain, 16 | swapchain: vk::SwapchainKHR, 17 | surface_format: vk::SurfaceFormatKHR, 18 | ) -> Self { 19 | let egui_integration = egui_winit_ash_integration::Integration::new( 20 | width, 21 | height, 22 | 1.0, 23 | egui::FontDefinitions::default(), 24 | egui::Style::default(), 25 | device.handle.clone(), 26 | device.gpu_allocator.clone(), 27 | swapchain_loader.clone(), 28 | swapchain, 29 | surface_format, 30 | ); 31 | 32 | Ui { egui_integration } 33 | } 34 | 35 | pub fn handle_events( 36 | &mut self, 37 | events: Vec, 38 | window_id: winit::window::WindowId, 39 | ) { 40 | // Update egui input 41 | // Note: this is not very pretty since we are recreating a Event from 42 | // an WindowEvent manually. Don't know enough rust to have the `events` 43 | // input of the correct type. 44 | for event in events { 45 | self.egui_integration 46 | .handle_event::>( 47 | &winit::event::Event::WindowEvent { window_id, event }, 48 | ); 49 | } 50 | } 51 | 52 | pub fn begin_frame(&mut self) { 53 | self.egui_integration.begin_frame(); 54 | } 55 | 56 | pub fn end_frame( 57 | &mut self, 58 | command_buffer: vk::CommandBuffer, 59 | present_index: u32, 60 | window: &winit::window::Window, 61 | ) { 62 | self.egui_integration 63 | .context() 64 | .set_visuals(egui::style::Visuals::dark()); 65 | 66 | let (_, shapes) = self.egui_integration.end_frame(window); 67 | let clipped_meshes = self.egui_integration.context().tessellate(shapes); 68 | self.egui_integration.paint( 69 | // This also does the transition of the swapchain image to PRESENT_SRC_KHR 70 | command_buffer, 71 | present_index as usize, 72 | clipped_meshes, 73 | ); 74 | } 75 | } 76 | 77 | pub struct U32Checkbox<'a> { 78 | value: &'a mut u32, 79 | text: &'a str, 80 | } 81 | 82 | impl<'a> U32Checkbox<'a> { 83 | pub fn new(value: &'a mut u32, text: &'a str) -> Self { 84 | U32Checkbox { value, text } 85 | } 86 | } 87 | 88 | impl egui::Widget for U32Checkbox<'_> { 89 | fn ui(self, ui: &mut egui::Ui) -> egui::Response { 90 | let mut bool_value = *self.value == 1; 91 | ui.label(self.text); 92 | let response = ui.add(egui::widgets::Checkbox::new(&mut bool_value, "")); 93 | ui.end_row(); 94 | *self.value = bool_value as u32; 95 | response 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /utopian/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "utopian" 3 | version = "0.0.3" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | winit = "0.26.0" 8 | gpu-allocator = "0.18.0" 9 | ash = { version = "0.37.0", features = ["linked", "debug"]} 10 | vk-sync = { git = "https://github.com/simplerr/vk-sync-rs" } 11 | ash-window = "0.10" 12 | shaderc = "0.7.3" 13 | rspirv-reflect = { git = "https://github.com/simplerr/rspirv-reflect.git" } # "0.6.0" 14 | image = "0.23.14" 15 | glam = "0.20.2" 16 | dolly = "0.2.0" 17 | gltf = "0.16.0" 18 | notify = "4.0.16" 19 | puffin = { git = "https://github.com/simplerr/puffin.git" } 20 | gpu-profiler = { git = "https://github.com/simplerr/gpu-profiler.git", features = ["use-ash"] } 21 | log = "0.4" 22 | -------------------------------------------------------------------------------- /utopian/data/printscreens/printscreen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/printscreens/printscreen1.png -------------------------------------------------------------------------------- /utopian/data/printscreens/printscreen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/printscreens/printscreen2.png -------------------------------------------------------------------------------- /utopian/data/printscreens/printscreen3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/printscreens/printscreen3.jpg -------------------------------------------------------------------------------- /utopian/data/printscreens/printscreen4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/printscreens/printscreen4.jpg -------------------------------------------------------------------------------- /utopian/data/textures/defaults/black_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/textures/defaults/black_texture.png -------------------------------------------------------------------------------- /utopian/data/textures/defaults/checker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/textures/defaults/checker.jpg -------------------------------------------------------------------------------- /utopian/data/textures/defaults/default_metallic_roughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/textures/defaults/default_metallic_roughness.png -------------------------------------------------------------------------------- /utopian/data/textures/defaults/flat_normal_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/textures/defaults/flat_normal_map.png -------------------------------------------------------------------------------- /utopian/data/textures/defaults/white_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simplerr/rust-renderer/bb73e9a6e138f6051f57eae9c49ee5fd2667798d/utopian/data/textures/defaults/white_texture.png -------------------------------------------------------------------------------- /utopian/shaders/atmosphere/atmosphere.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/atmosphere.glsl" 9 | 10 | layout (location = 0) in vec3 in_pos_l; 11 | 12 | layout (location = 0) out vec4 out_color; 13 | 14 | layout (set = 2, binding = 0) uniform samplerCube in_enviroment_map; 15 | 16 | void main() 17 | { 18 | //vec3 rayStart = sharedVariables.eyePos.xyz; 19 | vec3 rayStart = extract_camera_position(view.view); 20 | vec3 rayDir = normalize(in_pos_l); 21 | float rayLength = 999999999.0f; 22 | vec3 sunDir = view.sun_dir; 23 | vec3 lightColor = vec3(1.0f); 24 | 25 | vec3 color = vec3(0.0); 26 | 27 | if (view.cubemap_enabled == 1) { 28 | color = textureLod(in_enviroment_map, rayDir * vec3(1, -1, 1), 2).rgb; 29 | } 30 | else { 31 | vec3 transmittance; 32 | color = IntegrateScattering(rayStart, rayDir, rayLength, sunDir, lightColor, transmittance); 33 | } 34 | 35 | out_color = vec4(vec3(color), 1.0f); 36 | } 37 | -------------------------------------------------------------------------------- /utopian/shaders/atmosphere/atmosphere.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | 9 | layout (location = 0) in vec4 pos; 10 | layout (location = 1) in vec4 normal; 11 | layout (location = 2) in vec2 uv; 12 | layout (location = 3) in vec4 color; 13 | layout (location = 4) in vec4 tangent; 14 | 15 | layout (set = 3, binding = 0) uniform UBO_constants 16 | { 17 | mat4 projection; 18 | mat4 world; 19 | } ubo_constants; 20 | 21 | // layout(push_constant) uniform PushConsts { 22 | // mat4 view; 23 | // } pushConsts; 24 | 25 | layout (location = 0) out vec3 out_pos_l; 26 | 27 | out gl_PerVertex 28 | { 29 | vec4 gl_Position; 30 | }; 31 | 32 | void main() 33 | { 34 | out_pos_l = pos.xyz; 35 | 36 | // Removes the translation components of the matrix to always keep the skybox at the same distance 37 | //mat4 viewNoTranslation = mat4(mat3(pushConsts.view)); 38 | mat4 viewNoTranslation = mat4(mat3(view.view)); 39 | gl_Position = ubo_constants.projection * viewNoTranslation * ubo_constants.world * vec4(pos.xyz, 1.0); 40 | } 41 | -------------------------------------------------------------------------------- /utopian/shaders/blit/blit.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/pbr_lighting.glsl" 9 | 10 | layout (location = 0) in vec2 in_uv; 11 | 12 | layout (location = 0) out vec4 out_color; 13 | 14 | layout (set = 2, binding = 0) uniform sampler2D in_texture; 15 | 16 | void main() 17 | { 18 | vec2 uv = FLIP_UV_Y(in_uv); 19 | out_color = vec4(texture(in_texture, uv).rgb, 1.0); 20 | 21 | if (uv.x > 0.95 && uv.y > 0.95) { 22 | out_color = vec4(1, 1, 0, 1); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /utopian/shaders/common/fullscreen.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout (location = 0) out vec2 outUV; 4 | 5 | out gl_PerVertex 6 | { 7 | vec4 gl_Position; 8 | }; 9 | 10 | void main() 11 | { 12 | outUV = vec2(gl_VertexIndex & 2, (gl_VertexIndex << 1) & 2); 13 | gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f); 14 | } 15 | -------------------------------------------------------------------------------- /utopian/shaders/deferred/deferred.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/pbr_lighting.glsl" 9 | 10 | layout (location = 0) in vec2 in_uv; 11 | 12 | layout (location = 0) out vec4 out_color; 13 | 14 | layout (set = 2, binding = 0) uniform sampler2D in_gbuffer_position; 15 | layout (set = 2, binding = 1) uniform sampler2D in_gbuffer_normal; 16 | layout (set = 2, binding = 2) uniform sampler2D in_gbuffer_albedo; 17 | layout (set = 2, binding = 3) uniform sampler2D in_gbuffer_pbr; 18 | layout (set = 2, binding = 4) uniform sampler2DArray in_shadow_map; 19 | layout (set = 2, binding = 5) uniform sampler2D in_rt_shadows; 20 | layout (set = 2, binding = 6) uniform sampler2D in_rt_reflections; 21 | layout (set = 2, binding = 7) uniform sampler2D in_ssao; 22 | layout (set = 2, binding = 8) uniform samplerCube in_irradiance_map; 23 | layout (set = 2, binding = 9) uniform samplerCube in_specular_map; 24 | layout (set = 2, binding = 10) uniform sampler2D in_brdf_lut; 25 | 26 | // Todo: set=2 should be dedicated to input textures but the shader reflection 27 | // does not support gaps in the descriptor sets 28 | layout (std140, set = 3, binding = 0) uniform UBO_shadowmapParams 29 | { 30 | mat4 view_projection_matrices[4]; 31 | vec4 cascade_splits; 32 | } shadowmapParams; 33 | 34 | #include "include/shadow_mapping.glsl" 35 | 36 | layout(push_constant) uniform PushConsts { 37 | mat4 world; 38 | vec4 color; 39 | uint mesh_index; 40 | ivec3 pad; 41 | } pushConsts; 42 | 43 | void main() { 44 | vec2 uv = FLIP_UV_Y(in_uv); 45 | 46 | uint material_index = uint(texture(in_gbuffer_pbr, uv).a); 47 | Material material = materialsSSBO.materials[material_index]; 48 | 49 | vec3 position = texture(in_gbuffer_position, uv).rgb; 50 | vec3 normal = texture(in_gbuffer_normal, uv).rgb; 51 | vec3 diffuse_color = texture(in_gbuffer_albedo, uv).rgb; 52 | float metallic = texture(in_gbuffer_pbr, uv).r; 53 | float roughness = texture(in_gbuffer_pbr, uv).g; 54 | float occlusion = texture(in_gbuffer_pbr, uv).b; 55 | float ssao = texture(in_ssao, in_uv).r; 56 | 57 | roughness *= material.roughness_factor; 58 | metallic *= material.metallic_factor; 59 | 60 | // From sRGB space to Linear space 61 | diffuse_color.rgb = pow(diffuse_color.rgb, vec3(2.2)); 62 | 63 | PixelParams pixel; 64 | pixel.position = position; 65 | pixel.baseColor = diffuse_color.rgb * material.base_color_factor.rgb; 66 | pixel.normal = normal; 67 | pixel.metallic = metallic; 68 | pixel.roughness = roughness; 69 | pixel.occlusion = occlusion; 70 | 71 | /* Direct lighting */ 72 | vec3 Lo = vec3(0.0); 73 | 74 | Light sun_light = Light(vec4(1.0f), vec3(0.0f), 0.0f, view.sun_dir * vec3(-1, 1, -1), 0.0f, vec3(1.0), 0.0f, vec3(0.0f), 0.0f, vec4(0.0f)); 75 | Lo += surfaceShading(pixel, sun_light, view.eye_pos.xyz, 1.0f); 76 | 77 | for (int i = 0; i < view.num_lights; i++) 78 | { 79 | Lo += surfaceShading(pixel, lightsSSBO.lights[i], view.eye_pos.xyz, 1.0f); 80 | } 81 | 82 | 83 | vec3 ambient = vec3(0.03) * diffuse_color.rgb * occlusion; 84 | 85 | if (view.ibl_enabled == 1) 86 | { 87 | ambient = imageBasedLighting(pixel, view.eye_pos.xyz, in_irradiance_map, in_specular_map, in_brdf_lut); 88 | } 89 | 90 | vec3 color = ambient + Lo; 91 | 92 | if (view.raytracing_supported == 1 && material.raytrace_properties.x == 1) { 93 | vec3 reflectedColor = texture(in_rt_reflections, uv).rgb; 94 | color = mix(color, reflectedColor, 1.0); 95 | } 96 | 97 | // Shadow 98 | if (view.shadows_enabled == 1) { 99 | uint cascadeIndex = 0; 100 | float shadow = calculateShadow(position, cascadeIndex); 101 | color = color * shadow; 102 | 103 | //#define CASCADE_DEBUG 104 | #ifdef CASCADE_DEBUG 105 | color.rgb *= cascade_index_to_debug_color(cascadeIndex); 106 | #endif 107 | } 108 | else if (view.raytracing_supported == 1) { 109 | float shadow = texture(in_rt_shadows, uv).r; 110 | color = color * max(shadow, 0.3); 111 | } 112 | 113 | if (view.ssao_enabled == 1) { 114 | color *= ssao; 115 | } 116 | 117 | out_color = vec4(color, 1.0f); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /utopian/shaders/forward/forward.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/pbr_lighting.glsl" 9 | 10 | layout (location = 0) in vec3 in_pos; 11 | layout (location = 1) in vec2 in_uv; 12 | layout (location = 2) in vec3 in_normal; 13 | layout (location = 3) in vec4 in_color; 14 | layout (location = 4) in vec4 in_tangent; 15 | layout (location = 5) in mat3 in_tbn; 16 | 17 | layout (location = 0) out vec4 out_color; 18 | 19 | layout (set = 2, binding = 0) uniform sampler2DArray in_shadow_map; 20 | 21 | // Todo: set=2 should be dedicated to input textures but the shader reflection 22 | // does not support gaps in the descriptor sets 23 | layout (std140, set = 3, binding = 0) uniform UBO_shadowmapParams 24 | { 25 | mat4 view_projection_matrices[4]; 26 | vec4 cascade_splits; 27 | } shadowmapParams; 28 | 29 | #include "include/shadow_mapping.glsl" 30 | 31 | layout(push_constant) uniform PushConsts { 32 | mat4 world; 33 | vec4 color; 34 | uint mesh_index; 35 | ivec3 pad; 36 | } pushConsts; 37 | 38 | void main() { 39 | Mesh mesh = meshesSSBO.meshes[pushConsts.mesh_index]; 40 | Material material = materialsSSBO.materials[mesh.material]; 41 | 42 | vec4 diffuse_color = texture(samplerColor[material.diffuse_map], in_uv); 43 | vec4 normal_map = texture(samplerColor[material.normal_map], in_uv); 44 | float metallic = texture(samplerColor[material.metallic_roughness_map], in_uv).b; 45 | float roughness = texture(samplerColor[material.metallic_roughness_map], in_uv).g; 46 | float occlusion = texture(samplerColor[material.occlusion_map], in_uv).r; 47 | 48 | // From sRGB space to Linear space 49 | diffuse_color.rgb = pow(diffuse_color.rgb, vec3(2.2)); 50 | 51 | vec3 normal = normalize(in_normal); 52 | if (in_tangent.xyz != vec3(0.0f)) 53 | { 54 | normal = normalize(normal_map.xyz * 2.0 - 1.0); 55 | normal = normalize(in_tbn * normal); 56 | } 57 | 58 | PixelParams pixel; 59 | pixel.position = in_pos; 60 | pixel.baseColor = diffuse_color.rgb * material.base_color_factor.rgb; 61 | pixel.normal = normal; 62 | pixel.metallic = metallic; 63 | pixel.roughness = roughness; 64 | pixel.occlusion = occlusion; 65 | 66 | /* Direct lighting */ 67 | vec3 Lo = vec3(0.0); 68 | 69 | Light sun_light = Light(vec4(1.0f), vec3(0.0f), 0.0f, view.sun_dir * vec3(-1, 1, -1), 0.0f, vec3(1.0), 0.0f, vec3(0.0f), 0.0f, vec4(0.0f)); 70 | Lo += surfaceShading(pixel, sun_light, view.eye_pos.xyz, 1.0f); 71 | 72 | for (int i = 0; i < view.num_lights; i++) 73 | { 74 | Lo += surfaceShading(pixel, lightsSSBO.lights[i], view.eye_pos.xyz, 1.0f); 75 | } 76 | 77 | // Todo: IBL 78 | vec3 ambient = vec3(0.03) * diffuse_color.rgb * occlusion; 79 | vec3 color = ambient + Lo; 80 | 81 | // Shadow 82 | if (view.shadows_enabled == 1) { 83 | uint cascadeIndex = 0; 84 | float shadow = calculateShadow(in_pos, cascadeIndex); 85 | color = color * shadow; 86 | 87 | //#define CASCADE_DEBUG 88 | #ifdef CASCADE_DEBUG 89 | color.rgb *= cascade_index_to_debug_color(cascadeIndex); 90 | #endif 91 | } 92 | 93 | 94 | out_color = vec4(color, 1.0f); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /utopian/shaders/forward/forward.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | 9 | layout (location = 0) in vec4 pos; 10 | layout (location = 1) in vec4 normal; 11 | layout (location = 2) in vec2 uv; 12 | layout (location = 3) in vec4 color; 13 | layout (location = 4) in vec4 tangent; 14 | 15 | layout (location = 0) out vec3 out_pos; 16 | layout (location = 1) out vec2 out_uv; 17 | layout (location = 2) out vec3 out_normal; 18 | layout (location = 3) out vec4 out_color; 19 | layout (location = 4) out vec4 out_tangent; 20 | layout (location = 5) out mat3 out_tbn; 21 | 22 | layout(push_constant) uniform PushConsts { 23 | mat4 world; 24 | vec4 color; 25 | uint mesh_index; 26 | ivec3 pad; 27 | } pushConsts; 28 | 29 | void main() { 30 | Mesh mesh = meshesSSBO.meshes[pushConsts.mesh_index]; 31 | Vertex vertex = verticesSSBO[mesh.vertex_buffer].vertices[gl_VertexIndex]; 32 | 33 | //#define BINDLESS 34 | #ifdef BINDLESS 35 | vec3 bitangentL = cross(vertex.normal.xyz, vertex.tangent.xyz); 36 | vec3 T = normalize(mat3(pushConsts.world) * vertex.tangent.xyz); 37 | vec3 B = normalize(mat3(pushConsts.world) * bitangentL); 38 | vec3 N = normalize(mat3(pushConsts.world) * vertex.normal.xyz); 39 | out_tbn = mat3(T, B, N); 40 | 41 | out_pos = (pushConsts.world * vec4(vertex.pos.xyz, 1.0)).xyz; 42 | out_uv = vertex.uv; 43 | out_color = vertex.color; 44 | out_normal = mat3(transpose(inverse(pushConsts.world))) * vertex.normal.xyz; 45 | out_tangent = vertex.tangent; 46 | gl_Position = view.projection * view.view * pushConsts.world * vec4(vertex.pos.xyz, 1.0); 47 | #else 48 | vec3 bitangentL = cross(normal.xyz, tangent.xyz); 49 | vec3 T = normalize(mat3(pushConsts.world) * tangent.xyz); 50 | vec3 B = normalize(mat3(pushConsts.world) * bitangentL); 51 | vec3 N = normalize(mat3(pushConsts.world) * normal.xyz); 52 | out_tbn = mat3(T, B, N); 53 | 54 | out_pos = (pushConsts.world * vec4(pos.xyz, 1.0)).xyz; 55 | out_uv = uv; 56 | out_color = color; 57 | out_normal = mat3(transpose(inverse(pushConsts.world))) * normal.xyz; 58 | out_tangent = tangent; 59 | gl_Position = view.projection * view.view * pushConsts.world * vec4(pos.xyz, 1.0); 60 | #endif 61 | 62 | } 63 | -------------------------------------------------------------------------------- /utopian/shaders/gbuffer/gbuffer.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_GOOGLE_include_directive : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | 7 | layout (location = 0) in vec3 in_pos; 8 | layout (location = 1) in vec2 in_uv; 9 | layout (location = 2) in vec3 in_normal; 10 | layout (location = 3) in vec4 in_color; 11 | layout (location = 4) in vec4 in_tangent; 12 | layout (location = 5) in mat3 in_tbn; 13 | 14 | layout (location = 0) out vec4 out_gbuffer_position; 15 | layout (location = 1) out vec4 out_gbuffer_normal; 16 | layout (location = 2) out vec4 out_gbuffer_albedo; 17 | layout (location = 3) out vec4 out_gbuffer_pbr; 18 | 19 | layout(push_constant) uniform PushConsts { 20 | mat4 world; 21 | vec4 color; 22 | uint mesh_index; 23 | ivec3 pad; 24 | } pushConsts; 25 | 26 | void main() 27 | { 28 | Mesh mesh = meshesSSBO.meshes[pushConsts.mesh_index]; 29 | Material material = materialsSSBO.materials[mesh.material]; 30 | 31 | vec4 diffuse_color = texture(samplerColor[material.diffuse_map], in_uv); 32 | vec4 normal_map = texture(samplerColor[material.normal_map], in_uv); 33 | float metallic = texture(samplerColor[material.metallic_roughness_map], in_uv).b; 34 | float roughness = texture(samplerColor[material.metallic_roughness_map], in_uv).g; 35 | float occlusion = texture(samplerColor[material.occlusion_map], in_uv).r; 36 | 37 | // From sRGB space to Linear space 38 | //diffuse_color.rgb = pow(diffuse_color.rgb, vec3(2.2)); 39 | 40 | vec3 normal = normalize(in_normal); 41 | if (in_tangent.xyz != vec3(0.0f)) 42 | { 43 | normal = normalize(normal_map.xyz * 2.0 - 1.0); 44 | normal = normalize(in_tbn * normal); 45 | } 46 | 47 | out_gbuffer_position = vec4(in_pos, 1.0); 48 | out_gbuffer_normal = vec4(normal, 1.0); 49 | out_gbuffer_albedo = vec4(diffuse_color.rgb , 1.0); 50 | out_gbuffer_pbr = vec4(metallic, roughness, occlusion, mesh.material); 51 | } 52 | -------------------------------------------------------------------------------- /utopian/shaders/gbuffer/gbuffer.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | 9 | layout (location = 0) in vec4 pos; 10 | layout (location = 1) in vec4 normal; 11 | layout (location = 2) in vec2 uv; 12 | layout (location = 3) in vec4 color; 13 | layout (location = 4) in vec4 tangent; 14 | 15 | layout (location = 0) out vec3 out_pos; 16 | layout (location = 1) out vec2 out_uv; 17 | layout (location = 2) out vec3 out_normal; 18 | layout (location = 3) out vec4 out_color; 19 | layout (location = 4) out vec4 out_tangent; 20 | layout (location = 5) out mat3 out_tbn; 21 | 22 | layout(push_constant) uniform PushConsts { 23 | mat4 world; 24 | vec4 color; 25 | uint mesh_index; 26 | ivec3 pad; 27 | } pushConsts; 28 | 29 | void main() { 30 | Mesh mesh = meshesSSBO.meshes[pushConsts.mesh_index]; 31 | Vertex vertex = verticesSSBO[mesh.vertex_buffer].vertices[gl_VertexIndex]; 32 | 33 | #define BINDLESS 34 | #ifdef BINDLESS 35 | vec3 bitangentL = cross(vertex.normal.xyz, vertex.tangent.xyz); 36 | vec3 T = normalize(mat3(pushConsts.world) * vertex.tangent.xyz); 37 | vec3 B = normalize(mat3(pushConsts.world) * bitangentL); 38 | vec3 N = normalize(mat3(pushConsts.world) * vertex.normal.xyz); 39 | out_tbn = mat3(T, B, N); 40 | 41 | out_pos = (pushConsts.world * vec4(vertex.pos.xyz, 1.0)).xyz; 42 | out_uv = vertex.uv; 43 | out_color = vertex.color; 44 | out_normal = mat3(transpose(inverse(pushConsts.world))) * vertex.normal.xyz; 45 | out_tangent = vertex.tangent; 46 | gl_Position = view.projection * view.view * pushConsts.world * vec4(vertex.pos.xyz, 1.0); 47 | #else 48 | vec3 bitangentL = cross(normal.xyz, tangent.xyz); 49 | vec3 T = normalize(mat3(pushConsts.world) * tangent.xyz); 50 | vec3 B = normalize(mat3(pushConsts.world) * bitangentL); 51 | vec3 N = normalize(mat3(pushConsts.world) * normal.xyz); 52 | out_tbn = mat3(T, B, N); 53 | 54 | out_pos = (pushConsts.world * vec4(pos.xyz, 1.0)).xyz; 55 | out_uv = uv; 56 | out_color = color; 57 | out_normal = mat3(transpose(inverse(pushConsts.world))) * normal.xyz; 58 | out_tangent = tangent; 59 | gl_Position = view.projection * view.view * pushConsts.world * vec4(pos.xyz, 1.0); 60 | #endif 61 | 62 | } 63 | -------------------------------------------------------------------------------- /utopian/shaders/ibl/brdf_lut.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/brdf.glsl" 9 | 10 | layout (location = 0) in vec2 in_uv; 11 | 12 | layout (location = 0) out vec4 out_color; 13 | 14 | // From the filament docs. Geometric Shadowing function 15 | // https://google.github.io/filament/Filament.html#toc4.4.2 16 | float V_SmithGGXCorrelated(float NoV, float NoL, float roughness) { 17 | float a2 = pow(roughness, 4.0); 18 | float GGXV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2); 19 | float GGXL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2); 20 | return 0.5 / (GGXV + GGXL); 21 | } 22 | 23 | vec2 integrateBRDF(float roughness, float NoV) 24 | { 25 | vec3 V; 26 | V.x = sqrt(1.0 - NoV * NoV); // sin 27 | V.y = 0.0; 28 | V.z = NoV; // cos 29 | 30 | // N points straight upwards for this integration 31 | const vec3 N = vec3(0.0, 0.0, 1.0); 32 | 33 | float A = 0.0; 34 | float B = 0.0; 35 | const uint numSamples = 1024; 36 | 37 | for (uint i = 0u; i < numSamples; i++) { 38 | vec2 Xi = hammersley2d(i, numSamples); 39 | // Sample microfacet direction 40 | vec3 H = importanceSample_GGX(Xi, roughness, N); 41 | 42 | // Get the light direction 43 | vec3 L = 2.0 * dot(V, H) * H - V; 44 | 45 | float NoL = clamp(dot(N, L), 0.0, 1.0); 46 | float NoH = clamp(dot(N, H), 0.0, 1.0); 47 | float VoH = clamp(dot(V, H), 0.0, 1.0); 48 | 49 | if (NoL > 0.0) { 50 | // Terms besides V are from the GGX PDF we're dividing by 51 | float V_pdf = V_SmithGGXCorrelated(NoV, NoL, roughness) * VoH * NoL / NoH; 52 | float Fc = pow(1.0 - VoH, 5.0); 53 | A += (1.0 - Fc) * V_pdf; 54 | B += Fc * V_pdf; 55 | } 56 | } 57 | 58 | return 4.0 * vec2(A, B) / float(numSamples); 59 | } 60 | 61 | // Based on https://bruop.github.io/ibl/ 62 | void main() 63 | { 64 | vec2 res = integrateBRDF(in_uv.y, in_uv.x); 65 | 66 | out_color = vec4(res, 0.0, 0.0); 67 | } -------------------------------------------------------------------------------- /utopian/shaders/ibl/cubemap.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/atmosphere.glsl" 9 | 10 | layout (location = 0) in vec2 in_uv; 11 | 12 | layout (location = 0) out vec4 out_color; 13 | 14 | layout (std140, set = 2, binding = 0) uniform UBO_parameters 15 | { 16 | mat4 view; 17 | mat4 projection; 18 | } params; 19 | 20 | void main() 21 | { 22 | vec3 worldDir = world_dir_from_uv(in_uv, params.view, params.projection); 23 | 24 | vec3 rayStart = extract_camera_position(view.view); 25 | vec3 rayDir = worldDir; 26 | float rayLength = 999999999.0f; 27 | vec3 sunDir = view.sun_dir; 28 | vec3 lightColor = vec3(1.0f); 29 | 30 | vec3 transmittance; 31 | vec3 color = IntegrateScattering(rayStart, rayDir, rayLength, sunDir, lightColor, transmittance); 32 | 33 | // For testing filtering: 34 | // color = sin((rayDir * 0.5 + 0.5) * 60.0); 35 | 36 | // color = vec3(in_uv, 0.0); 37 | // color = worldDir; 38 | 39 | out_color = vec4(vec3(color), 1.0f); 40 | } 41 | -------------------------------------------------------------------------------- /utopian/shaders/ibl/fullscreen_with_pushconst.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // Todo: only supports push constants with vk::ShaderStageFlags::ALL currently 4 | // and this is used by specular_filter.frag 5 | layout(push_constant) uniform PushConsts { 6 | float roughness; 7 | } pushConsts; 8 | 9 | layout (location = 0) out vec2 outUV; 10 | 11 | out gl_PerVertex 12 | { 13 | vec4 gl_Position; 14 | }; 15 | 16 | void main() 17 | { 18 | float todo = pushConsts.roughness; 19 | outUV = vec2(gl_VertexIndex & 2, (gl_VertexIndex << 1) & 2); 20 | gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f); 21 | } 22 | -------------------------------------------------------------------------------- /utopian/shaders/ibl/irradiance_filter.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | 9 | layout (location = 0) in vec2 in_uv; 10 | 11 | layout (location = 0) out vec4 out_color; 12 | 13 | layout (set = 2, binding = 0) uniform samplerCube in_enviroment_map; 14 | 15 | layout (std140, set = 3, binding = 0) uniform UBO_parameters 16 | { 17 | mat4 view; 18 | mat4 projection; 19 | } params; 20 | 21 | #define PI 3.1415926535897932384626433832795 22 | 23 | // Generates an irradiance cube from an environment map using convolution 24 | // Source: https://learnopengl.com/PBR/IBL/Diffuse-irradiance 25 | void main() 26 | { 27 | vec3 irradiance = vec3(0.0); 28 | 29 | vec3 normal = world_dir_from_uv(in_uv, params.view, params.projection); 30 | vec3 up = vec3(0.0, 1.0, 0.0); 31 | vec3 right = normalize(cross(up, normal)); 32 | up = normalize(cross(normal, right)); 33 | 34 | float sampleDelta = 0.025; 35 | float nrSamples = 0.0; 36 | for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta) 37 | { 38 | for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) 39 | { 40 | // Spherical to cartesian (in tangent space) 41 | vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); 42 | // Tangent space to world 43 | vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal; 44 | 45 | irradiance += texture(in_enviroment_map, sampleVec).rgb * cos(theta) * sin(theta); 46 | nrSamples++; 47 | } 48 | } 49 | irradiance = PI * irradiance * (1.0 / float(nrSamples)); 50 | 51 | out_color = vec4(irradiance, 1.0f); 52 | } 53 | -------------------------------------------------------------------------------- /utopian/shaders/ibl/specular_filter.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/brdf.glsl" 9 | 10 | layout (location = 0) in vec2 in_uv; 11 | 12 | layout (location = 0) out vec4 out_color; 13 | 14 | layout (set = 2, binding = 0) uniform samplerCube in_enviroment_map; 15 | 16 | // Todo: could be push constants instead 17 | layout (std140, set = 3, binding = 0) uniform UBO_parameters 18 | { 19 | mat4 view; 20 | mat4 projection; 21 | } params; 22 | 23 | layout(push_constant) uniform PushConsts { 24 | float roughness; 25 | } pushConsts; 26 | 27 | #define PI 3.1415926535897932384626433832795 28 | 29 | // Normal Distribution function 30 | float D_GGX(float dotNH, float roughness) 31 | { 32 | float alpha = roughness * roughness; 33 | float alpha2 = alpha * alpha; 34 | float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; 35 | return (alpha2)/(PI * denom*denom); 36 | } 37 | 38 | // Generates an prefiltered specular cube from an environment map using convolution 39 | // Source: https://github.com/SaschaWillems/Vulkan/blob/master/data/shaders/glsl/pbrtexture/prefilterenvmap.frag 40 | vec3 prefilterEnvMap(vec3 R, float roughness) 41 | { 42 | vec3 N = R; 43 | vec3 V = R; 44 | vec3 color = vec3(0.0); 45 | float totalWeight = 0.0; 46 | float envMapDim = float(textureSize(in_enviroment_map, 0).s); 47 | const int numSamples = 32; 48 | for(uint i = 0u; i < numSamples; i++) { 49 | vec2 Xi = hammersley2d(i, numSamples); 50 | vec3 H = importanceSample_GGX(Xi, roughness, N); 51 | vec3 L = 2.0 * dot(V, H) * H - V; 52 | float dotNL = clamp(dot(N, L), 0.0, 1.0); 53 | if(dotNL > 0.0) { 54 | // Filtering based on https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/ 55 | 56 | float dotNH = clamp(dot(N, H), 0.0, 1.0); 57 | float dotVH = clamp(dot(V, H), 0.0, 1.0); 58 | 59 | // Probability Distribution Function 60 | float pdf = D_GGX(dotNH, roughness) * dotNH / (4.0 * dotVH) + 0.0001; 61 | // Slid angle of current smple 62 | float omegaS = 1.0 / (float(numSamples) * pdf); 63 | // Solid angle of 1 pixel across all cube faces 64 | float omegaP = 4.0 * PI / (6.0 * envMapDim * envMapDim); 65 | // Biased (+1.0) mip level for better result 66 | float mipLevel = roughness == 0.0 ? 0.0 : max(0.5 * log2(omegaS / omegaP) + 1.0, 0.0f); 67 | color += textureLod(in_enviroment_map, L, mipLevel).rgb * dotNL; 68 | totalWeight += dotNL; 69 | 70 | } 71 | } 72 | 73 | return (color / totalWeight); 74 | } 75 | 76 | void main() 77 | { 78 | vec3 normal = world_dir_from_uv(in_uv, params.view, params.projection); 79 | out_color = vec4(prefilterEnvMap(normal, pushConsts.roughness), 1.0); 80 | } -------------------------------------------------------------------------------- /utopian/shaders/include/bindless.glsl: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_scalar_block_layout : enable 2 | #extension GL_EXT_nonuniform_qualifier : enable 3 | 4 | struct Vertex 5 | { 6 | vec4 pos; 7 | vec4 normal; 8 | vec2 uv; 9 | vec4 color; 10 | vec4 tangent; 11 | }; 12 | 13 | struct Material 14 | { 15 | uint diffuse_map; 16 | uint normal_map; 17 | uint metallic_roughness_map; 18 | uint occlusion_map; 19 | vec4 base_color_factor; 20 | float metallic_factor; 21 | float roughness_factor; 22 | vec2 padding; 23 | 24 | // Ray tracing properties 25 | // x = type (0 = lambertian, 1 = metal, 2 = dielectric, 3 = diffuse light) 26 | // y = metal -> fuzz, dielectric -> index of refractions 27 | vec4 raytrace_properties; 28 | }; 29 | 30 | struct Mesh 31 | { 32 | uint vertex_buffer; 33 | uint index_buffer; 34 | uint material; 35 | }; 36 | 37 | struct Light 38 | { 39 | vec4 color; 40 | vec3 pos; 41 | float range; 42 | vec3 dir; 43 | float spot; 44 | vec3 att; 45 | float type; 46 | vec3 intensity; 47 | float id; 48 | vec4 pad; 49 | }; 50 | 51 | layout (set = 0, binding = 0) uniform sampler2D samplerColor[]; 52 | 53 | layout (std430, set = 0, binding = 1) readonly buffer VerticesSSBO 54 | { 55 | Vertex vertices[]; 56 | } verticesSSBO[]; 57 | 58 | layout (scalar, set = 0, binding = 2) readonly buffer IndicesSSBO 59 | { 60 | ivec3 indices[]; 61 | } indicesSSBO[]; 62 | 63 | layout (scalar, set = 0, binding = 3) readonly buffer MaterialsSSBO 64 | { 65 | Material materials[]; 66 | } materialsSSBO; 67 | 68 | layout (scalar, set = 0, binding = 4) readonly buffer MeshesSSBO 69 | { 70 | Mesh meshes[]; 71 | } meshesSSBO; 72 | 73 | layout (scalar, set = 0, binding = 5) readonly buffer LightsSSBO 74 | { 75 | Light lights[]; 76 | } lightsSSBO; 77 | 78 | -------------------------------------------------------------------------------- /utopian/shaders/include/brdf.glsl: -------------------------------------------------------------------------------- 1 | const float PI = 3.14159265359; 2 | 3 | float DistributionGGX(vec3 N, vec3 H, float roughness) 4 | { 5 | float a = roughness*roughness; 6 | float a2 = a*a; 7 | float NdotH = max(dot(N, H), 0.0); 8 | float NdotH2 = NdotH*NdotH; 9 | 10 | float num = a2; 11 | float denom = (NdotH2 * (a2 - 1.0) + 1.0); 12 | denom = PI * denom * denom; 13 | 14 | return num / denom; 15 | } 16 | 17 | float GeometrySchlickGGX(float NdotV, float roughness) 18 | { 19 | float r = (roughness + 1.0); 20 | float k = (r*r) / 8.0; 21 | 22 | float num = NdotV; 23 | float denom = NdotV * (1.0 - k) + k; 24 | 25 | return num / denom; 26 | } 27 | 28 | float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) 29 | { 30 | float NdotV = max(dot(N, V), 0.0); 31 | float NdotL = max(dot(N, L), 0.0); 32 | float ggx2 = GeometrySchlickGGX(NdotV, roughness); 33 | float ggx1 = GeometrySchlickGGX(NdotL, roughness); 34 | 35 | return ggx1 * ggx2; 36 | } 37 | 38 | // Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/ 39 | float random(vec2 co) 40 | { 41 | float a = 12.9898; 42 | float b = 78.233; 43 | float c = 43758.5453; 44 | float dt= dot(co.xy ,vec2(a,b)); 45 | float sn= mod(dt,3.14); 46 | return fract(sin(sn) * c); 47 | } 48 | 49 | // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html 50 | vec2 hammersley2d(uint i, uint N) 51 | { 52 | uint bits = (i << 16u) | (i >> 16u); 53 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 54 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 55 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 56 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 57 | float rdi = float(bits) * 2.3283064365386963e-10; 58 | return vec2(float(i) /float(N), rdi); 59 | } 60 | 61 | // Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf 62 | // https://github.com/SaschaWillems/Vulkan-glTF-PBR/blob/master/data/shaders/genbrdflut.frag 63 | vec3 importanceSample_GGX(vec2 Xi, float roughness, vec3 normal) 64 | { 65 | // Maps a 2D point to a hemisphere with spread based on roughness 66 | float alpha = roughness * roughness; 67 | float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1; 68 | float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y)); 69 | float sinTheta = sqrt(1.0 - cosTheta * cosTheta); 70 | vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); 71 | 72 | // Tangent space 73 | vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); 74 | vec3 tangentX = normalize(cross(up, normal)); 75 | vec3 tangentY = normalize(cross(normal, tangentX)); 76 | 77 | // Convert to world Space 78 | return normalize(tangentX * H.x + tangentY * H.y + normal * H.z); 79 | } 80 | 81 | 82 | vec3 fresnelSchlick(float cosTheta, vec3 F0) 83 | { 84 | return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); 85 | } 86 | 87 | vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) 88 | { 89 | return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); 90 | } 91 | 92 | -------------------------------------------------------------------------------- /utopian/shaders/include/pbr_lighting.glsl: -------------------------------------------------------------------------------- 1 | #extension GL_GOOGLE_include_directive : enable 2 | 3 | // PBR code reused from https://github.com/simplerr/UtopianEngine 4 | 5 | #include "include/brdf.glsl" 6 | 7 | 8 | 9 | struct PixelParams 10 | { 11 | vec3 position; 12 | vec3 baseColor; 13 | vec3 normal; 14 | vec3 F0; 15 | float metallic; 16 | float roughness; 17 | float occlusion; 18 | }; 19 | 20 | vec3 surfaceShading(const PixelParams pixel, const Light light, const vec3 eyePos, float lightColorFactor) 21 | { 22 | vec3 color = vec3(0.0f); 23 | 24 | /* Implementation from https://learnopengl.com/PBR/Theory */ 25 | vec3 N = pixel.normal; 26 | vec3 V = normalize(eyePos - pixel.position); 27 | vec3 R = reflect(V, N); 28 | 29 | vec3 F0 = vec3(0.04); 30 | F0 = mix(F0, pixel.baseColor, pixel.metallic); 31 | 32 | vec3 L = vec3(0.0); 33 | float attenuation = 1.0f; 34 | vec3 posToLight = light.pos - pixel.position; 35 | 36 | if(light.type == 0.0f) // Directional light 37 | { 38 | L = normalize(light.dir * vec3(-1,1,-1)); 39 | attenuation = 1.0f; 40 | } 41 | else if(light.type == 1.0f) // Point light 42 | { 43 | L = normalize(posToLight); 44 | float d = length(posToLight); 45 | attenuation = 1.0f / dot(light.att, vec3(1.0f, d, d*d)); 46 | } 47 | else if(light.type == 2.0f) // Spot light 48 | { 49 | L = normalize(posToLight); 50 | float d = length(posToLight); 51 | float spot = pow(max(dot(L, normalize(light.dir)), 0.0f), light.spot); 52 | attenuation = spot / dot(light.att, vec3(1.0f, d, d*d)); 53 | } 54 | 55 | // Reflectance equation 56 | vec3 Lo = vec3(0.0); 57 | 58 | vec3 H = normalize(V + L); 59 | vec3 radiance = light.color.rgb * attenuation * lightColorFactor; 60 | 61 | // Cook-torrance brdf 62 | float NDF = DistributionGGX(N, H, pixel.roughness); 63 | float G = GeometrySmith(N, V, L, pixel.roughness); 64 | vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); 65 | 66 | vec3 kS = F; 67 | vec3 kD = vec3(1.0) - kS; 68 | kD *= 1.0 - pixel.metallic; 69 | 70 | vec3 numerator = NDF * G * F; 71 | float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; 72 | vec3 specular = numerator / denominator; 73 | 74 | // Add to outgoing radiance Lo 75 | float NdotL = max(dot(N, L), 0.0); 76 | color = (kD * pixel.baseColor / PI + specular) * radiance * NdotL; 77 | 78 | return color; 79 | } 80 | 81 | vec3 imageBasedLighting(const PixelParams pixel, const vec3 eyePos, samplerCube in_irradiance_map, 82 | samplerCube in_specular_map, sampler2D in_brdf_lut) 83 | { 84 | vec3 V = normalize(eyePos - pixel.position); 85 | vec3 R = -reflect(V, pixel.normal); // Note: -1 indicates that the specular cubemp not being as expected 86 | 87 | vec3 F0 = vec3(0.04); 88 | F0 = mix(F0, pixel.baseColor, pixel.metallic); 89 | 90 | vec3 F = fresnelSchlickRoughness(max(dot(pixel.normal, V), 0.0), F0, pixel.roughness); 91 | vec3 kS = F; 92 | vec3 kD = 1.0 - kS; 93 | kD *= 1.0 - pixel.metallic; 94 | 95 | vec3 irradiance = texture(in_irradiance_map, pixel.normal).rgb; 96 | vec3 diffuse = irradiance * pixel.baseColor; 97 | 98 | // Sample both the pre-filter map and the BRDF lut and combine them together as per the Split-Sum approximation to get the IBL specular part. 99 | // Note: 1 - roughness, same as Vulkan-glTF-PBR but differs from LearnOpenGL 100 | const float MAX_REFLECTION_LOD = 7.0; 101 | vec3 prefilteredColor = textureLod(in_specular_map, R, pixel.roughness * MAX_REFLECTION_LOD).rgb; 102 | vec2 brdf = texture(in_brdf_lut, vec2(max(dot(pixel.normal, V), 0.0), 1.0f - pixel.roughness)).rg; 103 | vec3 specular = prefilteredColor * (F * brdf.x + brdf.y); 104 | 105 | vec3 ambient = (kD * diffuse + specular) * pixel.occlusion; 106 | 107 | return ambient; 108 | } 109 | -------------------------------------------------------------------------------- /utopian/shaders/include/random.glsl: -------------------------------------------------------------------------------- 1 | 2 | // From https://nvpro-samples.github.io/vk_mini_path_tracer 3 | // and https://github.com/boksajak/referencePT 4 | 5 | uint jenkinsHash(uint x) { 6 | x += x << 10; 7 | x ^= x >> 6; 8 | x += x << 3; 9 | x ^= x >> 11; 10 | x += x << 15; 11 | return x; 12 | } 13 | 14 | uint initRNG(uvec2 pixelCoords, uvec2 resolution, uint frameNumber) 15 | { 16 | uint seed = uint(dot(pixelCoords, uvec2(1, resolution.x))) ^ jenkinsHash(frameNumber); 17 | return jenkinsHash(seed); 18 | } 19 | 20 | // Random number generation using pcg32i_random_t, using inc = 1. Our random state is a uint. 21 | uint stepRNG(uint rngState) 22 | { 23 | return rngState * 747796405 + 1; 24 | } 25 | 26 | // Steps the RNG and returns a floating-point value between 0 and 1 inclusive. 27 | float randomFloat(inout uint rngState) 28 | { 29 | // Condensed version of pcg_output_rxs_m_xs_32_32, with simple conversion to floating-point [0,1]. 30 | rngState = stepRNG(rngState); 31 | uint word = ((rngState >> ((rngState >> 28) + 4)) ^ rngState) * 277803737; 32 | word = (word >> 22) ^ word; 33 | return float(word) / 4294967295.0f; 34 | } 35 | 36 | vec3 randomPointInUnitSphere(inout uint rngState) 37 | { 38 | for (;;) 39 | { 40 | vec3 point = 2 * vec3(randomFloat(rngState), randomFloat(rngState), randomFloat(rngState)) - 1; 41 | if (dot(point, point) < 1) 42 | { 43 | return point; 44 | } 45 | } 46 | } 47 | 48 | vec2 randomPointInUnitDisk(inout uint rngState) 49 | { 50 | for (;;) 51 | { 52 | vec2 point = 2 * vec2(randomFloat(rngState), randomFloat(rngState)) - 1; 53 | if (dot(point, point) < 1) 54 | { 55 | return point; 56 | } 57 | } 58 | } 59 | 60 | // Todo: use this in the future 61 | 62 | // inline vec3 random_cosine_direction() { 63 | // auto r1 = random_double(); 64 | // auto r2 = random_double(); 65 | 66 | // auto phi = 2*pi*r1; 67 | // auto x = cos(phi)*sqrt(r2); 68 | // auto y = sin(phi)*sqrt(r2); 69 | // auto z = sqrt(1-r2); 70 | 71 | // return vec3(x, y, z); 72 | // } 73 | 74 | // // Samples a direction within a hemisphere oriented along +Z axis with a cosine-weighted distribution 75 | // // Source: "Sampling Transformations Zoo" in Ray Tracing Gems by Shirley et al. 76 | // // A derivation and explanation is available at https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html#generatingrandomdirections 77 | // vec3 sampleHemisphere(vec2 u, out float pdf) 78 | // { 79 | // float a = sqrt(u.x); 80 | // float b = TWO_PI * u.y; 81 | 82 | // vec3 result = vec3( 83 | // a * cos(b), 84 | // a * sin(b), 85 | // sqrt(1.0f - u.x)); 86 | 87 | // pdf = result.z * ONE_OVER_PI; 88 | 89 | // return result; 90 | // } 91 | -------------------------------------------------------------------------------- /utopian/shaders/include/restir_sampling.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | Terms 3 | p_hat : target function/PDF which is hard to draw samples from 4 | p : proposal PDF which looks a little like the target PDF and drawing samples from it is easy 5 | Xi : candidate sample from the proposal PDF 6 | m_i : MIS weight, "If all Xi are identically distributed, use m_i = 1/M." [1] 7 | W_Xi : unbiased contribution weight for Xi. "If Xi has a known PDF p(Xi) use W_Xi = 1/p(Xi)." [1] 8 | w_i : resampling weight 9 | w_i = m_i(Xi) * p_hat(Xi) * W_Xi 10 | w_sum : sum of all w_i 11 | W_X : unbiased contribution weight for the selected sample 12 | W_X = (1 / p_hat(X)) * w_sum 13 | 14 | In the light sampling case, p_hat could be the contribution of a light source and p is simply a 15 | uniform distribution over all light sources i.e 1/N. A way to think about it is that it is very 16 | easy pick pick light samples uniformly but what we really want is to pick them according to their 17 | contribution so we approximate that PDF by using RIS. 18 | 19 | What is the target function in the light sampling case? 20 | "The integrand for direct illumination (with unspecified pixel index) is 21 | f(x) = fs(x)G(x)V(x)Le(x), and we recommend starting with the same target function, p_hat = f: 22 | p_hat(x) = f(x) = fs(x)G(x)V(x)Le(x)" [1] 23 | It's also possible to drop the visibility term as an optimization 24 | 25 | Explanation about that p(x) does not need to be a PDF but rather a weight: 26 | "Let's think-what's the role of 1/p(X) in the f(X)/p(X) estimator? It's a 27 | weight for the sample f(X). Is this weight needed? Yes, absolutely. Does the 28 | weight need to be a PDF? Not exactly. What? Well, you see, RIS provides 29 | the sample X a weight, which we denote Wx. This weight produces an 30 | unbiased contribution f(X)Wx that estimates the integral of f. Weights 31 | are needed, but they need not be PDFs." [1] 32 | 33 | How are two reservoirs combined? 34 | "To combine two reservoirs, we treat each reservoirs y as a fresh sample with weight wsum, 35 | and feed it as input to a new reservoir. The result is mathematically equivalent to having 36 | performed reservoir sampling on the two reservoirs combined input streams." [2] 37 | 38 | "To account for the fact that samples from the neighboring pixel q' are resampled following 39 | a different target distribution p_hat(q'), we reweight the samples with the factor p_hat_q(r.y) / p_hat_q'(r.y) 40 | to account for areas that were over- or undersampled at the neighbor compared to the current pixel. 41 | The resulting term p_hat_q(r.y) / p_hat_q'(r.y) * r.W_sum can be written more succinctly as 42 | p_hat_q(r.y) * r.W * r.M using the term already computed in Alg. 3, line 8" [2] 43 | 44 | References: 45 | [1] https://intro-to-restir.cwyman.org/presentations/2023ReSTIR_Course_Notes.pdf 46 | [2] https://benedikt-bitterli.me/restir/bitterli20restir.pdf 47 | */ 48 | 49 | #define RIS_CANDIDATES_LIGHTS 32 50 | 51 | struct Reservoir 52 | { 53 | int Y; // index of most important light 54 | float W_sum; // sum of weights 55 | float W_X; // unbiased contribution weight 56 | int M; // number of samples 57 | }; 58 | 59 | vec3 get_light_intensity(in Light light, float distance_to_light) 60 | { 61 | return light.intensity / pow(distance_to_light, 2.0); 62 | } 63 | 64 | float target_function(int light_index, in vec3 hit_position) 65 | { 66 | Light light = lightsSSBO.lights[light_index]; 67 | float distance_to_light = distance(light.pos, hit_position); 68 | return luminance(get_light_intensity(light, distance_to_light)); 69 | } 70 | 71 | void sample_light_uniform(inout uint rngState, out int selected_light_index, out float light_sample_weight) 72 | { 73 | uint num_used_lights = view.num_lights; 74 | num_used_lights = min(num_used_lights, view.max_num_lights_used); 75 | selected_light_index = int(randomFloat(rngState) * float(num_used_lights)); 76 | light_sample_weight = 1.0 / float(num_used_lights); 77 | } 78 | 79 | void finalize_resampling(inout Reservoir reservoir, float p_hat) 80 | { 81 | reservoir.W_X = (p_hat == 0.0) ? 0.0 : (1.0 / p_hat) * reservoir.W_sum / reservoir.M; 82 | } 83 | 84 | // Together with the resample() function this is Algorithm 2 from [1] 85 | void updateReservoir(inout uint rngState, inout Reservoir reservoir, int Xi, float w_i, int M) 86 | { 87 | reservoir.W_sum += w_i; 88 | reservoir.M += M; 89 | 90 | // random < (w_i / W_sum) in the papers but this avoids divide by 0 91 | if (randomFloat(rngState) * reservoir.W_sum < w_i) { 92 | reservoir.Y = Xi; 93 | } 94 | } 95 | 96 | Reservoir resample(inout uint rngState, in vec3 hit_position) 97 | { 98 | Reservoir reservoir; 99 | reservoir.Y = -1; 100 | reservoir.W_sum = 0.0; 101 | reservoir.W_X = 0.0; 102 | reservoir.M = 0; 103 | 104 | const int M = RIS_CANDIDATES_LIGHTS; 105 | 106 | for (int i = 0; i < M; i++) { 107 | // Generate candidate sample (Xi) 108 | int candidate_index; 109 | float p = 0.0; 110 | sample_light_uniform(rngState, candidate_index, p); 111 | 112 | // Calculate resampling weight (w_i) 113 | float m_i = 1.0 / float(M); // MIS weight 114 | float p_hat = target_function(candidate_index, hit_position); 115 | float W_Xi = 1.0 / p; 116 | float w_i = m_i * p_hat * W_Xi; 117 | 118 | updateReservoir(rngState, reservoir, candidate_index, w_i, 1); 119 | } 120 | 121 | // Does not really matter, will be the same for all reservoirs anyways 122 | reservoir.M = 1; 123 | 124 | // Todo: need to check so we don't have -1 as index when the sample is used 125 | if (reservoir.Y != -1) { 126 | float p_hat = target_function(reservoir.Y, hit_position); 127 | finalize_resampling(reservoir, p_hat); 128 | } 129 | 130 | return reservoir; 131 | } 132 | 133 | -------------------------------------------------------------------------------- /utopian/shaders/include/shadow_mapping.glsl: -------------------------------------------------------------------------------- 1 | #define SHADOW_MAP_CASCADE_COUNT 4 2 | 3 | float linearize_depth(float d, float zNear, float zFar) 4 | { 5 | return zNear * zFar / (zFar + d * (zNear - zFar)); 6 | } 7 | 8 | float calculateShadow(vec3 position, out uint cascadeIndex) 9 | { 10 | 11 | vec3 viewPosition = (view.view * vec4(position, 1.0f)).xyz; 12 | cascadeIndex = 0; 13 | for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; ++i) { 14 | if(viewPosition.z < -shadowmapParams.cascade_splits[i]) { 15 | cascadeIndex = i + 1; 16 | } 17 | } 18 | 19 | vec4 lightSpacePosition = shadowmapParams.view_projection_matrices[cascadeIndex] * vec4(position, 1.0f); 20 | vec4 projCoordinate = lightSpacePosition / lightSpacePosition.w; 21 | projCoordinate.xy = projCoordinate.xy * 0.5f + 0.5f; 22 | projCoordinate.xy = FLIP_UV_Y(projCoordinate.xy); 23 | 24 | float shadow = 0.0f; 25 | vec2 texelSize = 1.0 / textureSize(in_shadow_map, 0).xy; 26 | int count = 0; 27 | int range = 1; 28 | for (int x = -range; x <= range; x++) 29 | { 30 | for (int y = -range; y <= range; y++) 31 | { 32 | // If fragment depth is outside frustum do no shadowing 33 | if (projCoordinate.z <= 1.0f && projCoordinate.z > -1.0f) 34 | { 35 | vec2 offset = vec2(x, y) * texelSize; 36 | float closestDepth = texture(in_shadow_map, vec3(projCoordinate.xy + offset, cascadeIndex)).r; 37 | float bias = 0.0005; 38 | const float shadowFactor = 0.3f; 39 | float testDepth = projCoordinate.z - bias; 40 | shadow += (testDepth > closestDepth ? shadowFactor : 1.0f); 41 | } 42 | else 43 | { 44 | shadow += 1.0f; 45 | } 46 | 47 | count++; 48 | } 49 | } 50 | 51 | shadow /= (count); 52 | 53 | return shadow; 54 | } 55 | 56 | vec3 cascade_index_to_debug_color(uint cascade_index) { 57 | switch(cascade_index) { 58 | case 0 : 59 | return vec3(1.0f, 0.25f, 0.25f); 60 | case 1 : 61 | return vec3(0.25f, 1.0f, 0.25f); 62 | case 2 : 63 | return vec3(0.25f, 0.25f, 1.0f); 64 | case 3 : 65 | return vec3(1.0f, 1.0f, 0.25f); 66 | } 67 | 68 | return vec3(1.0f, 1.0f, 1.0f); 69 | } -------------------------------------------------------------------------------- /utopian/shaders/include/view.glsl: -------------------------------------------------------------------------------- 1 | 2 | layout (std140, set = 1, binding = 0) uniform UBO_view 3 | { 4 | mat4 view; 5 | mat4 projection; 6 | mat4 inverse_view; 7 | mat4 inverse_projection; 8 | mat4 prev_frame_projection_view; 9 | vec3 eye_pos; 10 | uint samples_per_frame; 11 | vec3 sun_dir; 12 | uint total_samples; 13 | uint num_bounces; 14 | uint viewport_width; 15 | uint viewport_height; 16 | float time; 17 | uint num_lights; 18 | 19 | // render settings 20 | uint shadows_enabled; 21 | uint ssao_enabled; 22 | uint fxaa_enabled; 23 | uint cubemap_enabled; 24 | uint ibl_enabled; 25 | uint sky_enabled; 26 | uint sun_shadow_enabled; 27 | uint lights_enabled; 28 | uint max_num_lights_used; 29 | uint marching_cubes_enabled; 30 | uint temporal_reuse_enabled; 31 | uint spatial_reuse_enabled; 32 | uint rebuild_tlas; 33 | uint accumulation_limit; 34 | uint use_ris_light_sampling; 35 | uint raytracing_supported; 36 | } view; 37 | 38 | // Due to gl_Position not being multiplied by -1 we need to flip the 39 | // y axis of the uv coordinates. Todo: this should be possible to get rid of. 40 | #define ENABLE_UV_Y_FLIP 41 | #ifdef ENABLE_UV_Y_FLIP 42 | #define FLIP_UV_Y(uv) vec2(uv.x, 1.0 - uv.y) 43 | #else 44 | #define FLIP_UV_Y(uv) uv 45 | #endif 46 | 47 | float luminance(vec3 rgb) 48 | { 49 | // Coefficents from the BT.709 standard 50 | return dot(rgb, vec3(0.2126f, 0.7152f, 0.0722f)); 51 | } 52 | 53 | float linearToSrgb(float linearColor) 54 | { 55 | if (linearColor < 0.0031308f) { 56 | return linearColor * 12.92f; 57 | } 58 | else { 59 | return 1.055f * float(pow(linearColor, 1.0f / 2.4f)) - 0.055f; 60 | } 61 | } 62 | 63 | vec3 linearToSrgb(vec3 linearColor) 64 | { 65 | return vec3(linearToSrgb(linearColor.x), linearToSrgb(linearColor.y), linearToSrgb(linearColor.z)); 66 | } 67 | 68 | vec3 extract_camera_position(mat4 viewMatrix) { 69 | mat4 inverseViewMatrix = inverse(viewMatrix); 70 | vec3 cameraPosition = vec3(inverseViewMatrix[3]); 71 | return cameraPosition; 72 | } 73 | 74 | vec3 world_dir_from_ndc(vec3 ndc, mat4 view, mat4 projection) 75 | { 76 | vec4 clipSpace = vec4(ndc, 1.0); 77 | vec4 viewSpace = inverse(projection) * clipSpace; 78 | viewSpace.w = 0.0; 79 | vec4 worldSpace = inverse(view) * viewSpace; 80 | vec3 worldDir = normalize(worldSpace.xyz); 81 | 82 | return worldDir; 83 | } 84 | 85 | vec3 world_dir_from_uv(vec2 uv, mat4 view, mat4 projection) 86 | { 87 | return world_dir_from_ndc(vec3(uv, 0.0) * 2.0 - 1.0, view, projection); 88 | } 89 | 90 | // Clever offset_ray function from Ray Tracing Gems chapter 6 91 | // Offsets the ray origin from current position p, along normal n (which must be geometric normal) 92 | // so that no self-intersection can occur. 93 | vec3 offsetRay(const vec3 p, const vec3 n) 94 | { 95 | const float origin = 1.0f / 32.0f; 96 | const float float_scale = 1.0f / 65536.0f; 97 | const float int_scale = 256.0f; 98 | 99 | ivec3 of_i = ivec3(int_scale * n.x, int_scale * n.y, int_scale * n.z); 100 | 101 | vec3 p_i = vec3( 102 | intBitsToFloat(floatBitsToInt(p.x) + ((p.x < 0) ? -of_i.x : of_i.x)), 103 | intBitsToFloat(floatBitsToInt(p.y) + ((p.y < 0) ? -of_i.y : of_i.y)), 104 | intBitsToFloat(floatBitsToInt(p.z) + ((p.z < 0) ? -of_i.z : of_i.z))); 105 | 106 | return vec3(abs(p.x) < origin ? p.x + float_scale * n.x : p_i.x, 107 | abs(p.y) < origin ? p.y + float_scale * n.y : p_i.y, 108 | abs(p.z) < origin ? p.z + float_scale * n.z : p_i.z); 109 | } 110 | -------------------------------------------------------------------------------- /utopian/shaders/marching_cubes/noise.glsl: -------------------------------------------------------------------------------- 1 | // Note: when using the 496.76 Nvidia driver this function causes 2 | // artifacts when used in the terrain fbm. The hash() function works. 3 | float random(vec2 st) 4 | { 5 | return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); 6 | } 7 | 8 | // Precision-adjusted variations of https://www.shadertoy.com/view/4djSRW 9 | float hash(float p) 10 | { 11 | p = fract(p * 0.011); 12 | p *= p + 7.5; 13 | p *= p + p; 14 | return fract(p); 15 | } 16 | 17 | float hash(vec2 p) 18 | { 19 | vec3 p3 = fract(vec3(p.xyx) * 0.13); 20 | p3 += dot(p3, p3.yzx + 3.333); 21 | return fract((p3.x + p3.y) * p3.z); 22 | } 23 | 24 | /** 25 | * From "The book of shaders" (https://thebookofshaders.com/13/) 26 | * Based on Morgan McGuire @morgan3d 27 | * https://www.shadertoy.com/view/4dS3Wd 28 | * 29 | * Other noise function that might be worth looking into: 30 | * http://www.kamend.com/2012/06/perlin-noise-and-glsl/ 31 | */ 32 | float noise(in vec2 st) 33 | { 34 | vec2 i = floor(st); 35 | vec2 f = fract(st); 36 | 37 | // Four corners in 2D of a tile 38 | float a = hash(i); 39 | float b = hash(i + vec2(1.0, 0.0)); 40 | float c = hash(i + vec2(0.0, 1.0)); 41 | float d = hash(i + vec2(1.0, 1.0)); 42 | 43 | vec2 u = f * f * (3.0 - 2.0 * f); 44 | 45 | float n = mix(a, b, u.x) + 46 | (c - a)* u.y * (1.0 - u.x) + 47 | (d - b) * u.x * u.y; 48 | 49 | // Ridge noise 50 | #ifdef RIDGE_NOISE 51 | n = (n * 2) - 1; 52 | return -abs(n); 53 | #else 54 | return n; 55 | #endif 56 | } 57 | 58 | #define OCTAVES 1 59 | float fbm (vec2 st) { 60 | // Initial values 61 | float value = 0.0; 62 | float amplitude = 0.5f; 63 | 64 | // Loop of octaves 65 | for (int i = 0; i < OCTAVES; i++) { 66 | value += amplitude * noise(st); 67 | st *= 2.; 68 | amplitude *= .5; 69 | } 70 | return value; 71 | } 72 | 73 | float noise(vec3 x) { 74 | const vec3 step = vec3(110, 241, 171); 75 | 76 | vec3 i = floor(x); 77 | vec3 f = fract(x); 78 | 79 | // For performance, compute the base input to a 1D hash from the integer part of the argument and the 80 | // incremental change to the 1D based on the 3D -> 1D wrapping 81 | float n = dot(i, step); 82 | 83 | vec3 u = f * f * (3.0 - 2.0 * f); 84 | return mix(mix(mix( hash(n + dot(step, vec3(0, 0, 0))), hash(n + dot(step, vec3(1, 0, 0))), u.x), 85 | mix( hash(n + dot(step, vec3(0, 1, 0))), hash(n + dot(step, vec3(1, 1, 0))), u.x), u.y), 86 | mix(mix( hash(n + dot(step, vec3(0, 0, 1))), hash(n + dot(step, vec3(1, 0, 1))), u.x), 87 | mix( hash(n + dot(step, vec3(0, 1, 1))), hash(n + dot(step, vec3(1, 1, 1))), u.x), u.y), u.z); 88 | } 89 | 90 | #define NUM_NOISE_OCTAVES 4 91 | float fbm(vec3 x) { 92 | float v = 0.0; 93 | float a = 0.5; 94 | vec3 shift = vec3(100); 95 | for (int i = 0; i < NUM_NOISE_OCTAVES; ++i) { 96 | v += a * noise(x); 97 | x = x * 2.0 + shift; 98 | a *= 0.5; 99 | } 100 | return v; 101 | } 102 | 103 | // Calculate normal, based on https://www.gamedev.net/forums/topic/692347-finite-difference-normal-calculation-for-sphere/ 104 | // Note: This is currently not used. 105 | vec3 getNormal(float x, float z) 106 | { 107 | float offset = 1.0f; 108 | 109 | float hL = fbm(vec2(x - offset, z)); 110 | float hR = fbm(vec2(x + offset, z)); 111 | float hD = fbm(vec2(x, z - offset)); 112 | float hU = fbm(vec2(x, z + offset)); 113 | 114 | vec3 normal = vec3(hL - hR, 2.0f, hD - hU); 115 | normal = normalize(normal); 116 | 117 | return normal; 118 | } -------------------------------------------------------------------------------- /utopian/shaders/marching_cubes/reset_counter.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_GOOGLE_include_directive : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | 7 | struct DrawCommand 8 | { 9 | uint vertexCount; 10 | uint instanceCount; 11 | uint firstVertex; 12 | uint firstInstance; 13 | }; 14 | 15 | layout(std430, set = 2, binding = 0) buffer DrawCommandSSBO 16 | { 17 | DrawCommand drawCommand; 18 | } drawCommandSSBO; 19 | 20 | void main(void) 21 | { 22 | drawCommandSSBO.drawCommand.vertexCount = 0; 23 | drawCommandSSBO.drawCommand.instanceCount = 1; 24 | drawCommandSSBO.drawCommand.firstVertex = 0; 25 | drawCommandSSBO.drawCommand.firstInstance = 0; 26 | } -------------------------------------------------------------------------------- /utopian/shaders/pathtrace_reference/payload.glsl: -------------------------------------------------------------------------------- 1 | 2 | struct Payload 3 | { 4 | vec4 colorDistance; // rgb + t 5 | vec4 scatterDirection; // xyz + is scattered 6 | vec4 normal; 7 | uint randomSeed; 8 | }; 9 | -------------------------------------------------------------------------------- /utopian/shaders/pathtrace_reference/reference.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | #include "include/bindless.glsl" 6 | #include "include/random.glsl" 7 | #include "payload.glsl" 8 | 9 | layout(location = 0) rayPayloadInEXT Payload rayPayload; 10 | hitAttributeEXT vec2 attribs; 11 | 12 | float schlick_reflectance(float cosine, float ref_idx) 13 | { 14 | // Schlick's approximation 15 | float r0 = (1.0 - ref_idx) / (1.0 + ref_idx); 16 | r0 = r0 * r0; 17 | return r0 + (1.0 - r0) * pow(1.0 - cosine, 5.0); 18 | } 19 | 20 | void main() 21 | { 22 | Mesh mesh = meshesSSBO.meshes[gl_InstanceCustomIndexEXT]; 23 | Material material = materialsSSBO.materials[mesh.material]; 24 | 25 | ivec3 indices = indicesSSBO[mesh.index_buffer].indices[gl_PrimitiveID]; 26 | Vertex v0 = verticesSSBO[mesh.vertex_buffer].vertices[indices.x]; 27 | Vertex v1 = verticesSSBO[mesh.vertex_buffer].vertices[indices.y]; 28 | Vertex v2 = verticesSSBO[mesh.vertex_buffer].vertices[indices.z]; 29 | 30 | const vec3 barycentrics = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); 31 | vec3 normal = v0.normal.xyz * barycentrics.x + v1.normal.xyz * barycentrics.y + v2.normal.xyz * barycentrics.z; 32 | vec3 world_normal = normalize(vec3(normal.xyz * gl_WorldToObjectEXT)); 33 | 34 | // Flip normal towards the incident ray direction 35 | if (dot(world_normal, gl_WorldRayDirectionEXT) > 0.0f) { 36 | world_normal = -world_normal; 37 | } 38 | 39 | vec2 uv = v0.uv * barycentrics.x + v1.uv * barycentrics.y + v2.uv * barycentrics.z; 40 | vec3 color = texture(samplerColor[material.diffuse_map], uv).xyz; 41 | color *= material.base_color_factor.rgb; 42 | 43 | vec3 scatterDirection; 44 | bool isScattered = false; 45 | 46 | // Lambertian 47 | if (material.raytrace_properties.x == 0) { 48 | scatterDirection = world_normal + randomPointInUnitSphere(rayPayload.randomSeed); 49 | isScattered = dot(gl_WorldRayDirectionEXT, world_normal) < 0; 50 | } 51 | // Metal 52 | else if (material.raytrace_properties.x == 1) { 53 | scatterDirection = reflect(normalize(gl_WorldRayDirectionEXT), world_normal); 54 | scatterDirection += material.raytrace_properties.y * randomPointInUnitSphere(rayPayload.randomSeed); 55 | 56 | // Note: the dot product below should be used but it's causing weird artifacts at sphere edges 57 | isScattered = true; // dot(scatterDirection, world_normal) > 0; 58 | color = vec3(1.0); // Note: Hardcode white color 59 | } 60 | // Dielectric 61 | else if (material.raytrace_properties.x == 2) { 62 | vec3 normalized_direction = normalize(gl_WorldRayDirectionEXT); 63 | const float dir_normal_dot = dot(normalized_direction, world_normal); 64 | const vec3 outward_normal = dir_normal_dot > 0 ? -world_normal : world_normal; 65 | float refraction_ratio = material.raytrace_properties.y; 66 | refraction_ratio = dir_normal_dot > 0 ? refraction_ratio : 1.0 / refraction_ratio; 67 | 68 | float cos_theta = min(dot(-1.0 * normalized_direction, outward_normal), 1.0); 69 | float sin_theta = sqrt(1.0 - cos_theta * cos_theta); 70 | 71 | bool cannot_refract = refraction_ratio * sin_theta > 1.0; 72 | float reflectance = schlick_reflectance(cos_theta, refraction_ratio); 73 | 74 | if (cannot_refract || reflectance > randomFloat(rayPayload.randomSeed)) { 75 | scatterDirection = reflect(normalized_direction, outward_normal); 76 | } 77 | else { 78 | scatterDirection = refract(normalized_direction, outward_normal, refraction_ratio); 79 | } 80 | 81 | isScattered = true; 82 | color = vec3(1.0); 83 | } 84 | // Diffuse light 85 | else { 86 | // Todo 87 | isScattered = false; 88 | color = vec3(1.0); 89 | } 90 | 91 | rayPayload = Payload(vec4(color, gl_HitTEXT), vec4(scatterDirection, isScattered ? 1 : 0), vec4(world_normal, 0.0), rayPayload.randomSeed); 92 | } 93 | -------------------------------------------------------------------------------- /utopian/shaders/pathtrace_reference/reference.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | #include "include/atmosphere.glsl" 7 | #include "include/random.glsl" 8 | #include "include/restir_sampling.glsl" 9 | #include "payload.glsl" 10 | 11 | layout(set = 2, binding = 0) uniform accelerationStructureEXT topLevelAS; 12 | layout(std430, set = 2, binding = 1) buffer ReservoirSSBO 13 | { 14 | Reservoir reservoirs[]; 15 | } reservoirSSBO; 16 | layout(set = 2, binding = 2, rgba8) uniform image2D output_image; 17 | layout(set = 2, binding = 3, rgba32f) uniform image2D accumulation_image; 18 | 19 | layout(location = 0) rayPayloadEXT Payload rayPayload; 20 | layout(location = 1) rayPayloadEXT Payload shadowRayPayload; 21 | 22 | void main() 23 | { 24 | uint rngState = initRNG(gl_LaunchIDEXT.xy, gl_LaunchSizeEXT.xy, int(float(view.total_samples) + view.time * 10000.0)); 25 | 26 | vec3 pixelColor = vec3(0.0); 27 | 28 | for (uint s = 0; s < view.samples_per_frame; s++) 29 | { 30 | rayPayload.randomSeed = rngState; 31 | const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(randomFloat(rngState), randomFloat(rngState)); 32 | vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy); 33 | inUV.y = 1.0 - inUV.y; 34 | vec2 d = inUV * 2.0 - 1.0; 35 | 36 | vec4 origin = view.inverse_view * vec4(0,0,0,1); 37 | vec4 target = view.inverse_projection * vec4(d.x, d.y, 1, 1) ; 38 | vec4 direction = view.inverse_view * vec4(normalize(target.xyz), 0) ; 39 | vec3 radiance = vec3(0.0); 40 | vec3 throughput = vec3(1.0); 41 | 42 | for (uint b = 0; b < view.num_bounces; b++) 43 | { 44 | float tmin = 0.001; 45 | float tmax = 10000.0; 46 | 47 | traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0); 48 | throughput *= rayPayload.colorDistance.rgb; 49 | const float hitDistance = rayPayload.colorDistance.w; 50 | const bool isScattered = bool(rayPayload.scatterDirection.w); 51 | 52 | // Hit sky 53 | if (hitDistance < 0 || !isScattered) 54 | { 55 | radiance += throughput; 56 | break; 57 | } 58 | 59 | origin = origin + hitDistance * direction; 60 | origin.xyz = offsetRay(origin.xyz, rayPayload.normal.xyz); 61 | direction = vec4(rayPayload.scatterDirection.xyz, 0.0); 62 | 63 | if (view.sun_shadow_enabled == 1) { 64 | vec3 sun_dir = normalize(view.sun_dir); 65 | 66 | // Todo: shall use gl_RayFlagsSkipClosestHitShaderEXT and gl_RayFlagsTerminateOnFirstHitEXT 67 | traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 68 | 0xff, 0, 0, 0, origin.xyz, tmin, sun_dir, tmax, 1); 69 | if (shadowRayPayload.colorDistance.w == -1) { 70 | #ifdef COLOR_FROM_SUN_EXPERIMENT 71 | // Experimentation using color from the sun 72 | vec3 transmittance; 73 | vec3 sky_color = IntegrateScattering(origin.xyz, -sun_dir, 999999999.0f, sun_dir, vec3(1.0), transmittance); 74 | radiance += 0.2 * throughput * min(sky_color, vec3(1.0)); // 0.2 is a magic constant to not make it too bright 75 | #else 76 | radiance += throughput; 77 | #endif // COLOR_FROM_SUN_EXPERIMENT 78 | } 79 | } 80 | 81 | if (view.lights_enabled == 1) { 82 | Light light; 83 | int light_index = 0; 84 | float light_sample_weight = 0.0; 85 | float total_weights = 1.0; 86 | 87 | if (gl_LaunchIDEXT.x > gl_LaunchSizeEXT.x / 2 && view.use_ris_light_sampling == 1) 88 | { 89 | // Reference that only uses RIS for light sampling to the left 90 | if (gl_LaunchIDEXT.x < gl_LaunchSizeEXT.x / 2) { 91 | Reservoir reservoir = resample(rngState, origin.xyz); 92 | light = lightsSSBO.lights[reservoir.Y]; 93 | light_sample_weight = reservoir.W_X; 94 | total_weights = reservoir.W_sum; 95 | light_index = reservoir.Y; 96 | } 97 | else { 98 | Reservoir reservoir = reservoirSSBO.reservoirs[gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x]; 99 | light = lightsSSBO.lights[reservoir.Y]; 100 | light_sample_weight = reservoir.W_X; 101 | total_weights = reservoir.W_sum; 102 | light_index = reservoir.Y; 103 | } 104 | } 105 | else 106 | { 107 | sample_light_uniform(rngState, light_index, light_sample_weight); 108 | light_sample_weight = 1.0 / light_sample_weight; 109 | light = lightsSSBO.lights[light_index]; 110 | } 111 | 112 | if (total_weights != 0.0) { 113 | vec3 light_dir = normalize(light.pos - origin.xyz); 114 | float distance_to_light = distance(light.pos, origin.xyz); 115 | traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 116 | 0xff, 0, 0, 0, origin.xyz, tmin, light_dir, tmax, 1); 117 | 118 | if(shadowRayPayload.colorDistance.w > distance_to_light 119 | || shadowRayPayload.colorDistance.w == -1) 120 | { 121 | radiance += throughput * target_function(light_index, origin.xyz) * light_sample_weight; 122 | } 123 | } 124 | } 125 | } 126 | 127 | pixelColor += radiance; 128 | } 129 | 130 | vec3 accumulatedColor = vec3(0.0); 131 | if (view.total_samples != view.samples_per_frame) 132 | { 133 | accumulatedColor = imageLoad(accumulation_image, ivec2(gl_LaunchIDEXT.xy)).rgb; 134 | } 135 | 136 | if (view.total_samples <= view.accumulation_limit) { 137 | accumulatedColor += pixelColor; 138 | } 139 | 140 | pixelColor = accumulatedColor / min(view.total_samples, view.accumulation_limit); 141 | pixelColor = linearToSrgb(pixelColor); 142 | 143 | imageStore(accumulation_image, ivec2(gl_LaunchIDEXT.xy), vec4(accumulatedColor, 0.0)); 144 | imageStore(output_image, ivec2(gl_LaunchIDEXT.xy), vec4(pixelColor, 0.0)); 145 | } 146 | -------------------------------------------------------------------------------- /utopian/shaders/pathtrace_reference/reference.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/atmosphere.glsl" 5 | #include "include/view.glsl" 6 | #include "payload.glsl" 7 | 8 | layout(location = 0) rayPayloadInEXT Payload rayPayload; 9 | 10 | void main() 11 | { 12 | vec3 sky_color = vec3(1.0); 13 | 14 | #ifndef FURNACE_TEST 15 | if (view.sky_enabled == 1) { 16 | vec3 light_dir = normalize(view.sun_dir); 17 | vec3 transmittance = vec3(0.0); 18 | sky_color = IntegrateScattering(gl_WorldRayOriginEXT, gl_WorldRayDirectionEXT, 999999999.0f, light_dir, vec3(1.0), transmittance); 19 | 20 | // Todo: we could use the atmosphere cubemap here 21 | 22 | // sky_color is in HDR range so clamp it for now to not get over exposure 23 | sky_color = min(sky_color, vec3(1.0)); 24 | } 25 | else { 26 | sky_color = vec3(0.0); 27 | } 28 | #endif 29 | 30 | rayPayload = Payload(vec4(sky_color, -1), vec4(0.0), vec4(0.0), 0); 31 | } 32 | -------------------------------------------------------------------------------- /utopian/shaders/present/present.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/pbr_lighting.glsl" 9 | 10 | layout (location = 0) in vec2 in_uv; 11 | 12 | layout (location = 0) out vec4 out_color; 13 | 14 | layout (set = 2, binding = 0) uniform sampler2D in_color_texture; 15 | 16 | layout(std140, set = 3, binding = 0) uniform FXAA_settings 17 | { 18 | vec4 enabled_debug_threshold; 19 | } settings_fxaa; 20 | 21 | #include "include/fxaa.glsl" 22 | 23 | void main() { 24 | vec2 uv = FLIP_UV_Y(in_uv); 25 | 26 | vec3 color = vec3(0.0); 27 | 28 | if (view.fxaa_enabled == 1) { 29 | color = fxaa(in_color_texture, uv); 30 | } 31 | else { 32 | color = texture(in_color_texture, uv).rgb; 33 | } 34 | 35 | /* Tonemapping */ 36 | // color = color / (color + vec3(1.0)); 37 | color = linearToSrgb(color); 38 | 39 | out_color = vec4(color, 1.0); 40 | } 41 | 42 | -------------------------------------------------------------------------------- /utopian/shaders/restir/initial_ris.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | #include "include/bindless.glsl" 6 | #include "include/random.glsl" 7 | 8 | layout(location = 0) rayPayloadInEXT int rayPayload; 9 | 10 | void main() 11 | { 12 | rayPayload = 1; 13 | } 14 | -------------------------------------------------------------------------------- /utopian/shaders/restir/initial_ris.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | #include "include/atmosphere.glsl" 7 | #include "include/random.glsl" 8 | #include "include/restir_sampling.glsl" 9 | 10 | layout (set = 2, binding = 0) uniform accelerationStructureEXT topLevelAS; 11 | layout (set = 2, binding = 1) uniform sampler2D in_gbuffer_position; 12 | layout (std430, set = 2, binding = 2) buffer ReservoirSSBO 13 | { 14 | Reservoir reservoirs[]; 15 | } reservoirSSBO; 16 | 17 | layout (location = 0) rayPayloadEXT int rayPayload; 18 | 19 | void main() 20 | { 21 | uint rngState = initRNG(gl_LaunchIDEXT.xy, gl_LaunchSizeEXT.xy, int(float(view.total_samples) + view.time * 10000.0)); 22 | vec2 uv = vec2(gl_LaunchIDEXT.xy) / vec2(gl_LaunchSizeEXT.xy); 23 | vec3 hit_position = texture(in_gbuffer_position, uv).rgb; 24 | 25 | Reservoir new_reservoir; 26 | new_reservoir.Y = -1; 27 | new_reservoir.W_sum = 0.0; 28 | new_reservoir.W_X = 0.0; 29 | new_reservoir.M = 0; 30 | 31 | Reservoir reservoir = resample(rngState, hit_position); 32 | updateReservoir(rngState, new_reservoir, reservoir.Y, reservoir.W_sum * reservoir.M, reservoir.M); 33 | 34 | float p_hat = target_function(new_reservoir.Y, hit_position); 35 | finalize_resampling(new_reservoir, p_hat); 36 | 37 | reservoirSSBO.reservoirs[gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x] = new_reservoir; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /utopian/shaders/restir/initial_ris.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/atmosphere.glsl" 5 | #include "include/view.glsl" 6 | 7 | layout(location = 0) rayPayloadInEXT int rayPayload; 8 | 9 | void main() 10 | { 11 | rayPayload = 0; 12 | } 13 | -------------------------------------------------------------------------------- /utopian/shaders/restir/reset_reservoirs.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_GOOGLE_include_directive : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | #include "include/random.glsl" 7 | #include "include/restir_sampling.glsl" 8 | 9 | layout (local_size_x = 16, local_size_y = 16) in; 10 | 11 | layout (std430, set = 2, binding = 0) buffer InitialRisReservoirSSBO 12 | { 13 | Reservoir reservoirs[]; 14 | } initialRisReservoirSSBO; 15 | layout (std430, set = 2, binding = 1) buffer SpatialReuseReservoirSSBO 16 | { 17 | Reservoir reservoirs[]; 18 | } spatialReuseReservoirSSBO; 19 | layout (std430, set = 2, binding = 2) buffer TemporalReuseReservoirSSBO 20 | { 21 | Reservoir reservoirs[]; 22 | } temporalReuseReservoirSSBO; 23 | 24 | void main(void) 25 | { 26 | uint index = view.viewport_width * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x; 27 | 28 | // Mainly for debugging purposes 29 | 30 | initialRisReservoirSSBO.reservoirs[index].Y = -1; 31 | initialRisReservoirSSBO.reservoirs[index].W_sum = 0.0; 32 | initialRisReservoirSSBO.reservoirs[index].W_X = 0.0; 33 | initialRisReservoirSSBO.reservoirs[index].M = 0; 34 | 35 | // if (view.total_samples == 1) { 36 | // spatialReuseReservoirSSBO.reservoirs[index].Y = -1; 37 | // spatialReuseReservoirSSBO.reservoirs[index].W_sum = 0.0; 38 | // spatialReuseReservoirSSBO.reservoirs[index].W_X = 0.0; 39 | // spatialReuseReservoirSSBO.reservoirs[index].M = 0; 40 | // } 41 | 42 | temporalReuseReservoirSSBO.reservoirs[index].Y = -1; 43 | temporalReuseReservoirSSBO.reservoirs[index].W_sum = 0.0; 44 | temporalReuseReservoirSSBO.reservoirs[index].W_X = 0.0; 45 | temporalReuseReservoirSSBO.reservoirs[index].M = 0; 46 | } -------------------------------------------------------------------------------- /utopian/shaders/restir/spatial_reuse.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | #include "include/bindless.glsl" 6 | #include "include/random.glsl" 7 | 8 | layout(location = 0) rayPayloadInEXT int rayPayload; 9 | 10 | void main() 11 | { 12 | rayPayload = 1; 13 | } 14 | -------------------------------------------------------------------------------- /utopian/shaders/restir/spatial_reuse.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | #include "include/atmosphere.glsl" 7 | #include "include/random.glsl" 8 | #include "include/restir_sampling.glsl" 9 | 10 | layout (set = 2, binding = 0) uniform accelerationStructureEXT topLevelAS; 11 | layout (set = 2, binding = 1) uniform sampler2D in_gbuffer_position; 12 | layout (std430, set = 2, binding = 2) buffer TemporalReuseReservoirSSBO 13 | { 14 | Reservoir reservoirs[]; 15 | } temporalReuseReservoirSSBO; 16 | layout (std430, set = 2, binding = 3) buffer SpatialReuseReservoirSSBO 17 | { 18 | Reservoir reservoirs[]; 19 | } spatialReuseReservoirSSBO; 20 | 21 | layout (location = 0) rayPayloadEXT int rayPayload; 22 | 23 | void main() 24 | { 25 | uint index = gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x; 26 | uint rngState = initRNG(gl_LaunchIDEXT.xy, gl_LaunchSizeEXT.xy, int(float(view.total_samples) + view.time * 10000.0)); 27 | vec2 uv = vec2(gl_LaunchIDEXT.xy) / vec2(gl_LaunchSizeEXT.xy); 28 | vec3 hit_position = texture(in_gbuffer_position, uv).rgb; 29 | 30 | if (view.spatial_reuse_enabled == 0) { 31 | spatialReuseReservoirSSBO.reservoirs[index] = temporalReuseReservoirSSBO.reservoirs[index]; 32 | return; 33 | } 34 | 35 | // This implements Algorithm 4 and parts of Algorithm 5 from [2] but uses the generalized 36 | // formulation (from GRIS) that moves the 1/M (MIS) factor into the resampling weights w_i. 37 | 38 | Reservoir new_reservoir; 39 | new_reservoir.Y = -1; 40 | new_reservoir.W_sum = 0.0; 41 | new_reservoir.W_X = 0.0; 42 | new_reservoir.M = 0; 43 | 44 | // There should be no need to reweight with p_hat because we are using the same target 45 | // distribution (p_hat) as the initial sample but for now we do it anyways to match Algorithm 4 in [2] 46 | Reservoir temporal_reservoir = temporalReuseReservoirSSBO.reservoirs[index]; 47 | float p_hat = target_function(temporal_reservoir.Y, hit_position); 48 | updateReservoir(rngState, new_reservoir, temporal_reservoir.Y, p_hat * temporal_reservoir.W_X * temporal_reservoir.M, temporal_reservoir.M); 49 | 50 | const int radius = 30; 51 | const int num_neighbours = 5; 52 | 53 | for (int i = 0; i < num_neighbours; i++) { 54 | vec2 offset = vec2(randomFloat(rngState), randomFloat(rngState)) * 2.0 - 1.0; 55 | offset *= float(radius); 56 | uvec2 neighbour_coord = gl_LaunchIDEXT.xy + uvec2(offset); 57 | neighbour_coord = clamp(neighbour_coord, uvec2(0), gl_LaunchSizeEXT.xy - uvec2(1)); 58 | Reservoir neighbour_reservoir = temporalReuseReservoirSSBO.reservoirs[neighbour_coord.y * gl_LaunchSizeEXT.x + neighbour_coord.x]; 59 | 60 | // We cannot simply use the neighbours W_sum because it used a different target distribution (p_hat) 61 | // than the current pixel. A concrete example where this problem is visible is for pixels close to 62 | // an object in screenspace but far away in worldspace. The neighbour will have a high p_hat because 63 | // it is close to the object, but the current pixel will have a low p_hat because it is far away. 64 | float p_hat = target_function(neighbour_reservoir.Y, hit_position); 65 | updateReservoir(rngState, new_reservoir, neighbour_reservoir.Y, p_hat * neighbour_reservoir.W_X * neighbour_reservoir.M, neighbour_reservoir.M); 66 | } 67 | 68 | if (new_reservoir.Y != -1) { 69 | float p_hat = target_function(new_reservoir.Y, hit_position); 70 | finalize_resampling(new_reservoir, p_hat); 71 | } 72 | 73 | spatialReuseReservoirSSBO.reservoirs[index] = new_reservoir; 74 | } 75 | -------------------------------------------------------------------------------- /utopian/shaders/restir/spatial_reuse.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/atmosphere.glsl" 5 | #include "include/view.glsl" 6 | 7 | layout(location = 0) rayPayloadInEXT int rayPayload; 8 | 9 | void main() 10 | { 11 | rayPayload = 0; 12 | } 13 | -------------------------------------------------------------------------------- /utopian/shaders/restir/temporal_reuse.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | #include "include/bindless.glsl" 6 | #include "include/random.glsl" 7 | 8 | layout(location = 0) rayPayloadInEXT int rayPayload; 9 | 10 | void main() 11 | { 12 | rayPayload = 1; 13 | } 14 | -------------------------------------------------------------------------------- /utopian/shaders/restir/temporal_reuse.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_EXT_debug_printf : enable 4 | 5 | #include "include/bindless.glsl" 6 | #include "include/view.glsl" 7 | #include "include/atmosphere.glsl" 8 | #include "include/random.glsl" 9 | #include "include/restir_sampling.glsl" 10 | 11 | layout (set = 2, binding = 0) uniform accelerationStructureEXT topLevelAS; 12 | layout (set = 2, binding = 1) uniform sampler2D in_gbuffer_position; 13 | layout (set = 2, binding = 2) uniform sampler2D in_gbuffer_normal; 14 | layout (std430, set = 2, binding = 3) buffer InitialRisReservoirSSBO 15 | { 16 | Reservoir reservoirs[]; 17 | } initialRisReservoirSSBO; 18 | layout (std430, set = 2, binding = 4) buffer PrevFrameReservoirSSBO 19 | { 20 | Reservoir reservoirs[]; 21 | } prevFrameReservoirSSBO; 22 | layout (std430, set = 2, binding = 5) buffer TemporalReuseReservoirSSBO 23 | { 24 | Reservoir reservoirs[]; 25 | } temporalReuseReservoirSSBO; 26 | 27 | layout (location = 0) rayPayloadEXT int rayPayload; 28 | 29 | bool debug() 30 | { 31 | return false; 32 | return gl_LaunchIDEXT.x == gl_LaunchSizeEXT.x / 2 && gl_LaunchIDEXT.y == gl_LaunchSizeEXT.y / 2; 33 | } 34 | 35 | void main() 36 | { 37 | uint index = gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x; 38 | uint rngState = initRNG(gl_LaunchIDEXT.xy, gl_LaunchSizeEXT.xy, int(float(view.total_samples) + view.time * 10000.0)); 39 | vec2 uv = vec2(gl_LaunchIDEXT.xy) / vec2(gl_LaunchSizeEXT.xy); 40 | vec3 hit_position = texture(in_gbuffer_position, uv).rgb; 41 | vec3 hit_normal = texture(in_gbuffer_normal, uv).rgb; 42 | 43 | if (view.temporal_reuse_enabled == 0) { 44 | temporalReuseReservoirSSBO.reservoirs[index] = initialRisReservoirSSBO.reservoirs[index]; 45 | return; 46 | } 47 | 48 | // This implements Algorithm 4 and parts of Algorithm 5 from [2] but uses the generalized 49 | // formulation (from GRIS) that moves the 1/M (MIS) factor into the resampling weights w_i. 50 | 51 | Reservoir new_reservoir; 52 | new_reservoir.Y = -1; 53 | new_reservoir.W_sum = 0.0; 54 | new_reservoir.W_X = 0.0; 55 | new_reservoir.M = 0; 56 | 57 | // There should be no need to reweight with p_hat because we are using the same target 58 | // distribution (p_hat) as the initial sample but for now we do it anyways to match Algorithm 4 in [2] 59 | Reservoir initial_reservoir = initialRisReservoirSSBO.reservoirs[index]; 60 | float p_hat = target_function(initial_reservoir.Y, hit_position); 61 | float initial_weight = p_hat * initial_reservoir.W_X * initial_reservoir.M; 62 | updateReservoir(rngState, 63 | new_reservoir, 64 | initial_reservoir.Y, 65 | initial_weight, 66 | initial_reservoir.M); 67 | 68 | // Visibility reuse, we don't want to include samples that are occluded 69 | // Todo: does not work with spatial reuse 70 | #if 0 71 | vec3 ray_origin = offsetRay(hit_position.xyz, hit_normal.xyz); 72 | //ray_origin = hit_position.xyz; 73 | vec3 light_dir = normalize(lightsSSBO.lights[initial_reservoir.Y].pos - ray_origin.xyz); 74 | traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, ray_origin, 0.001, light_dir, 10000.0, 0); 75 | 76 | if(rayPayload == 1)// && gl_LaunchIDEXT.x < gl_LaunchSizeEXT.x * 0.75) 77 | { 78 | new_reservoir.W_sum = 0.0; 79 | new_reservoir.W_X = 0.0; 80 | } 81 | #endif 82 | 83 | Reservoir prev_frame_reservoir; 84 | prev_frame_reservoir.Y = -1; 85 | prev_frame_reservoir.W_sum = 0.0; 86 | prev_frame_reservoir.W_X = 0.0; 87 | prev_frame_reservoir.M = 0; 88 | 89 | // Backproject this pixel to last frame 90 | vec4 prev_frame_uv = view.prev_frame_projection_view * vec4(hit_position, 1.0); 91 | prev_frame_uv.xy /= prev_frame_uv.w; 92 | prev_frame_uv.xy = prev_frame_uv.xy * 0.5 + 0.5; 93 | prev_frame_uv.y = 1.0 - prev_frame_uv.y; 94 | 95 | if (prev_frame_uv.x >= 0.0 && prev_frame_uv.x <= 1.0 && prev_frame_uv.y >= 0.0 && prev_frame_uv.y <= 1.0) { 96 | ivec2 prev_uv = ivec2(prev_frame_uv.xy * gl_LaunchSizeEXT.xy + 0.5); // + 0.5 to round up 97 | uint temporal_index = prev_uv.y * gl_LaunchSizeEXT.x + prev_uv.x; 98 | prev_frame_reservoir = prevFrameReservoirSSBO.reservoirs[temporal_index]; 99 | } 100 | 101 | // We cannot simply use the neighbours W_sum because it used a different target distribution (p_hat) 102 | // than the current pixel. A concrete example where this problem is visible is for pixels close to 103 | // an object in screenspace but far away in worldspace. The neighbour will have a high p_hat because 104 | // it is close to the object, but the current pixel will have a low p_hat because it is far away. 105 | p_hat = prev_frame_reservoir.Y == -1 ? 0.0 : target_function(prev_frame_reservoir.Y, hit_position); 106 | prev_frame_reservoir.M = min(20 * initial_reservoir.M, prev_frame_reservoir.M); 107 | float prev_frame_weight = p_hat * prev_frame_reservoir.W_X * prev_frame_reservoir.M; 108 | updateReservoir(rngState, 109 | new_reservoir, 110 | prev_frame_reservoir.Y, 111 | prev_frame_weight, 112 | prev_frame_reservoir.M); 113 | 114 | if (new_reservoir.Y != -1) { 115 | p_hat = target_function(new_reservoir.Y, hit_position); 116 | finalize_resampling(new_reservoir, p_hat); 117 | } 118 | 119 | temporalReuseReservoirSSBO.reservoirs[index] = new_reservoir; 120 | 121 | // Debug prints 122 | if (debug()) { 123 | debugPrintfEXT("==\n"); 124 | debugPrintfEXT("c: %d, initial_reservoir Y: %d, weight: %f, W_sum: %f, W_X: %f M: %d p_hat: %f\n", 125 | view.total_samples, initial_reservoir.Y, initial_weight, initial_reservoir.W_sum, 126 | initial_reservoir.W_X, initial_reservoir.M, target_function(initial_reservoir.Y, hit_position)); 127 | 128 | debugPrintfEXT("c: %d, prev_frame_reservoir Y: %d, weight: %f, W_sum: %f, W_X: %f M: %d p_hat: %f\n", 129 | view.total_samples, prev_frame_reservoir.Y, prev_frame_weight, prev_frame_reservoir.W_sum, 130 | prev_frame_reservoir.W_X, prev_frame_reservoir.M, target_function(prev_frame_reservoir.Y, hit_position)); 131 | 132 | debugPrintfEXT("c: %d, new_reservoir Y: %d, W_sum: %f, W_X: %f M: %d p_hat: %f\n", 133 | view.total_samples, new_reservoir.Y, new_reservoir.W_sum, 134 | new_reservoir.W_X, new_reservoir.M, p_hat); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /utopian/shaders/restir/temporal_reuse.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/atmosphere.glsl" 5 | #include "include/view.glsl" 6 | 7 | layout(location = 0) rayPayloadInEXT int rayPayload; 8 | 9 | void main() 10 | { 11 | rayPayload = 0; 12 | } 13 | -------------------------------------------------------------------------------- /utopian/shaders/rt_reflections/rt_reflections.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | #include "include/bindless.glsl" 6 | #include "include/view.glsl" 7 | #include "include/pbr_lighting.glsl" 8 | 9 | layout (set = 2, binding = 4) uniform samplerCube in_irradiance_map; 10 | layout (set = 2, binding = 5) uniform samplerCube in_specular_map; 11 | layout (set = 2, binding = 6) uniform sampler2D in_brdf_lut; 12 | 13 | layout(location = 0) rayPayloadInEXT vec3 rayPayload; 14 | hitAttributeEXT vec2 attribs; 15 | 16 | float schlick_reflectance(float cosine, float ref_idx) 17 | { 18 | // Schlick's approximation 19 | float r0 = (1.0 - ref_idx) / (1.0 + ref_idx); 20 | r0 = r0 * r0; 21 | return r0 + (1.0 - r0) * pow(1.0 - cosine, 5.0); 22 | } 23 | 24 | void main() 25 | { 26 | Mesh mesh = meshesSSBO.meshes[gl_InstanceCustomIndexEXT]; 27 | Material material = materialsSSBO.materials[mesh.material]; 28 | 29 | ivec3 indices = indicesSSBO[mesh.index_buffer].indices[gl_PrimitiveID]; 30 | Vertex v0 = verticesSSBO[mesh.vertex_buffer].vertices[indices.x]; 31 | Vertex v1 = verticesSSBO[mesh.vertex_buffer].vertices[indices.y]; 32 | Vertex v2 = verticesSSBO[mesh.vertex_buffer].vertices[indices.z]; 33 | 34 | const vec3 barycentrics = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); 35 | vec3 position = v0.pos.xyz * barycentrics.x + v1.pos.xyz * barycentrics.y + v2.pos.xyz * barycentrics.z; 36 | vec3 normal = v0.normal.xyz * barycentrics.x + v1.normal.xyz * barycentrics.y + v2.normal.xyz * barycentrics.z; 37 | vec3 world_position = vec3(position.xyz * gl_ObjectToWorldEXT ); 38 | vec3 world_normal = normalize(vec3(normal.xyz * gl_WorldToObjectEXT)); 39 | 40 | // Flip normal towards the incident ray direction 41 | if (dot(world_normal, gl_WorldRayDirectionEXT) > 0.0f) { 42 | world_normal = -world_normal; 43 | } 44 | 45 | vec2 uv = v0.uv * barycentrics.x + v1.uv * barycentrics.y + v2.uv * barycentrics.z; 46 | 47 | vec3 color = texture(samplerColor[material.diffuse_map], uv).xyz; 48 | color *= material.base_color_factor.rgb; 49 | 50 | if (view.ibl_enabled == 1) 51 | { 52 | PixelParams pixel; 53 | pixel.position = world_position; 54 | pixel.baseColor = color; 55 | pixel.normal = world_normal; 56 | pixel.metallic = texture(samplerColor[material.metallic_roughness_map], uv).b; 57 | pixel.roughness = texture(samplerColor[material.metallic_roughness_map], uv).g; 58 | pixel.occlusion = texture(samplerColor[material.occlusion_map], uv).r; 59 | 60 | rayPayload = imageBasedLighting(pixel, view.eye_pos.xyz, in_irradiance_map, in_specular_map, in_brdf_lut); 61 | } 62 | else { 63 | rayPayload = vec3(0.1) * color; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /utopian/shaders/rt_reflections/rt_reflections.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | #include "include/pbr_lighting.glsl" 7 | 8 | layout(set = 2, binding = 0) uniform accelerationStructureEXT topLevelAS; 9 | layout (set = 2, binding = 1) uniform sampler2D in_gbuffer_position; 10 | layout (set = 2, binding = 2) uniform sampler2D in_gbuffer_normal; 11 | layout (set = 2, binding = 3) uniform sampler2D in_gbuffer_pbr; 12 | layout (set = 2, binding = 4) uniform samplerCube in_irradiance_map; 13 | layout (set = 2, binding = 5) uniform samplerCube in_specular_map; 14 | layout (set = 2, binding = 6) uniform sampler2D in_brdf_lut; 15 | layout(set = 2, binding = 7, rgba8) uniform image2D output_image; 16 | 17 | layout(location = 0) rayPayloadEXT vec3 rayPayload; 18 | 19 | void main() 20 | { 21 | vec3 pixelColor = vec3(1.0); 22 | 23 | const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy); 24 | vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy); 25 | 26 | vec3 origin = texture(in_gbuffer_position, inUV).xyz; 27 | vec3 normal = texture(in_gbuffer_normal, inUV).xyz; 28 | origin.xyz = offsetRay(origin.xyz, normal.xyz); 29 | 30 | vec3 toEye = normalize(view.eye_pos - origin.xyz); 31 | vec3 reflectDir = reflect(-toEye, normal.xyz); 32 | 33 | uint material_index = uint(texture(in_gbuffer_pbr, inUV).a); 34 | Material material = materialsSSBO.materials[material_index]; 35 | 36 | // Metal 37 | if (material.raytrace_properties.x == 1) { 38 | float tmin = 0.001; 39 | float tmax = 10000.0; 40 | 41 | traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 42 | 0xff, 0, 0, 0, origin.xyz, tmin, reflectDir, tmax, 0); 43 | 44 | imageStore(output_image, ivec2(gl_LaunchIDEXT.xy), vec4(rayPayload, 0.0)); 45 | } 46 | else { 47 | imageStore(output_image, ivec2(gl_LaunchIDEXT.xy), vec4(0.0)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /utopian/shaders/rt_reflections/rt_reflections.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/atmosphere.glsl" 5 | #include "include/view.glsl" 6 | 7 | layout(location = 0) rayPayloadInEXT vec3 rayPayload; 8 | 9 | void main() 10 | { 11 | #ifdef FURNACE_TEST 12 | vec3 sky_color = vec3(1.0); 13 | #else 14 | vec3 light_dir = normalize(view.sun_dir); 15 | vec3 transmittance = vec3(0.0); 16 | vec3 sky_color = IntegrateScattering(gl_WorldRayOriginEXT, gl_WorldRayDirectionEXT, 999999999.0f, light_dir, vec3(1.0), transmittance); 17 | 18 | // Todo: we could use the atmosphere cubemap here 19 | 20 | // sky_color is in HDR range so clamp it for now to not get over exposure 21 | sky_color = min(sky_color, vec3(1.0)); 22 | #endif 23 | 24 | rayPayload = sky_color; 25 | } 26 | -------------------------------------------------------------------------------- /utopian/shaders/rt_shadows/rt_shadows.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | #include "include/bindless.glsl" 6 | 7 | layout(location = 0) rayPayloadInEXT bool rayPayload; 8 | 9 | void main() 10 | { 11 | rayPayload = true; 12 | } 13 | -------------------------------------------------------------------------------- /utopian/shaders/rt_shadows/rt_shadows.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/bindless.glsl" 5 | #include "include/view.glsl" 6 | 7 | layout(set = 2, binding = 0) uniform accelerationStructureEXT topLevelAS; 8 | layout (set = 2, binding = 1) uniform sampler2D in_gbuffer_position; 9 | layout (set = 2, binding = 2) uniform sampler2D in_gbuffer_normal; 10 | layout(set = 2, binding = 3, rgba8) uniform image2D output_image; 11 | 12 | layout(location = 0) rayPayloadEXT bool rayPayload; 13 | 14 | void main() 15 | { 16 | vec3 pixelColor = vec3(1.0); 17 | 18 | const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy); 19 | vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy); 20 | 21 | vec3 origin = texture(in_gbuffer_position, inUV).xyz; 22 | vec3 normal = texture(in_gbuffer_normal, inUV).xyz; 23 | origin.xyz = offsetRay(origin.xyz, normal.xyz); 24 | 25 | vec3 light_dir = normalize(view.sun_dir); 26 | 27 | float tmin = 0.001; 28 | float tmax = 10000.0; 29 | 30 | rayPayload = true; 31 | traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT | gl_RayFlagsTerminateOnFirstHitEXT, 32 | 0xff, 0, 0, 0, origin.xyz, tmin, light_dir, tmax, 0); 33 | 34 | if (rayPayload) { 35 | pixelColor = vec3(0.0); 36 | } 37 | 38 | imageStore(output_image, ivec2(gl_LaunchIDEXT.xy), vec4(pixelColor, 0.0)); 39 | } 40 | -------------------------------------------------------------------------------- /utopian/shaders/rt_shadows/rt_shadows.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : enable 3 | 4 | #include "include/atmosphere.glsl" 5 | #include "include/view.glsl" 6 | 7 | layout(location = 0) rayPayloadInEXT bool rayPayload; 8 | 9 | void main() 10 | { 11 | rayPayload = false; 12 | } 13 | -------------------------------------------------------------------------------- /utopian/shaders/shadow/shadow.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | #include "include/pbr_lighting.glsl" 9 | 10 | layout (location = 0) in vec3 in_pos; 11 | layout (location = 1) in vec2 in_uv; 12 | layout (location = 2) in vec3 in_normal; 13 | layout (location = 3) in vec4 in_color; 14 | layout (location = 4) in vec4 in_tangent; 15 | layout (location = 5) in mat3 in_tbn; 16 | 17 | //layout (location = 0) out vec4 out_color; 18 | 19 | void main() { 20 | // Empty 21 | } 22 | 23 | -------------------------------------------------------------------------------- /utopian/shaders/shadow/shadow.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | 9 | layout (location = 0) in vec4 pos; 10 | layout (location = 1) in vec4 normal; 11 | layout (location = 2) in vec2 uv; 12 | layout (location = 3) in vec4 color; 13 | layout (location = 4) in vec4 tangent; 14 | 15 | layout (location = 0) out vec3 out_pos; 16 | layout (location = 1) out vec2 out_uv; 17 | layout (location = 2) out vec3 out_normal; 18 | layout (location = 3) out vec4 out_color; 19 | layout (location = 4) out vec4 out_tangent; 20 | layout (location = 5) out mat3 out_tbn; 21 | 22 | layout(push_constant) uniform PushConsts { 23 | mat4 world; 24 | vec4 color; 25 | uint mesh_index; 26 | ivec3 pad; 27 | } pushConsts; 28 | 29 | layout (std140, set = 2, binding = 0) uniform UBO_parameters 30 | { 31 | mat4 matrix; 32 | } cascade_view_projection; 33 | 34 | void main() { 35 | Mesh mesh = meshesSSBO.meshes[pushConsts.mesh_index]; 36 | Vertex vertex = verticesSSBO[mesh.vertex_buffer].vertices[gl_VertexIndex]; 37 | 38 | #define BINDLESS 39 | #ifdef BINDLESS 40 | vec3 bitangentL = cross(vertex.normal.xyz, vertex.tangent.xyz); 41 | vec3 T = normalize(mat3(pushConsts.world) * vertex.tangent.xyz); 42 | vec3 B = normalize(mat3(pushConsts.world) * bitangentL); 43 | vec3 N = normalize(mat3(pushConsts.world) * vertex.normal.xyz); 44 | out_tbn = mat3(T, B, N); 45 | 46 | out_pos = (pushConsts.world * vec4(vertex.pos.xyz, 1.0)).xyz; 47 | out_uv = vertex.uv; 48 | out_color = vertex.color; 49 | out_normal = mat3(transpose(inverse(pushConsts.world))) * vertex.normal.xyz; 50 | out_tangent = vertex.tangent; 51 | //gl_Position = view.projection * view.view * pushConsts.world * vec4(vertex.pos.xyz, 1.0); 52 | gl_Position = cascade_view_projection.matrix * pushConsts.world * vec4(vertex.pos.xyz, 1.0); 53 | #else 54 | vec3 bitangentL = cross(normal.xyz, tangent.xyz); 55 | vec3 T = normalize(mat3(pushConsts.world) * tangent.xyz); 56 | vec3 B = normalize(mat3(pushConsts.world) * bitangentL); 57 | vec3 N = normalize(mat3(pushConsts.world) * normal.xyz); 58 | out_tbn = mat3(T, B, N); 59 | 60 | out_pos = (pushConsts.world * vec4(pos.xyz, 1.0)).xyz; 61 | out_uv = uv; 62 | out_color = color; 63 | out_normal = mat3(transpose(inverse(pushConsts.world))) * normal.xyz; 64 | out_tangent = tangent; 65 | gl_Position = cascade_view_projection.matrix * pushConsts.world * vec4(pos.xyz, 1.0); 66 | #endif 67 | 68 | } 69 | -------------------------------------------------------------------------------- /utopian/shaders/ssao/blur.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_ARB_separate_shader_objects : enable 4 | #extension GL_ARB_shading_language_420pack : enable 5 | 6 | layout (std140, set = 0, binding = 0) uniform UBO_settings 7 | { 8 | int blurRadius; 9 | } ubo; 10 | 11 | layout (set = 1, binding = 0) uniform sampler2D inputTexture; 12 | 13 | layout (location = 0) in vec2 InTex; 14 | 15 | layout (location = 0) out vec4 OutFragColor; 16 | 17 | void main() 18 | { 19 | int blurRange = ubo.blurRadius; 20 | int n = 0; 21 | vec2 texelSize = 1.0 / vec2(textureSize(inputTexture, 0)); 22 | float result = 0.0; 23 | for (int x = -blurRange; x < blurRange; x++) 24 | { 25 | for (int y = -blurRange; y < blurRange; y++) 26 | { 27 | vec2 offset = vec2(float(x), float(y)) * texelSize; 28 | result += texture(inputTexture, InTex + offset).r; 29 | n++; 30 | } 31 | } 32 | 33 | OutFragColor = vec4(vec3(result / (float(n))), 1.0); 34 | } -------------------------------------------------------------------------------- /utopian/shaders/ssao/ssao.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_GOOGLE_include_directive : enable 5 | 6 | #include "include/bindless.glsl" 7 | #include "include/view.glsl" 8 | 9 | layout (location = 0) in vec2 in_uv; 10 | 11 | layout (location = 0) out vec4 out_color; 12 | 13 | layout (set = 2, binding = 0) uniform sampler2D in_gbuffer_position; 14 | layout (set = 2, binding = 1) uniform sampler2D in_gbuffer_normal; 15 | 16 | const int KERNEL_SIZE = 32; 17 | 18 | // layout (std140, set = 0, binding = 0) uniform UBO_parameters 19 | // { 20 | // vec4 kernelSamples[KERNEL_SIZE]; 21 | // } ubo; 22 | 23 | layout(std140, set = 3, binding = 0) uniform UBO_settings 24 | { 25 | float radius; 26 | float bias; 27 | } settings_ubo; 28 | 29 | // Hardcoded kernel samples for the moment, will be replaced by a random kernel 30 | // These are generated from UtopianEngine 31 | vec4 kernelSamples[KERNEL_SIZE] = vec4[]( 32 | vec4(-0.68217, 0.23565, 0.48243, 0.0), 33 | vec4(-0.14448, 0.01628, 0.22807, 0.0), 34 | vec4(0.00604, 0.01909, 0.0127, 0.0), 35 | vec4(0.09733, 0.39072, 0.7324, 0.0), 36 | vec4(0.06055, 0.87847, 0.33303, 0.0), 37 | vec4(0.00734, 0.19034, 0.13091, 0.0), 38 | vec4(-0.01377, 0.01745, 0.00399, 0.0), 39 | vec4(0.01468, 0.16627, 0.09108, 0.0), 40 | vec4(-0.10093, -0.08015, 0.06625, 0.0), 41 | vec4(-0.27125, -0.39937, 0.0601, 0.0), 42 | vec4(-0.06181, -0.03065, 0.01213, 0.0), 43 | vec4(-0.40189, -0.48095, 0.21808, 0.0), 44 | vec4(0.04027, -0.05818, 0.26542, 0.0), 45 | vec4(-0.33535, -0.07516, 0.24997, 0.0), 46 | vec4(0.32748, -0.18112, 0.27292, 0.0), 47 | vec4(0.53962, -0.03361, 0.58926, 0.0), 48 | vec4(-0.09598, -0.25424, 0.35754, 0.0), 49 | vec4(-0.17368, 0.01261, 0.23964, 0.0), 50 | vec4(0.1283, 0.12573, 0.16467, 0.0), 51 | vec4(-0.34418, 0.19403, 0.70285, 0.0), 52 | vec4(-0.09686, -0.0928, 0.11447, 0.0), 53 | vec4(0.32727, -0.49713, 0.17518, 0.0), 54 | vec4(0.12345, 0.13862, 0.23822, 0.0), 55 | vec4(-0.39258, -0.31128, 0.67374, 0.0), 56 | vec4(0.03308, 0.07616, 0.03422, 0.0), 57 | vec4(-0.31777, 0.1885, 0.40808, 0.0), 58 | vec4(-0.17464, 0.28096, 0.11686, 0.0), 59 | vec4(-0.50199, -0.49002, 0.2709, 0.0), 60 | vec4(0.38629, 0.15627, 0.56716, 0.00), 61 | vec4(0.06649, -0.05762, 0.0857, 0.00), 62 | vec4(-0.1065, -0.11726, 0.10818, 0.00), 63 | vec4(0.53236, -0.5286, 0.45444, 0.00) 64 | ); 65 | 66 | void main() 67 | { 68 | // Note: not sure why this does not need to be flipped with FLIP_UV_Y 69 | vec2 uv = in_uv; 70 | 71 | // Get G-Buffer values 72 | vec3 positionWorld = texture(in_gbuffer_position, uv).xyz; 73 | vec3 fragPosView = (view.view * vec4(positionWorld, 1.0f)).xyz; 74 | 75 | // The position texture is cleared with 1 so this is a way to detect if we are in the skybox 76 | if (positionWorld == vec3(1.0)) { 77 | out_color.r = 1.0; 78 | return; 79 | } 80 | 81 | mat4 normalMatrix = transpose(inverse(view.view)); 82 | vec3 normalWorld = texture(in_gbuffer_normal, uv).rgb; 83 | vec3 normalView = normalize((normalMatrix * vec4(normalWorld, 0.0)).xyz); 84 | 85 | // Todo: 86 | // Get a random vector using a noise lookup 87 | vec3 randomVec = vec3(1, 1, 0); 88 | 89 | // Create TBN matrix 90 | vec3 tangent = normalize(randomVec - normalView * dot(randomVec, normalView)); 91 | vec3 bitangent = cross(tangent, normalView); 92 | mat3 TBN = mat3(tangent, bitangent, normalView); 93 | 94 | // Calculate occlusion value 95 | float occlusion = 0.0f; 96 | for(int i = 0; i < KERNEL_SIZE; i++) 97 | { 98 | vec3 samplePos = TBN * kernelSamples[i].xyz; 99 | samplePos = fragPosView + samplePos * settings_ubo.radius; 100 | 101 | // Project to NDC 102 | vec4 offset = vec4(samplePos, 1.0f); 103 | offset = view.projection * offset; 104 | offset.xyz /= offset.w; 105 | offset.xyz = offset.xyz * 0.5f + 0.5f; 106 | offset.xy = FLIP_UV_Y(offset.xy); 107 | 108 | float sampleDepth = (view.view * vec4(texture(in_gbuffer_position, offset.xy).xyz, 1.0f)).z; 109 | 110 | float rangeCheck = smoothstep(0.0f, 1.0f, settings_ubo.radius / abs(fragPosView.z - sampleDepth)); 111 | occlusion += (sampleDepth >= samplePos.z ? 1.0f : 0.0f) * rangeCheck; 112 | } 113 | 114 | float strength = 1.6; 115 | occlusion = 1.0 - (occlusion / float(KERNEL_SIZE)) * strength; 116 | 117 | out_color.r = occlusion; 118 | } 119 | -------------------------------------------------------------------------------- /utopian/src/bindless.rs: -------------------------------------------------------------------------------- 1 | use crate::device::*; 2 | use ash::vk; 3 | 4 | // RTX 3070 device limit maxDescriptorSetUpdateAfterBindStorageBuffers is 512x512 5 | // so leave 1024 to be used for non-bindless descriptors 6 | pub const MAX_BINDLESS_DESCRIPTOR_COUNT: usize = 512 * 400; 7 | 8 | pub fn create_bindless_descriptor_set_layout(device: &Device) -> vk::DescriptorSetLayout { 9 | let descriptor_set_layout_binding = vec![ 10 | // Textures 11 | vk::DescriptorSetLayoutBinding::builder() 12 | .binding(0) 13 | .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) 14 | .descriptor_count(MAX_BINDLESS_DESCRIPTOR_COUNT as u32) 15 | .stage_flags(vk::ShaderStageFlags::ALL) 16 | .build(), 17 | // Vertex buffers 18 | vk::DescriptorSetLayoutBinding::builder() 19 | .binding(1) 20 | .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) 21 | .descriptor_count(MAX_BINDLESS_DESCRIPTOR_COUNT as u32) 22 | .stage_flags(vk::ShaderStageFlags::ALL) 23 | .build(), 24 | // Index buffers 25 | vk::DescriptorSetLayoutBinding::builder() 26 | .binding(2) 27 | .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) 28 | .descriptor_count(MAX_BINDLESS_DESCRIPTOR_COUNT as u32) 29 | .stage_flags(vk::ShaderStageFlags::ALL) 30 | .build(), 31 | // Materials (not bindless) 32 | vk::DescriptorSetLayoutBinding::builder() 33 | .binding(3) 34 | .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) 35 | .descriptor_count(MAX_BINDLESS_DESCRIPTOR_COUNT as u32) // Hack: actually 1 36 | .stage_flags(vk::ShaderStageFlags::ALL) 37 | .build(), 38 | // Meshes (not bindless) 39 | vk::DescriptorSetLayoutBinding::builder() 40 | .binding(4) 41 | .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) 42 | .descriptor_count(MAX_BINDLESS_DESCRIPTOR_COUNT as u32) // Hack: actually 1 43 | .stage_flags(vk::ShaderStageFlags::ALL) 44 | .build(), 45 | // Lights (not bindless) 46 | vk::DescriptorSetLayoutBinding::builder() 47 | .binding(5) 48 | .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) 49 | .descriptor_count(MAX_BINDLESS_DESCRIPTOR_COUNT as u32) // Hack: actually 1 50 | .stage_flags(vk::ShaderStageFlags::ALL) 51 | .build(), 52 | ]; 53 | 54 | let binding_flags: Vec = vec![ 55 | vk::DescriptorBindingFlags::PARTIALLY_BOUND, 56 | vk::DescriptorBindingFlags::PARTIALLY_BOUND, 57 | vk::DescriptorBindingFlags::PARTIALLY_BOUND, 58 | vk::DescriptorBindingFlags::PARTIALLY_BOUND, 59 | vk::DescriptorBindingFlags::PARTIALLY_BOUND, 60 | vk::DescriptorBindingFlags::PARTIALLY_BOUND 61 | | vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT, 62 | ]; 63 | 64 | let mut binding_flags_create_info = 65 | vk::DescriptorSetLayoutBindingFlagsCreateInfo::builder().binding_flags(&binding_flags); 66 | 67 | let descriptor_sets_layout_info = vk::DescriptorSetLayoutCreateInfo::builder() 68 | .bindings(&descriptor_set_layout_binding) 69 | .flags(vk::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL) 70 | .push_next(&mut binding_flags_create_info) 71 | .build(); 72 | 73 | unsafe { 74 | device 75 | .handle 76 | .create_descriptor_set_layout(&descriptor_sets_layout_info, None) 77 | .expect("Error creating descriptor set layout") 78 | } 79 | } 80 | 81 | pub fn create_bindless_descriptor_set( 82 | device: &Device, 83 | layout: vk::DescriptorSetLayout, 84 | ) -> vk::DescriptorSet { 85 | let descriptor_sizes = [vk::DescriptorPoolSize { 86 | ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 87 | descriptor_count: MAX_BINDLESS_DESCRIPTOR_COUNT as u32, 88 | }]; 89 | 90 | let descriptor_pool_info = vk::DescriptorPoolCreateInfo::builder() 91 | .pool_sizes(&descriptor_sizes) 92 | .flags(vk::DescriptorPoolCreateFlags::UPDATE_AFTER_BIND) 93 | .max_sets(1); 94 | 95 | let descriptor_pool = unsafe { 96 | device 97 | .handle 98 | .create_descriptor_pool(&descriptor_pool_info, None) 99 | .expect("Error allocating bindless descriptor pool") 100 | }; 101 | 102 | let variable_descriptor_count = MAX_BINDLESS_DESCRIPTOR_COUNT as u32; 103 | let mut variable_descriptor_count_allocate_info = 104 | vk::DescriptorSetVariableDescriptorCountAllocateInfo::builder() 105 | .descriptor_counts(std::slice::from_ref(&variable_descriptor_count)) 106 | .build(); 107 | 108 | let descriptor_set = unsafe { 109 | device 110 | .handle 111 | .allocate_descriptor_sets( 112 | &vk::DescriptorSetAllocateInfo::builder() 113 | .descriptor_pool(descriptor_pool) 114 | .set_layouts(std::slice::from_ref(&layout)) 115 | .push_next(&mut variable_descriptor_count_allocate_info) 116 | .build(), 117 | ) 118 | .expect("Error allocating bindless descriptor pool")[0] 119 | }; 120 | 121 | descriptor_set 122 | } 123 | -------------------------------------------------------------------------------- /utopian/src/buffer.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use gpu_allocator::vulkan::*; 3 | 4 | use crate::device::*; 5 | use crate::image::*; 6 | 7 | pub struct Buffer { 8 | pub buffer: vk::Buffer, 9 | pub allocation: Allocation, 10 | pub memory_req: vk::MemoryRequirements, 11 | pub memory_location: gpu_allocator::MemoryLocation, 12 | pub size: u64, 13 | pub debug_name: String, 14 | } 15 | 16 | impl Buffer { 17 | pub fn create_buffer( 18 | device: &Device, 19 | size: u64, // todo: get rid of this 20 | usage_flags: vk::BufferUsageFlags, 21 | memory_location: gpu_allocator::MemoryLocation, 22 | ) -> Buffer { 23 | puffin::profile_function!(); 24 | 25 | unsafe { 26 | let buffer_info = vk::BufferCreateInfo::builder() 27 | .size(size) 28 | .usage(usage_flags) 29 | .sharing_mode(vk::SharingMode::EXCLUSIVE); 30 | 31 | let buffer = device 32 | .handle 33 | .create_buffer(&buffer_info, None) 34 | .expect("Failed to create buffer"); 35 | 36 | let buffer_memory_req = device.handle.get_buffer_memory_requirements(buffer); 37 | 38 | let allocation = device 39 | .gpu_allocator 40 | .lock() 41 | .unwrap() 42 | .allocate(&AllocationCreateDesc { 43 | name: "Example allocation", 44 | requirements: buffer_memory_req, 45 | location: memory_location, 46 | linear: true, 47 | }) 48 | .unwrap(); 49 | 50 | device 51 | .handle 52 | .bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) 53 | .unwrap(); 54 | 55 | Buffer { 56 | buffer, 57 | allocation, 58 | memory_req: buffer_memory_req, 59 | memory_location, 60 | size, 61 | debug_name: String::from("unnamed_buffer"), 62 | } 63 | } 64 | } 65 | 66 | pub fn new( 67 | device: &Device, 68 | initial_data: Option<&[T]>, 69 | size: u64, // todo: get rid of this 70 | usage_flags: vk::BufferUsageFlags, 71 | location: gpu_allocator::MemoryLocation, 72 | ) -> Buffer { 73 | let mut buffer = Buffer::create_buffer( 74 | device, 75 | size, 76 | usage_flags | vk::BufferUsageFlags::TRANSFER_DST, 77 | location, 78 | ); 79 | 80 | if let Some(initial_data) = initial_data { 81 | buffer.update_memory(device, initial_data); 82 | } 83 | 84 | buffer.set_debug_name(device, "unnamed_buffer"); 85 | 86 | buffer 87 | } 88 | 89 | pub fn update_memory(&mut self, device: &Device, data: &[T]) { 90 | unsafe { 91 | let src = data.as_ptr() as *const u8; 92 | let src_bytes = std::mem::size_of_val(data); 93 | 94 | if self.memory_location != gpu_allocator::MemoryLocation::GpuOnly { 95 | let dst = self.allocation.mapped_ptr().unwrap().as_ptr() as *mut u8; 96 | let dst_bytes = self.allocation.size() as usize; 97 | std::ptr::copy_nonoverlapping(src, dst, std::cmp::min(src_bytes, dst_bytes)); 98 | } else { 99 | // This is expensive and should not be done in a hot loop 100 | let staging_buffer = Buffer::create_buffer( 101 | device, 102 | self.size, 103 | vk::BufferUsageFlags::TRANSFER_SRC, 104 | gpu_allocator::MemoryLocation::CpuToGpu, 105 | ); 106 | 107 | let dst = staging_buffer.allocation.mapped_ptr().unwrap().as_ptr() as *mut u8; 108 | let dst_bytes = staging_buffer.allocation.size() as usize; 109 | std::ptr::copy_nonoverlapping(src, dst, std::cmp::min(src_bytes, dst_bytes)); 110 | 111 | device.execute_and_submit(|device, cb| { 112 | let regions = vk::BufferCopy::builder() 113 | .size(self.size) 114 | .src_offset(0) 115 | .dst_offset(0) 116 | .build(); 117 | 118 | device.handle.cmd_copy_buffer( 119 | cb, 120 | staging_buffer.buffer, 121 | self.buffer, 122 | &[regions], 123 | ); 124 | }); 125 | 126 | device 127 | .gpu_allocator 128 | .lock() 129 | .unwrap() 130 | .free(staging_buffer.allocation) 131 | .unwrap(); 132 | device.handle.destroy_buffer(staging_buffer.buffer, None); 133 | } 134 | } 135 | } 136 | 137 | pub fn copy_to_buffer(&self, device: &Device, cb: vk::CommandBuffer, dst: &Buffer) { 138 | let buffer_copy_regions = vk::BufferCopy::builder() 139 | .size(self.size) 140 | .src_offset(0) 141 | .dst_offset(0) 142 | .build(); 143 | 144 | unsafe { 145 | device 146 | .handle 147 | .cmd_copy_buffer(cb, self.buffer, dst.buffer, &[buffer_copy_regions]); 148 | } 149 | } 150 | 151 | pub fn copy_to_image(&self, device: &Device, cb: vk::CommandBuffer, image: &Image) { 152 | let buffer_copy_regions = vk::BufferImageCopy::builder() 153 | .image_subresource( 154 | vk::ImageSubresourceLayers::builder() 155 | .aspect_mask(image.desc.aspect_flags) 156 | .layer_count(1) 157 | .build(), 158 | ) 159 | .image_extent(vk::Extent3D { 160 | width: image.width(), 161 | height: image.height(), 162 | depth: 1, 163 | }); 164 | 165 | unsafe { 166 | device.handle.cmd_copy_buffer_to_image( 167 | cb, 168 | self.buffer, 169 | image.image, 170 | vk::ImageLayout::TRANSFER_DST_OPTIMAL, 171 | &[buffer_copy_regions.build()], 172 | ); 173 | } 174 | } 175 | 176 | pub fn get_device_address(&self, device: &Device) -> vk::DeviceAddress { 177 | let info = vk::BufferDeviceAddressInfo::builder() 178 | .buffer(self.buffer) 179 | .build(); 180 | 181 | unsafe { device.handle.get_buffer_device_address(&info) } 182 | } 183 | 184 | pub fn set_debug_name(&mut self, device: &Device, name: &str) { 185 | self.debug_name = String::from(name); 186 | device.set_debug_name( 187 | vk::Handle::as_raw(self.buffer), 188 | vk::ObjectType::BUFFER, 189 | name, 190 | ); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /utopian/src/camera.rs: -------------------------------------------------------------------------------- 1 | use dolly::prelude::*; 2 | use glam::{Mat3, Mat4, Quat, Vec3}; 3 | 4 | use crate::Input; 5 | 6 | pub struct Camera { 7 | camera_rig: CameraRig, 8 | fov_degrees: f32, 9 | aspect_ratio: f32, 10 | z_near: f32, 11 | z_far: f32, 12 | speed: f32, 13 | } 14 | 15 | impl Camera { 16 | pub fn new( 17 | pos: Vec3, 18 | target: Vec3, 19 | fov_degrees: f32, 20 | aspect_ratio: f32, 21 | z_near: f32, 22 | z_far: f32, 23 | speed: f32, 24 | ) -> Camera { 25 | let rotation = Self::get_lookat_rotation(pos, target); 26 | 27 | let camera_rig = CameraRig::builder() 28 | .with(Position::new(pos)) 29 | .with(YawPitch::new().rotation_quat(rotation)) 30 | .with(Smooth::new_position_rotation(1.0, 1.0)) 31 | .build(); 32 | 33 | Camera { 34 | camera_rig, 35 | fov_degrees, 36 | aspect_ratio, 37 | z_near, 38 | z_far, 39 | speed, 40 | } 41 | } 42 | 43 | pub fn get_lookat_rotation(pos: Vec3, target: Vec3) -> Quat { 44 | // Rotation calculation from 45 | // https://github.com/h3r2tic/dolly/blob/main/src/drivers/look_at.rs 46 | 47 | (target - pos) 48 | .try_normalize() 49 | .and_then(|forward| { 50 | let right = forward.cross(Vec3::Y).try_normalize()?; 51 | let up = right.cross(forward); 52 | Some(Quat::from_mat3(&Mat3::from_cols(right, up, -forward))) 53 | }) 54 | .unwrap_or_default() 55 | } 56 | 57 | pub fn update(&mut self, input: &Input) -> bool { 58 | let transform = self.camera_rig.final_transform; 59 | 60 | let mut movement = Vec3::new(0.0, 0.0, 0.0); 61 | if input.key_down(winit::event::VirtualKeyCode::W) { 62 | movement += self.speed * transform.forward(); 63 | } 64 | if input.key_down(winit::event::VirtualKeyCode::S) { 65 | movement -= self.speed * transform.forward(); 66 | } 67 | if input.key_down(winit::event::VirtualKeyCode::A) { 68 | movement -= self.speed * transform.right(); 69 | } 70 | if input.key_down(winit::event::VirtualKeyCode::D) { 71 | movement += self.speed * transform.right(); 72 | } 73 | 74 | self.camera_rig.driver_mut::().translate(movement); 75 | 76 | let mut view_changed = false; 77 | if input.right_mouse_down { 78 | self.camera_rig 79 | .driver_mut::() 80 | .rotate_yaw_pitch(-0.3 * input.mouse_delta.x, -0.3 * input.mouse_delta.y); 81 | view_changed = true; 82 | } 83 | 84 | // Todo: proper frame delta time 85 | self.camera_rig.update(1.0); 86 | 87 | movement != Vec3::new(0.0, 0.0, 0.0) || view_changed 88 | } 89 | 90 | pub fn get_view(&self) -> Mat4 { 91 | let transform = self.camera_rig.final_transform; 92 | 93 | glam::Mat4::look_at_rh( 94 | transform.position, 95 | transform.position + transform.forward(), 96 | transform.up(), 97 | ) 98 | } 99 | 100 | pub fn get_projection(&self) -> Mat4 { 101 | glam::Mat4::perspective_rh( 102 | f32::to_radians(self.fov_degrees), 103 | self.aspect_ratio, 104 | self.z_near, 105 | self.z_far, 106 | ) 107 | } 108 | 109 | pub fn get_position(&self) -> Vec3 { 110 | self.camera_rig.final_transform.position 111 | } 112 | 113 | pub fn get_forward(&self) -> Vec3 { 114 | self.camera_rig.final_transform.forward() 115 | } 116 | 117 | pub fn set_position_target(&mut self, position: Vec3, target: Vec3) { 118 | self.camera_rig.driver_mut::().position = position; 119 | 120 | let rotation = Self::get_lookat_rotation(position, target); 121 | self.camera_rig 122 | .driver_mut::() 123 | .set_rotation_quat(rotation); 124 | } 125 | 126 | pub(crate) fn get_near_plane(&self) -> f32 { 127 | self.z_near 128 | } 129 | 130 | pub(crate) fn get_far_plane(&self) -> f32 { 131 | self.z_far 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /utopian/src/directory_watcher.rs: -------------------------------------------------------------------------------- 1 | use notify::{RecommendedWatcher, RecursiveMode, Watcher}; 2 | use std::path::Path; 3 | use std::sync::mpsc; 4 | use std::time::Duration; 5 | 6 | pub struct DirectoryWatcher { 7 | _directory_watcher: RecommendedWatcher, 8 | watcher_rx: mpsc::Receiver, 9 | } 10 | 11 | impl DirectoryWatcher { 12 | pub fn new>(path: P) -> Self { 13 | let (watcher_tx, watcher_rx) = mpsc::channel(); 14 | let mut directory_watcher: RecommendedWatcher = 15 | Watcher::new(watcher_tx, Duration::from_millis(100)).unwrap(); 16 | directory_watcher 17 | .watch(path, RecursiveMode::Recursive) 18 | .unwrap(); 19 | 20 | DirectoryWatcher { 21 | _directory_watcher: directory_watcher, 22 | watcher_rx, 23 | } 24 | } 25 | 26 | pub fn check_if_modification(&self) -> Option { 27 | if let Ok(_event) = self.watcher_rx.try_recv() { 28 | match self.watcher_rx.recv() { 29 | Ok(event) => { 30 | if let notify::DebouncedEvent::Write(path) = event { 31 | //println!("File {:?} modified", path); 32 | return Some(path); 33 | } 34 | } 35 | Err(e) => log::error!("recv Err {:?}", e), 36 | } 37 | } 38 | 39 | None 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /utopian/src/fps_timer.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | pub struct FpsTimer { 3 | fps_period_start_time: Instant, 4 | application_start_time: Instant, 5 | fps: u32, 6 | elapsed_frames: u32, 7 | } 8 | 9 | impl Default for FpsTimer { 10 | fn default() -> Self { 11 | Self::new() 12 | } 13 | } 14 | 15 | impl FpsTimer { 16 | pub fn new() -> Self { 17 | FpsTimer { 18 | fps_period_start_time: Instant::now(), 19 | application_start_time: Instant::now(), 20 | fps: 0, 21 | elapsed_frames: 0, 22 | } 23 | } 24 | 25 | pub fn calculate(&mut self) -> u32 { 26 | self.elapsed_frames += 1; 27 | let elapsed = self.fps_period_start_time.elapsed().as_millis() as u32; 28 | if elapsed > 1000 { 29 | self.fps = self.elapsed_frames; 30 | self.fps_period_start_time = Instant::now(); 31 | self.elapsed_frames = 0; 32 | } 33 | 34 | self.fps 35 | } 36 | 37 | pub fn elapsed_seconds_from_start(&self) -> f32 { 38 | self.application_start_time.elapsed().as_secs_f32() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /utopian/src/input.rs: -------------------------------------------------------------------------------- 1 | use glam::Vec2; 2 | use std::collections::HashMap; 3 | use winit::dpi::PhysicalPosition; 4 | use winit::event::WindowEvent; 5 | use winit::event::{ElementState, VirtualKeyCode}; 6 | 7 | pub struct Input { 8 | key_states: HashMap, 9 | prev_key_states: HashMap, 10 | pub mouse_pos: PhysicalPosition, 11 | pub mouse_delta: Vec2, 12 | pub right_mouse_down: bool, 13 | } 14 | 15 | impl Default for Input { 16 | fn default() -> Input { 17 | Input { 18 | key_states: HashMap::new(), 19 | prev_key_states: HashMap::new(), 20 | mouse_pos: PhysicalPosition { x: 0.0, y: 0.0 }, 21 | mouse_delta: Vec2::new(0.0, 0.0), 22 | right_mouse_down: false, 23 | } 24 | } 25 | } 26 | 27 | impl Input { 28 | pub fn update(&mut self, events: &[WindowEvent]) { 29 | self.prev_key_states = self.key_states.clone(); 30 | let prev_mouse_pos = self.mouse_pos; 31 | 32 | for event in events { 33 | if let WindowEvent::KeyboardInput { input, .. } = event { 34 | if let Some(vk) = input.virtual_keycode { 35 | if input.state == ElementState::Pressed { 36 | self.key_states.entry(vk).or_insert(true); 37 | } else { 38 | self.key_states.remove(&vk); 39 | } 40 | } 41 | } 42 | if let WindowEvent::CursorMoved { position, .. } = event { 43 | self.mouse_pos = *position; 44 | } 45 | if let WindowEvent::MouseInput { state, button, .. } = event { 46 | if *button == winit::event::MouseButton::Right && *state == ElementState::Pressed { 47 | self.right_mouse_down = true; 48 | } 49 | if *button == winit::event::MouseButton::Right && *state == ElementState::Released { 50 | self.right_mouse_down = false; 51 | } 52 | } 53 | } 54 | 55 | // Ignore big delta in the first call 56 | if prev_mouse_pos.x != 0.0 && prev_mouse_pos.y != 0.0 { 57 | self.mouse_delta = Vec2::new( 58 | (self.mouse_pos.x - prev_mouse_pos.x) as f32, 59 | (self.mouse_pos.y - prev_mouse_pos.y) as f32, 60 | ); 61 | } 62 | } 63 | 64 | pub fn key_pressed(&self, key: VirtualKeyCode) -> bool { 65 | self.key_states.contains_key(&key) && !self.prev_key_states.contains_key(&key) 66 | } 67 | 68 | pub fn key_down(&self, key: VirtualKeyCode) -> bool { 69 | self.key_states.contains_key(&key) 70 | } 71 | 72 | pub fn right_mouse_down(&self) -> bool { 73 | self.right_mouse_down 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /utopian/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod bindless; 2 | pub mod buffer; 3 | pub mod camera; 4 | pub mod descriptor_set; 5 | pub mod device; 6 | pub mod directory_watcher; 7 | pub mod fps_timer; 8 | pub mod gltf_loader; 9 | pub mod graph; 10 | pub mod image; 11 | pub mod input; 12 | pub mod model_loader; 13 | pub mod pass; 14 | pub mod pipeline; 15 | pub mod primitive; 16 | pub mod profiler_backend; 17 | pub mod raytracing; 18 | pub mod render_utils; 19 | pub mod renderer; 20 | pub mod renderers; 21 | pub mod shader; 22 | pub mod synch; 23 | pub mod texture; 24 | pub mod vulkan_base; 25 | 26 | pub use crate::image::Image; 27 | pub use crate::image::ImageCopyDescBuilder; 28 | pub use bindless::*; 29 | pub use buffer::Buffer; 30 | pub use camera::Camera; 31 | pub use descriptor_set::DescriptorSet; 32 | pub use device::Device; 33 | pub use directory_watcher::DirectoryWatcher; 34 | pub use fps_timer::FpsTimer; 35 | pub use gltf_loader::Model; 36 | pub use gltf_loader::DEFAULT_TEXTURE_MAP; 37 | pub use graph::Graph; 38 | pub use graph::TextureId; 39 | pub use input::Input; 40 | pub use model_loader::ModelLoader; 41 | pub use pass::RenderPass; 42 | pub use pipeline::Pipeline; 43 | pub use pipeline::PipelineDesc; 44 | pub use pipeline::PipelineDescBuilder; 45 | pub use primitive::Primitive; 46 | pub use primitive::Vertex; 47 | pub use raytracing::Raytracing; 48 | pub use renderer::Renderer; 49 | pub use renderer::*; 50 | pub use texture::Texture; 51 | pub use vulkan_base::VulkanBase; 52 | -------------------------------------------------------------------------------- /utopian/src/model_loader.rs: -------------------------------------------------------------------------------- 1 | use glam::{Mat4, Vec2, Vec4}; 2 | 3 | use crate::device::*; 4 | use crate::gltf_loader::*; 5 | use crate::primitive::*; 6 | use crate::Model; 7 | 8 | pub struct ModelLoader {} 9 | 10 | pub fn add_triangle(indices: &mut Vec, v1: u32, v2: u32, v3: u32) { 11 | indices.push(v1); 12 | indices.push(v2); 13 | indices.push(v3); 14 | } 15 | 16 | #[allow(clippy::too_many_arguments)] 17 | pub fn add_vertex( 18 | vertices: &mut Vec, 19 | x: f32, 20 | y: f32, 21 | z: f32, 22 | nx: f32, 23 | ny: f32, 24 | nz: f32, 25 | u: f32, 26 | v: f32, 27 | ) { 28 | vertices.push(Vertex { 29 | pos: Vec4::new(x, y, z, 0.0), 30 | normal: Vec4::new(nx, ny, nz, 0.0), 31 | uv: Vec2::new(u, v), 32 | color: Vec4::new(1.0, 1.0, 1.0, 1.0), 33 | tangent: Vec4::new(0.0, 0.0, 0.0, 0.0), 34 | }); 35 | } 36 | 37 | impl ModelLoader { 38 | pub fn load_triangle(device: &Device) -> Model { 39 | let indices = vec![0, 1, 2]; 40 | 41 | let mut vertices = vec![]; 42 | add_vertex(&mut vertices, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0); 43 | add_vertex(&mut vertices, -1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0); 44 | add_vertex(&mut vertices, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0); 45 | 46 | Model { 47 | meshes: vec![Mesh { 48 | primitive: Primitive::new(device, indices, vertices), 49 | material: Material { 50 | diffuse_map: DEFAULT_TEXTURE_MAP, 51 | normal_map: DEFAULT_TEXTURE_MAP, 52 | metallic_roughness_map: DEFAULT_TEXTURE_MAP, 53 | occlusion_map: DEFAULT_TEXTURE_MAP, 54 | base_color_factor: Vec4::new(1.0, 1.0, 1.0, 1.0), 55 | metallic_factor: 0.0, 56 | roughness_factor: 0.5, 57 | material_type: MaterialType::Lambertian, 58 | material_property: 0.0, 59 | }, 60 | gpu_mesh: 0, 61 | }], 62 | transforms: vec![Mat4::IDENTITY], 63 | textures: vec![], 64 | } 65 | } 66 | 67 | pub fn load_cube(device: &Device) -> Model { 68 | let mut model = Model { 69 | meshes: vec![], 70 | transforms: vec![], 71 | textures: vec![], 72 | }; 73 | 74 | let mut indices = vec![]; 75 | let mut vertices = vec![]; 76 | 77 | // Front 78 | add_triangle(&mut indices, 2, 0, 1); 79 | add_triangle(&mut indices, 0, 2, 3); 80 | 81 | // Back 82 | add_triangle(&mut indices, 4, 6, 5); 83 | add_triangle(&mut indices, 6, 4, 7); 84 | 85 | // Top 86 | add_triangle(&mut indices, 10, 8, 9); 87 | add_triangle(&mut indices, 8, 10, 11); 88 | 89 | // Bottom 90 | add_triangle(&mut indices, 12, 14, 13); 91 | add_triangle(&mut indices, 14, 12, 15); 92 | 93 | // Let 94 | add_triangle(&mut indices, 16, 18, 17); 95 | add_triangle(&mut indices, 18, 16, 19); 96 | 97 | // Right 98 | add_triangle(&mut indices, 22, 20, 21); 99 | add_triangle(&mut indices, 20, 22, 23); 100 | 101 | // Front 102 | add_vertex(&mut vertices, -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 1.0); 103 | add_vertex(&mut vertices, 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0); 104 | add_vertex(&mut vertices, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 0.0); 105 | add_vertex(&mut vertices, -0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0); 106 | 107 | // Back 108 | add_vertex(&mut vertices, -0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 0.0, 1.0); 109 | add_vertex(&mut vertices, 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 1.0); 110 | add_vertex(&mut vertices, 0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 0.0); 111 | add_vertex(&mut vertices, -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 0.0, 0.0); 112 | 113 | // Top 114 | add_vertex(&mut vertices, -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 0.0, 1.0); 115 | add_vertex(&mut vertices, 0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 1.0, 1.0); 116 | add_vertex(&mut vertices, 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, 1.0, 0.0); 117 | add_vertex(&mut vertices, -0.5, -0.5, 0.5, 0.0, -1.0, 0.0, 0.0, 0.0); 118 | 119 | // Bottom 120 | add_vertex(&mut vertices, -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 0.0, 1.0); 121 | add_vertex(&mut vertices, 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0, 1.0); 122 | add_vertex(&mut vertices, 0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 0.0); 123 | add_vertex(&mut vertices, -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 0.0); 124 | 125 | // Let 126 | add_vertex(&mut vertices, -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, 0.0, 1.0); 127 | add_vertex(&mut vertices, -0.5, 0.5, -0.5, -1.0, 0.0, 0.0, 1.0, 1.0); 128 | add_vertex(&mut vertices, -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, 1.0, 0.0); 129 | add_vertex(&mut vertices, -0.5, -0.5, 0.5, -1.0, 0.0, 0.0, 0.0, 0.0); 130 | 131 | // Right 132 | add_vertex(&mut vertices, 0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.0, 1.0); 133 | add_vertex(&mut vertices, 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 1.0, 1.0); 134 | add_vertex(&mut vertices, 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 0.0); 135 | add_vertex(&mut vertices, 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0.0); 136 | 137 | model.meshes.push(Mesh { 138 | primitive: Primitive::new(device, indices, vertices), 139 | material: Material { 140 | diffuse_map: DEFAULT_TEXTURE_MAP, 141 | normal_map: DEFAULT_TEXTURE_MAP, 142 | metallic_roughness_map: DEFAULT_TEXTURE_MAP, 143 | occlusion_map: DEFAULT_TEXTURE_MAP, 144 | base_color_factor: Vec4::new(1.0, 1.0, 1.0, 1.0), 145 | metallic_factor: 0.0, 146 | roughness_factor: 0.5, 147 | material_type: MaterialType::Lambertian, 148 | material_property: 0.0, 149 | }, 150 | gpu_mesh: 0, 151 | }); 152 | model.transforms.push(Mat4::IDENTITY); 153 | 154 | model 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /utopian/src/primitive.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use glam::{Vec2, Vec4}; 3 | use std::mem; 4 | 5 | use crate::buffer::*; 6 | use crate::device::*; 7 | use crate::offset_of; 8 | 9 | #[derive(Clone, Copy)] 10 | #[repr(C)] 11 | pub struct Vertex { 12 | pub pos: Vec4, 13 | pub normal: Vec4, 14 | pub uv: Vec2, 15 | pub color: Vec4, 16 | pub tangent: Vec4, 17 | } 18 | 19 | pub struct Primitive { 20 | pub index_buffer: Buffer, 21 | pub vertex_buffer: Buffer, 22 | pub indices: Vec, 23 | pub vertices: Vec, 24 | } 25 | 26 | impl Vertex { 27 | pub fn new(x: f32, y: f32, z: f32) -> Vertex { 28 | Vertex { 29 | pos: Vec4::new(x, y, z, 0.0), 30 | normal: Vec4::new(0.0, 0.0, 0.0, 0.0), 31 | uv: Vec2::new(0.0, 0.0), 32 | color: Vec4::new(1.0, 1.0, 1.0, 1.0), 33 | tangent: Vec4::new(0.0, 0.0, 0.0, 0.0), 34 | } 35 | } 36 | } 37 | 38 | impl Primitive { 39 | pub fn new(device: &Device, indices: Vec, vertices: Vec) -> Primitive { 40 | let index_buffer = Buffer::new( 41 | device, 42 | Some(indices.as_slice()), 43 | std::mem::size_of_val(&*indices) as u64, 44 | vk::BufferUsageFlags::INDEX_BUFFER 45 | | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS 46 | | vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR 47 | | vk::BufferUsageFlags::STORAGE_BUFFER, 48 | gpu_allocator::MemoryLocation::GpuOnly, 49 | ); 50 | 51 | let vertex_buffer = Buffer::new( 52 | device, 53 | Some(vertices.as_slice()), 54 | std::mem::size_of_val(&*vertices) as u64, 55 | vk::BufferUsageFlags::VERTEX_BUFFER 56 | | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS 57 | | vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR 58 | | vk::BufferUsageFlags::STORAGE_BUFFER, 59 | gpu_allocator::MemoryLocation::GpuOnly, 60 | ); 61 | 62 | // Todo: device local index and vertex buffers 63 | 64 | Primitive { 65 | index_buffer, 66 | vertex_buffer, 67 | indices, 68 | vertices, 69 | } 70 | } 71 | 72 | pub fn get_vertex_input_binding_descriptions() -> Vec { 73 | [vk::VertexInputBindingDescription { 74 | binding: 0, 75 | stride: mem::size_of::() as u32, 76 | input_rate: vk::VertexInputRate::VERTEX, 77 | }] 78 | .to_vec() 79 | } 80 | 81 | pub fn get_vertex_input_attribute_descriptions() -> Vec { 82 | [ 83 | vk::VertexInputAttributeDescription { 84 | location: 0, 85 | binding: 0, 86 | format: vk::Format::R32G32B32A32_SFLOAT, 87 | offset: offset_of!(Vertex, pos) as u32, 88 | }, 89 | vk::VertexInputAttributeDescription { 90 | location: 1, 91 | binding: 0, 92 | format: vk::Format::R32G32B32A32_SFLOAT, 93 | offset: offset_of!(Vertex, normal) as u32, 94 | }, 95 | vk::VertexInputAttributeDescription { 96 | location: 2, 97 | binding: 0, 98 | format: vk::Format::R32G32_SFLOAT, 99 | offset: offset_of!(Vertex, uv) as u32, 100 | }, 101 | vk::VertexInputAttributeDescription { 102 | location: 3, 103 | binding: 0, 104 | format: vk::Format::R32G32B32A32_SFLOAT, 105 | offset: offset_of!(Vertex, color) as u32, 106 | }, 107 | vk::VertexInputAttributeDescription { 108 | location: 4, 109 | binding: 0, 110 | format: vk::Format::R32G32B32A32_SFLOAT, 111 | offset: offset_of!(Vertex, tangent) as u32, 112 | }, 113 | ] 114 | .to_vec() 115 | } 116 | 117 | pub fn get_vertex_input_create_info() -> vk::PipelineVertexInputStateCreateInfo { 118 | let vertex_input_binding_descriptions = [vk::VertexInputBindingDescription { 119 | binding: 0, 120 | stride: mem::size_of::() as u32, 121 | input_rate: vk::VertexInputRate::VERTEX, 122 | }]; 123 | 124 | let vertex_input_attribute_descriptions = [ 125 | vk::VertexInputAttributeDescription { 126 | location: 0, 127 | binding: 0, 128 | format: vk::Format::R32G32B32A32_SFLOAT, 129 | offset: offset_of!(Vertex, pos) as u32, 130 | }, 131 | vk::VertexInputAttributeDescription { 132 | location: 1, 133 | binding: 0, 134 | format: vk::Format::R32G32B32A32_SFLOAT, 135 | offset: offset_of!(Vertex, normal) as u32, 136 | }, 137 | vk::VertexInputAttributeDescription { 138 | location: 2, 139 | binding: 0, 140 | format: vk::Format::R32G32_SFLOAT, 141 | offset: offset_of!(Vertex, uv) as u32, 142 | }, 143 | vk::VertexInputAttributeDescription { 144 | location: 3, 145 | binding: 0, 146 | format: vk::Format::R32G32B32A32_SFLOAT, 147 | offset: offset_of!(Vertex, color) as u32, 148 | }, 149 | vk::VertexInputAttributeDescription { 150 | location: 4, 151 | binding: 0, 152 | format: vk::Format::R32G32B32A32_SFLOAT, 153 | offset: offset_of!(Vertex, tangent) as u32, 154 | }, 155 | ]; 156 | 157 | let vertex_input_state_info = vk::PipelineVertexInputStateCreateInfo::builder() 158 | .vertex_attribute_descriptions(&vertex_input_attribute_descriptions) 159 | .vertex_binding_descriptions(&vertex_input_binding_descriptions) 160 | .build(); 161 | 162 | vertex_input_state_info 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /utopian/src/profiler_backend.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use gpu_profiler::backend::ash::VulkanProfilerFrame; 3 | 4 | pub struct ProfilerBuffer { 5 | buffer: vk::Buffer, 6 | allocation: gpu_allocator::vulkan::Allocation, 7 | } 8 | 9 | pub struct ProfilerBackend<'dev, 'alloc> { 10 | device: &'dev ash::Device, 11 | allocator: &'alloc mut gpu_allocator::vulkan::Allocator, 12 | timestamp_period: f32, 13 | } 14 | 15 | impl<'device, 'alloc> ProfilerBackend<'device, 'alloc> { 16 | pub fn new( 17 | device: &'device ash::Device, 18 | allocator: &'alloc mut gpu_allocator::vulkan::Allocator, 19 | timestamp_period: f32, 20 | ) -> ProfilerBackend<'device, 'alloc> { 21 | Self { 22 | device, 23 | allocator, 24 | timestamp_period, 25 | } 26 | } 27 | } 28 | 29 | impl gpu_profiler::backend::ash::VulkanBackend for ProfilerBackend<'_, '_> { 30 | type Buffer = ProfilerBuffer; 31 | 32 | fn create_query_result_buffer(&mut self, bytes: usize) -> Self::Buffer { 33 | let usage = vk::BufferUsageFlags::TRANSFER_DST; 34 | 35 | let buffer_info = vk::BufferCreateInfo { 36 | size: bytes as u64, 37 | usage, 38 | sharing_mode: vk::SharingMode::EXCLUSIVE, 39 | ..Default::default() 40 | }; 41 | 42 | let buffer = unsafe { 43 | self.device 44 | .create_buffer(&buffer_info, None) 45 | .expect("create_buffer") 46 | }; 47 | let requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) }; 48 | 49 | let allocation = self 50 | .allocator 51 | .allocate(&gpu_allocator::vulkan::AllocationCreateDesc { 52 | name: "buffer", 53 | requirements, 54 | location: gpu_allocator::MemoryLocation::GpuToCpu, 55 | linear: true, // Buffers are always linear 56 | }) 57 | .unwrap(); 58 | 59 | // Bind memory to the buffer 60 | unsafe { 61 | self.device 62 | .bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) 63 | .expect("bind_buffer_memory") 64 | }; 65 | 66 | ProfilerBuffer { buffer, allocation } 67 | } 68 | 69 | fn timestamp_period(&self) -> f32 { 70 | self.timestamp_period 71 | } 72 | } 73 | 74 | impl gpu_profiler::backend::ash::VulkanBuffer for ProfilerBuffer { 75 | fn mapped_slice(&self) -> &[u8] { 76 | self.allocation.mapped_slice().unwrap() 77 | } 78 | 79 | fn raw(&self) -> ash::vk::Buffer { 80 | self.buffer 81 | } 82 | } 83 | 84 | pub type VkProfilerData = VulkanProfilerFrame; 85 | -------------------------------------------------------------------------------- /utopian/src/render_utils.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | 3 | // Flip along Y axis https://www.saschawillems.de/blog/2019/03/29/flipping-the-vulkan-viewport/ 4 | pub fn viewport(width: u32, height: u32) -> vk::Viewport { 5 | vk::Viewport { 6 | x: 0.0, 7 | y: height as f32, 8 | width: width as f32, 9 | height: -(height as f32), 10 | min_depth: 0.0, 11 | max_depth: 1.0, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /utopian/src/renderers/atmosphere.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use glam::{Mat4, Vec3}; 3 | 4 | pub fn setup_atmosphere_pass( 5 | device: &crate::Device, 6 | graph: &mut crate::Graph, 7 | base: &crate::VulkanBase, 8 | atmosphere_output: crate::TextureId, 9 | environment_map: crate::TextureId, 10 | camera: &crate::camera::Camera, 11 | enabled: bool, 12 | ) { 13 | puffin::profile_function!(); 14 | 15 | let projection = camera.get_projection(); 16 | let world = Mat4::from_scale(Vec3::splat(1000.0)); 17 | 18 | graph 19 | .add_pass_from_desc( 20 | "atmosphere_pass", 21 | crate::PipelineDesc::builder() 22 | .vertex_path("utopian/shaders/atmosphere/atmosphere.vert") 23 | .fragment_path("utopian/shaders/atmosphere/atmosphere.frag") 24 | .default_primitive_vertex_bindings() 25 | .default_primitive_vertex_attributes(), 26 | ) 27 | .load_write(atmosphere_output) 28 | .read(environment_map) 29 | .uniforms("ubo_constants", &(projection, world)) 30 | .external_depth_attachment(base.depth_image.clone(), vk::AttachmentLoadOp::LOAD) 31 | .render( 32 | move |device, command_buffer, _renderer, _pass, _resources| unsafe { 33 | // Todo: This is a hack to get around the fact that we can't properly disable a pass 34 | // Todo: Using the first model instanced added to the scene 35 | if enabled { 36 | device.handle.cmd_bind_vertex_buffers( 37 | command_buffer, 38 | 0, 39 | &[_renderer.instances[0].model.meshes[0] 40 | .primitive 41 | .vertex_buffer 42 | .buffer], 43 | &[0], 44 | ); 45 | device.handle.cmd_bind_index_buffer( 46 | command_buffer, 47 | _renderer.instances[0].model.meshes[0] 48 | .primitive 49 | .index_buffer 50 | .buffer, 51 | 0, 52 | vk::IndexType::UINT32, 53 | ); 54 | device.handle.cmd_draw_indexed( 55 | command_buffer, 56 | _renderer.instances[0].model.meshes[0] 57 | .primitive 58 | .indices 59 | .len() as u32, 60 | 1, 61 | 0, 62 | 0, 63 | 1, 64 | ); 65 | } 66 | }, 67 | ) 68 | .build(device, graph); 69 | } 70 | -------------------------------------------------------------------------------- /utopian/src/renderers/deferred.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | struct PushConstants { 3 | world: glam::Mat4, 4 | color: glam::Vec4, 5 | mesh_index: u32, 6 | pad: [u32; 3], 7 | } 8 | 9 | #[allow(clippy::too_many_arguments)] 10 | pub fn setup_deferred_pass( 11 | device: &crate::Device, 12 | graph: &mut crate::Graph, 13 | gbuffer_position: crate::TextureId, 14 | gbuffer_normal: crate::TextureId, 15 | gbuffer_albedo: crate::TextureId, 16 | gbuffer_pbr: crate::TextureId, 17 | shadow_map: crate::TextureId, 18 | rt_shadows: crate::TextureId, 19 | rt_reflections: crate::TextureId, 20 | ssao_output: crate::TextureId, 21 | irradiance_map: crate::TextureId, 22 | specular_map: crate::TextureId, 23 | brdf_lut: crate::TextureId, 24 | cascade_data: ([glam::Mat4; 4], [f32; 4]), 25 | deferred_output: crate::TextureId, 26 | ) { 27 | puffin::profile_function!(); 28 | 29 | graph 30 | .add_pass_from_desc( 31 | "deferred_pass", 32 | crate::PipelineDesc::builder() 33 | .vertex_path("utopian/shaders/common/fullscreen.vert") 34 | .fragment_path("utopian/shaders/deferred/deferred.frag"), 35 | ) 36 | .read(gbuffer_position) 37 | .read(gbuffer_normal) 38 | .read(gbuffer_albedo) 39 | .read(gbuffer_pbr) 40 | .read(shadow_map) 41 | .read(rt_shadows) 42 | .read(rt_reflections) 43 | .read(ssao_output) 44 | .read(irradiance_map) 45 | .read(specular_map) 46 | .read(brdf_lut) 47 | .write(deferred_output) 48 | .uniforms("shadowmapParams", &(cascade_data)) 49 | .render( 50 | move |device, command_buffer, _renderer, _pass, _resources| unsafe { 51 | device.handle.cmd_draw(command_buffer, 3, 1, 0, 0); 52 | }, 53 | ) 54 | .build(device, graph); 55 | } 56 | -------------------------------------------------------------------------------- /utopian/src/renderers/forward.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | 3 | #[allow(dead_code)] 4 | struct PushConstants { 5 | world: glam::Mat4, 6 | color: glam::Vec4, 7 | mesh_index: u32, 8 | pad: [u32; 3], 9 | } 10 | 11 | pub fn setup_forward_pass( 12 | device: &crate::Device, 13 | graph: &mut crate::Graph, 14 | base: &crate::VulkanBase, 15 | forward_output: crate::TextureId, 16 | shadow_map: crate::TextureId, 17 | cascade_data: ([glam::Mat4; 4], [f32; 4]), 18 | ) { 19 | puffin::profile_function!(); 20 | 21 | graph 22 | .add_pass_from_desc( 23 | "forward_pass", 24 | crate::PipelineDesc::builder() 25 | .vertex_path("utopian/shaders/forward/forward.vert") 26 | .fragment_path("utopian/shaders/forward/forward.frag") 27 | .default_primitive_vertex_bindings() 28 | .default_primitive_vertex_attributes(), 29 | ) 30 | .read(shadow_map) 31 | .write(forward_output) 32 | .uniforms("shadowmapParams", &(cascade_data)) 33 | .external_depth_attachment(base.depth_image.clone(), vk::AttachmentLoadOp::CLEAR) // Todo: create own Depth image 34 | .render(move |device, command_buffer, renderer, pass, resources| { 35 | let pipeline = resources.pipeline(pass.pipeline_handle); 36 | 37 | renderer.draw_meshes(device, command_buffer, pipeline.pipeline_layout); 38 | }) 39 | .build(device, graph); 40 | } 41 | -------------------------------------------------------------------------------- /utopian/src/renderers/gbuffer.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | 3 | #[allow(dead_code)] 4 | struct PushConstants { 5 | world: glam::Mat4, 6 | color: glam::Vec4, 7 | mesh_index: u32, 8 | pad: [u32; 3], 9 | } 10 | 11 | pub fn setup_gbuffer_pass( 12 | device: &crate::Device, 13 | graph: &mut crate::Graph, 14 | base: &crate::VulkanBase, 15 | gbuffer_position: crate::TextureId, 16 | gbuffer_normal: crate::TextureId, 17 | gbuffer_albedo: crate::TextureId, 18 | gbuffer_pbr: crate::TextureId, 19 | ) { 20 | puffin::profile_function!(); 21 | 22 | // let depth_image = crate::Image::new( 23 | // device, 24 | // graph.resources[gbuffer_position].texture.image.width, 25 | // graph.resources[gbuffer_position].texture.image.height, 26 | // vk::Format::D32_SFLOAT, 27 | // vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, 28 | // vk::ImageAspectFlags::DEPTH, 29 | // ); 30 | 31 | graph 32 | .add_pass_from_desc( 33 | "gbuffer_pass", 34 | crate::PipelineDesc::builder() 35 | .vertex_path("utopian/shaders/gbuffer/gbuffer.vert") 36 | .fragment_path("utopian/shaders/gbuffer/gbuffer.frag") 37 | .default_primitive_vertex_bindings() 38 | .default_primitive_vertex_attributes(), 39 | ) 40 | .write(gbuffer_position) 41 | .write(gbuffer_normal) 42 | .write(gbuffer_albedo) 43 | .write(gbuffer_pbr) 44 | //.depth_attachment(depth_image) 45 | .external_depth_attachment(base.depth_image.clone(), vk::AttachmentLoadOp::CLEAR) // Todo: create own Depth image 46 | .render(move |device, command_buffer, renderer, pass, resources| { 47 | let pipeline = resources.pipeline(pass.pipeline_handle); 48 | 49 | renderer.draw_meshes(device, command_buffer, pipeline.pipeline_layout); 50 | }) 51 | .build(device, graph); 52 | } 53 | -------------------------------------------------------------------------------- /utopian/src/renderers/marching_cubes.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use glam::Vec4; 3 | 4 | use crate::{PipelineDesc, Vertex}; 5 | 6 | pub fn setup_marching_cubes_pass( 7 | device: &crate::Device, 8 | graph: &mut crate::Graph, 9 | base: &crate::VulkanBase, 10 | deferred_output: crate::TextureId, 11 | shadow_map: crate::TextureId, 12 | cascade_data: ([glam::Mat4; 4], [f32; 4]), 13 | _enabled: bool, 14 | ) { 15 | puffin::profile_function!(); 16 | 17 | let num_voxels = 32_u32; 18 | let mut size = num_voxels.pow(3) * std::mem::size_of::() as u32; 19 | size *= 5; 20 | 21 | let voxel_size: f32 = 1.0; 22 | let view_distance: f32 = 100.0; 23 | let flat_normals: bool = false; 24 | 25 | let offsets = [ 26 | Vec4::new(0.0, 0.0, 0.0, 0.0), 27 | Vec4::new(voxel_size, 0.0, 0.0, 0.0), 28 | Vec4::new(voxel_size, voxel_size, 0.0, 0.0), 29 | Vec4::new(0.0, voxel_size, 0.0, 0.0), 30 | Vec4::new(0.0, 0.0, voxel_size, 0.0), 31 | Vec4::new(voxel_size, 0.0, voxel_size, 0.0), 32 | Vec4::new(voxel_size, voxel_size, voxel_size, 0.0), 33 | Vec4::new(0.0, voxel_size, voxel_size, 0.0), 34 | ]; 35 | 36 | let color = Vec4::new(0.0, 1.0, 0.0, 1.0); 37 | 38 | let data_tuple = ( 39 | offsets, 40 | color, 41 | voxel_size, 42 | view_distance, 43 | num_voxels, 44 | flat_normals, 45 | ); 46 | 47 | let vertex_buffer = graph.create_buffer( 48 | "marching_cubes_vertex_buffer", 49 | device, 50 | size as u64, 51 | ash::vk::BufferUsageFlags::STORAGE_BUFFER | ash::vk::BufferUsageFlags::VERTEX_BUFFER, 52 | gpu_allocator::MemoryLocation::GpuOnly, 53 | ); 54 | 55 | let draw_command_buffer = graph.create_buffer( 56 | "marching_cubes_draw_command_buffer", 57 | device, 58 | std::mem::size_of::() as u64, 59 | ash::vk::BufferUsageFlags::STORAGE_BUFFER | ash::vk::BufferUsageFlags::INDIRECT_BUFFER, 60 | gpu_allocator::MemoryLocation::GpuOnly, 61 | ); 62 | 63 | graph 64 | .add_pass_from_desc( 65 | "marching_cubes_reset_counter_pass", 66 | PipelineDesc::builder() 67 | .compute_path("utopian/shaders/marching_cubes/reset_counter.comp"), 68 | ) 69 | .write_buffer(draw_command_buffer) 70 | .dispatch(1, 1, 1) 71 | .build(device, graph); 72 | 73 | graph 74 | .add_pass_from_desc( 75 | "marching_cubes_pass", 76 | PipelineDesc::builder() 77 | .compute_path("utopian/shaders/marching_cubes/marching_cubes.comp"), 78 | ) 79 | .write_buffer(draw_command_buffer) 80 | .write_buffer(vertex_buffer) 81 | .uniforms("ubo", &data_tuple) 82 | .dispatch(num_voxels, num_voxels, num_voxels) 83 | .build(device, graph); 84 | 85 | graph 86 | .add_pass_from_desc( 87 | "marching_cubes_forward_pass", 88 | crate::PipelineDesc::builder() 89 | // Reusing the forward shader is not ideal since this mesh is not part of the bindless data 90 | .vertex_path("utopian/shaders/forward/forward.vert") 91 | .fragment_path("utopian/shaders/forward/forward.frag") 92 | .default_primitive_vertex_bindings() 93 | .default_primitive_vertex_attributes(), 94 | ) 95 | .read(shadow_map) 96 | .load_write(deferred_output) 97 | .external_depth_attachment(base.depth_image.clone(), vk::AttachmentLoadOp::LOAD) 98 | .extra_barriers(&[ 99 | (draw_command_buffer, vk_sync::AccessType::IndirectBuffer), 100 | (vertex_buffer, vk_sync::AccessType::VertexBuffer), 101 | ]) 102 | .uniforms("shadowmapParams", &(cascade_data)) 103 | .render( 104 | move |device, command_buffer, _renderer, pass, resources| unsafe { 105 | let pipeline = resources.pipeline(pass.pipeline_handle); 106 | 107 | device.cmd_push_constants( 108 | command_buffer, 109 | pipeline.pipeline_layout, 110 | ( 111 | glam::Mat4::IDENTITY, 112 | glam::Vec4::new(0.0, 0.0, 0.0, 1.0), 113 | 0, // Mesh index not used 114 | [0; 3], 115 | ), 116 | ); 117 | 118 | device.handle.cmd_bind_vertex_buffers( 119 | command_buffer, 120 | 0, 121 | &[resources.buffer(vertex_buffer).buffer.buffer], 122 | &[0], 123 | ); 124 | 125 | let stride = std::mem::size_of::() as u32; 126 | device.handle.cmd_draw_indirect( 127 | command_buffer, 128 | resources.buffer(draw_command_buffer).buffer.buffer, 129 | 0, 130 | 1, 131 | stride, 132 | ); 133 | }, 134 | ) 135 | .build(device, graph); 136 | } 137 | -------------------------------------------------------------------------------- /utopian/src/renderers/present.rs: -------------------------------------------------------------------------------- 1 | use crate::PipelineDesc; 2 | 3 | pub fn setup_present_pass( 4 | device: &crate::Device, 5 | graph: &mut crate::Graph, 6 | color_output: crate::TextureId, 7 | ) { 8 | puffin::profile_function!(); 9 | 10 | let fxaa_threshold = 0.45; 11 | 12 | graph 13 | .add_pass_from_desc( 14 | "present_pass", 15 | PipelineDesc::builder() 16 | .vertex_path("utopian/shaders/common/fullscreen.vert") 17 | .fragment_path("utopian/shaders/present/present.frag"), 18 | ) 19 | .read(color_output) 20 | .uniforms( 21 | "settings_fxaa", 22 | &(glam::Vec4::new(1.0, 0.0, fxaa_threshold, 0.0)), 23 | ) 24 | .presentation_pass(true) 25 | .render( 26 | move |device, command_buffer, _renderer, _pass, _pipeline_cache| unsafe { 27 | device.handle.cmd_draw(command_buffer, 3, 1, 0, 0); 28 | }, 29 | ) 30 | .build(device, graph); 31 | } 32 | -------------------------------------------------------------------------------- /utopian/src/renderers/rt_reflections.rs: -------------------------------------------------------------------------------- 1 | use crate::image::ImageDesc; 2 | use ash::vk; 3 | 4 | #[allow(clippy::too_many_arguments)] 5 | pub fn setup_rt_reflections_pass( 6 | device: &crate::Device, 7 | graph: &mut crate::Graph, 8 | gbuffer_position: crate::TextureId, 9 | gbuffer_normal: crate::TextureId, 10 | gbuffer_pbr: crate::TextureId, 11 | irradiance_map: crate::TextureId, 12 | specular_map: crate::TextureId, 13 | brdf_lut: crate::TextureId, 14 | width: u32, 15 | height: u32, 16 | enabled: bool, 17 | ) -> crate::TextureId { 18 | puffin::profile_function!(); 19 | 20 | let output_image = graph.create_texture( 21 | "rt_reflection_output_image", 22 | device, 23 | ImageDesc::new_2d(width, height, vk::Format::R8G8B8A8_UNORM), 24 | ); 25 | 26 | if enabled { 27 | graph 28 | .add_pass_from_desc( 29 | "rt_reflections_pass", 30 | crate::PipelineDesc::builder() 31 | .raygen_path("utopian/shaders/rt_reflections/rt_reflections.rgen") 32 | .miss_path("utopian/shaders/rt_reflections/rt_reflections.rmiss") 33 | .hit_path("utopian/shaders/rt_reflections/rt_reflections.rchit"), 34 | ) 35 | .tlas(0) 36 | .read(gbuffer_position) 37 | .read(gbuffer_normal) 38 | .read(gbuffer_pbr) 39 | .read(irradiance_map) 40 | .read(specular_map) 41 | .read(brdf_lut) 42 | .image_write(output_image) 43 | .trace_rays(width, height, 1) 44 | .build(device, graph); 45 | } 46 | 47 | output_image 48 | } 49 | -------------------------------------------------------------------------------- /utopian/src/renderers/rt_shadows.rs: -------------------------------------------------------------------------------- 1 | use crate::image::ImageDesc; 2 | use ash::vk; 3 | 4 | pub fn setup_rt_shadows_pass( 5 | device: &crate::Device, 6 | graph: &mut crate::Graph, 7 | gbuffer_position: crate::TextureId, 8 | gbuffer_normal: crate::TextureId, 9 | width: u32, 10 | height: u32, 11 | enabled: bool, 12 | ) -> crate::TextureId { 13 | puffin::profile_function!(); 14 | 15 | let output_image = graph.create_texture( 16 | "rt_shadows_output_image", 17 | device, 18 | ImageDesc::new_2d(width, height, vk::Format::R8_UNORM), 19 | ); 20 | 21 | if enabled { 22 | graph 23 | .add_pass_from_desc( 24 | "rt_shadows_pass", 25 | crate::PipelineDesc::builder() 26 | .raygen_path("utopian/shaders/rt_shadows/rt_shadows.rgen") 27 | .miss_path("utopian/shaders/rt_shadows/rt_shadows.rmiss") 28 | .hit_path("utopian/shaders/rt_shadows/rt_shadows.rchit"), 29 | ) 30 | .tlas(0) 31 | .read(gbuffer_position) 32 | .read(gbuffer_normal) 33 | .image_write(output_image) 34 | .trace_rays(width, height, 1) 35 | .build(device, graph); 36 | } 37 | 38 | output_image 39 | } 40 | -------------------------------------------------------------------------------- /utopian/src/renderers/shadow.rs: -------------------------------------------------------------------------------- 1 | use crate::camera; 2 | use glam::{Mat4, Vec3, Vec4Swizzles}; 3 | 4 | pub fn setup_shadow_pass( 5 | device: &crate::Device, 6 | graph: &mut crate::Graph, 7 | shadow_map: crate::TextureId, 8 | sun_dir: glam::Vec3, 9 | camera: &camera::Camera, 10 | enabled: bool, 11 | ) -> ([glam::Mat4; 4], [f32; 4]) { 12 | puffin::profile_function!(); 13 | 14 | const SHADOW_MAP_CASCADE_COUNT: u32 = 4; 15 | 16 | // Outputs 17 | let mut out_cascade_matrices = [glam::Mat4::IDENTITY; SHADOW_MAP_CASCADE_COUNT as usize]; 18 | let mut out_split_depths = [0.0; SHADOW_MAP_CASCADE_COUNT as usize]; 19 | 20 | if !enabled { 21 | return (out_cascade_matrices, out_split_depths); 22 | } 23 | 24 | let mut cascade_splits = [0.0; SHADOW_MAP_CASCADE_COUNT as usize]; 25 | 26 | let near_clip = camera.get_near_plane(); 27 | let far_clip = camera.get_far_plane(); 28 | let clip_range = far_clip - near_clip; 29 | 30 | let min_z = near_clip; 31 | let max_z = near_clip + clip_range; 32 | 33 | let range = max_z - min_z; 34 | let ratio = max_z / min_z; 35 | 36 | let cascade_split_lambda = 0.927; 37 | 38 | // Calculate split depths based on view camera frustum 39 | // Based on method presented in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html 40 | for i in 0..SHADOW_MAP_CASCADE_COUNT { 41 | let p = (i + 1) as f32 / SHADOW_MAP_CASCADE_COUNT as f32; 42 | let log = min_z * ratio.powf(p); 43 | let uniform = min_z + range * p; 44 | let d = cascade_split_lambda * (log - uniform) + uniform; 45 | cascade_splits[i as usize] = (d - near_clip) / clip_range; 46 | } 47 | 48 | // Calculate orthographic projection matrix for each cascade 49 | let mut last_split_dist = 0.0; 50 | for i in 0..SHADOW_MAP_CASCADE_COUNT { 51 | let split_dist = cascade_splits[i as usize]; 52 | 53 | let mut frustum_corners: [Vec3; 8] = [ 54 | Vec3::new(-1.0, 1.0, 0.0), 55 | Vec3::new(1.0, 1.0, 0.0), 56 | Vec3::new(1.0, -1.0, 0.0), 57 | Vec3::new(-1.0, -1.0, 0.0), 58 | Vec3::new(-1.0, 1.0, 1.0), 59 | Vec3::new(1.0, 1.0, 1.0), 60 | Vec3::new(1.0, -1.0, 1.0), 61 | Vec3::new(-1.0, -1.0, 1.0), 62 | ]; 63 | 64 | // Project frustum corners into world space 65 | let inv_cam = (camera.get_projection() * camera.get_view()).inverse(); 66 | for corner in &mut frustum_corners { 67 | let inv_corner = inv_cam * corner.extend(1.0); 68 | *corner = inv_corner.xyz() / inv_corner.w; 69 | } 70 | 71 | for i in 0..4 { 72 | let dist = frustum_corners[i + 4] - frustum_corners[i]; 73 | frustum_corners[i + 4] = frustum_corners[i] + (dist * split_dist); 74 | frustum_corners[i] += dist * last_split_dist; 75 | } 76 | 77 | // Get frustum center 78 | let frustum_center: Vec3 = frustum_corners.iter().sum::() / 8.0; 79 | 80 | let mut radius: f32 = 0.0; 81 | for corner in &frustum_corners { 82 | let distance = (*corner - frustum_center).length(); 83 | radius = radius.max(distance); 84 | } 85 | radius = f32::ceil(radius * 16.0) / 16.0; 86 | 87 | let max_extents = Vec3::new(radius, radius, radius); 88 | let min_extents = -max_extents; 89 | 90 | let light_view_matrix = Mat4::look_at_rh( 91 | frustum_center - sun_dir * min_extents.z, 92 | frustum_center, 93 | Vec3::Y, 94 | ); 95 | 96 | let light_ortho_matrix = Mat4::orthographic_rh( 97 | min_extents.x, 98 | max_extents.x, 99 | min_extents.y, 100 | max_extents.y, 101 | -(max_extents.z - min_extents.z), 102 | max_extents.z - min_extents.z, 103 | ); 104 | 105 | let view_projection_matrix = light_ortho_matrix * light_view_matrix; 106 | out_cascade_matrices[i as usize] = view_projection_matrix; 107 | out_split_depths[i as usize] = near_clip + split_dist * clip_range; 108 | 109 | last_split_dist = split_dist; 110 | 111 | graph 112 | .add_pass_from_desc( 113 | format!("shadow_pass_{i}").as_str(), 114 | crate::PipelineDesc::builder() 115 | .vertex_path("utopian/shaders/shadow/shadow.vert") 116 | .fragment_path("utopian/shaders/shadow/shadow.frag") 117 | .default_primitive_vertex_bindings() 118 | .default_primitive_vertex_attributes(), 119 | ) 120 | .uniforms("cascade_view_projection", &view_projection_matrix) 121 | .depth_attachment_layer(shadow_map, i) 122 | .render(move |device, command_buffer, renderer, pass, resources| { 123 | // Todo: This is a hack to get around the fact that we can't properly disable a pass 124 | if enabled { 125 | let pipeline = resources.pipeline(pass.pipeline_handle); 126 | 127 | renderer.draw_meshes(device, command_buffer, pipeline.pipeline_layout); 128 | } 129 | }) 130 | .build(device, graph); 131 | } 132 | 133 | (out_cascade_matrices, out_split_depths) 134 | } 135 | -------------------------------------------------------------------------------- /utopian/src/renderers/ssao.rs: -------------------------------------------------------------------------------- 1 | pub fn setup_ssao_pass( 2 | device: &crate::Device, 3 | graph: &mut crate::Graph, 4 | gbuffer_position: crate::TextureId, 5 | gbuffer_normal: crate::TextureId, 6 | ssao_output: crate::TextureId, 7 | enabled: bool, 8 | ) { 9 | puffin::profile_function!(); 10 | 11 | let radius_bias = glam::Vec4::new(0.1, 0.0, 0.0, 0.0); 12 | 13 | graph 14 | .add_pass_from_desc( 15 | "ssao_pass", 16 | crate::PipelineDesc::builder() 17 | .vertex_path("utopian/shaders/common/fullscreen.vert") 18 | .fragment_path("utopian/shaders/ssao/ssao.frag"), 19 | ) 20 | .read(gbuffer_position) 21 | .read(gbuffer_normal) 22 | .write(ssao_output) 23 | .uniforms("settings_ubo", &(radius_bias)) 24 | .render( 25 | move |device, command_buffer, _renderer, _pass, _resources| unsafe { 26 | // Todo: This is a hack to get around the fact that we can't properly disable a pass 27 | if enabled { 28 | device.handle.cmd_draw(command_buffer, 3, 1, 0, 0); 29 | } 30 | }, 31 | ) 32 | .build(device, graph); 33 | 34 | // It is common to also have a blur pass for SSAO which can be added here. 35 | // The SSAO effect looks decent without it, but it should be added here in the future. 36 | } 37 | -------------------------------------------------------------------------------- /utopian/src/synch.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | 3 | use crate::Device; 4 | use crate::Image; 5 | 6 | pub fn global_pipeline_barrier( 7 | device: &Device, 8 | command_buffer: vk::CommandBuffer, 9 | prev_access: vk_sync::AccessType, 10 | next_access: vk_sync::AccessType, 11 | ) -> vk_sync::AccessType { 12 | vk_sync::cmd::pipeline_barrier( 13 | &device.handle, 14 | command_buffer, 15 | Some(vk_sync::GlobalBarrier { 16 | previous_accesses: &[prev_access], 17 | next_accesses: &[next_access], 18 | }), 19 | &[], 20 | &[], 21 | ); 22 | 23 | next_access 24 | } 25 | 26 | pub fn image_pipeline_barrier( 27 | device: &Device, 28 | command_buffer: vk::CommandBuffer, 29 | image: &Image, 30 | prev_access: vk_sync::AccessType, 31 | next_access: vk_sync::AccessType, 32 | discard_contents: bool, 33 | ) -> vk_sync::AccessType { 34 | vk_sync::cmd::pipeline_barrier( 35 | &device.handle, 36 | command_buffer, 37 | None, 38 | &[], 39 | &[vk_sync::ImageBarrier { 40 | previous_accesses: &[prev_access], 41 | next_accesses: &[next_access], 42 | previous_layout: vk_sync::ImageLayout::Optimal, 43 | next_layout: vk_sync::ImageLayout::Optimal, 44 | discard_contents, 45 | src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, 46 | dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, 47 | image: image.image, // Todo transition all images 48 | range: vk::ImageSubresourceRange::builder() 49 | .aspect_mask(image.desc.aspect_flags) 50 | .layer_count(vk::REMAINING_ARRAY_LAYERS) 51 | .level_count(vk::REMAINING_MIP_LEVELS) 52 | .build(), 53 | }], 54 | ); 55 | 56 | next_access 57 | } 58 | -------------------------------------------------------------------------------- /utopian/src/texture.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | 3 | use crate::buffer::*; 4 | use crate::device::*; 5 | use crate::image::*; 6 | 7 | pub struct Texture { 8 | pub image: Image, 9 | pub sampler: vk::Sampler, 10 | pub descriptor_info: vk::DescriptorImageInfo, 11 | } 12 | 13 | impl Texture { 14 | pub fn load(device: &Device, path: &str) -> Texture { 15 | let image = match image::open(path) { 16 | Ok(image) => image, 17 | Err(_err) => panic!("Unable to load \"{}\"", path), 18 | }; 19 | 20 | let image = image.to_rgba8(); 21 | let (width, height) = (image.width(), image.height()); 22 | let image_data = image.into_raw(); 23 | 24 | let mut texture = Texture::create( 25 | device, 26 | Some(&image_data), 27 | ImageDesc::new_2d(width, height, vk::Format::R8G8B8A8_UNORM), 28 | path, 29 | ); 30 | 31 | texture.image.set_debug_name(device, path); 32 | 33 | texture 34 | } 35 | 36 | pub fn create( 37 | device: &Device, 38 | pixels: Option<&[u8]>, 39 | image_desc: ImageDesc, 40 | debug_name: &str, 41 | ) -> Texture { 42 | let mut image = Image::new_from_desc(device, image_desc); 43 | 44 | image.set_debug_name(device, debug_name); 45 | 46 | device.execute_and_submit(|device, cb| { 47 | crate::synch::image_pipeline_barrier( 48 | device, 49 | cb, 50 | &image, 51 | vk_sync::AccessType::General, 52 | vk_sync::AccessType::TransferWrite, 53 | true, 54 | ); 55 | 56 | if let Some(pixels) = pixels { 57 | let staging_buffer = Buffer::new( 58 | device, 59 | Some(pixels), 60 | std::mem::size_of_val(pixels) as u64, 61 | vk::BufferUsageFlags::TRANSFER_SRC, 62 | gpu_allocator::MemoryLocation::CpuToGpu, 63 | ); 64 | staging_buffer.copy_to_image(device, cb, &image); 65 | } 66 | 67 | if Image::is_depth_image_fmt(image.desc.format) { 68 | image.transition_layout( 69 | device, 70 | cb, 71 | vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL, 72 | ); 73 | } else { 74 | crate::synch::image_pipeline_barrier( 75 | device, 76 | cb, 77 | &image, 78 | vk_sync::AccessType::TransferWrite, 79 | vk_sync::AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, 80 | false, 81 | ); 82 | } 83 | }); 84 | 85 | let sampler_info = vk::SamplerCreateInfo { 86 | mag_filter: vk::Filter::LINEAR, 87 | min_filter: vk::Filter::LINEAR, 88 | mipmap_mode: vk::SamplerMipmapMode::LINEAR, 89 | address_mode_u: vk::SamplerAddressMode::MIRRORED_REPEAT, 90 | address_mode_v: vk::SamplerAddressMode::MIRRORED_REPEAT, 91 | address_mode_w: vk::SamplerAddressMode::MIRRORED_REPEAT, 92 | max_anisotropy: 1.0, 93 | border_color: vk::BorderColor::FLOAT_OPAQUE_WHITE, 94 | compare_op: vk::CompareOp::NEVER, 95 | min_lod: 0.0, 96 | max_lod: image_desc.mip_levels as f32, 97 | ..Default::default() 98 | }; 99 | 100 | let sampler = unsafe { 101 | device 102 | .handle 103 | .create_sampler(&sampler_info, None) 104 | .expect("Unable to create sampler") 105 | }; 106 | 107 | let descriptor_info = vk::DescriptorImageInfo { 108 | image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, 109 | image_view: image.image_view, 110 | sampler, 111 | }; 112 | 113 | Texture { 114 | image, 115 | sampler, 116 | descriptor_info, 117 | } 118 | } 119 | } 120 | --------------------------------------------------------------------------------