├── .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 | 
4 | 
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 | 
6 |
7 | ## License Information
8 |
9 | Donated by Microsoft for glTF testing
10 |
11 | [](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 | 
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 |
--------------------------------------------------------------------------------