├── .gitignore
├── .gitmodules
├── LICENSE.md
├── materials
├── PGS120_LCPCG60_NNCG85.png
├── PGS_105iters_vs_LCPCG_50iters.png
└── PGSvsLCPCGvsNNCG_convergence.png
├── readme.md
├── vkEngine.sln
└── vkEngine
├── PropertySheet.props
├── shaders
├── debug_vis.fs
├── debug_vis.vs
├── fs_blobs.fs
├── fs_blobs.vs
├── mesh.fs
├── mesh.vs
├── pathtracer.fs
├── shader_bindings.h
├── shadowmap.fs
├── shadowmap.vs
└── test.vs
├── source
├── app.cpp
├── app.h
├── camera.cpp
├── camera.h
├── drawing_primitives.h
├── main.cpp
├── physics_scenes.h
├── sim.h
└── vulkan
│ ├── definitions.h
│ ├── helpers.cpp
│ ├── helpers.h
│ ├── manager.cpp
│ ├── manager.h
│ ├── mesh.cpp
│ └── mesh.h
├── vkEngine.vcxproj
└── vkEngine.vcxproj.filters
/.gitignore:
--------------------------------------------------------------------------------
1 | /_interm/*
2 | /_out/*
3 | /.vs/*
4 | /vkEngine.VC.db
5 | /vkEngine.VC.VC.opendb
6 | /vkEngine/shaders/bin/*
7 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "core"]
2 | path = core
3 | url = https://github.com/avoroshilov/core.git
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License
2 |
3 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
4 |
5 | ## Section 1 – Definitions.
6 |
7 | a. **Adapted Material** means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
8 |
9 | b. **Adapter's License** means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
10 |
11 | c. **BY-NC-SA Compatible License** means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License.
12 |
13 | d. **Copyright and Similar Rights** means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
14 |
15 | e. **Effective Technological Measures** means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
16 |
17 | f. **Exceptions and Limitations** means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
18 |
19 | g. **License Elements** means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike.
20 |
21 | h. **Licensed Material** means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
22 |
23 | i. **Licensed Rights** means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
24 |
25 | j. **Licensor** means the individual(s) or entity(ies) granting rights under this Public License.
26 |
27 | k. **NonCommercial** means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
28 |
29 | l. **Share** means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
30 |
31 | m. **Sui Generis Database Rights** means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
32 |
33 | n. **You** means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
34 |
35 | ## Section 2 – Scope.
36 |
37 | a. **License grant.**
38 | 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
39 | A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
40 | B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
41 | 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
42 | 3. Term. The term of this Public License is specified in Section 6(a).
43 | 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
44 | 5. Downstream recipients.
45 | A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
46 | B. Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
47 | C. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
48 | 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
49 |
50 | b. **Other rights.**
51 |
52 | 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
53 | 2. Patent and trademark rights are not licensed under this Public License.
54 | 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
55 |
56 | ## Section 3 – License Conditions.
57 |
58 | Your exercise of the Licensed Rights is expressly made subject to the following conditions.
59 |
60 | a. **Attribution.**
61 |
62 | 1. If You Share the Licensed Material (including in modified form), You must:
63 | A. retain the following if it is supplied by the Licensor with the Licensed Material:
64 | i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
65 | ii. a copyright notice;
66 | iii. a notice that refers to this Public License;
67 | iv. a notice that refers to the disclaimer of warranties;
68 | v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
69 | B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
70 | C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
71 | 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
72 | 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
73 |
74 | b. **ShareAlike.**
75 |
76 | In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.
77 |
78 | 1. The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License.
79 | 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
80 | 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
81 |
82 | ## Section 4 – Sui Generis Database Rights.
83 |
84 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
85 |
86 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
87 |
88 | b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and
89 |
90 | c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
91 |
92 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
93 |
94 | ## Section 5 – Disclaimer of Warranties and Limitation of Liability.
95 |
96 | **a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.**
97 |
98 | **b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.**
99 |
100 | c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
101 |
102 | ## Section 6 – Term and Termination.
103 |
104 | a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
105 |
106 | b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
107 | 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
108 | 2. upon express reinstatement by the Licensor.
109 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
110 |
111 | c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
112 |
113 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
114 |
115 | ## Section 7 – Other Terms and Conditions.
116 |
117 | a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
118 |
119 | b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
120 |
121 | ## Section 8 – Interpretation.
122 |
123 | a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
124 |
125 | b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
126 |
127 | c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
128 |
129 | d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
130 |
--------------------------------------------------------------------------------
/materials/PGS120_LCPCG60_NNCG85.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avoroshilov/physics_playground/06208f51d1b1a6a62a0c6cd5f54270b6ec0e42fe/materials/PGS120_LCPCG60_NNCG85.png
--------------------------------------------------------------------------------
/materials/PGS_105iters_vs_LCPCG_50iters.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avoroshilov/physics_playground/06208f51d1b1a6a62a0c6cd5f54270b6ec0e42fe/materials/PGS_105iters_vs_LCPCG_50iters.png
--------------------------------------------------------------------------------
/materials/PGSvsLCPCGvsNNCG_convergence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avoroshilov/physics_playground/06208f51d1b1a6a62a0c6cd5f54270b6ec0e42fe/materials/PGSvsLCPCGvsNNCG_convergence.png
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Enhanced version of coupled FEM and constrained rigid body simulation
2 |
3 | ## Description
4 | This little playground aimed to test our Conjugate Gradients based MLCP solver versus some other types of solvers (at the moment, conventional Projected Gauss Seidel, and its modification that changes constraint order), as well as some hacks that improve joints stiffness - for example local mass scaling.
5 |
6 | The simulation framework capabilities:
7 | 1. Old-fashioned linearized dynamics, meaning:
8 | * calculates constraint properties, such as effective mass, correction parameters, etc - twice per timestep, first time frictionless solve to estimate normal force, second time - full solve with friction limits; both solves use same MLCP solver;
9 | * uses canonical matrix form with decomposition (`[J]*[M]^-1*[J]^T + [D]`), and not applied form like Sequential/Split Impulses - to facilitate custom solvers implementation.
10 | 1. MLCP solvers: **PGS**, **shuffled PGS** (shuffle types: random, forward/backward, even-odd local, even-odd global, could also be more than just 2-tuple shuffle), **CG-based** and **NNCG**.
11 | 1. Local mass scaling (see below).
12 | 1. Gyroscopic forces effect calculation - better handling of rotating oblong objects, enabling Dzhanibekov effect.
13 | 1. Coupled rigid body and corotational FEM-based deformables simulation (see below).
14 | 1. General convex collision detection, convex hull represented via support mapping, Minkowski Portal Refinement is used to calculate contact normal.
15 | 1. Several basic joint types (ball joint, slider, fixed rotation, limits/motors, etc.).
16 | 1. Composite bodies with mass properties calculation.
17 | 1. Baumgarte correction (no pseudo-velocities or nonlinear correction) and regularization (a.k.a. constraint force mixing)
18 |
19 | The framework is more of a research framework than physics engine ready for release, it requires quite some work to reach the product-eligible state: island detection, sleeping, to name a few. Additionally, data layout is not the most cache-friendly.
20 |
21 | ## Local mass scaling
22 | Technique, similar to shock propagation - allows to set mass scaling per joint (per constraint row, to be precise); by making joints in an open loop system (such as ragdoll, or a tree) scale masses higher for bodies closer to root - one could improve system stability and stiffness. Similarly, there is n option to scale masses for contact joints, based on their relative positions in the gravity field. It is still a hack however, so artifacts could arise; one example is stack being too stable and resisting tumbling, but the effect is barely noticeable if mass scaling is low (around 2.0 total).
23 |
24 | ## Preconditioned Conjugate Gradients MLCP solver
25 | Port from an earlier research collaboration (available [on github](https://github.com/avoroshilov/physics_fem_rbd) as well). Since it was developed to solve MLCPs - supports contacts with friction pyramid, limits, constraint forces boundaries, etc. Preconditioned using double-Jacobi preconditioner to maintain desired matrix properties. Solver that has better convergence characteristics than conventional Projected Gauss-Seidel.
26 |
27 | The solver is based on the `MPRGP` (Modified Proportioning with Reduced Gradient Projections) by **Z. Dostál**; we modified the original MPRGP to support box limits (low/high, instead of just lower limit), and incorporated preconditioner that is fairly cheap - double (left+right) Jacobi preconditioner. Solver also benefits from the `J*M^-1*J^T` decomposition that significantly speeds up matrix-vector multiplications.
28 |
29 | **Pros**:
30 | * Significantly better convergence characteristics
31 | * Naturally parallel (as opposed to sequential PGS), not requiring colorizing or splitting to utilize highly-parallel HW (e.g. GPUs)
32 |
33 | **Cons**:
34 | * Requires spectral radius calculation (estimated using several Power Iterations, each iteration is also inherently parallel)
35 | * Provides noisy solution on low iteration count
36 | * One iteration is more expensive than one iteration of PGS
37 |
38 | These properties make it worse candidate than PGS for game-like scenarios when accuracy is not important, but much better candidate when joint stiffness is required, or just for very complex systems.
39 |
40 | Details of the maths behind the solver design available in the [project paper](https://github.com/avoroshilov/physics_fem_rbd/raw/master/materials/fem_paper.pdf).
41 |
42 | Example convergence of conventional PGS vs our CG-based MLCP solver vs the NNCG (NNCG described in "A nonsmooth nonlinear conjugate gradient method for interactive contact force problems" by M. Silcowitz-Hansen et al.) - stiff FEM rod, made of `(48x2x1)*5=480` FEM tetrahedra, making total `2880` constraint rows. The amount of iterations is not equal, but set so that the total frametime is equal (20ms on ultrabook Core i7-6500U). Red is PGS 180 iterations, and Green is LCPCG 90 iterations (plus 15 iterations overhead for Power Iteration) and Blue is NNCG 127 iterations.
43 |
44 |
45 |
46 | This scene clearly shows advantage of our CG-based solver over PGS in relatively complex systems.
47 |
48 | Our solver shows better results than NNCG as well; NNCG comes close, but our solver shows better convergence/stiffness, and will show much better results when optimized for parallel hardware (e.g., GPU), as NNCG depends on the PGS iteration, which would require either colorization or splitting, both ways have their drawbacks. On the other hand, NNCG is much simpler to implement, and seems to be more suitable for lower iteration count scenarios.
49 |
50 | ## FEM-based deformables w/ coupling
51 | As the PCG-based MLCP solver described above, port of the [earlier research collaboration](https://github.com/avoroshilov/physics_fem_rbd), which formulates corotational FEM joints, that connects 4 linear nodes (mass points), thus allowing the deformable bodies to be part of a single system with rigid bodies, and providing natural two-way coupling. Additional ball joint that connects rigid body and FE face (3 linear nodes) is implemented.
52 |
53 | Details of the approach are also described in the [project paper](https://github.com/avoroshilov/physics_fem_rbd/raw/master/materials/fem_paper.pdf).
54 |
55 | ## Build/run
56 | The sample uses Vulkan graphics API, and one needs to perform following steps in order to build and run the sample:
57 | 1. Download and install [LunarG Vulkan SDK](https://vulkan.lunarg.com/sdk/home) (for the reference, the framework uses 1.0.61.1, but any recent should do);
58 | 2. Update `vkEngine\PropertySheet.props` to contain correct SDK installation path, like this:
59 | ```
60 | d:\Program Files\VulkanSDK\1.0.61.1
61 | ```
62 | 3. Run `vkEngine.sln` and build as usual.
63 |
64 | ## Controls:
65 | * `WASD`+`PgDn`/`PgUp`+mouse - control camera, use `[shift]` to move faster and `[ctrl]` to move slower
66 | * `q` to pause/unpause
67 | * `p` to perform a single step
68 | * `e` to throw a box
69 | * `RMB` (right mouse button) to switch mouse modes (default is camera control mode, alternative is picking mode - use `LMB` to pick bodies when in alternative mouse mode)
70 |
71 | ## License
72 | [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode)
73 |
--------------------------------------------------------------------------------
/vkEngine.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26430.16
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vkEngine", "vkEngine\vkEngine.vcxproj", "{35C0E3E4-4770-4581-98DC-8A7D4178BDEC}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "core\Core.vcxproj", "{4504335C-7145-485F-AE40-6824DA670A3E}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|x64 = Debug|x64
13 | Debug|x86 = Debug|x86
14 | Release|x64 = Release|x64
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Debug|x64.ActiveCfg = Debug|x64
19 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Debug|x64.Build.0 = Debug|x64
20 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Debug|x86.ActiveCfg = Debug|Win32
21 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Debug|x86.Build.0 = Debug|Win32
22 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Release|x64.ActiveCfg = Release|x64
23 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Release|x64.Build.0 = Release|x64
24 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Release|x86.ActiveCfg = Release|Win32
25 | {35C0E3E4-4770-4581-98DC-8A7D4178BDEC}.Release|x86.Build.0 = Release|Win32
26 | {4504335C-7145-485F-AE40-6824DA670A3E}.Debug|x64.ActiveCfg = Debug|x64
27 | {4504335C-7145-485F-AE40-6824DA670A3E}.Debug|x64.Build.0 = Debug|x64
28 | {4504335C-7145-485F-AE40-6824DA670A3E}.Debug|x86.ActiveCfg = Debug|Win32
29 | {4504335C-7145-485F-AE40-6824DA670A3E}.Debug|x86.Build.0 = Debug|Win32
30 | {4504335C-7145-485F-AE40-6824DA670A3E}.Release|x64.ActiveCfg = Release|x64
31 | {4504335C-7145-485F-AE40-6824DA670A3E}.Release|x64.Build.0 = Release|x64
32 | {4504335C-7145-485F-AE40-6824DA670A3E}.Release|x86.ActiveCfg = Release|Win32
33 | {4504335C-7145-485F-AE40-6824DA670A3E}.Release|x86.Build.0 = Release|Win32
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | EndGlobal
39 |
--------------------------------------------------------------------------------
/vkEngine/PropertySheet.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | c:\Program Files\VulkanSDK\1.0.61.1
6 |
7 |
8 |
9 |
10 | $(VulkanSDKRoot)
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/vkEngine/shaders/debug_vis.fs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec4 in_color;
8 |
9 | layout(location = 0) out vec4 outColor;
10 |
11 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
12 | {
13 | float time;
14 | } ubo;
15 |
16 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
17 | {
18 | mat4 view;
19 | mat4 proj;
20 | } transformUBO;
21 |
22 | layout(set = 0, binding = 2) uniform sampler2D texSampler;
23 |
24 | void main()
25 | {
26 | outColor = in_color;
27 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/debug_vis.vs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec3 in_position;
8 | layout(location = 1) in vec4 in_color;
9 |
10 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
11 | {
12 | float time;
13 | } ubo;
14 |
15 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
16 | {
17 | mat4 view;
18 | mat4 proj;
19 | } transformUBO;
20 |
21 | out gl_PerVertex
22 | {
23 | vec4 gl_Position;
24 | };
25 |
26 | layout(location = 0) out vec4 out_color;
27 |
28 | void main()
29 | {
30 | vec4 modelVertex = vec4(in_position, 1.0);
31 | gl_Position = transformUBO.proj * transformUBO.view * modelVertex;
32 | out_color = in_color;
33 | //out_time = ubo.time;
34 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/fs_blobs.fs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec4 in_color;
8 | layout(location = 1) in vec2 in_texCoords;
9 | layout(location = 2) in float in_time;
10 | layout(location = 3) in vec2 blobCenters[5];
11 |
12 | layout(location = 0) out vec4 outColor;
13 |
14 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
15 | {
16 | float time;
17 | } ubo;
18 |
19 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
20 | {
21 | mat4 view;
22 | mat4 proj;
23 | } transformUBO;
24 |
25 | void main()
26 | {
27 | const float width = 800.0;
28 | const float height = 600.0;
29 | const float aspect = width/height;
30 |
31 | const float radii[5] = { 0.4, 0.3, 0.2, 0.35, 0.25 };
32 |
33 | float scalar = 0.0;
34 | for (int i = 0; i < 5; ++i)
35 | {
36 | vec2 blobCenter = blobCenters[i];
37 | scalar += smoothstep(0.0, 1.0, clamp(1.0 - length(in_texCoords*vec2(aspect,1.0) - blobCenter) / radii[i], 0.0, 1.0));
38 | }
39 |
40 | #define PI_DIV2 1.57079632679
41 |
42 | const float base = 0.2, range = 0.2;
43 | if (scalar > base && scalar < (base + range))
44 | scalar = sin(PI_DIV2 / (range/2.0) * (scalar - base));
45 | else if (scalar < base)
46 | scalar = 0.0;
47 | else
48 | scalar = sin(scalar - (base + range));
49 |
50 | outColor = vec4(scalar, scalar, scalar, 1.0);
51 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/fs_blobs.vs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec3 in_position;
8 | layout(location = 1) in vec3 in_normal;
9 | layout(location = 2) in vec4 in_color;
10 | layout(location = 3) in vec2 in_texCoords;
11 |
12 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
13 | {
14 | float time;
15 | } ubo;
16 |
17 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
18 | {
19 | mat4 view;
20 | mat4 proj;
21 | } transformUBO;
22 |
23 | layout(push_constant) uniform MeshPushConst
24 | {
25 | mat4 model;
26 | } meshPushConst;
27 |
28 | out gl_PerVertex
29 | {
30 | vec4 gl_Position;
31 | };
32 |
33 | layout(location = 0) out vec4 out_color;
34 | layout(location = 1) out vec2 out_texCoords;
35 | layout(location = 2) out float out_time;
36 | layout(location = 3) out vec2 blobCenters[5];
37 |
38 | void main()
39 | {
40 | gl_Position = vec4(in_position, 1.0);
41 | out_texCoords = in_texCoords;
42 | out_color = in_color;
43 | out_time = ubo.time;
44 |
45 | const float mulX[5] = { 0.5, 0.7, 0.8, 0.3, 0.6 };
46 | const float mulY[5] = { 0.8, 0.6, 0.5, 0.7, 0.4 };
47 | const float shiftX1[5] = { 0.1, 1.5, 3.5, 0.6, 1.9 };
48 | const float shiftY1[5] = { 0.6, 1.6, 2.8, 0.1, 1.3 };
49 | const float shiftX2[5] = { 0.9, 1.3, 2.9, 0.4, 2.3 };
50 | const float shiftY2[5] = { 0.3, 1.9, 3.2, 0.9, 2.4 };
51 | const float timeMulX1[5] = { 1.0, 1.2, 0.5, 0.6, 0.9 };
52 | const float timeMulY1[5] = { 1.0, 1.3, 0.8, 1.1, 1.3 };
53 |
54 | const float blobTime = ubo.time * 0.001;
55 | for (int i = 0; i < 5; ++i)
56 | {
57 | blobCenters[i] = vec2(
58 | mulX[i] * cos(timeMulX1[i] * blobTime + shiftX1[i]) * sin(blobTime + shiftX2[i]) + 0.5,
59 | mulY[i] * sin(timeMulY1[i] * blobTime + shiftY1[i]) * sin(blobTime + shiftY2[i]) + 0.25
60 | );
61 | }
62 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/mesh.fs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec4 in_color;
8 | layout(location = 1) in vec3 in_normal_cam;
9 | layout(location = 2) in vec2 in_texCoords;
10 | layout(location = 3) in float in_time;
11 | layout(location = 4) in vec3 in_L_cam;
12 | layout(location = 5) in vec3 in_worldPos;
13 |
14 | layout(location = 0) out vec4 outColor;
15 |
16 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
17 | {
18 | float time;
19 | } ubo;
20 |
21 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
22 | {
23 | mat4 view;
24 | mat4 proj;
25 | } transformUBO;
26 |
27 | layout(set = 0, binding = SH_BIND_FWDSH_ALBEDO_TEX) uniform sampler2D texSampler;
28 | layout(set = 0, binding = SH_BIND_FWDSH_SHADOWMAP_TEX) uniform sampler2DShadow shadowmapSampler;
29 | layout(set = 0, binding = SH_BIND_FWDSH_LIGHTPROJ_TEX) uniform sampler2D lightSampler;
30 |
31 | layout(set = 0, binding = SH_BIND_FWDSH_LIGHTMATRIX) uniform LightMatrixUBO
32 | {
33 | mat4 view;
34 | mat4 proj;
35 | } lightMatrixUBO;
36 |
37 | void main()
38 | {
39 | mat4 shadowMatrix = lightMatrixUBO.proj * lightMatrixUBO.view;
40 | vec4 shadowmapPos = shadowMatrix * vec4(in_worldPos, 1.0);
41 | shadowmapPos /= shadowmapPos.w;
42 |
43 | vec4 lightProjColor = vec4(0.0, 0.0, 0.0, 0.0);
44 | vec4 shadowMaskColor = vec4(0.0, 0.0, 0.0, 0.0);
45 |
46 | if (shadowmapPos.x > -1.0 && shadowmapPos.x < 1.0 &&
47 | shadowmapPos.y > -1.0 && shadowmapPos.y < 1.0)
48 | {
49 | ivec2 shadowMapSize = textureSize(shadowmapSampler, 0);
50 | vec2 stepSize = vec2(1.0 / shadowMapSize.x, 1.0 / shadowMapSize.y);
51 |
52 | float depthBias = 0.0;
53 |
54 | #define MANUAL_DEPTH_BIAS 0
55 | #if (MANUAL_DEPTH_BIAS == 1)
56 | depthBias = 0.00017;
57 | #endif
58 |
59 | shadowmapPos.x = 0.5 * shadowmapPos.x + 0.5;
60 | shadowmapPos.y = 0.5 * shadowmapPos.y + 0.5;
61 | shadowmapPos.z -= depthBias;
62 | // Central
63 | float shadowMapCmp = texture(shadowmapSampler, shadowmapPos.xyz);
64 | shadowMaskColor += vec4(shadowMapCmp, shadowMapCmp, shadowMapCmp, 1.0);
65 |
66 | #define MORE_SHADOWMAP_FILTERING 1
67 | #if (MORE_SHADOWMAP_FILTERING == 1)
68 | shadowMapCmp = texture(shadowmapSampler, vec3(-stepSize.x, 0.0, 0.0) + shadowmapPos.xyz);
69 | shadowMaskColor += vec4(shadowMapCmp, shadowMapCmp, shadowMapCmp, 1.0);
70 | shadowMapCmp = texture(shadowmapSampler, vec3( stepSize.x, 0.0, 0.0) + shadowmapPos.xyz);
71 | shadowMaskColor += vec4(shadowMapCmp, shadowMapCmp, shadowMapCmp, 1.0);
72 | shadowMapCmp = texture(shadowmapSampler, vec3(0.0, -stepSize.y, 0.0) + shadowmapPos.xyz);
73 | shadowMaskColor += vec4(shadowMapCmp, shadowMapCmp, shadowMapCmp, 1.0);
74 | shadowMapCmp = texture(shadowmapSampler, vec3(0.0, stepSize.y, 0.0) + shadowmapPos.xyz);
75 | shadowMaskColor += vec4(shadowMapCmp, shadowMapCmp, shadowMapCmp, 1.0);
76 |
77 | shadowMaskColor /= 5.0;
78 | #endif
79 |
80 | lightProjColor = texture(lightSampler, shadowmapPos.xy);
81 | }
82 | else
83 | {
84 | shadowMaskColor = vec4(1.0, 1.0, 1.0, 1.0);
85 | }
86 |
87 | shadowMaskColor = lightProjColor*shadowMaskColor;
88 |
89 | vec4 colorTex = texture(texSampler, in_texCoords);
90 | #if 0
91 | //outColor = in_color * vec4(0.5 * in_normal + 0.5, 1.0);
92 | outColor = in_color * colorTex;
93 | #else
94 | // Working in camera (rather than in world) space
95 |
96 | // Renormalize vectors after the interpolator
97 | vec3 normal_cam = normalize(in_normal_cam);
98 | vec3 L_cam = normalize(in_L_cam);
99 |
100 | float cosLightDir = dot(L_cam, normal_cam);
101 |
102 | #define PI 3.14159265
103 |
104 | // Lambertian diffuse
105 | vec4 lightColor = vec4(0.9, 1.0, 0.85, 1.0);
106 | float lambertN = 1.0 / PI;
107 | // Two sided diffuse (abs instead of max(..., 0.0))
108 | vec4 diffuse = (lambertN * abs(cosLightDir)) * shadowMaskColor*lightColor;
109 |
110 | // Blinn-phong reflectance
111 | float shininess = 20.0;
112 | vec4 lightSpecularColor = vec4(0.8, 1.0, 0.6, 1.0);
113 |
114 | vec3 viewer_cam = vec3(0.0, 0.0, 1.0);
115 |
116 | vec4 specular;
117 | if (cosLightDir > 0.0)
118 | {
119 | vec3 halfVec_cam = L_cam + viewer_cam;
120 | halfVec_cam = normalize(halfVec_cam);
121 | float blinnPhongN = (shininess + 2) / (4 * PI * (2 - exp2(-shininess/2.0)));
122 | specular = blinnPhongN * vec4(pow( max(dot(halfVec_cam, normal_cam), 0.0), shininess )) * shadowMaskColor*lightSpecularColor;
123 | }
124 | else
125 | {
126 | specular = vec4(0.0, 0.0, 0.0, 0.0);
127 | }
128 |
129 | vec4 ambient = vec4(0.5, 0.5, 0.5, 0.5);
130 | outColor = in_color * ((diffuse + ambient) * colorTex + specular);
131 | #endif
132 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/mesh.vs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec3 in_position;
8 | layout(location = 1) in vec3 in_normal;
9 | layout(location = 2) in vec4 in_color;
10 | layout(location = 3) in vec2 in_texCoords;
11 |
12 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
13 | {
14 | float time;
15 | } ubo;
16 |
17 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
18 | {
19 | mat4 view;
20 | mat4 proj;
21 | } transformUBO;
22 |
23 | layout(set = 0, binding = SH_BIND_FWDSH_LIGHTMATRIX) uniform LightMatrixUBO
24 | {
25 | mat4 view;
26 | mat4 proj;
27 | } lightMatrixUBO;
28 |
29 | layout(push_constant) uniform MeshPushConst
30 | {
31 | mat4 model;
32 | } meshPushConst;
33 |
34 | out gl_PerVertex
35 | {
36 | vec4 gl_Position;
37 | };
38 |
39 | layout(location = 0) out vec4 out_color;
40 | layout(location = 1) out vec3 out_normal_cam;
41 | layout(location = 2) out vec2 out_texCoords;
42 | layout(location = 3) out float out_time;
43 | layout(location = 4) out vec3 out_L_cam;
44 | layout(location = 5) out vec3 out_worldPos;
45 |
46 | void main()
47 | {
48 | vec4 vertexWorldPos = meshPushConst.model * vec4(in_position, 1.0);
49 | out_worldPos = vertexWorldPos.xyz / vertexWorldPos.w;
50 | gl_Position = transformUBO.proj * transformUBO.view * vertexWorldPos;
51 | out_texCoords = in_texCoords;
52 | out_color = in_color;
53 | // Suppose view is orthonormal
54 | out_normal_cam = mat3(transformUBO.view) * mat3(meshPushConst.model) * in_normal;
55 | out_time = ubo.time;
56 |
57 | //
58 | mat4 lightToWorld = inverse(lightMatrixUBO.view);
59 | vec3 light_pos = (lightToWorld[3]).xyz;//vec3(3.0, 3.0, 3.0);
60 | out_L_cam = normalize((transformUBO.view * (vec4(light_pos, 1.0) - vertexWorldPos)).xyz);
61 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/pathtracer.fs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec4 in_color;
8 | layout(location = 1) in vec2 in_texCoords;
9 | layout(location = 2) in float in_time;
10 |
11 | layout(location = 0) out vec4 outColor;
12 |
13 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
14 | {
15 | float time;
16 | } ubo;
17 |
18 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
19 | {
20 | mat4 view;
21 | mat4 proj;
22 | } transformUBO;
23 |
24 | layout(set = 0, binding = SH_BIND_PATHTRACER_NOISE_TEX) uniform sampler2D texSampler;
25 |
26 | float fakeRand(vec2 co, float time)
27 | {
28 | return fract(sin(dot(time * co.xy, vec2(12.9898, 78.233))) * 43758.5453);
29 | }
30 |
31 | #define PI 3.14159265358979323846
32 | #define _2PI 6.28318530717958647692
33 |
34 | vec3 randInUnitSphere(vec2 co, float time)
35 | {
36 | float angTheta = _2PI*fakeRand(co, time);
37 | float angPhi = _2PI*fakeRand(12.3456*co, time);
38 | float rad = fakeRand(45.6789*co, time);
39 |
40 | return vec3(rad*sin(angTheta)*cos(angPhi), rad*sin(angTheta)*sin(angPhi), rad*cos(angTheta));
41 | }
42 | vec2 randOnDisk(vec2 co, float time)
43 | {
44 | float ang = _2PI*fakeRand(co, time);
45 | float rad = fakeRand(34.5678*co, time);
46 |
47 | return vec2(rad*cos(ang), rad*sin(ang));
48 | }
49 |
50 | /* Ray */
51 | struct Ray
52 | {
53 | vec3 O;
54 | vec3 D;
55 | };
56 |
57 | Ray getRay(vec3 O, vec3 D)
58 | {
59 | Ray r;
60 | r.O = O;
61 | r.D = D;
62 | return r;
63 | }
64 |
65 | vec3 getRayPoint(Ray ray, float t)
66 | {
67 | return ray.O + t * ray.D;
68 | }
69 | /* End of Ray */
70 |
71 | /* Hitting Routines */
72 | struct HitData
73 | {
74 | vec3 p;
75 | vec3 n;
76 | float t;
77 | int materialIndex;
78 | };
79 |
80 | bool hitSphere(vec3 center, float radius, int materialIndex, Ray r, float t_min, float t_max, out HitData hitData)
81 | {
82 | /*
83 |
84 | Sphere eqn: dot( (p-c), (p-c) ) = rad*rad
85 | where p - point, c - sphere center, rad - sphere radius
86 | subs p for r(t)=r.O+r.D*t - ray:
87 | dot( (r(t)-c), (r(t)-c) ) = rad*rad
88 | dot( (r.O+r.D*t-c), (r.O+r.D*t-c) ) = rad*rad
89 | =>
90 | + - + + - - - + = rad*rad
91 | + t* - + t* + t* - t* - - t* + = rad*rad
92 |
93 | t*t* + t*( + - - ) + ( - - + ) = rad*rad
94 | t*t* + t*(2* - 2*) + ( - 2* + ) = rad*rad
95 |
96 | also,
97 | 2* - 2* = 2*
98 | = - - + = - 2* +
99 |
100 | and,
101 | rayO2sphC = r.O-c
102 |
103 | hence,
104 | t*t* + t*(2*) + () = rad*rad
105 |
106 | */
107 |
108 | vec3 rayO2sphC = r.O - center;
109 | float a = dot(r.D, r.D);
110 | float b = 2.0 * dot(r.D, rayO2sphC);
111 | float c = dot(rayO2sphC, rayO2sphC) - radius*radius;
112 | float discriminant = b*b - 4*a*c;
113 |
114 | if (discriminant < 0)
115 | {
116 | return false;
117 | }
118 |
119 | // x0,x1 = [-b +- sqrt(D)] / [2*a]
120 | float sqrtD_div2a = sqrt(discriminant) / (2*a);
121 | float negB_div2a = -b / (2*a);
122 |
123 | float hitT1 = negB_div2a - sqrtD_div2a;
124 | if (hitT1 > t_min && hitT1 < t_max)
125 | {
126 | hitData.t = hitT1;
127 | hitData.p = getRayPoint(r, hitT1);
128 | hitData.n = (hitData.p - center) / radius;
129 | hitData.materialIndex = materialIndex;
130 | return true;
131 | }
132 |
133 | float hitT2 = negB_div2a + sqrtD_div2a;
134 | if (hitT2 > t_min && hitT2 < t_max)
135 | {
136 | hitData.t = hitT2;
137 | hitData.p = getRayPoint(r, hitT2);
138 | hitData.n = (hitData.p - center) / radius;
139 | hitData.materialIndex = materialIndex;
140 | return true;
141 | }
142 |
143 | return false;
144 | }
145 |
146 | bool refract(vec3 v, vec3 n, float ni_over_nt, out vec3 outV)
147 | {
148 | // Snell's law of refraction
149 | // n1 * sin(theta1) = n2 * sin(theta2)
150 | // where n - refractive index, theta - is the angle measured from the normal of the boundary
151 |
152 | vec3 v_nrm = normalize(v);
153 | float vdotn = dot(v_nrm, n);
154 | float discriminant = 1.0 - ni_over_nt*ni_over_nt * (1.0 - vdotn*vdotn);
155 | if (discriminant <= 0)
156 | return false;
157 |
158 | outV = ni_over_nt * (v_nrm - vdotn*n) - n*sqrt(discriminant);
159 | return true;
160 | }
161 |
162 | float schlick(float cosine, float refIdx)
163 | {
164 | float r0 = (1.0 - refIdx) / (1.0 + refIdx);
165 | r0 = r0*r0;
166 | float one_minus_cos = 1.0 - cosine;
167 | // Cannot use pow() here as per spec, it's undefined for x < 0
168 | float one_minus_cos_sq = one_minus_cos*one_minus_cos;
169 | return r0 + (1.0 - r0) * one_minus_cos*one_minus_cos_sq*one_minus_cos_sq;
170 | }
171 |
172 | #define MaterialTypeLambert 1
173 | #define MaterialTypeMetal 2
174 | #define MaterialTypeGlass 3
175 | struct Material
176 | {
177 | int type;
178 | vec3 albedo;
179 | float roughness;
180 | };
181 |
182 | const int numMaterials = 5;
183 | #define MaterialIdxBlueLambert 0
184 | #define MaterialIdxGreyMetal 1
185 | #define MaterialIdxOrangeMetal2 2
186 | #define MaterialIdxGlass 3
187 | #define MaterialIdxOrangeLambert 4
188 |
189 | bool materialScatterRay(int materialIndex, Ray inR, HitData hitData, vec2 uv, float times, out vec3 attenuation, out Ray outR)
190 | {
191 | Material materials[numMaterials];
192 |
193 | materials[MaterialIdxBlueLambert].type = MaterialTypeLambert;
194 | materials[MaterialIdxBlueLambert].albedo = vec3(0.1, 0.2, 0.4);
195 | materials[MaterialIdxBlueLambert].roughness = 0.0;
196 |
197 | materials[MaterialIdxGreyMetal].type = MaterialTypeMetal;
198 | materials[MaterialIdxGreyMetal].albedo = vec3(0.45, 0.45, 0.45);
199 | materials[MaterialIdxGreyMetal].roughness = 0.55;
200 |
201 | materials[MaterialIdxOrangeLambert].type = MaterialTypeLambert;
202 | materials[MaterialIdxOrangeLambert].albedo = vec3(0.5, 0.45, 0.05);
203 | materials[MaterialIdxOrangeLambert].roughness = 0.0;
204 |
205 | materials[MaterialIdxOrangeMetal2].type = MaterialTypeMetal;
206 | materials[MaterialIdxOrangeMetal2].albedo = vec3(0.75, 0.45, 0.05);
207 | materials[MaterialIdxOrangeMetal2].roughness = 0.1;
208 |
209 | materials[MaterialIdxGlass].type = MaterialTypeGlass;
210 | materials[MaterialIdxGlass].albedo = vec3(0.95, 0.95, 0.95);
211 | materials[MaterialIdxGlass].roughness = 0.0;
212 |
213 | if (materialIndex >= numMaterials || materialIndex < 0)
214 | return false;
215 |
216 | int materialType = materials[materialIndex].type;
217 | if (materialType == MaterialTypeLambert)
218 | {
219 | vec3 target = hitData.p + hitData.n + randInUnitSphere(uv, times);
220 | outR.O = hitData.p;
221 | outR.D = target - hitData.p;
222 | attenuation = materials[materialIndex].albedo;
223 | return true;
224 | }
225 | else if (materialType == MaterialTypeMetal)
226 | {
227 | vec3 reflected = reflect(normalize(inR.D), hitData.n);
228 | outR.O = hitData.p;
229 | outR.D = reflected + materials[materialIndex].roughness*randInUnitSphere(uv, times);
230 | attenuation = materials[materialIndex].albedo;
231 | return (dot(outR.D, hitData.n) > 0);
232 | }
233 | else if (materialType == MaterialTypeGlass)
234 | {
235 | vec3 rayD_nrm = normalize(inR.D);
236 | vec3 reflected = reflect(rayD_nrm, hitData.n);
237 | float ni_over_nt;
238 | attenuation = materials[materialIndex].albedo;
239 |
240 | const float refIdx = 1.5;
241 |
242 | vec3 outNormal;
243 | float cosine;
244 | if (dot(rayD_nrm, hitData.n) > 0)
245 | {
246 | outNormal = -hitData.n;
247 | ni_over_nt = refIdx;
248 | cosine = refIdx*dot(rayD_nrm, hitData.n);
249 | }
250 | else
251 | {
252 | outNormal = hitData.n;
253 | ni_over_nt = 1.0 / refIdx;
254 | cosine = -dot(rayD_nrm, hitData.n);
255 | }
256 |
257 | vec3 refracted;
258 | float reflProb;
259 | if (refract(rayD_nrm, outNormal, ni_over_nt, refracted))
260 | {
261 | reflProb = schlick(cosine, refIdx);
262 | }
263 | else
264 | {
265 | reflProb = 1.0;
266 | }
267 |
268 | if (fakeRand(uv, 23.45*times) > reflProb)
269 | {
270 | outR.O = hitData.p;
271 | outR.D = refracted + materials[materialIndex].roughness*randInUnitSphere(uv, times);
272 | return true;
273 | }
274 | else
275 | {
276 | outR.O = hitData.p;
277 | outR.D = reflected;
278 | return true;
279 | }
280 | }
281 | return false;
282 | }
283 |
284 | #define HitObjectSphere 1
285 | struct HitObject
286 | {
287 | int type;
288 | int materialIndex;
289 | vec4 param0;
290 | vec4 param1;
291 | };
292 |
293 | bool hitWorld(Ray r, out HitData hitData)
294 | {
295 | // We need non-zero t_min, as sometimes due to FP errors, reflecting rays will hit the same
296 | // surface they were reflected from, causing artifacts and inf-loops
297 | const float t_min = 0.00001;
298 | const float t_max = 10000.0;
299 |
300 | #define MANY_OBJECTS 0
301 |
302 | #if (MANY_OBJECTS == 1)
303 | const int numHitObjects = 106;
304 | #else
305 | const int numHitObjects = 6;
306 | #endif
307 | HitObject hitObjects[numHitObjects];
308 |
309 | hitObjects[0].type = HitObjectSphere;
310 | hitObjects[0].materialIndex = MaterialIdxBlueLambert;
311 | hitObjects[0].param0 = vec4(vec3(0.0, 0.0, -1.0), 0.5);
312 |
313 | hitObjects[1].type = HitObjectSphere;
314 | hitObjects[1].materialIndex = MaterialIdxOrangeLambert;
315 | hitObjects[1].param0 = vec4(vec3(0.0, -100.5, -1.0), 100.0);
316 |
317 | hitObjects[2].type = HitObjectSphere;
318 | hitObjects[2].materialIndex = MaterialIdxOrangeMetal2;
319 | hitObjects[2].param0 = vec4(vec3( 1.0, 0.0, -1.0), 0.5);
320 |
321 | hitObjects[3].type = HitObjectSphere;
322 | hitObjects[3].materialIndex = MaterialIdxGlass;
323 | hitObjects[3].param0 = vec4(vec3(-1.0, 0.0, -1.0), 0.5);
324 |
325 | hitObjects[4].type = HitObjectSphere;
326 | hitObjects[4].materialIndex = MaterialIdxGreyMetal;
327 | hitObjects[4].param0 = vec4(vec3( 0.0, -0.5, -2.0), 0.5);
328 |
329 | hitObjects[5].type = HitObjectSphere;
330 | hitObjects[5].materialIndex = MaterialIdxGreyMetal;
331 | hitObjects[5].param0 = vec4(vec3( 0.0, -0.5, 0.0), 0.5);
332 |
333 | #if (MANY_OBJECTS == 1)
334 | int offsetCnt = 0;
335 | for (int i = 0; i < 10; ++i)
336 | {
337 | for (int j = 0; j < 10; ++j)
338 | {
339 | int objIndex = 4+i*10+j;
340 | int materialIndex = MaterialIdxGlass;
341 |
342 | materialIndex = ((i+j+1) & 3);
343 |
344 | ++offsetCnt;
345 | if (offsetCnt > 5)
346 | offsetCnt = 0;
347 |
348 | vec3 offset;
349 | if (offsetCnt == 0)
350 | {
351 | offset = vec3(-0.25, 0.15, 0.88);
352 | }
353 | else if (offsetCnt == 1)
354 | {
355 | offset = vec3(0.12, -0.73, -0.45);
356 | }
357 | else if (offsetCnt == 2)
358 | {
359 | offset = vec3(-0.49, 0.33, -0.57);
360 | }
361 | else if (offsetCnt == 3)
362 | {
363 | offset = vec3(0.92, -0.15, 0.41);
364 | }
365 | else if (offsetCnt == 4)
366 | {
367 | offset = vec3(0.26, 0.53, 0.29);
368 | }
369 | else
370 | {
371 | offset = vec3(-0.39, -0.25, -0.72);
372 | }
373 |
374 | if (j < 5)
375 | offset.z -= 0.5;
376 | else
377 | offset.z += 0.5;
378 |
379 | hitObjects[objIndex].type = HitObjectSphere;
380 | hitObjects[objIndex].materialIndex = materialIndex;
381 | hitObjects[objIndex].param0 = vec4(vec3((i*0.1 - 0.5)*7.5, -0.3, (j*0.1 - 0.5)*7.5) + vec3(0.3, 0.1, 0.3)*offset, 0.1);
382 | }
383 | }
384 | #endif
385 |
386 | bool anyHit = false;
387 | HitData hitDataTemp;
388 | float closestHit = t_max;
389 |
390 | for (int i = 0; i < numHitObjects; ++i)
391 | {
392 | if (hitObjects[i].type == HitObjectSphere)
393 | {
394 | bool isSphereHit = hitSphere(hitObjects[i].param0.xyz, hitObjects[i].param0.w, hitObjects[i].materialIndex, r, t_min, closestHit, hitDataTemp);
395 | if (isSphereHit && (hitDataTemp.t < closestHit))
396 | {
397 | anyHit = true;
398 | closestHit = hitDataTemp.t;
399 | hitData = hitDataTemp;
400 | }
401 | }
402 | }
403 |
404 | return anyHit;
405 | }
406 | /* End of Hitting Routines */
407 |
408 | vec3 getColor(Ray r, vec2 uv, float times)
409 | {
410 | Ray curRay = r;
411 | bool needRayCast = true;
412 | HitData hitData;
413 | float recastCount = 0.0;
414 |
415 | vec3 dissipation = vec3(1.0, 1.0, 1.0);
416 |
417 | while (needRayCast)
418 | {
419 | recastCount += 1.0;
420 | if (recastCount > 16.0)
421 | break;
422 |
423 | bool isAnythingHit = hitWorld(curRay, hitData);
424 | if (isAnythingHit)
425 | {
426 | Ray inRay = curRay;
427 | vec3 attenuation;
428 | needRayCast = materialScatterRay(hitData.materialIndex, inRay, hitData, uv, times + recastCount*1000, attenuation, curRay);
429 | dissipation *= attenuation;
430 | }
431 | else
432 | {
433 | break;
434 | }
435 | }
436 |
437 | vec3 nrmD = normalize(curRay.D);
438 | float t = clamp(0.5*(nrmD.y + 1.0), 0.0, 1.0);
439 | return dissipation*((1-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0)) + vec3(0.1, 0.1, 0.1);
440 | }
441 |
442 | void main()
443 | {
444 | const float fov = 90 * (PI / 180.0);
445 | const float width = 800.0;
446 | const float height = 600.0;
447 | const float aspect = width/height;
448 |
449 | const float aperture = 0.25;
450 |
451 | const vec2 rndShift = vec2(fract(ubo.time*0.001), fract(ubo.time*0.0013));
452 | const vec3 viewup = vec3(0.0, 1.0, 0.0);
453 | const vec3 viewpoint = vec3(-2.0 * sin(ubo.time*0.001), 1.0, 1.0);
454 | const vec3 viewtarget = vec3(0.0, 0.0, -1.0);
455 |
456 | vec3 origin = viewpoint;
457 |
458 | // Basis vectors
459 | vec3 basZ = normalize(viewpoint - viewtarget);
460 | vec3 basX = normalize(cross(viewup, basZ));
461 | vec3 basY = normalize(cross(basZ, basX));
462 |
463 | // Since range is [-1; 1]
464 | float fov_tan = tan(fov/2.0);
465 | vec3 axisX = aspect*fov_tan*basX;
466 | vec3 axisY = fov_tan*basY;
467 |
468 | float lensRad = aperture / 2.0;
469 | float focusDist = length(viewpoint - viewtarget);
470 |
471 | // Lower left corner
472 | vec3 llc = origin - 0.5*focusDist*axisX - 0.5*focusDist*axisY - focusDist*basZ;
473 |
474 | const int numSubSamples = 32;
475 | vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
476 | for (int i = 0; i < numSubSamples; ++i)
477 | {
478 | vec2 rndDisk = lensRad*randOnDisk(in_texCoords.xy+rndShift, 12.3*(i+23.4));
479 | vec3 offset = basX*rndDisk.x + basY*rndDisk.y;
480 | vec3 rayTarget = llc + focusDist*(in_texCoords.x + fakeRand(in_texCoords.xy+rndShift, i)/width) * axisX + focusDist*(in_texCoords.y + fakeRand(in_texCoords.xy+rndShift, i+numSubSamples)/height) * axisY;
481 | Ray r = getRay(origin + offset, rayTarget - origin - offset);
482 | color += vec4(getColor(r, in_texCoords.xy+rndShift, i), 1.0);
483 | }
484 |
485 | #if 0
486 | vec4 colorTex = texture(texSampler, in_texCoords);
487 |
488 | float interp = 0.5;
489 | outColor = in_color * ((1.0 - interp) * (color / numSubSamples) + interp * colorTex);
490 | #else
491 | outColor = in_color * (color / numSubSamples);
492 | #endif
493 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/shader_bindings.h:
--------------------------------------------------------------------------------
1 | #ifndef IG_SHADER_BINDINGS_H
2 | #define IG_SHADER_BINDINGS_H
3 |
4 | #define SH_BIND_GLOBAL_CONSTANTS 0
5 | #define SH_BIND_TRANSFORM 1
6 |
7 | // Forward shading
8 | #define SH_BIND_FWDSH_ALBEDO_TEX 2
9 | #define SH_BIND_FWDSH_SHADOWMAP_TEX 3
10 | #define SH_BIND_FWDSH_LIGHTPROJ_TEX 4
11 | #define SH_BIND_FWDSH_LIGHTMATRIX 5
12 |
13 | // Pathtracer
14 | #define SH_BIND_PATHTRACER_NOISE_TEX 2
15 |
16 |
17 | #endif // IG_SHADER_BINDINGS_H
--------------------------------------------------------------------------------
/vkEngine/shaders/shadowmap.fs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec4 in_color;
8 | layout(location = 1) in vec3 in_normal_cam;
9 | layout(location = 2) in vec2 in_texCoords;
10 | layout(location = 3) in float in_time;
11 |
12 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
13 | {
14 | float time;
15 | } ubo;
16 |
17 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
18 | {
19 | mat4 view;
20 | mat4 proj;
21 | } transformUBO;
22 |
23 | void main()
24 | {
25 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/shadowmap.vs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec3 in_position;
8 | layout(location = 1) in vec3 in_normal;
9 | layout(location = 2) in vec4 in_color;
10 | layout(location = 3) in vec2 in_texCoords;
11 |
12 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
13 | {
14 | float time;
15 | } ubo;
16 |
17 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
18 | {
19 | mat4 view;
20 | mat4 proj;
21 | } transformUBO;
22 |
23 | layout(push_constant) uniform MeshPushConst
24 | {
25 | mat4 model;
26 | } meshPushConst;
27 |
28 | out gl_PerVertex
29 | {
30 | vec4 gl_Position;
31 | };
32 |
33 | layout(location = 0) out vec4 out_color;
34 | layout(location = 1) out vec3 out_normal_cam;
35 | layout(location = 2) out vec2 out_texCoords;
36 | layout(location = 3) out float out_time;
37 |
38 | void main()
39 | {
40 | vec4 modelVertex = meshPushConst.model * vec4(in_position, 1.0);
41 | gl_Position = transformUBO.proj * transformUBO.view * modelVertex;
42 | out_texCoords = in_texCoords;
43 | out_color = in_color;
44 | // Suppose view is orthonormal
45 | out_normal_cam = mat3(transformUBO.view) * mat3(meshPushConst.model) * in_normal;
46 | out_time = ubo.time;
47 | }
--------------------------------------------------------------------------------
/vkEngine/shaders/test.vs:
--------------------------------------------------------------------------------
1 | #version 450
2 | #extension GL_ARB_separate_shader_objects : enable
3 | #extension GL_GOOGLE_include_directive : enable
4 |
5 | #include "shader_bindings.h"
6 |
7 | layout(location = 0) in vec3 in_position;
8 | layout(location = 1) in vec3 in_normal;
9 | layout(location = 2) in vec4 in_color;
10 | layout(location = 3) in vec2 in_texCoords;
11 |
12 | layout(set = 0, binding = SH_BIND_GLOBAL_CONSTANTS) uniform UniformBufferObject
13 | {
14 | float time;
15 | } ubo;
16 |
17 | layout(set = 0, binding = SH_BIND_TRANSFORM) uniform TransformUBO
18 | {
19 | mat4 view;
20 | mat4 proj;
21 | } transformUBO;
22 |
23 | layout(push_constant) uniform MeshPushConst
24 | {
25 | mat4 model;
26 | } meshPushConst;
27 |
28 | out gl_PerVertex
29 | {
30 | vec4 gl_Position;
31 | };
32 |
33 | layout(location = 0) out vec4 out_color;
34 | layout(location = 1) out vec2 out_texCoords;
35 | layout(location = 2) out float out_time;
36 |
37 | void main()
38 | {
39 | gl_Position = vec4(in_position, 1.0);
40 | out_texCoords = in_texCoords;
41 | out_color = in_color;
42 | out_time = ubo.time;
43 | }
--------------------------------------------------------------------------------
/vkEngine/source/app.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avoroshilov/physics_playground/06208f51d1b1a6a62a0c6cd5f54270b6ec0e42fe/vkEngine/source/app.cpp
--------------------------------------------------------------------------------
/vkEngine/source/app.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "vulkan\manager.h"
4 |
5 | class App
6 | {
7 | protected:
8 |
9 | bool m_isExitting = false;
10 |
11 | int m_width = -1, m_height = -1;
12 | HWND m_hWnd = nullptr;
13 | vulkan::Wrapper * m_renderingWrapper;
14 |
15 | double m_elapsedTimeMS = 0.0;
16 |
17 | public:
18 |
19 | void setRenderManager(vulkan::Wrapper * renderingWrapper)
20 | {
21 | m_renderingWrapper = renderingWrapper;
22 | }
23 |
24 | void init(HWND hWnd, int width, int height)
25 | {
26 | m_width = width;
27 | m_height = height;
28 | m_hWnd = hWnd;
29 | m_renderingWrapper->init(m_hWnd, m_width, m_height);
30 | }
31 | void deinit()
32 | {
33 | m_renderingWrapper->deinit();
34 | }
35 |
36 | double getElapsedTime() const { return m_elapsedTimeMS; }
37 |
38 | void update(double dtMS)
39 | {
40 | m_elapsedTimeMS += dtMS;
41 | m_renderingWrapper->increaseDTime(dtMS);
42 | }
43 |
44 | void onWindowResize(int width, int height)
45 | {
46 | if (width == 0 || height == 0)
47 | return;
48 |
49 | m_width = width;
50 | m_height = height;
51 |
52 | m_renderingWrapper->onWindowResize(width, height);
53 | }
54 |
55 | void setIsExitting(bool isExitting) { m_isExitting = isExitting; }
56 | bool getIsExitting() const { return m_isExitting; }
57 |
58 | void requestCapture(bool captureRequested)
59 | {
60 | m_renderingWrapper->requestCapture(captureRequested);
61 | }
62 |
63 | static const int c_titleBufSize = 256;
64 | wchar_t m_titleBuf[c_titleBufSize];
65 | void setWindowTitle(wchar_t * title)
66 | {
67 | swprintf_s(m_titleBuf, c_titleBufSize, L"%s", title);
68 | SetWindowText(m_hWnd, m_titleBuf);
69 | }
70 |
71 | void setDTime(double dtimeMS)
72 | {
73 | const int titleBufSizeAdditional = 16;
74 | const int titleBufSizeAugmented = c_titleBufSize + titleBufSizeAdditional;
75 | wchar_t titleBuf[titleBufSizeAugmented];
76 | swprintf_s(titleBuf, titleBufSizeAugmented, L"%s: %.1f (%.3f ms)", m_titleBuf, 1000.0 / dtimeMS, dtimeMS);
77 | SetWindowText(m_hWnd, titleBuf);
78 | }
79 | };
80 |
81 | struct CallbackData
82 | {
83 | App * app;
84 |
85 | enum class MovementKindBits
86 | {
87 | eForward = (1 << 0),
88 | eBackward = (1 << 1),
89 | eLeft = (1 << 2),
90 | eRight = (1 << 3),
91 | eUp = (1 << 4),
92 | eDown = (1 << 5),
93 | eAccel = (1 << 6),
94 | eDeccel = (1 << 7),
95 | };
96 | uint32_t movementFlags;
97 |
98 | bool reqDropBox;
99 |
100 | bool isActive;
101 | bool isPaused;
102 | bool isAnimStep;
103 |
104 | enum class MouseMode
105 | {
106 | eCamera = 0,
107 | ePicking = 1,
108 |
109 | eNUM_ENTRIES,
110 | eStartingMode = eCamera
111 | };
112 | MouseMode mouseMode;
113 |
114 | int lmbState;
115 |
116 | // Mouse coordinates
117 | int mx, my;
118 |
119 | int dmx, dmy;
120 | int dwheel;
121 | };
122 |
--------------------------------------------------------------------------------
/vkEngine/source/camera.cpp:
--------------------------------------------------------------------------------
1 | #include "camera.h"
2 |
3 | void Camera::update(const math::Vec3 & posOffset, float rotX, float rotY)
4 | {
5 | using namespace math;
6 |
7 | m_angX += rotX;
8 | m_angY += rotY;
9 |
10 | const float angYLimit = 0.99f * _PI2;
11 | if (m_angY > angYLimit)
12 | m_angY = angYLimit;
13 | else if (m_angY < -angYLimit)
14 | m_angY = -angYLimit;
15 |
16 | float angX = m_angX;
17 | float angY = m_angY;
18 |
19 | const bool invert = false;
20 | if (!invert)
21 | {
22 | angX = -angX;
23 | angY = -angY;
24 | }
25 | angY += _PI2;
26 |
27 | m_view = Vec3C(sinf(angX)*sinf(angY), cosf(angY), cosf(angX)*sinf(angY));
28 | m_view.normalize();
29 |
30 | Vec3 right = m_up.cross(m_view);
31 | right.normalize();
32 |
33 | Vec3 up = m_view.cross(right);
34 | up.normalize();
35 |
36 | m_pos += right * posOffset.x + up * posOffset.y + m_view * posOffset.z;
37 | }
38 |
39 | void Camera::fillMatrix(math::Mat34 * mat) const
40 | {
41 | if (!mat)
42 | return;
43 |
44 | using namespace math;
45 |
46 | Vec3 right = m_up.cross(m_view);
47 | right.normalize();
48 |
49 | Vec3 up = m_view.cross(right);
50 | up.normalize();
51 |
52 | mat->setBasis0(right);
53 | mat->setBasis1(up);
54 | mat->setBasis2(m_view);
55 | mat->setBasis3(m_pos);
56 |
57 | *mat = mat->invertRTCopy();
58 | }
59 |
--------------------------------------------------------------------------------
/vkEngine/source/camera.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "math/AuxMath.h"
5 | #include "math/Vec3.h"
6 | #include "math/Mat34.h"
7 |
8 | #include
9 |
10 | class Camera
11 | {
12 | protected:
13 |
14 | math::Vec3 m_pos = math::Vec3C(0.0f, 0.0f, 0.0f);
15 | math::Vec3 m_up = math::Vec3C(0.0f, 1.0f, 0.0f), m_view = math::Vec3C(0.0f, 0.0f, -1.0f);
16 |
17 | float m_angX = 0.0f, m_angY = 0.0f;
18 |
19 | public:
20 |
21 | void setPosition(const math::Vec3 & pos) { m_pos = pos; }
22 |
23 | math::Vec3 getPosition() const { return m_pos; }
24 | math::Vec3 getUp() const { return m_up; }
25 | math::Vec3 getView() const { return m_view; }
26 |
27 | void update(const math::Vec3 & posOffset, float rotX, float rotY);
28 | void fillMatrix(math::Mat34 * mat) const;
29 | };
--------------------------------------------------------------------------------
/vkEngine/source/drawing_primitives.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include