diff --git a/.gitignore b/.gitignore
index 6c57396..35abf77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -253,6 +253,7 @@ bld/
# Visual Studio 2015 cache/options directory
.vs/
+.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..1ca4626
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,10 @@
+*.txt
+external
+cmake
+*.json
+*.comp
+*.frag
+*.vert
+*.tese
+*.tesc
+*.geom
diff --git a/README.md b/README.md
index 20ee451..60df580 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,140 @@
-Vulkan Grass Rendering
-==================================
+# Vulkan Grass Rendering
-**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5**
+========================
-* (TODO) YOUR NAME HERE
-* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
+**University of Pennsylvania, CIS 5650: GPU Programming and Architecture, Project 5 - Vulkan Grass Rendering**
-### (TODO: Your README)
+- Jordan Hochman
+ - [LinkedIn](https://www.linkedin.com/in/jhochman24), [Personal Website](https://jordanh.xyz), [GitHub](https://github.com/JHawk0224)
+- Tested on: Windows 11, Ryzen 7 5800 @ 3.4GHz 32GB, GeForce RTX 3060 Ti 8GB (Compute Capability: 8.6)
-*DO NOT* leave the README to the last minute! It is a crucial part of the
-project, and we will not be able to grade you without a good README.
+## Welcome to my Vulkan Grass Rendering Project!
+
+
+
+
+In this project, I implemented a grass renderer in Vulkan following this [Responsive Real-Time Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf) paper. It models each blade of grass as a triangle affected by 3 forces, gravity, the resistance of the blade, and the wind. More details about the exact implementation of these forces can be found in `INSTRUCTION.md` [here](INSTRUCTION.md).
+
+
+
+The image above demonstrates how each blade is broken down.
+
+Note that the FPS cap/framiness of the gif above, and all the ones in this README, is not due to the actual graphics pipeline. This is instead just an actual limit of the gifs FPS as it only has so many frames, but the actual FPS of the simulation is much higher and it appears much smoother in real time. The same is true for all of the other gifs in this README.
+
+I will now walk through the process of creating this and demonstrate images at each stage. First however, I will explain the tunable parameters:
+
+In `Blades.h`:
+
+- `NUM_BLADES`: The number of grass blades
+- `MIN_HEIGHT`: Minimum height of the randomly generated blades
+- `MAX_HEIGHT`: Maximum height of the randomly generated blades
+- `MIN_WIDTH`: Minimum width of the randomly generated blades
+- `MAX_WIDTH`: Maximum width of the randomly generated blades
+- `MIN_BEND`: Minimum bend of the randomly generated blades
+- `MAX_BEND`: Maximum bend of the randomly generated blades
+
+In `grass.tese`:
+
+- `BLADE_SHAPE`: Changes the shape of the blades
+ - Use the following values: 0 = square, 1 = triangle, 2 = parabola, 3 = triangle tip
+
+In `grass.tesc`:
+
+- `MAX_TESSELLATION`: The max tessellation level of detail
+- `MIN_TESSELLATION`: The min tessellation level of detail
+- `TESSELLATION_FALLOFF_DISTANCE`: How fast the tessellation level of detail falls off. After this distance, the level of detail is as small as possible.
+
+In `grass.frag`:
+
+- `baseColor`: The color at the base of each blade of grass
+- `tipColor`: The color at the tip of each blade of grass
+
+In `compute.comp`:
+
+- `COMPUTE_FORCES`: A boolean representing if forces should be calculated or not
+- `GRAVITY_DIRECTION`: A normalized vector representing the direction of gravity
+- `GRAVITY_MAGNITUDE`: The magnitude of gravity
+- `BLADE_MASS`: The mass of each blade of grass
+- `WIND_TYPE`: Sets the type of wind
+ - The three values are 0 for random wind, 1 for uniform wind in one direction, and 2 for radial wind about a central point
+- `WIND_MAGNITUDE`: The magnitude of the wind
+- `WIND_FREQUENCY`: The frequency of the wind (how fast it changes with time)
+- `ENABLE_ORIENTATION_CULLING`: Whether or not orientation culling is turned on
+- `ENABLE_FRUSTUM_CULLING`: Whether or not view frustum culling is turned on
+- `ENABLE_DISTANCE_CULLING`: Whether or not distance culling is turned on
+- `ORIENTATION_CULLING_THRESHOLD`: The orientation threshold at which to cull
+- `FRUSTUM_CULLING_PADDING`: The padding added to the frustum during culling
+- `DISTANCE_CULLING_THRESHOLD`: The distance at which culled items are removed
+- `DISTANCE_CULLING_NUM_BUCKETS`: The number of buckets for culling blades
+
+Feel free to tweak any of these if you want to run these yourself!
+
+### Feature Walkthrough
+
+The first thing I implemented was getting the grass blades to display, without any physics calculations or culling. This is what it initially looks like:
+
+
+
+Then I added tessellation levels of detail, so the further from the camera, the lower the detail of the blades of grass. Here are a few gifs that demonstrate this:
+
+
+
+
+As you can see, as the camera gets further and further away, the level of detail drops off.
+
+After getting this working, I added the physics simulation. This included forces for gravity, the resistance of the blade, and wind. When implementing this, the wind can actually be any arbitrary function. All that's needed for this function is to compute the direction vector of the wind at any given point for any given time. Then based on this function, it can be applied to all the blades in the scene.
+
+Based on this, I created three wind functions leading to three different wind patterns.
+
+The first is a somewhat random distribution of wind where each point is effectively random. The second is a mostly uniform wind across the entire grid, with some small variation so it looks better. The third is a radial wind pattern, such as with a helicopter or fan. In this one, the wind moves outward from a central position. Here they are side by side:
+
+
+
+
+
+And a few more with movement:
+
+
+
+
+
+After implementing the physics calculations, I then added culling. The only one easily visible from these three is distance culling, and you can see a demonstration of it here:
+
+
+
+Notice how once the camera is far enough away, the blades are entirely removed. And before that, certain fractions of the blades are removed. This is determined by the number of bins which can be seen in the tunable parameter above.
+
+Finally, after getting all of these working, it was time to play around with the simulation! Here is some nice looking grass that is thin and dense:
+
+
+
+And now that same version much much sparser (to see the difference in number of blades):
+
+
+
+Finally here are some more interesting grass types I created:
+
+
+
+
+
+
+### Performance Analysis
+
+To gauge the performance of my grass blade renderer, I analyzed the FPS with different numbers of blades of grass and different culling options. I chose to test on 2^9, 2^11, 2^13, 2^15, 2^17, 2^19, and 2^21 blades of grass as this offered a wide variety of capabilities and performance measurements from my GPU. For each of these, I tested it with no culling, with each type of culling, and finally with all types of culling. The FPS was measured by taking the average FPS for the first 5 seconds of the rendering.
+
+Here is the raw data, although the details can be found [here](performance-data.xlsx) in `performance-data.xlsx`.
+
+
+
+In here, each cell measures FPS, so higher is better. Here is a graph which shows this data more clearly (in log scale):
+
+
+
+As you can see based on this graph, it is very clear that as the number of blades increases, the performance decreases. This holds at all measurements of number of blades and for all culling tests.
+
+Another interesting point is that with a very low count of blades (at 2^9 = 512) there was no noticeable difference in the performance between any of the culling options. This is likely because with so few blades, culling a few or not doesn't impact the performance. Most of it just comes from the overhead of setting up the rendering pipeline, and not from rendering the number of blades themselves.
+
+However, as the number of blades increases, the effect of culling becomes more and more apparent. It starts improving the performance by a large margin, and this can be seen in the graph. This makes sense because we would expect that the pipeline has to do much less work when culling since the number of triangles is large.
+
+Another interesting point is that orientation and distance culling mostly outperform view-frustum culling. One explanation for this is that the view-frustum culling can only reduce the number of blades rendered by about a fourth (the FOV over the total screen portion). However, the other two can reduce this by a much larger amount if there are a large number of blades either turned away from the camera or far away respectively. Therefore it makes sense that in the extremes both of these can cull much more than view-frustum culling.
diff --git a/bin/Release/vulkan_grass_rendering.exe b/bin/Release/vulkan_grass_rendering.exe
index f68db3a..80a78ad 100644
Binary files a/bin/Release/vulkan_grass_rendering.exe and b/bin/Release/vulkan_grass_rendering.exe differ
diff --git a/img/data.png b/img/data.png
new file mode 100644
index 0000000..fba66ce
Binary files /dev/null and b/img/data.png differ
diff --git a/img/distance-culling.gif b/img/distance-culling.gif
new file mode 100644
index 0000000..5ffaac4
Binary files /dev/null and b/img/distance-culling.gif differ
diff --git a/img/graph.jpg b/img/graph.jpg
new file mode 100644
index 0000000..78f8e9f
Binary files /dev/null and b/img/graph.jpg differ
diff --git a/img/short-dense-blue.gif b/img/short-dense-blue.gif
new file mode 100644
index 0000000..13a3405
Binary files /dev/null and b/img/short-dense-blue.gif differ
diff --git a/img/short-dense-red-close.gif b/img/short-dense-red-close.gif
new file mode 100644
index 0000000..d8f972e
Binary files /dev/null and b/img/short-dense-red-close.gif differ
diff --git a/img/short-dense-red-radial.gif b/img/short-dense-red-radial.gif
new file mode 100644
index 0000000..cc8bf54
Binary files /dev/null and b/img/short-dense-red-radial.gif differ
diff --git a/img/short-dense.gif b/img/short-dense.gif
new file mode 100644
index 0000000..f2b42bf
Binary files /dev/null and b/img/short-dense.gif differ
diff --git a/img/still.png b/img/still.png
new file mode 100644
index 0000000..c43c9a5
Binary files /dev/null and b/img/still.png differ
diff --git a/img/tessellation-lod.gif b/img/tessellation-lod.gif
new file mode 100644
index 0000000..124ed2f
Binary files /dev/null and b/img/tessellation-lod.gif differ
diff --git a/img/thin-dense.gif b/img/thin-dense.gif
new file mode 100644
index 0000000..c47c057
Binary files /dev/null and b/img/thin-dense.gif differ
diff --git a/img/thin-sparse.gif b/img/thin-sparse.gif
new file mode 100644
index 0000000..b34be51
Binary files /dev/null and b/img/thin-sparse.gif differ
diff --git a/img/wind-radial-move.gif b/img/wind-radial-move.gif
new file mode 100644
index 0000000..d77451a
Binary files /dev/null and b/img/wind-radial-move.gif differ
diff --git a/img/wind-radial.gif b/img/wind-radial.gif
new file mode 100644
index 0000000..26fbbcb
Binary files /dev/null and b/img/wind-radial.gif differ
diff --git a/img/wind-random-move-lod.gif b/img/wind-random-move-lod.gif
new file mode 100644
index 0000000..0822922
Binary files /dev/null and b/img/wind-random-move-lod.gif differ
diff --git a/img/wind-random.gif b/img/wind-random.gif
new file mode 100644
index 0000000..ee4f1b1
Binary files /dev/null and b/img/wind-random.gif differ
diff --git a/img/wind-uniform-move.gif b/img/wind-uniform-move.gif
new file mode 100644
index 0000000..259d876
Binary files /dev/null and b/img/wind-uniform-move.gif differ
diff --git a/img/wind-uniform.gif b/img/wind-uniform.gif
new file mode 100644
index 0000000..fe582bf
Binary files /dev/null and b/img/wind-uniform.gif differ
diff --git a/performance-data.xlsx b/performance-data.xlsx
new file mode 100644
index 0000000..c5f4fae
Binary files /dev/null and b/performance-data.xlsx differ
diff --git a/src/Blades.cpp b/src/Blades.cpp
index 80e3d76..fd02f8c 100644
--- a/src/Blades.cpp
+++ b/src/Blades.cpp
@@ -1,10 +1,10 @@
-#include
#include "Blades.h"
+
+#include
+
#include "BufferUtils.h"
-float generateRandomFloat() {
- return rand() / (float)RAND_MAX;
-}
+float generateRandomFloat() { return rand() / (float)RAND_MAX; }
Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Model(device, commandPool, {}, {}) {
std::vector blades;
@@ -44,22 +44,21 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode
indirectDraw.firstVertex = 0;
indirectDraw.firstInstance = 0;
- BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory);
- BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory);
- BufferUtils::CreateBufferFromData(device, commandPool, &indirectDraw, sizeof(BladeDrawIndirect), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, numBladesBuffer, numBladesBufferMemory);
+ BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), NUM_BLADES * sizeof(Blade),
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory);
+ BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade),
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory);
+ BufferUtils::CreateBufferFromData(device, commandPool, &indirectDraw, sizeof(BladeDrawIndirect),
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
+ numBladesBuffer, numBladesBufferMemory);
}
-VkBuffer Blades::GetBladesBuffer() const {
- return bladesBuffer;
-}
+VkBuffer Blades::GetBladesBuffer() const { return bladesBuffer; }
-VkBuffer Blades::GetCulledBladesBuffer() const {
- return culledBladesBuffer;
-}
+VkBuffer Blades::GetCulledBladesBuffer() const { return culledBladesBuffer; }
-VkBuffer Blades::GetNumBladesBuffer() const {
- return numBladesBuffer;
-}
+VkBuffer Blades::GetNumBladesBuffer() const { return numBladesBuffer; }
Blades::~Blades() {
vkDestroyBuffer(device->GetVkDevice(), bladesBuffer, nullptr);
diff --git a/src/Blades.h b/src/Blades.h
index 9bd1eed..6c9233d 100644
--- a/src/Blades.h
+++ b/src/Blades.h
@@ -1,7 +1,9 @@
#pragma once
#include
-#include
+
#include
+#include
+
#include "Model.h"
constexpr static unsigned int NUM_BLADES = 1 << 13;
@@ -70,7 +72,7 @@ struct BladeDrawIndirect {
};
class Blades : public Model {
-private:
+ private:
VkBuffer bladesBuffer;
VkBuffer culledBladesBuffer;
VkBuffer numBladesBuffer;
@@ -79,7 +81,7 @@ class Blades : public Model {
VkDeviceMemory culledBladesBufferMemory;
VkDeviceMemory numBladesBufferMemory;
-public:
+ public:
Blades(Device* device, VkCommandPool commandPool, float planeDim);
VkBuffer GetBladesBuffer() const;
VkBuffer GetCulledBladesBuffer() const;
diff --git a/src/BufferUtils.cpp b/src/BufferUtils.cpp
index acf617e..0f23aef 100644
--- a/src/BufferUtils.cpp
+++ b/src/BufferUtils.cpp
@@ -1,7 +1,9 @@
#include "BufferUtils.h"
+
#include "Instance.h"
-void BufferUtils::CreateBuffer(Device* device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
+void BufferUtils::CreateBuffer(Device* device, VkDeviceSize size, VkBufferUsageFlags usage,
+ VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
// Create buffer
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@@ -24,14 +26,15 @@ void BufferUtils::CreateBuffer(Device* device, VkDeviceSize size, VkBufferUsageF
allocInfo.memoryTypeIndex = device->GetInstance()->GetMemoryTypeIndex(memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(device->GetVkDevice(), &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
- throw std::runtime_error("Failed to allocate vertex buffer");
+ throw std::runtime_error("Failed to allocate vertex buffer");
}
// Associate allocated memory with vertex buffer
vkBindBufferMemory(device->GetVkDevice(), buffer, bufferMemory, 0);
}
-void BufferUtils::CopyBuffer(Device* device, VkCommandPool commandPool, VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
+void BufferUtils::CopyBuffer(Device* device, VkCommandPool commandPool, VkBuffer srcBuffer, VkBuffer dstBuffer,
+ VkDeviceSize size) {
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
@@ -63,17 +66,20 @@ void BufferUtils::CopyBuffer(Device* device, VkCommandPool commandPool, VkBuffer
vkFreeCommandBuffers(device->GetVkDevice(), commandPool, 1, &commandBuffer);
}
-void BufferUtils::CreateBufferFromData(Device* device, VkCommandPool commandPool, void* bufferData, VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsage, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
+void BufferUtils::CreateBufferFromData(Device* device, VkCommandPool commandPool, void* bufferData,
+ VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsage, VkBuffer& buffer,
+ VkDeviceMemory& bufferMemory) {
// Create the staging buffer
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
VkBufferUsageFlags stagingUsage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- VkMemoryPropertyFlags stagingProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ VkMemoryPropertyFlags stagingProperties =
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
BufferUtils::CreateBuffer(device, bufferSize, stagingUsage, stagingProperties, stagingBuffer, stagingBufferMemory);
// Fill the staging buffer
- void *data;
+ void* data;
vkMapMemory(device->GetVkDevice(), stagingBufferMemory, 0, bufferSize, 0, &data);
memcpy(data, bufferData, static_cast(bufferSize));
vkUnmapMemory(device->GetVkDevice(), stagingBufferMemory);
diff --git a/src/BufferUtils.h b/src/BufferUtils.h
index 04e784a..1aa5844 100644
--- a/src/BufferUtils.h
+++ b/src/BufferUtils.h
@@ -1,10 +1,13 @@
#pragma once
#include
+
#include "Device.h"
namespace BufferUtils {
- void CreateBuffer(Device* device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
- void CopyBuffer(Device* device, VkCommandPool commandPool, VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
- void CreateBufferFromData(Device* device, VkCommandPool commandPool, void* bufferData, VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsage, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
-}
+void CreateBuffer(Device* device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
+ VkBuffer& buffer, VkDeviceMemory& bufferMemory);
+void CopyBuffer(Device* device, VkCommandPool commandPool, VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
+void CreateBufferFromData(Device* device, VkCommandPool commandPool, void* bufferData, VkDeviceSize bufferSize,
+ VkBufferUsageFlags bufferUsage, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
+} // namespace BufferUtils
diff --git a/src/Camera.cpp b/src/Camera.cpp
index 3afb5b8..317416e 100644
--- a/src/Camera.cpp
+++ b/src/Camera.cpp
@@ -5,25 +5,26 @@
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include
-#include "Camera.h"
#include "BufferUtils.h"
+#include "Camera.h"
Camera::Camera(Device* device, float aspectRatio) : device(device) {
r = 10.0f;
theta = 0.0f;
phi = 0.0f;
- cameraBufferObject.viewMatrix = glm::lookAt(glm::vec3(0.0f, 1.0f, 10.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
+ cameraBufferObject.viewMatrix =
+ glm::lookAt(glm::vec3(0.0f, 1.0f, 10.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
cameraBufferObject.projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);
- cameraBufferObject.projectionMatrix[1][1] *= -1; // y-coordinate is flipped
+ cameraBufferObject.projectionMatrix[1][1] *= -1; // y-coordinate is flipped
- BufferUtils::CreateBuffer(device, sizeof(CameraBufferObject), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer, bufferMemory);
+ BufferUtils::CreateBuffer(device, sizeof(CameraBufferObject), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer,
+ bufferMemory);
vkMapMemory(device->GetVkDevice(), bufferMemory, 0, sizeof(CameraBufferObject), 0, &mappedData);
memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject));
}
-VkBuffer Camera::GetBuffer() const {
- return buffer;
-}
+VkBuffer Camera::GetBuffer() const { return buffer; }
void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) {
theta += deltaX;
@@ -33,8 +34,10 @@ void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) {
float radTheta = glm::radians(theta);
float radPhi = glm::radians(phi);
- glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radTheta, glm::vec3(0.0f, 1.0f, 0.0f)) * glm::rotate(glm::mat4(1.0f), radPhi, glm::vec3(1.0f, 0.0f, 0.0f));
- glm::mat4 finalTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f)) * rotation * glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, r));
+ glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radTheta, glm::vec3(0.0f, 1.0f, 0.0f)) *
+ glm::rotate(glm::mat4(1.0f), radPhi, glm::vec3(1.0f, 0.0f, 0.0f));
+ glm::mat4 finalTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f)) * rotation *
+ glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, r));
cameraBufferObject.viewMatrix = glm::inverse(finalTransform);
@@ -42,7 +45,7 @@ void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) {
}
Camera::~Camera() {
- vkUnmapMemory(device->GetVkDevice(), bufferMemory);
- vkDestroyBuffer(device->GetVkDevice(), buffer, nullptr);
- vkFreeMemory(device->GetVkDevice(), bufferMemory, nullptr);
+ vkUnmapMemory(device->GetVkDevice(), bufferMemory);
+ vkDestroyBuffer(device->GetVkDevice(), buffer, nullptr);
+ vkFreeMemory(device->GetVkDevice(), bufferMemory, nullptr);
}
diff --git a/src/Camera.h b/src/Camera.h
index 6b10747..0952697 100644
--- a/src/Camera.h
+++ b/src/Camera.h
@@ -2,19 +2,20 @@
#pragma once
#include
+
#include "Device.h"
struct CameraBufferObject {
- glm::mat4 viewMatrix;
- glm::mat4 projectionMatrix;
+ glm::mat4 viewMatrix;
+ glm::mat4 projectionMatrix;
};
class Camera {
-private:
+ private:
Device* device;
-
+
CameraBufferObject cameraBufferObject;
-
+
VkBuffer buffer;
VkDeviceMemory bufferMemory;
@@ -22,11 +23,11 @@ class Camera {
float r, theta, phi;
-public:
+ public:
Camera(Device* device, float aspectRatio);
~Camera();
VkBuffer GetBuffer() const;
-
+
void UpdateOrbit(float deltaX, float deltaY, float deltaZ);
};
diff --git a/src/Device.cpp b/src/Device.cpp
index a242759..8d529d0 100644
--- a/src/Device.cpp
+++ b/src/Device.cpp
@@ -1,30 +1,20 @@
#include "Device.h"
+
#include "Instance.h"
Device::Device(Instance* instance, VkDevice vkDevice, Queues queues)
- : instance(instance), vkDevice(vkDevice), queues(queues) {
-}
+ : instance(instance), vkDevice(vkDevice), queues(queues) {}
-Instance* Device::GetInstance() {
- return instance;
-}
+Instance* Device::GetInstance() { return instance; }
-VkDevice Device::GetVkDevice() {
- return vkDevice;
-}
+VkDevice Device::GetVkDevice() { return vkDevice; }
-VkQueue Device::GetQueue(QueueFlags flag) {
- return queues[flag];
-}
+VkQueue Device::GetQueue(QueueFlags flag) { return queues[flag]; }
-unsigned int Device::GetQueueIndex(QueueFlags flag) {
- return GetInstance()->GetQueueFamilyIndices()[flag];
-}
+unsigned int Device::GetQueueIndex(QueueFlags flag) { return GetInstance()->GetQueueFamilyIndices()[flag]; }
SwapChain* Device::CreateSwapChain(VkSurfaceKHR surface, unsigned int numBuffers) {
return new SwapChain(this, surface, numBuffers);
}
-Device::~Device() {
- vkDestroyDevice(vkDevice, nullptr);
-}
+Device::~Device() { vkDestroyDevice(vkDevice, nullptr); }
diff --git a/src/Device.h b/src/Device.h
index 163204b..d4e1fb4 100644
--- a/src/Device.h
+++ b/src/Device.h
@@ -1,8 +1,10 @@
#pragma once
+#include
+
#include
#include
-#include
+
#include "QueueFlags.h"
#include "SwapChain.h"
@@ -10,7 +12,7 @@ class SwapChain;
class Device {
friend class Instance;
-public:
+ public:
SwapChain* CreateSwapChain(VkSurfaceKHR surface, unsigned int numBuffers);
Instance* GetInstance();
VkDevice GetVkDevice();
@@ -18,9 +20,9 @@ class Device {
unsigned int GetQueueIndex(QueueFlags flag);
~Device();
-private:
+ private:
using Queues = std::array;
-
+
Device() = delete;
Device(Instance* instance, VkDevice vkDevice, Queues queues);
diff --git a/src/Image.cpp b/src/Image.cpp
index 64bfe82..e4bad19 100644
--- a/src/Image.cpp
+++ b/src/Image.cpp
@@ -1,12 +1,15 @@
#define STB_IMAGE_IMPLEMENTATION
+#include "Image.h"
+
#include
-#include "Image.h"
+#include "BufferUtils.h"
#include "Device.h"
#include "Instance.h"
-#include "BufferUtils.h"
-void Image::Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
+void Image::Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
+ VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image,
+ VkDeviceMemory& imageMemory) {
// Create Vulkan image
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
@@ -44,10 +47,11 @@ void Image::Create(Device* device, uint32_t width, uint32_t height, VkFormat for
vkBindImageMemory(device->GetVkDevice(), image, imageMemory, 0);
}
-void Image::TransitionLayout(Device* device, VkCommandPool commandPool, VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
+void Image::TransitionLayout(Device* device, VkCommandPool commandPool, VkImage image, VkFormat format,
+ VkImageLayout oldLayout, VkImageLayout newLayout) {
auto hasStencilComponent = [](VkFormat format) {
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
- };
+ };
// Use an image memory barrier (type of pipeline barrier) to transition image layout
VkImageMemoryBarrier barrier = {};
@@ -57,42 +61,44 @@ void Image::TransitionLayout(Device* device, VkCommandPool commandPool, VkImage
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
-
+
if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
-
+
if (hasStencilComponent(format)) {
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
- }
- else {
+ } else {
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
-
+
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
-
+
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
-
+
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-
+
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
- } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
+ newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
-
+
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
- } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
+ } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED &&
+ newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
barrier.srcAccessMask = 0;
- barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
-
+ barrier.dstAccessMask =
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
} else {
@@ -113,11 +119,11 @@ void Image::TransitionLayout(Device* device, VkCommandPool commandPool, VkImage
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
-
+
vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
-
+
vkEndCommandBuffer(commandBuffer);
-
+
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
@@ -144,13 +150,14 @@ VkImageView Image::CreateView(Device* device, VkImage image, VkFormat format, Vk
VkImageView imageView;
if (vkCreateImageView(device->GetVkDevice(), &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
- throw std::runtime_error("Failed to texture image view");
+ throw std::runtime_error("Failed to texture image view");
}
return imageView;
}
-void Image::CopyFromBuffer(Device* device, VkCommandPool commandPool, VkBuffer buffer, VkImage& image, uint32_t width, uint32_t height) {
+void Image::CopyFromBuffer(Device* device, VkCommandPool commandPool, VkBuffer buffer, VkImage& image, uint32_t width,
+ uint32_t height) {
// Specify which part of the buffer is going to be copied to which part of the image
VkBufferImageCopy region = {};
region.bufferOffset = 0;
@@ -162,8 +169,8 @@ void Image::CopyFromBuffer(Device* device, VkCommandPool commandPool, VkBuffer b
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
- region.imageOffset = { 0, 0, 0 };
- region.imageExtent = { width, height, 1 };
+ region.imageOffset = {0, 0, 0};
+ region.imageExtent = {width, height, 1};
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
@@ -194,7 +201,9 @@ void Image::CopyFromBuffer(Device* device, VkCommandPool commandPool, VkBuffer b
vkFreeCommandBuffers(device->GetVkDevice(), commandPool, 1, &commandBuffer);
}
-void Image::FromFile(Device* device, VkCommandPool commandPool, const char* path, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkImageLayout layout, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
+void Image::FromFile(Device* device, VkCommandPool commandPool, const char* path, VkFormat format, VkImageTiling tiling,
+ VkImageUsageFlags usage, VkImageLayout layout, VkMemoryPropertyFlags properties, VkImage& image,
+ VkDeviceMemory& imageMemory) {
int texWidth, texHeight, texChannels;
stbi_uc* pixels = stbi_load(path, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
VkDeviceSize imageSize = texWidth * texHeight * 4;
@@ -208,7 +217,8 @@ void Image::FromFile(Device* device, VkCommandPool commandPool, const char* path
VkDeviceMemory stagingBufferMemory;
VkBufferUsageFlags stagingUsage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- VkMemoryPropertyFlags stagingProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ VkMemoryPropertyFlags stagingProperties =
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
BufferUtils::CreateBuffer(device, imageSize, stagingUsage, stagingProperties, stagingBuffer, stagingBufferMemory);
// Copy pixel values to the buffer
@@ -221,12 +231,15 @@ void Image::FromFile(Device* device, VkCommandPool commandPool, const char* path
stbi_image_free(pixels);
// Create Vulkan image
- Image::Create(device, texWidth, texHeight, format, tiling, VK_IMAGE_USAGE_TRANSFER_DST_BIT | usage, properties, image, imageMemory);
+ Image::Create(device, texWidth, texHeight, format, tiling, VK_IMAGE_USAGE_TRANSFER_DST_BIT | usage, properties,
+ image, imageMemory);
// Copy the staging buffer to the texture image
// --> First need to transition the texture image to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
- Image::TransitionLayout(device, commandPool, image, format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
- Image::CopyFromBuffer(device, commandPool, stagingBuffer, image, static_cast(texWidth), static_cast(texHeight));
+ Image::TransitionLayout(device, commandPool, image, format, VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+ Image::CopyFromBuffer(device, commandPool, stagingBuffer, image, static_cast(texWidth),
+ static_cast(texHeight));
// Transition texture image for shader access
Image::TransitionLayout(device, commandPool, image, format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout);
diff --git a/src/Image.h b/src/Image.h
index 8edf74f..ce1477d 100644
--- a/src/Image.h
+++ b/src/Image.h
@@ -1,13 +1,19 @@
#pragma once
#include
+
#include "Device.h"
namespace Image {
- void Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
- void TransitionLayout(Device* device, VkCommandPool commandPool, VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout);
- VkImageView CreateView(Device* device, VkImage image, VkFormat format, VkImageAspectFlags aspectFlags);
- void CopyFromBuffer(Device* device, VkCommandPool commandPool, VkBuffer buffer, VkImage& image, uint32_t width, uint32_t height);
- void FromFile(Device* device, VkCommandPool commandPool, const char* path, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkImageLayout layout, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
-}
+void Create(Device* device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
+ VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
+void TransitionLayout(Device* device, VkCommandPool commandPool, VkImage image, VkFormat format,
+ VkImageLayout oldLayout, VkImageLayout newLayout);
+VkImageView CreateView(Device* device, VkImage image, VkFormat format, VkImageAspectFlags aspectFlags);
+void CopyFromBuffer(Device* device, VkCommandPool commandPool, VkBuffer buffer, VkImage& image, uint32_t width,
+ uint32_t height);
+void FromFile(Device* device, VkCommandPool commandPool, const char* path, VkFormat format, VkImageTiling tiling,
+ VkImageUsageFlags usage, VkImageLayout layout, VkMemoryPropertyFlags properties, VkImage& image,
+ VkDeviceMemory& imageMemory);
+} // namespace Image
diff --git a/src/Instance.cpp b/src/Instance.cpp
index 7f6b01c..be4eb61 100644
--- a/src/Instance.cpp
+++ b/src/Instance.cpp
@@ -1,7 +1,8 @@
-#include
+#include "Instance.h"
+
#include
+#include
#include
-#include "Instance.h"
#ifdef NDEBUG
const bool ENABLE_VALIDATION = false;
@@ -10,38 +11,30 @@ const bool ENABLE_VALIDATION = true;
#endif
namespace {
- const std::vector validationLayers = {
- "VK_LAYER_KHRONOS_validation"
- };
-
- // Get the required list of extensions based on whether validation layers are enabled
- std::vector getRequiredExtensions() {
- std::vector extensions;
+const std::vector validationLayers = {"VK_LAYER_KHRONOS_validation"};
- if (ENABLE_VALIDATION) {
- extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
- }
+// Get the required list of extensions based on whether validation layers are enabled
+std::vector getRequiredExtensions() {
+ std::vector extensions;
- return extensions;
+ if (ENABLE_VALIDATION) {
+ extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
}
- // Callback function to allow messages from validation layers to be received
- VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
- VkDebugReportFlagsEXT flags,
- VkDebugReportObjectTypeEXT objType,
- uint64_t obj,
- size_t location,
- int32_t code,
- const char* layerPrefix,
- const char* msg,
- void *userData) {
-
- fprintf(stderr, "Validation layer: %s\n", msg);
- return VK_FALSE;
- }
+ return extensions;
+}
+
+// Callback function to allow messages from validation layers to be received
+VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType,
+ uint64_t obj, size_t location, int32_t code, const char* layerPrefix,
+ const char* msg, void* userData) {
+ fprintf(stderr, "Validation layer: %s\n", msg);
+ return VK_FALSE;
}
+} // namespace
-Instance::Instance(const char* applicationName, unsigned int additionalExtensionCount, const char** additionalExtensions) {
+Instance::Instance(const char* applicationName, unsigned int additionalExtensionCount,
+ const char** additionalExtensions) {
// --- Specify details about our application ---
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
@@ -50,7 +43,7 @@ Instance::Instance(const char* applicationName, unsigned int additionalExtension
appInfo.pEngineName = "No Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
-
+
// --- Create Vulkan instance ---
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
@@ -72,6 +65,12 @@ Instance::Instance(const char* applicationName, unsigned int additionalExtension
createInfo.enabledLayerCount = 0;
}
+ if (ENABLE_VALIDATION) {
+ const char* monitorLayers[] = {"VK_LAYER_LUNARG_monitor"};
+ createInfo.enabledLayerCount = 1;
+ createInfo.ppEnabledLayerNames = monitorLayers;
+ }
+
// Create instance
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("Failed to create instance");
@@ -80,29 +79,17 @@ Instance::Instance(const char* applicationName, unsigned int additionalExtension
initDebugReport();
}
-VkInstance Instance::GetVkInstance() {
- return instance;
-}
+VkInstance Instance::GetVkInstance() { return instance; }
-VkPhysicalDevice Instance::GetPhysicalDevice() {
- return physicalDevice;
-}
+VkPhysicalDevice Instance::GetPhysicalDevice() { return physicalDevice; }
-const VkSurfaceCapabilitiesKHR& Instance::GetSurfaceCapabilities() const {
- return surfaceCapabilities;
-}
+const VkSurfaceCapabilitiesKHR& Instance::GetSurfaceCapabilities() const { return surfaceCapabilities; }
-const QueueFamilyIndices& Instance::GetQueueFamilyIndices() const {
- return queueFamilyIndices;
-}
+const QueueFamilyIndices& Instance::GetQueueFamilyIndices() const { return queueFamilyIndices; }
-const std::vector& Instance::GetSurfaceFormats() const {
- return surfaceFormats;
-}
+const std::vector& Instance::GetSurfaceFormats() const { return surfaceFormats; }
-const std::vector& Instance::GetPresentModes() const {
- return presentModes;
-}
+const std::vector& Instance::GetPresentModes() const { return presentModes; }
uint32_t Instance::GetMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const {
// Iterate over all memory types available for the device used in this example
@@ -117,15 +104,15 @@ uint32_t Instance::GetMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags p
throw std::runtime_error("Could not find a suitable memory type!");
}
-VkFormat Instance::GetSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const {
+VkFormat Instance::GetSupportedFormat(const std::vector& candidates, VkImageTiling tiling,
+ VkFormatFeatureFlags features) const {
for (VkFormat format : candidates) {
VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &properties);
if (tiling == VK_IMAGE_TILING_LINEAR && (properties.linearTilingFeatures & features) == features) {
return format;
- }
- else if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features) {
+ } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features) {
return format;
}
}
@@ -142,101 +129,102 @@ void Instance::initDebugReport() {
createInfo.pfnCallback = debugCallback;
if ([&]() {
- auto func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
- if (func != nullptr) {
- return func(instance, &createInfo, nullptr, &debugReportCallback);
- }
- else {
- return VK_ERROR_EXTENSION_NOT_PRESENT;
- }
- }() != VK_SUCCESS) {
+ auto func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance,
+ "vkCreateDebugReportCallbackEXT");
+ if (func != nullptr) {
+ return func(instance, &createInfo, nullptr, &debugReportCallback);
+ } else {
+ return VK_ERROR_EXTENSION_NOT_PRESENT;
+ }
+ }() != VK_SUCCESS) {
throw std::runtime_error("Failed to set up debug callback");
}
}
}
-
namespace {
- QueueFamilyIndices checkDeviceQueueSupport(VkPhysicalDevice device, QueueFlagBits requiredQueues, VkSurfaceKHR surface = VK_NULL_HANDLE) {
- uint32_t queueFamilyCount = 0;
- vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
-
- std::vector queueFamilies(queueFamilyCount);
- vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
+QueueFamilyIndices checkDeviceQueueSupport(VkPhysicalDevice device, QueueFlagBits requiredQueues,
+ VkSurfaceKHR surface = VK_NULL_HANDLE) {
+ uint32_t queueFamilyCount = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
- VkQueueFlags requiredVulkanQueues = 0;
- if (requiredQueues[QueueFlags::Graphics]) {
- requiredVulkanQueues |= VK_QUEUE_GRAPHICS_BIT;
- }
- if (requiredQueues[QueueFlags::Compute]) {
- requiredVulkanQueues |= VK_QUEUE_COMPUTE_BIT;
- }
- if (requiredQueues[QueueFlags::Transfer]) {
- requiredVulkanQueues |= VK_QUEUE_TRANSFER_BIT;
- }
+ std::vector queueFamilies(queueFamilyCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
- QueueFamilyIndices indices = {};
- indices.fill(-1);
- VkQueueFlags supportedQueues = 0;
- bool needsPresent = requiredQueues[QueueFlags::Present];
- bool presentSupported = false;
+ VkQueueFlags requiredVulkanQueues = 0;
+ if (requiredQueues[QueueFlags::Graphics]) {
+ requiredVulkanQueues |= VK_QUEUE_GRAPHICS_BIT;
+ }
+ if (requiredQueues[QueueFlags::Compute]) {
+ requiredVulkanQueues |= VK_QUEUE_COMPUTE_BIT;
+ }
+ if (requiredQueues[QueueFlags::Transfer]) {
+ requiredVulkanQueues |= VK_QUEUE_TRANSFER_BIT;
+ }
- int i = 0;
- for (const auto& queueFamily : queueFamilies) {
- if (queueFamily.queueCount > 0) {
- supportedQueues |= queueFamily.queueFlags;
- }
+ QueueFamilyIndices indices = {};
+ indices.fill(-1);
+ VkQueueFlags supportedQueues = 0;
+ bool needsPresent = requiredQueues[QueueFlags::Present];
+ bool presentSupported = false;
- if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
- indices[QueueFlags::Graphics] = i;
- }
+ int i = 0;
+ for (const auto& queueFamily : queueFamilies) {
+ if (queueFamily.queueCount > 0) {
+ supportedQueues |= queueFamily.queueFlags;
+ }
- if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT) {
- indices[QueueFlags::Compute] = i;
- }
+ if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ indices[QueueFlags::Graphics] = i;
+ }
- if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT) {
- indices[QueueFlags::Transfer] = i;
- }
+ if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT) {
+ indices[QueueFlags::Compute] = i;
+ }
- if (needsPresent) {
- VkBool32 presentSupport = false;
- vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
- if (queueFamily.queueCount > 0 && presentSupport) {
- presentSupported = true;
- indices[QueueFlags::Present] = i;
- }
- }
+ if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT) {
+ indices[QueueFlags::Transfer] = i;
+ }
- if ((requiredVulkanQueues & supportedQueues) == requiredVulkanQueues && (!needsPresent || presentSupported)) {
- break;
+ if (needsPresent) {
+ VkBool32 presentSupport = false;
+ vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
+ if (queueFamily.queueCount > 0 && presentSupport) {
+ presentSupported = true;
+ indices[QueueFlags::Present] = i;
}
+ }
- i++;
+ if ((requiredVulkanQueues & supportedQueues) == requiredVulkanQueues && (!needsPresent || presentSupported)) {
+ break;
}
- return indices;
+ i++;
}
- // Check the physical device for specified extension support
- bool checkDeviceExtensionSupport(VkPhysicalDevice device, std::vector requiredExtensions) {
- uint32_t extensionCount;
- vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
+ return indices;
+}
- std::vector availableExtensions(extensionCount);
- vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
+// Check the physical device for specified extension support
+bool checkDeviceExtensionSupport(VkPhysicalDevice device, std::vector requiredExtensions) {
+ uint32_t extensionCount;
+ vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
- std::set requiredExtensionSet(requiredExtensions.begin(), requiredExtensions.end());
+ std::vector availableExtensions(extensionCount);
+ vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
- for (const auto& extension : availableExtensions) {
- requiredExtensionSet.erase(extension.extensionName);
- }
+ std::set requiredExtensionSet(requiredExtensions.begin(), requiredExtensions.end());
- return requiredExtensionSet.empty();
+ for (const auto& extension : availableExtensions) {
+ requiredExtensionSet.erase(extension.extensionName);
}
+
+ return requiredExtensionSet.empty();
}
+} // namespace
-void Instance::PickPhysicalDevice(std::vector deviceExtensions, QueueFlagBits requiredQueues, VkSurfaceKHR surface) {
+void Instance::PickPhysicalDevice(std::vector deviceExtensions, QueueFlagBits requiredQueues,
+ VkSurfaceKHR surface) {
// List the graphics cards on the machine
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
@@ -281,17 +269,15 @@ void Instance::PickPhysicalDevice(std::vector deviceExtensions, Que
}
}
- if (queueSupport &&
- checkDeviceExtensionSupport(device, deviceExtensions) &&
- (!requiredQueues[QueueFlags::Present] || (!surfaceFormats.empty() && ! presentModes.empty()))
- ) {
+ if (queueSupport && checkDeviceExtensionSupport(device, deviceExtensions) &&
+ (!requiredQueues[QueueFlags::Present] || (!surfaceFormats.empty() && !presentModes.empty()))) {
physicalDevice = device;
break;
}
}
this->deviceExtensions = deviceExtensions;
-
+
if (physicalDevice == VK_NULL_HANDLE) {
throw std::runtime_error("Failed to find a suitable GPU");
}
@@ -362,7 +348,8 @@ Device* Instance::CreateDevice(QueueFlagBits requiredQueues, VkPhysicalDeviceFea
Instance::~Instance() {
if (ENABLE_VALIDATION) {
- auto func = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
+ auto func =
+ (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
if (func != nullptr) {
func(instance, debugReportCallback, nullptr);
}
diff --git a/src/Instance.h b/src/Instance.h
index afc54c5..e2bd1f0 100644
--- a/src/Instance.h
+++ b/src/Instance.h
@@ -1,18 +1,20 @@
#pragma once
+#include
+
#include
#include
-#include
-#include "QueueFlags.h"
+
#include "Device.h"
+#include "QueueFlags.h"
extern const bool ENABLE_VALIDATION;
class Instance {
-
-public:
+ public:
Instance() = delete;
- Instance(const char* applicationName, unsigned int additionalExtensionCount = 0, const char** additionalExtensions = nullptr);
+ Instance(const char* applicationName, unsigned int additionalExtensionCount = 0,
+ const char** additionalExtensions = nullptr);
VkInstance GetVkInstance();
VkPhysicalDevice GetPhysicalDevice();
@@ -20,18 +22,19 @@ class Instance {
const VkSurfaceCapabilitiesKHR& GetSurfaceCapabilities() const;
const std::vector& GetSurfaceFormats() const;
const std::vector& GetPresentModes() const;
-
+
uint32_t GetMemoryTypeIndex(uint32_t types, VkMemoryPropertyFlags properties) const;
- VkFormat GetSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const;
+ VkFormat GetSupportedFormat(const std::vector& candidates, VkImageTiling tiling,
+ VkFormatFeatureFlags features) const;
- void PickPhysicalDevice(std::vector deviceExtensions, QueueFlagBits requiredQueues, VkSurfaceKHR surface = VK_NULL_HANDLE);
+ void PickPhysicalDevice(std::vector deviceExtensions, QueueFlagBits requiredQueues,
+ VkSurfaceKHR surface = VK_NULL_HANDLE);
Device* CreateDevice(QueueFlagBits requiredQueues, VkPhysicalDeviceFeatures deviceFeatures);
~Instance();
-private:
-
+ private:
void initDebugReport();
VkInstance instance;
diff --git a/src/Model.cpp b/src/Model.cpp
index 6faa35c..a51085a 100644
--- a/src/Model.cpp
+++ b/src/Model.cpp
@@ -1,20 +1,24 @@
#include "Model.h"
+
#include "BufferUtils.h"
#include "Image.h"
-Model::Model(Device* device, VkCommandPool commandPool, const std::vector &vertices, const std::vector &indices)
- : device(device), vertices(vertices), indices(indices) {
-
+Model::Model(Device* device, VkCommandPool commandPool, const std::vector& vertices,
+ const std::vector& indices)
+ : device(device), vertices(vertices), indices(indices) {
if (vertices.size() > 0) {
- BufferUtils::CreateBufferFromData(device, commandPool, this->vertices.data(), vertices.size() * sizeof(Vertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, vertexBuffer, vertexBufferMemory);
+ BufferUtils::CreateBufferFromData(device, commandPool, this->vertices.data(), vertices.size() * sizeof(Vertex),
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, vertexBuffer, vertexBufferMemory);
}
if (indices.size() > 0) {
- BufferUtils::CreateBufferFromData(device, commandPool, this->indices.data(), indices.size() * sizeof(uint32_t), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, indexBuffer, indexBufferMemory);
+ BufferUtils::CreateBufferFromData(device, commandPool, this->indices.data(), indices.size() * sizeof(uint32_t),
+ VK_BUFFER_USAGE_INDEX_BUFFER_BIT, indexBuffer, indexBufferMemory);
}
modelBufferObject.modelMatrix = glm::mat4(1.0f);
- BufferUtils::CreateBufferFromData(device, commandPool, &modelBufferObject, sizeof(ModelBufferObject), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, modelBuffer, modelBufferMemory);
+ BufferUtils::CreateBufferFromData(device, commandPool, &modelBufferObject, sizeof(ModelBufferObject),
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, modelBuffer, modelBufferMemory);
}
Model::~Model() {
@@ -82,34 +86,18 @@ void Model::SetTexture(VkImage texture) {
}
}
-const std::vector& Model::getVertices() const {
- return vertices;
-}
+const std::vector& Model::getVertices() const { return vertices; }
-VkBuffer Model::getVertexBuffer() const {
- return vertexBuffer;
-}
+VkBuffer Model::getVertexBuffer() const { return vertexBuffer; }
-const std::vector& Model::getIndices() const {
- return indices;
-}
+const std::vector& Model::getIndices() const { return indices; }
-VkBuffer Model::getIndexBuffer() const {
- return indexBuffer;
-}
+VkBuffer Model::getIndexBuffer() const { return indexBuffer; }
-const ModelBufferObject& Model::getModelBufferObject() const {
- return modelBufferObject;
-}
+const ModelBufferObject& Model::getModelBufferObject() const { return modelBufferObject; }
-VkBuffer Model::GetModelBuffer() const {
- return modelBuffer;
-}
+VkBuffer Model::GetModelBuffer() const { return modelBuffer; }
-VkImageView Model::GetTextureView() const {
- return textureView;
-}
+VkImageView Model::GetTextureView() const { return textureView; }
-VkSampler Model::GetTextureSampler() const {
- return textureSampler;
-}
+VkSampler Model::GetTextureSampler() const { return textureSampler; }
diff --git a/src/Model.h b/src/Model.h
index 33bbccb..766cdf8 100644
--- a/src/Model.h
+++ b/src/Model.h
@@ -1,18 +1,19 @@
#pragma once
#include
+
#include
#include
-#include "Vertex.h"
#include "Device.h"
+#include "Vertex.h"
struct ModelBufferObject {
glm::mat4 modelMatrix;
};
class Model {
-protected:
+ protected:
Device* device;
std::vector vertices;
@@ -32,9 +33,10 @@ class Model {
VkImageView textureView = VK_NULL_HANDLE;
VkSampler textureSampler = VK_NULL_HANDLE;
-public:
+ public:
Model() = delete;
- Model(Device* device, VkCommandPool commandPool, const std::vector &vertices, const std::vector &indices);
+ Model(Device* device, VkCommandPool commandPool, const std::vector& vertices,
+ const std::vector& indices);
virtual ~Model();
void SetTexture(VkImage texture);
diff --git a/src/QueueFlags.h b/src/QueueFlags.h
index 9ca298a..e5f2c76 100644
--- a/src/QueueFlags.h
+++ b/src/QueueFlags.h
@@ -1,7 +1,7 @@
#pragma once
-#include
#include
+#include
enum QueueFlags {
Graphics,
@@ -11,11 +11,11 @@ enum QueueFlags {
};
namespace QueueFlagBit {
- static constexpr unsigned int GraphicsBit = 1 << 0;
- static constexpr unsigned int ComputeBit = 1 << 1;
- static constexpr unsigned int TransferBit = 1 << 2;
- static constexpr unsigned int PresentBit = 1 << 3;
-}
+static constexpr unsigned int GraphicsBit = 1 << 0;
+static constexpr unsigned int ComputeBit = 1 << 1;
+static constexpr unsigned int TransferBit = 1 << 2;
+static constexpr unsigned int PresentBit = 1 << 3;
+} // namespace QueueFlagBit
using QueueFlagBits = std::bitset;
using QueueFamilyIndices = std::array;
diff --git a/src/Renderer.cpp b/src/Renderer.cpp
index b445d04..9ca350a 100644
--- a/src/Renderer.cpp
+++ b/src/Renderer.cpp
@@ -1,20 +1,16 @@
#include "Renderer.h"
-#include "Instance.h"
-#include "ShaderModule.h"
-#include "Vertex.h"
+
#include "Blades.h"
#include "Camera.h"
#include "Image.h"
+#include "Instance.h"
+#include "ShaderModule.h"
+#include "Vertex.h"
static constexpr unsigned int WORKGROUP_SIZE = 32;
Renderer::Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera)
- : device(device),
- logicalDevice(device->GetVkDevice()),
- swapChain(swapChain),
- scene(scene),
- camera(camera) {
-
+ : device(device), logicalDevice(device->GetVkDevice()), swapChain(swapChain), scene(scene), camera(camera) {
CreateCommandPools();
CreateRenderPass();
CreateCameraDescriptorSetLayout();
@@ -73,7 +69,9 @@ void Renderer::CreateRenderPass() {
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
// Depth buffer attachment
- VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
+ VkFormat depthFormat = device->GetInstance()->GetSupportedFormat(
+ {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, VK_IMAGE_TILING_OPTIMAL,
+ VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
VkAttachmentDescription depthAttachment = {};
depthAttachment.format = depthFormat;
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
@@ -96,7 +94,7 @@ void Renderer::CreateRenderPass() {
subpass.pColorAttachments = &colorAttachmentRef;
subpass.pDepthStencilAttachment = &depthAttachmentRef;
- std::array attachments = { colorAttachment, depthAttachment };
+ std::array attachments = {colorAttachment, depthAttachment};
// Specify subpass dependency
VkSubpassDependency dependency = {};
@@ -131,7 +129,7 @@ void Renderer::CreateCameraDescriptorSetLayout() {
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL;
uboLayoutBinding.pImmutableSamplers = nullptr;
- std::vector bindings = { uboLayoutBinding };
+ std::vector bindings = {uboLayoutBinding};
// Create the descriptor set layout
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
@@ -159,7 +157,7 @@ void Renderer::CreateModelDescriptorSetLayout() {
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
samplerLayoutBinding.pImmutableSamplers = nullptr;
- std::vector bindings = { uboLayoutBinding, samplerLayoutBinding };
+ std::vector bindings = {uboLayoutBinding, samplerLayoutBinding};
// Create the descriptor set layout
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
@@ -181,7 +179,7 @@ void Renderer::CreateTimeDescriptorSetLayout() {
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
uboLayoutBinding.pImmutableSamplers = nullptr;
- std::vector bindings = { uboLayoutBinding };
+ std::vector bindings = {uboLayoutBinding};
// Create the descriptor set layout
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
@@ -196,26 +194,61 @@ void Renderer::CreateTimeDescriptorSetLayout() {
void Renderer::CreateComputeDescriptorSetLayout() {
// TODO: Create the descriptor set layout for the compute pipeline
- // Remember this is like a class definition stating why types of information
+ // Remember this is like a class definition stating what types of information
// will be stored at each binding
+ VkDescriptorSetLayoutBinding bladesLayoutBinding = {};
+ bladesLayoutBinding.binding = 0;
+ bladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ bladesLayoutBinding.descriptorCount = 1;
+ bladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ bladesLayoutBinding.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutBinding culledBladesLayoutBinding = {};
+ culledBladesLayoutBinding.binding = 1;
+ culledBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ culledBladesLayoutBinding.descriptorCount = 1;
+ culledBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ culledBladesLayoutBinding.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutBinding numBladesLayoutBinding = {};
+ numBladesLayoutBinding.binding = 2;
+ numBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ numBladesLayoutBinding.descriptorCount = 1;
+ numBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+ numBladesLayoutBinding.pImmutableSamplers = nullptr;
+
+ std::vector bindings = {bladesLayoutBinding, culledBladesLayoutBinding,
+ numBladesLayoutBinding};
+
+ VkDescriptorSetLayoutCreateInfo layoutInfo = {};
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = static_cast(bindings.size());
+ layoutInfo.pBindings = bindings.data();
+
+ if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to create descriptor set layout");
+ }
}
void Renderer::CreateDescriptorPool() {
// Describe which descriptor types that the descriptor sets will contain
std::vector poolSizes = {
// Camera
- { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1},
+ {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1},
// Models + Blades
- { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , static_cast(scene->GetModels().size() + scene->GetBlades().size()) },
+ {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ static_cast(scene->GetModels().size() + scene->GetBlades().size())},
// Models + Blades
- { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , static_cast(scene->GetModels().size() + scene->GetBlades().size()) },
+ {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ static_cast(scene->GetModels().size() + scene->GetBlades().size())},
// Time (compute)
- { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 },
+ {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1},
// TODO: Add any additional types and counts of descriptors you will need to allocate
+ {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, static_cast(3 * scene->GetBlades().size())},
};
VkDescriptorPoolCreateInfo poolInfo = {};
@@ -230,8 +263,8 @@ void Renderer::CreateDescriptorPool() {
}
void Renderer::CreateCameraDescriptorSet() {
- // Describe the desciptor set
- VkDescriptorSetLayout layouts[] = { cameraDescriptorSetLayout };
+ // Describe the descriptor set
+ VkDescriptorSetLayout layouts[] = {cameraDescriptorSetLayout};
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
@@ -261,14 +294,15 @@ void Renderer::CreateCameraDescriptorSet() {
descriptorWrites[0].pTexelBufferView = nullptr;
// Update descriptor sets
- vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
+ vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0,
+ nullptr);
}
void Renderer::CreateModelDescriptorSets() {
modelDescriptorSets.resize(scene->GetModels().size());
- // Describe the desciptor set
- VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout };
+ // Describe the descriptor set
+ VkDescriptorSetLayout layouts[] = {modelDescriptorSetLayout};
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
@@ -314,17 +348,57 @@ void Renderer::CreateModelDescriptorSets() {
}
// Update descriptor sets
- vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
+ vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0,
+ nullptr);
}
void Renderer::CreateGrassDescriptorSets() {
// TODO: Create Descriptor sets for the grass.
// This should involve creating descriptor sets which point to the model matrix of each group of grass blades
+ const std::vector& blades = scene->GetBlades();
+
+ grassDescriptorSets.resize(blades.size());
+
+ // Describe the descriptor set
+ VkDescriptorSetLayout layouts[] = {modelDescriptorSetLayout};
+ VkDescriptorSetAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = descriptorPool;
+ allocInfo.descriptorSetCount = static_cast(grassDescriptorSets.size());
+ allocInfo.pSetLayouts = layouts;
+
+ // Allocate descriptor sets
+ if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to allocate descriptor set");
+ }
+
+ std::vector descriptorWrites(grassDescriptorSets.size());
+
+ for (uint32_t i = 0; i < blades.size(); ++i) {
+ VkDescriptorBufferInfo bladesModelBufferInfo = {};
+ bladesModelBufferInfo.buffer = blades[i]->GetModelBuffer();
+ bladesModelBufferInfo.offset = 0;
+ bladesModelBufferInfo.range = sizeof(ModelBufferObject);
+
+ descriptorWrites[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[i].dstSet = grassDescriptorSets[i];
+ descriptorWrites[i].dstBinding = 0;
+ descriptorWrites[i].dstArrayElement = 0;
+ descriptorWrites[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descriptorWrites[i].descriptorCount = 1;
+ descriptorWrites[i].pBufferInfo = &bladesModelBufferInfo;
+ descriptorWrites[i].pImageInfo = nullptr;
+ descriptorWrites[i].pTexelBufferView = nullptr;
+ }
+
+ // Update descriptor sets
+ vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0,
+ nullptr);
}
void Renderer::CreateTimeDescriptorSet() {
- // Describe the desciptor set
- VkDescriptorSetLayout layouts[] = { timeDescriptorSetLayout };
+ // Describe the descriptor set
+ VkDescriptorSetLayout layouts[] = {timeDescriptorSetLayout};
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
@@ -354,12 +428,83 @@ void Renderer::CreateTimeDescriptorSet() {
descriptorWrites[0].pTexelBufferView = nullptr;
// Update descriptor sets
- vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
+ vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0,
+ nullptr);
}
void Renderer::CreateComputeDescriptorSets() {
// TODO: Create Descriptor sets for the compute pipeline
- // The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades
+ // The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and
+ // the output number of grass blades
+ const std::vector& blades = scene->GetBlades();
+
+ computeDescriptorSets.resize(blades.size());
+
+ // Describe the descriptor set
+ VkDescriptorSetLayout layouts[] = {computeDescriptorSetLayout};
+ VkDescriptorSetAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorPool = descriptorPool;
+ allocInfo.descriptorSetCount = static_cast(computeDescriptorSets.size());
+ allocInfo.pSetLayouts = layouts;
+
+ // Allocate descriptor sets
+ if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) {
+ throw std::runtime_error("Failed to allocate descriptor set");
+ }
+
+ std::vector descriptorWrites(3 * computeDescriptorSets.size());
+
+ for (uint32_t i = 0; i < blades.size(); ++i) {
+ VkDescriptorBufferInfo bladesBufferInfo = {};
+ bladesBufferInfo.buffer = blades[i]->GetBladesBuffer();
+ bladesBufferInfo.offset = 0;
+ bladesBufferInfo.range = NUM_BLADES * sizeof(Blade);
+
+ VkDescriptorBufferInfo culledBladesBufferInfo = {};
+ culledBladesBufferInfo.buffer = blades[i]->GetCulledBladesBuffer();
+ culledBladesBufferInfo.offset = 0;
+ culledBladesBufferInfo.range = NUM_BLADES * sizeof(Blade);
+
+ VkDescriptorBufferInfo numBladesBufferInfo = {};
+ numBladesBufferInfo.buffer = blades[i]->GetNumBladesBuffer();
+ numBladesBufferInfo.offset = 0;
+ numBladesBufferInfo.range = sizeof(BladeDrawIndirect);
+
+ descriptorWrites[3 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 0].dstSet = computeDescriptorSets[i];
+ descriptorWrites[3 * i + 0].dstBinding = 0;
+ descriptorWrites[3 * i + 0].dstArrayElement = 0;
+ descriptorWrites[3 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ descriptorWrites[3 * i + 0].descriptorCount = 1;
+ descriptorWrites[3 * i + 0].pBufferInfo = &bladesBufferInfo;
+ descriptorWrites[3 * i + 0].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 0].pTexelBufferView = nullptr;
+
+ descriptorWrites[3 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 1].dstSet = computeDescriptorSets[i];
+ descriptorWrites[3 * i + 1].dstBinding = 1;
+ descriptorWrites[3 * i + 1].dstArrayElement = 0;
+ descriptorWrites[3 * i + 1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ descriptorWrites[3 * i + 1].descriptorCount = 1;
+ descriptorWrites[3 * i + 1].pBufferInfo = &culledBladesBufferInfo;
+ descriptorWrites[3 * i + 1].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 1].pTexelBufferView = nullptr;
+
+ descriptorWrites[3 * i + 2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptorWrites[3 * i + 2].dstSet = computeDescriptorSets[i];
+ descriptorWrites[3 * i + 2].dstBinding = 2;
+ descriptorWrites[3 * i + 2].dstArrayElement = 0;
+ descriptorWrites[3 * i + 2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ descriptorWrites[3 * i + 2].descriptorCount = 1;
+ descriptorWrites[3 * i + 2].pBufferInfo = &numBladesBufferInfo;
+ descriptorWrites[3 * i + 2].pImageInfo = nullptr;
+ descriptorWrites[3 * i + 2].pTexelBufferView = nullptr;
+ }
+
+ // Update descriptor sets
+ vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0,
+ nullptr);
}
void Renderer::CreateGraphicsPipeline() {
@@ -379,7 +524,7 @@ void Renderer::CreateGraphicsPipeline() {
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
- VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
+ VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
// --- Set up fixed-function stages ---
@@ -411,7 +556,7 @@ void Renderer::CreateGraphicsPipeline() {
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
- scissor.offset = { 0, 0 };
+ scissor.offset = {0, 0};
scissor.extent = swapChain->GetVkExtent();
VkPipelineViewportStateCreateInfo viewportState = {};
@@ -459,7 +604,8 @@ void Renderer::CreateGraphicsPipeline() {
// Color blending (turned off here, but showing options for learning)
// --> Configuration per attached framebuffer
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
- colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ colorBlendAttachment.colorWriteMask =
+ VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
@@ -480,7 +626,7 @@ void Renderer::CreateGraphicsPipeline() {
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
- std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout };
+ std::vector descriptorSetLayouts = {cameraDescriptorSetLayout, modelDescriptorSetLayout};
// Pipeline layout: used to specify uniform values
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
@@ -513,7 +659,8 @@ void Renderer::CreateGraphicsPipeline() {
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
- if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
+ if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) !=
+ VK_SUCCESS) {
throw std::runtime_error("Failed to create graphics pipeline");
}
@@ -553,7 +700,8 @@ void Renderer::CreateGrassPipeline() {
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
- VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo, fragShaderStageInfo };
+ VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, tescShaderStageInfo, teseShaderStageInfo,
+ fragShaderStageInfo};
// --- Set up fixed-function stages ---
@@ -585,7 +733,7 @@ void Renderer::CreateGrassPipeline() {
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
- scissor.offset = { 0, 0 };
+ scissor.offset = {0, 0};
scissor.extent = swapChain->GetVkExtent();
VkPipelineViewportStateCreateInfo viewportState = {};
@@ -633,7 +781,8 @@ void Renderer::CreateGrassPipeline() {
// Color blending (turned off here, but showing options for learning)
// --> Configuration per attached framebuffer
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
- colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ colorBlendAttachment.colorWriteMask =
+ VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
@@ -654,7 +803,7 @@ void Renderer::CreateGrassPipeline() {
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
- std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, modelDescriptorSetLayout };
+ std::vector descriptorSetLayouts = {cameraDescriptorSetLayout, modelDescriptorSetLayout};
// Pipeline layout: used to specify uniform values
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
@@ -695,7 +844,8 @@ void Renderer::CreateGrassPipeline() {
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
- if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) != VK_SUCCESS) {
+ if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &grassPipeline) !=
+ VK_SUCCESS) {
throw std::runtime_error("Failed to create graphics pipeline");
}
@@ -716,8 +866,9 @@ void Renderer::CreateComputePipeline() {
computeShaderStageInfo.module = computeShaderModule;
computeShaderStageInfo.pName = "main";
- // TODO: Add the compute dsecriptor set layout you create to this list
- std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout };
+ // TODO: Add the compute descriptor set layout you create to this list
+ std::vector descriptorSetLayouts = {cameraDescriptorSetLayout, timeDescriptorSetLayout,
+ computeDescriptorSetLayout};
// Create pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
@@ -741,7 +892,8 @@ void Renderer::CreateComputePipeline() {
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
- if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) != VK_SUCCESS) {
+ if (vkCreateComputePipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &computePipeline) !=
+ VK_SUCCESS) {
throw std::runtime_error("Failed to create compute pipeline");
}
@@ -781,32 +933,24 @@ void Renderer::CreateFrameResources() {
}
}
- VkFormat depthFormat = device->GetInstance()->GetSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
+ VkFormat depthFormat = device->GetInstance()->GetSupportedFormat(
+ {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, VK_IMAGE_TILING_OPTIMAL,
+ VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
// CREATE DEPTH IMAGE
- Image::Create(device,
- swapChain->GetVkExtent().width,
- swapChain->GetVkExtent().height,
- depthFormat,
- VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
- VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
- depthImage,
- depthImageMemory
- );
+ Image::Create(device, swapChain->GetVkExtent().width, swapChain->GetVkExtent().height, depthFormat,
+ VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
depthImageView = Image::CreateView(device, depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
-
+
// Transition the image for use as depth-stencil
- Image::TransitionLayout(device, graphicsCommandPool, depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+ Image::TransitionLayout(device, graphicsCommandPool, depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
-
// CREATE FRAMEBUFFERS
framebuffers.resize(swapChain->GetCount());
for (size_t i = 0; i < swapChain->GetCount(); i++) {
- std::vector attachments = {
- imageViews[i],
- depthImageView
- };
+ std::vector attachments = {imageViews[i], depthImageView};
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
@@ -820,7 +964,6 @@ void Renderer::CreateFrameResources() {
if (vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("Failed to create framebuffer");
}
-
}
}
@@ -843,7 +986,8 @@ void Renderer::RecreateFrameResources() {
vkDestroyPipeline(logicalDevice, grassPipeline, nullptr);
vkDestroyPipelineLayout(logicalDevice, graphicsPipelineLayout, nullptr);
vkDestroyPipelineLayout(logicalDevice, grassPipelineLayout, nullptr);
- vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data());
+ vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()),
+ commandBuffers.data());
DestroyFrameResources();
CreateFrameResources();
@@ -878,12 +1022,20 @@ void Renderer::RecordComputeCommandBuffer() {
vkCmdBindPipeline(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline);
// Bind camera descriptor set
- vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr);
+ vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1,
+ &cameraDescriptorSet, 0, nullptr);
// Bind descriptor set for time uniforms
- vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr);
+ vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1,
+ &timeDescriptorSet, 0, nullptr);
// TODO: For each group of blades bind its descriptor set and dispatch
+ for (uint32_t i = 0; i < scene->GetBlades().size(); ++i) {
+ vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1,
+ &computeDescriptorSets[i], 0, nullptr);
+
+ vkCmdDispatch(computeCommandBuffer, (NUM_BLADES + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE, 1, 1);
+ }
// ~ End recording ~
if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) {
@@ -922,12 +1074,12 @@ void Renderer::RecordCommandBuffers() {
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = framebuffers[i];
- renderPassInfo.renderArea.offset = { 0, 0 };
+ renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = swapChain->GetVkExtent();
std::array clearValues = {};
- clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
- clearValues[1].depthStencil = { 1.0f, 0 };
+ clearValues[0].color = {0.0f, 0.0f, 0.0f, 1.0f};
+ clearValues[1].depthStencil = {1.0f, 0};
renderPassInfo.clearValueCount = static_cast(clearValues.size());
renderPassInfo.pClearValues = clearValues.data();
@@ -943,10 +1095,13 @@ void Renderer::RecordCommandBuffers() {
barriers[j].size = sizeof(BladeDrawIndirect);
}
- vkCmdPipelineBarrier(commandBuffers[i], VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, 0, nullptr, barriers.size(), barriers.data(), 0, nullptr);
+ vkCmdPipelineBarrier(commandBuffers[i], VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, 0, nullptr, barriers.size(), barriers.data(), 0,
+ nullptr);
// Bind the camera descriptor set. This is set 0 in all pipelines so it will be inherited
- vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 0, 1, &cameraDescriptorSet, 0, nullptr);
+ vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 0, 1,
+ &cameraDescriptorSet, 0, nullptr);
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
@@ -955,14 +1110,15 @@ void Renderer::RecordCommandBuffers() {
for (uint32_t j = 0; j < scene->GetModels().size(); ++j) {
// Bind the vertex and index buffers
- VkBuffer vertexBuffers[] = { scene->GetModels()[j]->getVertexBuffer() };
- VkDeviceSize offsets[] = { 0 };
+ VkBuffer vertexBuffers[] = {scene->GetModels()[j]->getVertexBuffer()};
+ VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffers[i], scene->GetModels()[j]->getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);
// Bind the descriptor set for each model
- vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 1, 1, &modelDescriptorSets[j], 0, nullptr);
+ vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 1, 1,
+ &modelDescriptorSets[j], 0, nullptr);
// Draw
std::vector indices = scene->GetModels()[j]->getIndices();
@@ -973,16 +1129,19 @@ void Renderer::RecordCommandBuffers() {
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipeline);
for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) {
- VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() };
- VkDeviceSize offsets[] = { 0 };
+ VkBuffer vertexBuffers[] = {scene->GetBlades()[j]->GetCulledBladesBuffer()};
+ VkDeviceSize offsets[] = {0};
// TODO: Uncomment this when the buffers are populated
- // vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
+ vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
// TODO: Bind the descriptor set for each grass blades model
+ vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipelineLayout, 1, 1,
+ &grassDescriptorSets[j], 0, nullptr);
// Draw
// TODO: Uncomment this when the buffers are populated
- // vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect));
+ vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1,
+ sizeof(BladeDrawIndirect));
}
// End render pass
@@ -996,7 +1155,6 @@ void Renderer::RecordCommandBuffers() {
}
void Renderer::Frame() {
-
VkSubmitInfo computeSubmitInfo = {};
computeSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@@ -1016,8 +1174,8 @@ void Renderer::Frame() {
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- VkSemaphore waitSemaphores[] = { swapChain->GetImageAvailableVkSemaphore() };
- VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
+ VkSemaphore waitSemaphores[] = {swapChain->GetImageAvailableVkSemaphore()};
+ VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
@@ -1025,7 +1183,7 @@ void Renderer::Frame() {
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[swapChain->GetIndex()];
- VkSemaphore signalSemaphores[] = { swapChain->GetRenderFinishedVkSemaphore() };
+ VkSemaphore signalSemaphores[] = {swapChain->GetRenderFinishedVkSemaphore()};
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
@@ -1043,9 +1201,10 @@ Renderer::~Renderer() {
// TODO: destroy any resources you created
- vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()), commandBuffers.data());
+ vkFreeCommandBuffers(logicalDevice, graphicsCommandPool, static_cast(commandBuffers.size()),
+ commandBuffers.data());
vkFreeCommandBuffers(logicalDevice, computeCommandPool, 1, &computeCommandBuffer);
-
+
vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr);
vkDestroyPipeline(logicalDevice, grassPipeline, nullptr);
vkDestroyPipeline(logicalDevice, computePipeline, nullptr);
@@ -1057,6 +1216,7 @@ Renderer::~Renderer() {
vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr);
+ vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr);
vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr);
diff --git a/src/Renderer.h b/src/Renderer.h
index 95e025f..7243bd7 100644
--- a/src/Renderer.h
+++ b/src/Renderer.h
@@ -1,12 +1,12 @@
#pragma once
+#include "Camera.h"
#include "Device.h"
-#include "SwapChain.h"
#include "Scene.h"
-#include "Camera.h"
+#include "SwapChain.h"
class Renderer {
-public:
+ public:
Renderer() = delete;
Renderer(Device* device, SwapChain* swapChain, Scene* scene, Camera* camera);
~Renderer();
@@ -41,7 +41,7 @@ class Renderer {
void Frame();
-private:
+ private:
Device* device;
VkDevice logicalDevice;
SwapChain* swapChain;
@@ -56,12 +56,15 @@ class Renderer {
VkDescriptorSetLayout cameraDescriptorSetLayout;
VkDescriptorSetLayout modelDescriptorSetLayout;
VkDescriptorSetLayout timeDescriptorSetLayout;
-
+ VkDescriptorSetLayout computeDescriptorSetLayout;
+
VkDescriptorPool descriptorPool;
VkDescriptorSet cameraDescriptorSet;
std::vector modelDescriptorSets;
VkDescriptorSet timeDescriptorSet;
+ std::vector computeDescriptorSets;
+ std::vector grassDescriptorSets;
VkPipelineLayout graphicsPipelineLayout;
VkPipelineLayout grassPipelineLayout;
diff --git a/src/Scene.cpp b/src/Scene.cpp
index 86894f2..c55a867 100644
--- a/src/Scene.cpp
+++ b/src/Scene.cpp
@@ -1,27 +1,22 @@
#include "Scene.h"
+
#include "BufferUtils.h"
Scene::Scene(Device* device) : device(device) {
- BufferUtils::CreateBuffer(device, sizeof(Time), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, timeBuffer, timeBufferMemory);
+ BufferUtils::CreateBuffer(device, sizeof(Time), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, timeBuffer,
+ timeBufferMemory);
vkMapMemory(device->GetVkDevice(), timeBufferMemory, 0, sizeof(Time), 0, &mappedData);
memcpy(mappedData, &time, sizeof(Time));
}
-const std::vector& Scene::GetModels() const {
- return models;
-}
+const std::vector& Scene::GetModels() const { return models; }
-const std::vector& Scene::GetBlades() const {
- return blades;
-}
+const std::vector& Scene::GetBlades() const { return blades; }
-void Scene::AddModel(Model* model) {
- models.push_back(model);
-}
+void Scene::AddModel(Model* model) { models.push_back(model); }
-void Scene::AddBlades(Blades* blades) {
- this->blades.push_back(blades);
-}
+void Scene::AddBlades(Blades* blades) { this->blades.push_back(blades); }
void Scene::UpdateTime() {
high_resolution_clock::time_point currentTime = high_resolution_clock::now();
@@ -34,9 +29,7 @@ void Scene::UpdateTime() {
memcpy(mappedData, &time, sizeof(Time));
}
-VkBuffer Scene::GetTimeBuffer() const {
- return timeBuffer;
-}
+VkBuffer Scene::GetTimeBuffer() const { return timeBuffer; }
Scene::~Scene() {
vkUnmapMemory(device->GetVkDevice(), timeBufferMemory);
diff --git a/src/Scene.h b/src/Scene.h
index 7699d78..6ad8f3d 100644
--- a/src/Scene.h
+++ b/src/Scene.h
@@ -1,10 +1,10 @@
#pragma once
-#include
#include
+#include
-#include "Model.h"
#include "Blades.h"
+#include "Model.h"
using namespace std::chrono;
@@ -14,28 +14,28 @@ struct Time {
};
class Scene {
-private:
+ private:
Device* device;
-
+
VkBuffer timeBuffer;
VkDeviceMemory timeBufferMemory;
Time time;
-
+
void* mappedData;
std::vector models;
std::vector blades;
-high_resolution_clock::time_point startTime = high_resolution_clock::now();
+ high_resolution_clock::time_point startTime = high_resolution_clock::now();
-public:
+ public:
Scene() = delete;
Scene(Device* device);
~Scene();
const std::vector& GetModels() const;
const std::vector& GetBlades() const;
-
+
void AddModel(Model* model);
void AddBlades(Blades* blades);
diff --git a/src/ShaderModule.cpp b/src/ShaderModule.cpp
index 6ba70f8..ea74a58 100644
--- a/src/ShaderModule.cpp
+++ b/src/ShaderModule.cpp
@@ -1,24 +1,25 @@
-#include
#include "ShaderModule.h"
+#include
+
namespace {
- std::vector readFile(const std::string& filename) {
- std::ifstream file(filename, std::ios::ate | std::ios::binary);
+std::vector readFile(const std::string& filename) {
+ std::ifstream file(filename, std::ios::ate | std::ios::binary);
- if (!file.is_open()) {
- throw std::runtime_error("Failed to open file");
- }
+ if (!file.is_open()) {
+ throw std::runtime_error("Failed to open file");
+ }
- size_t fileSize = (size_t)file.tellg();
- std::vector buffer(fileSize);
+ size_t fileSize = (size_t)file.tellg();
+ std::vector buffer(fileSize);
- file.seekg(0);
- file.read(buffer.data(), fileSize);
+ file.seekg(0);
+ file.read(buffer.data(), fileSize);
- file.close();
- return buffer;
- }
+ file.close();
+ return buffer;
}
+} // namespace
// Wrap the shaders in shader modules
VkShaderModule ShaderModule::Create(const std::vector& code, VkDevice logicalDevice) {
diff --git a/src/ShaderModule.h b/src/ShaderModule.h
index ec22160..43fe1e1 100644
--- a/src/ShaderModule.h
+++ b/src/ShaderModule.h
@@ -1,10 +1,11 @@
#pragma once
#include
+
#include
#include
namespace ShaderModule {
- VkShaderModule Create(const std::vector& code, VkDevice logicalDevice);
- VkShaderModule Create(const std::string& filename, VkDevice logicalDevice);
-}
+VkShaderModule Create(const std::vector& code, VkDevice logicalDevice);
+VkShaderModule Create(const std::string& filename, VkDevice logicalDevice);
+} // namespace ShaderModule
diff --git a/src/SwapChain.cpp b/src/SwapChain.cpp
index 711fec0..9b4773e 100644
--- a/src/SwapChain.cpp
+++ b/src/SwapChain.cpp
@@ -1,68 +1,71 @@
-#include
#include "SwapChain.h"
-#include "Instance.h"
+
+#include
+
#include "Device.h"
+#include "Instance.h"
#include "Window.h"
namespace {
- // Specify the color channel format and color space type
- VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) {
- // VK_FORMAT_UNDEFINED indicates that the surface has no preferred format, so we can choose any
- if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) {
- return{ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
- }
-
- // Otherwise, choose a preferred combination
- for (const auto& availableFormat : availableFormats) {
- // Ideal format and color space
- if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
- return availableFormat;
- }
- }
-
- // Otherwise, return any format
- return availableFormats[0];
- }
-
- // Specify the presentation mode of the swap chain
- VkPresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) {
- // Second choice
- VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
-
- for (const auto& availablePresentMode : availablePresentModes) {
- if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
- // First choice
- return availablePresentMode;
- }
- else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
- // Third choice
- bestMode = availablePresentMode;
- }
- }
-
- return bestMode;
- }
-
- // Specify the swap extent (resolution) of the swap chain
- VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window) {
- if (capabilities.currentExtent.width != std::numeric_limits::max()) {
- return capabilities.currentExtent;
- } else {
- int width, height;
- glfwGetWindowSize(window, &width, &height);
- VkExtent2D actualExtent = { static_cast(width), static_cast(height) };
-
- actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width));
- actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height));
-
- return actualExtent;
- }
- }
+// Specify the color channel format and color space type
+VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) {
+ // VK_FORMAT_UNDEFINED indicates that the surface has no preferred format, so we can choose any
+ if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) {
+ return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
+ }
+
+ // Otherwise, choose a preferred combination
+ for (const auto& availableFormat : availableFormats) {
+ // Ideal format and color space
+ if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM &&
+ availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+ return availableFormat;
+ }
+ }
+
+ // Otherwise, return any format
+ return availableFormats[0];
+}
+
+// Specify the presentation mode of the swap chain
+VkPresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) {
+ // Second choice
+ VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
+
+ for (const auto& availablePresentMode : availablePresentModes) {
+ if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
+ // First choice
+ return availablePresentMode;
+ } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
+ // Third choice
+ bestMode = availablePresentMode;
+ }
+ }
+
+ return bestMode;
}
+// Specify the swap extent (resolution) of the swap chain
+VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window) {
+ if (capabilities.currentExtent.width != std::numeric_limits::max()) {
+ return capabilities.currentExtent;
+ } else {
+ int width, height;
+ glfwGetWindowSize(window, &width, &height);
+ VkExtent2D actualExtent = {static_cast(width), static_cast(height)};
+
+ actualExtent.width = std::max(capabilities.minImageExtent.width,
+ std::min(capabilities.maxImageExtent.width, actualExtent.width));
+ actualExtent.height = std::max(capabilities.minImageExtent.height,
+ std::min(capabilities.maxImageExtent.height, actualExtent.height));
+
+ return actualExtent;
+ }
+}
+} // namespace
+
SwapChain::SwapChain(Device* device, VkSurfaceKHR vkSurface, unsigned int numBuffers)
- : device(device), vkSurface(vkSurface), numBuffers(numBuffers) {
-
+ : device(device), vkSurface(vkSurface), numBuffers(numBuffers) {
Create();
VkSemaphoreCreateInfo semaphoreInfo = {};
@@ -74,14 +77,17 @@ SwapChain::SwapChain(Device* device, VkSurfaceKHR vkSurface, unsigned int numBuf
}
}
-void SwapChain::Create() {
+void SwapChain::Create(int w, int h) {
auto* instance = device->GetInstance();
const auto& surfaceCapabilities = instance->GetSurfaceCapabilities();
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(instance->GetSurfaceFormats());
VkPresentModeKHR presentMode = chooseSwapPresentMode(instance->GetPresentModes());
- VkExtent2D extent = chooseSwapExtent(surfaceCapabilities, GetGLFWWindow());
+ VkExtent2D extent{w, h};
+ if (w == 0 || h == 0) {
+ extent = chooseSwapExtent(surfaceCapabilities, GetGLFWWindow());
+ }
uint32_t imageCount = surfaceCapabilities.minImageCount + 1;
imageCount = numBuffers > imageCount ? numBuffers : imageCount;
@@ -109,13 +115,10 @@ void SwapChain::Create() {
// Images can be used across multiple queue families without explicit ownership transfers
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
- unsigned int indices[] = {
- static_cast(queueFamilyIndices[QueueFlags::Graphics]),
- static_cast(queueFamilyIndices[QueueFlags::Present])
- };
+ unsigned int indices[] = {static_cast(queueFamilyIndices[QueueFlags::Graphics]),
+ static_cast(queueFamilyIndices[QueueFlags::Present])};
createInfo.pQueueFamilyIndices = indices;
- }
- else {
+ } else {
// An image is owned by one queue family at a time and ownership must be explicitly transfered between uses
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
@@ -151,46 +154,27 @@ void SwapChain::Create() {
vkSwapChainExtent = extent;
}
-void SwapChain::Destroy() {
- vkDestroySwapchainKHR(device->GetVkDevice(), vkSwapChain, nullptr);
-}
-
-VkSwapchainKHR SwapChain::GetVkSwapChain() const {
- return vkSwapChain;
-}
+void SwapChain::Destroy() { vkDestroySwapchainKHR(device->GetVkDevice(), vkSwapChain, nullptr); }
-VkFormat SwapChain::GetVkImageFormat() const {
- return vkSwapChainImageFormat;
-}
+VkSwapchainKHR SwapChain::GetVkSwapChain() const { return vkSwapChain; }
-VkExtent2D SwapChain::GetVkExtent() const {
- return vkSwapChainExtent;
-}
+VkFormat SwapChain::GetVkImageFormat() const { return vkSwapChainImageFormat; }
-uint32_t SwapChain::GetIndex() const {
- return imageIndex;
-}
+VkExtent2D SwapChain::GetVkExtent() const { return vkSwapChainExtent; }
-uint32_t SwapChain::GetCount() const {
- return static_cast(vkSwapChainImages.size());
-}
+uint32_t SwapChain::GetIndex() const { return imageIndex; }
-VkImage SwapChain::GetVkImage(uint32_t index) const {
- return vkSwapChainImages[index];
-}
+uint32_t SwapChain::GetCount() const { return static_cast(vkSwapChainImages.size()); }
-VkSemaphore SwapChain::GetImageAvailableVkSemaphore() const {
- return imageAvailableSemaphore;
+VkImage SwapChain::GetVkImage(uint32_t index) const { return vkSwapChainImages[index]; }
-}
+VkSemaphore SwapChain::GetImageAvailableVkSemaphore() const { return imageAvailableSemaphore; }
-VkSemaphore SwapChain::GetRenderFinishedVkSemaphore() const {
- return renderFinishedSemaphore;
-}
+VkSemaphore SwapChain::GetRenderFinishedVkSemaphore() const { return renderFinishedSemaphore; }
-void SwapChain::Recreate() {
+void SwapChain::Recreate(int w, int h) {
Destroy();
- Create();
+ Create(w, h);
}
bool SwapChain::Acquire() {
@@ -198,7 +182,8 @@ bool SwapChain::Acquire() {
// the validation layer implementation expects the application to explicitly synchronize with the GPU
vkQueueWaitIdle(device->GetQueue(QueueFlags::Present));
}
- VkResult result = vkAcquireNextImageKHR(device->GetVkDevice(), vkSwapChain, std::numeric_limits::max(), imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
+ VkResult result = vkAcquireNextImageKHR(device->GetVkDevice(), vkSwapChain, std::numeric_limits::max(),
+ imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
throw std::runtime_error("Failed to acquire swap chain image");
}
@@ -212,7 +197,7 @@ bool SwapChain::Acquire() {
}
bool SwapChain::Present() {
- VkSemaphore signalSemaphores[] = { renderFinishedSemaphore };
+ VkSemaphore signalSemaphores[] = {renderFinishedSemaphore};
// Submit result back to swap chain for presentation
VkPresentInfoKHR presentInfo = {};
@@ -220,7 +205,7 @@ bool SwapChain::Present() {
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
- VkSwapchainKHR swapChains[] = { vkSwapChain };
+ VkSwapchainKHR swapChains[] = {vkSwapChain};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
diff --git a/src/SwapChain.h b/src/SwapChain.h
index dbafcf0..2089d05 100644
--- a/src/SwapChain.h
+++ b/src/SwapChain.h
@@ -1,13 +1,14 @@
#pragma once
#include
+
#include "Device.h"
class Device;
class SwapChain {
friend class Device;
-public:
+ public:
VkSwapchainKHR GetVkSwapChain() const;
VkFormat GetVkImageFormat() const;
VkExtent2D GetVkExtent() const;
@@ -16,15 +17,15 @@ class SwapChain {
VkImage GetVkImage(uint32_t index) const;
VkSemaphore GetImageAvailableVkSemaphore() const;
VkSemaphore GetRenderFinishedVkSemaphore() const;
-
- void Recreate();
+
+ void Recreate(int w = 0, int h = 0);
bool Acquire();
bool Present();
~SwapChain();
-private:
+ private:
SwapChain(Device* device, VkSurfaceKHR vkSurface, unsigned int numBuffers);
- void Create();
+ void Create(int w = 0, int h = 0);
void Destroy();
Device* device;
diff --git a/src/Vertex.h b/src/Vertex.h
index 2023a6c..29a4ca7 100644
--- a/src/Vertex.h
+++ b/src/Vertex.h
@@ -1,9 +1,9 @@
#pragma once
#include
-#include
#include
+#include
struct Vertex {
glm::vec3 pos;
diff --git a/src/Window.cpp b/src/Window.cpp
index a365dc9..df8ed10 100644
--- a/src/Window.cpp
+++ b/src/Window.cpp
@@ -1,13 +1,12 @@
-#include
#include "Window.h"
+#include
+
namespace {
- GLFWwindow* window = nullptr;
+GLFWwindow* window = nullptr;
}
-GLFWwindow* GetGLFWWindow() {
- return window;
-}
+GLFWwindow* GetGLFWWindow() { return window; }
void InitializeWindow(int width, int height, const char* name) {
if (!glfwInit()) {
@@ -15,7 +14,7 @@ void InitializeWindow(int width, int height, const char* name) {
exit(EXIT_FAILURE);
}
- if (!glfwVulkanSupported()){
+ if (!glfwVulkanSupported()) {
fprintf(stderr, "Vulkan not supported\n");
exit(EXIT_FAILURE);
}
@@ -30,9 +29,7 @@ void InitializeWindow(int width, int height, const char* name) {
}
}
-bool ShouldQuit() {
- return !!glfwWindowShouldClose(window);
-}
+bool ShouldQuit() { return !!glfwWindowShouldClose(window); }
void DestroyWindow() {
glfwDestroyWindow(window);
diff --git a/src/main.cpp b/src/main.cpp
index 8bf822b..aa6a8d3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,10 +1,11 @@
#include
+
+#include "Camera.h"
+#include "Image.h"
#include "Instance.h"
-#include "Window.h"
#include "Renderer.h"
-#include "Camera.h"
#include "Scene.h"
-#include "Image.h"
+#include "Window.h"
Device* device;
SwapChain* swapChain;
@@ -12,58 +13,56 @@ Renderer* renderer;
Camera* camera;
namespace {
- void resizeCallback(GLFWwindow* window, int width, int height) {
- if (width == 0 || height == 0) return;
+void resizeCallback(GLFWwindow* window, int width, int height) {
+ if (width == 0 || height == 0) return;
- vkDeviceWaitIdle(device->GetVkDevice());
- swapChain->Recreate();
- renderer->RecreateFrameResources();
- }
+ vkDeviceWaitIdle(device->GetVkDevice());
+ swapChain->Recreate(width, height);
+ renderer->RecreateFrameResources();
+}
- bool leftMouseDown = false;
- bool rightMouseDown = false;
- double previousX = 0.0;
- double previousY = 0.0;
-
- void mouseDownCallback(GLFWwindow* window, int button, int action, int mods) {
- if (button == GLFW_MOUSE_BUTTON_LEFT) {
- if (action == GLFW_PRESS) {
- leftMouseDown = true;
- glfwGetCursorPos(window, &previousX, &previousY);
- }
- else if (action == GLFW_RELEASE) {
- leftMouseDown = false;
- }
- } else if (button == GLFW_MOUSE_BUTTON_RIGHT) {
- if (action == GLFW_PRESS) {
- rightMouseDown = true;
- glfwGetCursorPos(window, &previousX, &previousY);
- }
- else if (action == GLFW_RELEASE) {
- rightMouseDown = false;
- }
+bool leftMouseDown = false;
+bool rightMouseDown = false;
+double previousX = 0.0;
+double previousY = 0.0;
+
+void mouseDownCallback(GLFWwindow* window, int button, int action, int mods) {
+ if (button == GLFW_MOUSE_BUTTON_LEFT) {
+ if (action == GLFW_PRESS) {
+ leftMouseDown = true;
+ glfwGetCursorPos(window, &previousX, &previousY);
+ } else if (action == GLFW_RELEASE) {
+ leftMouseDown = false;
+ }
+ } else if (button == GLFW_MOUSE_BUTTON_RIGHT) {
+ if (action == GLFW_PRESS) {
+ rightMouseDown = true;
+ glfwGetCursorPos(window, &previousX, &previousY);
+ } else if (action == GLFW_RELEASE) {
+ rightMouseDown = false;
}
}
+}
- void mouseMoveCallback(GLFWwindow* window, double xPosition, double yPosition) {
- if (leftMouseDown) {
- double sensitivity = 0.5;
- float deltaX = static_cast((previousX - xPosition) * sensitivity);
- float deltaY = static_cast((previousY - yPosition) * sensitivity);
+void mouseMoveCallback(GLFWwindow* window, double xPosition, double yPosition) {
+ if (leftMouseDown) {
+ double sensitivity = 0.5;
+ float deltaX = static_cast((previousX - xPosition) * sensitivity);
+ float deltaY = static_cast((previousY - yPosition) * sensitivity);
- camera->UpdateOrbit(deltaX, deltaY, 0.0f);
+ camera->UpdateOrbit(deltaX, deltaY, 0.0f);
- previousX = xPosition;
- previousY = yPosition;
- } else if (rightMouseDown) {
- double deltaZ = static_cast((previousY - yPosition) * 0.05);
+ previousX = xPosition;
+ previousY = yPosition;
+ } else if (rightMouseDown) {
+ double deltaZ = static_cast((previousY - yPosition) * 0.05);
- camera->UpdateOrbit(0.0f, 0.0f, deltaZ);
+ camera->UpdateOrbit(0.0f, 0.0f, deltaZ);
- previousY = yPosition;
- }
+ previousY = yPosition;
}
}
+} // namespace
int main() {
static constexpr char* applicationName = "Vulkan Grass Rendering";
@@ -79,14 +78,19 @@ int main() {
throw std::runtime_error("Failed to create window surface");
}
- instance->PickPhysicalDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME }, QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::ComputeBit | QueueFlagBit::PresentBit, surface);
+ instance->PickPhysicalDevice(
+ {VK_KHR_SWAPCHAIN_EXTENSION_NAME},
+ QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::ComputeBit | QueueFlagBit::PresentBit,
+ surface);
VkPhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.tessellationShader = VK_TRUE;
deviceFeatures.fillModeNonSolid = VK_TRUE;
deviceFeatures.samplerAnisotropy = VK_TRUE;
- device = instance->CreateDevice(QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::ComputeBit | QueueFlagBit::PresentBit, deviceFeatures);
+ device = instance->CreateDevice(
+ QueueFlagBit::GraphicsBit | QueueFlagBit::TransferBit | QueueFlagBit::ComputeBit | QueueFlagBit::PresentBit,
+ deviceFeatures);
swapChain = device->CreateSwapChain(surface, 5);
@@ -104,31 +108,20 @@ int main() {
VkImage grassImage;
VkDeviceMemory grassImageMemory;
- Image::FromFile(device,
- transferCommandPool,
- "images/grass.jpg",
- VK_FORMAT_R8G8B8A8_UNORM,
- VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_SAMPLED_BIT,
- VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
- VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
- grassImage,
- grassImageMemory
- );
+ Image::FromFile(device, transferCommandPool, "images/grass.jpg", VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, grassImage, grassImageMemory);
float planeDim = 15.f;
float halfWidth = planeDim * 0.5f;
Model* plane = new Model(device, transferCommandPool,
- {
- { { -halfWidth, 0.0f, halfWidth }, { 1.0f, 0.0f, 0.0f },{ 1.0f, 0.0f } },
- { { halfWidth, 0.0f, halfWidth }, { 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f } },
- { { halfWidth, 0.0f, -halfWidth }, { 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f } },
- { { -halfWidth, 0.0f, -halfWidth }, { 1.0f, 1.0f, 1.0f },{ 1.0f, 1.0f } }
- },
- { 0, 1, 2, 2, 3, 0 }
- );
+ {{{-halfWidth, 0.0f, halfWidth}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
+ {{halfWidth, 0.0f, halfWidth}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
+ {{halfWidth, 0.0f, -halfWidth}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
+ {{-halfWidth, 0.0f, -halfWidth}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}},
+ {0, 1, 2, 2, 3, 0});
plane->SetTexture(grassImage);
-
+
Blades* blades = new Blades(device, transferCommandPool, planeDim);
vkDestroyCommandPool(device->GetVkDevice(), transferCommandPool, nullptr);
diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp
index 0fd0224..100f5cd 100644
--- a/src/shaders/compute.comp
+++ b/src/shaders/compute.comp
@@ -1,6 +1,27 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
+#define COMPUTE_FORCES 1
+
+#define GRAVITY_DIRECTION vec3(0.0, -1.0, 0.0)
+#define GRAVITY_MAGNITUDE 9.81
+
+#define BLADE_MASS 1.0
+#define COLLISION_STRENGTH 0.0
+
+#define WIND_TYPE 1
+#define WIND_MAGNITUDE 3.0
+#define WIND_FREQUENCY 0.75
+
+#define ENABLE_ORIENTATION_CULLING 1
+#define ENABLE_FRUSTUM_CULLING 1
+#define ENABLE_DISTANCE_CULLING 1
+
+#define ORIENTATION_CULLING_THRESHOLD 0.9
+#define FRUSTUM_CULLING_PADDING 0.1
+#define DISTANCE_CULLING_THRESHOLD 50.0
+#define DISTANCE_CULLING_NUM_BUCKETS 10
+
#define WORKGROUP_SIZE 32
layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
@@ -23,34 +44,168 @@ struct Blade {
// TODO: Add bindings to:
// 1. Store the input blades
+layout(set = 2, binding = 0) buffer GrassBlades {
+ Blade blades[];
+};
+
// 2. Write out the culled blades
-// 3. Write the total number of blades remaining
+layout(set = 2, binding = 1) buffer CulledBlades {
+ Blade culledBlades[];
+};
+// 3. Write the total number of blades remaining
// The project is using vkCmdDrawIndirect to use a buffer as the arguments for a draw call
// This is sort of an advanced feature so we've showed you what this buffer should look like
//
-// layout(set = ???, binding = ???) buffer NumBlades {
-// uint vertexCount; // Write the number of blades remaining here
-// uint instanceCount; // = 1
-// uint firstVertex; // = 0
-// uint firstInstance; // = 0
-// } numBlades;
+layout(set = 2, binding = 2) buffer NumBlades {
+ uint vertexCount; // Write the number of blades remaining here
+ uint instanceCount; // = 1
+ uint firstVertex; // = 0
+ uint firstInstance; // = 0
+} numBlades;
bool inBounds(float value, float bounds) {
return (value >= -bounds) && (value <= bounds);
}
+bool inViewFrustum(vec3 pos) {
+ vec4 clipPos = camera.proj * camera.view * vec4(pos, 1.0);
+ clipPos /= clipPos.w;
+ float bounds = 1.0 + FRUSTUM_CULLING_PADDING;
+ return inBounds(clipPos.x, bounds) && inBounds(clipPos.y, bounds) && inBounds(clipPos.z, bounds);
+}
+
+vec3 getRandomWind(vec3 pos, float time) {
+ float x = sin(WIND_FREQUENCY * pos.x + time) * cos(WIND_FREQUENCY * pos.z - time);
+ float y = cos(WIND_FREQUENCY * pos.y + time * 0.52345) * 0.1; // minor vertical wind
+ float z = sin(WIND_FREQUENCY * pos.z * 32052.119 + time * 0.73267) * cos(WIND_FREQUENCY * pos.z * 1923.14 - time * 0.91932);
+
+ return WIND_MAGNITUDE * vec3(x, y, z);
+}
+
+vec3 getUniformWind(vec3 pos, float time) {
+ float phase = WIND_FREQUENCY * sin(12452.1249 * pos.x + 9724.88 * pos.z) * (pos.x + pos.z) / 5 + time * 2;
+ float magnitude = WIND_MAGNITUDE * ((1 + sin(time * 1.4)) / 3 + 0.8);
+ float x = sin(phase) * magnitude * 1.6;
+ float y = 0.0; // no vertical wind
+ float z = cos(phase) * magnitude * 1.1;
+
+ return vec3(x, y, z);
+}
+
+vec3 getRadialWind(vec3 pos, float time) {
+ vec3 center = vec3(0.0);
+ vec3 direction = normalize(pos - center);
+ float dist = length(pos - center);
+ float wave = 2.5 * sin(WIND_FREQUENCY * dist - time * 10);
+
+ return WIND_MAGNITUDE * direction * wave;
+}
+
void main() {
// Reset the number of blades to 0
if (gl_GlobalInvocationID.x == 0) {
- // numBlades.vertexCount = 0;
+ numBlades.vertexCount = 0;
}
barrier(); // Wait till all threads reach this point
+ Blade currBlade = blades[gl_GlobalInvocationID.x];
+ vec3 v0 = currBlade.v0.xyz;
+ vec3 v1 = currBlade.v1.xyz;
+ vec3 v2 = currBlade.v2.xyz;
+ vec3 up = currBlade.up.xyz;
+
+ float orientation = currBlade.v0.w;
+ float height = currBlade.v1.w;
+ float width = currBlade.v2.w;
+ float stiffness = currBlade.up.w;
+
+ vec3 t1 = vec3(-cos(orientation), 0.0, -sin(orientation)); // bitangent
+ vec3 f = normalize(cross(t1, up)); // normal (of grass blade)
+
+#if COMPUTE_FORCES
// TODO: Apply forces on every blade and update the vertices in the buffer
+ // Compute Recovery force
+ vec3 Iv2 = v0 + height * up;
+ vec3 r = (Iv2 - v2) * stiffness * max(1 - COLLISION_STRENGTH, 0.1);
+
+ // Compute Gravity force
+ vec3 D = GRAVITY_DIRECTION;
+ float Dw = GRAVITY_MAGNITUDE;
+ vec3 gE = BLADE_MASS * (normalize(D) * Dw);
+ vec3 gF = 0.25 * length(gE) * f;
+ vec3 g = gE + gF;
+
+ // Compute Wind force
+#if WIND_TYPE == 0
+ vec3 w_v0 = getRandomWind(v0, totalTime);
+#elif WIND_TYPE == 1
+ vec3 w_v0 = getUniformWind(v0, totalTime);
+#elif WIND_TYPE == 2
+ vec3 w_v0 = getRadialWind(v0, totalTime);
+#endif
+ float fd = 1 - abs(dot(normalize(w_v0), normalize(v2 - v0)));
+ float fr = dot(v2 - v0, up) / height;
+ float theta = fd * fr;
+ vec3 w = w_v0 * theta;
+
+ // Compute total force
+ vec3 force = (r + g + w) * deltaTime;
+ v2 += force;
+
+ // State validation
+ // 1. v2 not below the ground
+ v2 -= up * min(dot(up, v2 - v0), 0);
+
+ // 2. v1 set according to v2
+ float l_proj = length(v2 - v0 - up * dot(v2 - v0, up));
+ v1 = v0 + height * up * max(1 - l_proj / height, 0.05 * max(l_proj / height, 1));
+
+ // 3. length of curve equals height of blade
+ float L0 = distance(v0, v2);
+ float L1 = distance(v0, v1) + distance(v1, v2);
+ float L = (2.0 * L0 + L1) / 3.0;
+ float scale = height / L; // r
+
+ vec3 original_v1 = v1;
+ v1 = v0 + scale * (v1 - v0);
+ v2 = v1 + scale * (v2 - original_v1);
+
+ currBlade.v1.xyz = v1;
+ currBlade.v2.xyz = v2;
+ blades[gl_GlobalInvocationID.x] = currBlade;
+#endif
// TODO: Cull blades that are too far away or not in the camera frustum and write them
// to the culled blades buffer
// Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount
// You want to write the visible blades to the buffer without write conflicts between threads
+
+ vec3 c = vec3(inverse(camera.view)[3]);
+ vec3 dir_c = v0 - c - up * dot(v0 - c, up);
+#if ENABLE_ORIENTATION_CULLING
+ // Orientation culling
+ if (abs(dot(normalize(dir_c), t1)) > ORIENTATION_CULLING_THRESHOLD) {
+ return;
+ }
+#endif
+
+#if ENABLE_FRUSTUM_CULLING
+ // View-Frustum culling
+ vec3 m = 0.25 * v0 + 0.5 * v1 + 0.25 * v2;
+ if (!inViewFrustum(m) && !inViewFrustum(v0) && !inViewFrustum(v2)) {
+ return;
+ }
+#endif
+
+#if ENABLE_DISTANCE_CULLING
+ // Distance culling
+ float d_proj = length(dir_c);
+ int n = DISTANCE_CULLING_NUM_BUCKETS;
+ if (gl_GlobalInvocationID.x % n >= floor(n * (1.0 - d_proj / DISTANCE_CULLING_THRESHOLD))) {
+ return;
+ }
+#endif
+
+ culledBlades[(atomicAdd(numBlades.vertexCount, 1))] = blades[gl_GlobalInvocationID.x];
}
diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag
index c7df157..1e5cfbe 100644
--- a/src/shaders/grass.frag
+++ b/src/shaders/grass.frag
@@ -7,11 +7,20 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;
// TODO: Declare fragment shader inputs
+layout(location = 0) in vec2 fragUV;
+layout(location = 1) in vec3 fragNormal;
layout(location = 0) out vec4 outColor;
void main() {
// TODO: Compute fragment color
+ vec3 baseColor = vec3(121, 208, 33) / 255.0;
+ vec3 tipColor = vec3(81, 154, 21) / 255.0;
+ vec3 color = mix(baseColor, tipColor, fragUV.y);
- outColor = vec4(1.0);
+ vec3 lightDir = normalize(vec3(0.0, 1.0, 0.1));
+ float ambientLight = 1.0;
+ float diffuseLight = clamp(dot(fragNormal, lightDir), 0.0, 1.0);
+
+ outColor = vec4(color * (ambientLight + diffuseLight), 1.0);
}
diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc
index f9ffd07..5bc27b9 100644
--- a/src/shaders/grass.tesc
+++ b/src/shaders/grass.tesc
@@ -1,6 +1,10 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
+#define MAX_TESSELLATION 20.0
+#define MIN_TESSELLATION 1.0
+#define TESSELLATION_FALLOFF_DISTANCE 25.0
+
layout(vertices = 1) out;
layout(set = 0, binding = 0) uniform CameraBufferObject {
@@ -9,18 +13,33 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;
// TODO: Declare tessellation control shader inputs and outputs
+layout(location = 0) in vec4 in_v0[];
+layout(location = 1) in vec4 in_v1[];
+layout(location = 2) in vec4 in_v2[];
+
+layout(location = 0) out vec4 out_v0[];
+layout(location = 1) out vec4 out_v1[];
+layout(location = 2) out vec4 out_v2[];
void main() {
// Don't move the origin location of the patch
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
// TODO: Write any shader outputs
+ out_v0[gl_InvocationID] = in_v0[gl_InvocationID];
+ out_v1[gl_InvocationID] = in_v1[gl_InvocationID];
+ out_v2[gl_InvocationID] = in_v2[gl_InvocationID];
// TODO: Set level of tesselation
- // gl_TessLevelInner[0] = ???
- // gl_TessLevelInner[1] = ???
- // gl_TessLevelOuter[0] = ???
- // gl_TessLevelOuter[1] = ???
- // gl_TessLevelOuter[2] = ???
- // gl_TessLevelOuter[3] = ???
+ vec3 bladePos = vec3(in_v0[gl_InvocationID]);
+ vec3 cameraPos = vec3(inverse(camera.view)[3]);
+ float dist = distance(bladePos, cameraPos);
+ float tessLevel = mix(MAX_TESSELLATION, MIN_TESSELLATION, clamp(dist / TESSELLATION_FALLOFF_DISTANCE, 0.0, 1.0));
+
+ gl_TessLevelInner[0] = tessLevel;
+ gl_TessLevelInner[1] = tessLevel;
+ gl_TessLevelOuter[0] = tessLevel;
+ gl_TessLevelOuter[1] = tessLevel;
+ gl_TessLevelOuter[2] = tessLevel;
+ gl_TessLevelOuter[3] = tessLevel;
}
diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese
index 751fff6..921f3bb 100644
--- a/src/shaders/grass.tese
+++ b/src/shaders/grass.tese
@@ -1,6 +1,9 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
+// 0 = square, 1 = triangle, 2 = parabola, 3 = triangle tip
+#define BLADE_SHAPE 1
+
layout(quads, equal_spacing, ccw) in;
layout(set = 0, binding = 0) uniform CameraBufferObject {
@@ -9,10 +12,65 @@ layout(set = 0, binding = 0) uniform CameraBufferObject {
} camera;
// TODO: Declare tessellation evaluation shader inputs and outputs
+layout(location = 0) in vec4 in_v0[];
+layout(location = 1) in vec4 in_v1[];
+layout(location = 2) in vec4 in_v2[];
+
+layout(location = 0) out vec2 out_UV;
+layout(location = 1) out vec3 out_Nor;
+
+float getSquareT(float u, float v) {
+ return u;
+}
+
+float getTriangleT(float u, float v) {
+ return u + 0.5 * v - u * v;
+}
+
+float getParabolaT(float u, float v) {
+ return u - u * v * v;
+}
+
+float getTriangleTipT(float u, float v, float tau) {
+ // tau is triangle tip height threshold
+ return 0.5 + (u - 0.5) * (1 - max(v - tau, 0) / 1 - tau);
+}
void main() {
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;
// TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade
+ vec3 v0 = in_v0[0].xyz;
+ vec3 v1 = in_v1[0].xyz;
+ vec3 v2 = in_v2[0].xyz;
+
+ float orientation = in_v0[0].w;
+ float width = in_v2[0].w;
+
+ vec3 a = mix(v0, v1, v);
+ vec3 b = mix(v1, v2, v);
+ vec3 c = mix(a, b, v);
+
+ vec3 t0 = normalize(b - a); // tangent
+ vec3 t1 = vec3(-cos(orientation), 0.0, -sin(orientation)); // bitangent
+ vec3 n = normalize(cross(t0, t1)); // normal
+
+ vec3 c0 = c - width * t1;
+ vec3 c1 = c + width * t1;
+
+#if BLADE_SHAPE == 0
+ float t = getTriangleT(u, v);
+#elif BLADE_SHAPE == 1
+ float t = getTriangleT(u, v);
+#elif BLADE_SHAPE == 2
+ float t = getParabolaT(u, v);
+#elif BLADE_SHAPE == 3
+ float t = getTriangleTipT(u, v, 0.2);
+#endif
+ vec3 pos = mix(c0, c1, t);
+
+ out_UV = vec2(u, v);
+ out_Nor = n;
+ gl_Position = camera.proj * camera.view * vec4(pos, 1.0);
}
diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert
index db9dfe9..8bb0a99 100644
--- a/src/shaders/grass.vert
+++ b/src/shaders/grass.vert
@@ -7,11 +7,27 @@ layout(set = 1, binding = 0) uniform ModelBufferObject {
};
// TODO: Declare vertex shader inputs and outputs
+layout(location = 0) in vec4 in_v0;
+layout(location = 1) in vec4 in_v1;
+layout(location = 2) in vec4 in_v2;
-out gl_PerVertex {
- vec4 gl_Position;
-};
+layout(location = 0) out vec4 out_v0;
+layout(location = 1) out vec4 out_v1;
+layout(location = 2) out vec4 out_v2;
+
+// out gl_PerVertex {
+// vec4 gl_Position;
+// };
void main() {
// TODO: Write gl_Position and any other shader outputs
+ vec4 temp_v0 = model * vec4(in_v0.xyz, 1.0);
+ vec4 temp_v1 = model * vec4(in_v1.xyz, 1.0);
+ vec4 temp_v2 = model * vec4(in_v2.xyz, 1.0);
+
+ out_v0 = vec4(temp_v0.xyz, in_v0.w);
+ out_v1 = vec4(temp_v1.xyz, in_v1.w);
+ out_v2 = vec4(temp_v2.xyz, in_v2.w);
+
+ // gl_Position = out_v0;
}