From 66de1a84bd03c2ef487222a0c210ec9b430cc3ff Mon Sep 17 00:00:00 2001 From: Tobias Frisch <tfrisch@uni-koblenz.de> Date: Mon, 10 Oct 2022 21:12:22 +0200 Subject: [PATCH] Add instancing demo application Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de> --- demos/CMakeLists.txt | 1 + demos/InstancingDemo/.gitignore | 1 + demos/InstancingDemo/CMakeLists.txt | 15 ++ demos/InstancingDemo/shaders/instancing.frag | 35 +++ demos/InstancingDemo/shaders/instancing.vert | 41 ++++ demos/InstancingDemo/src/main.cpp | 226 +++++++++++++++++++ 6 files changed, 319 insertions(+) create mode 100644 demos/InstancingDemo/.gitignore create mode 100644 demos/InstancingDemo/CMakeLists.txt create mode 100644 demos/InstancingDemo/shaders/instancing.frag create mode 100644 demos/InstancingDemo/shaders/instancing.vert create mode 100644 demos/InstancingDemo/src/main.cpp diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index 8b5172c..671c999 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -1,2 +1,3 @@ +add_subdirectory(InstancingDemo) add_subdirectory(NormalMapping) diff --git a/demos/InstancingDemo/.gitignore b/demos/InstancingDemo/.gitignore new file mode 100644 index 0000000..1a87bbc --- /dev/null +++ b/demos/InstancingDemo/.gitignore @@ -0,0 +1 @@ +InstancingDemo \ No newline at end of file diff --git a/demos/InstancingDemo/CMakeLists.txt b/demos/InstancingDemo/CMakeLists.txt new file mode 100644 index 0000000..bc49c44 --- /dev/null +++ b/demos/InstancingDemo/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(InstancingDemo) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(InstancingDemo src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(InstancingDemo SYSTEM BEFORE PRIVATE ${vkcv_includes}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(InstancingDemo ${vkcv_libraries}) diff --git a/demos/InstancingDemo/shaders/instancing.frag b/demos/InstancingDemo/shaders/instancing.frag new file mode 100644 index 0000000..052705a --- /dev/null +++ b/demos/InstancingDemo/shaders/instancing.frag @@ -0,0 +1,35 @@ +#version 450 + +layout(location = 0) in vec3 passPosition; +layout(location = 1) in vec3 passNormal; +layout(location = 2) in vec3 passColor; + +layout(location = 0) out vec4 fragmentColor; + +void main() { + // set fix light position + vec3 lightPosition = vec3(0, 10, 0); + // compute the light vector as the normalized vector between + // the vertex position and the light position: + vec3 lightVector = normalize(lightPosition - passPosition); + + // compute the eye vector as the normalized negative vertex position in camera coordinates: + vec3 eye = normalize(-passPosition); + + // compute the normalized reflection vector using GLSL's built-in reflect() function: + vec3 reflection = normalize(reflect(-lightVector, normalize(passNormal))); + + // variables used in the phong lighting model: + float phi = max(dot(passNormal, lightVector), 0); + float psi = pow(max(dot(reflection, eye), 0), 15); + + vec3 ambientColor = vec3(0.3, 0.2, 0.2); + vec3 specularColor = vec3(1.0, 1.0, 1.0); + + fragmentColor = vec4( + ambientColor + + phi * passColor + + psi * specularColor, + 1 + ); +} \ No newline at end of file diff --git a/demos/InstancingDemo/shaders/instancing.vert b/demos/InstancingDemo/shaders/instancing.vert new file mode 100644 index 0000000..96c0274 --- /dev/null +++ b/demos/InstancingDemo/shaders/instancing.vert @@ -0,0 +1,41 @@ +#version 450 + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 normal; + +layout(set=0, binding=0) uniform matrixBuffer { + mat4 viewMatrix; + mat4 projectionMatrix; +}; + +struct InstanceParams { + vec3 offset; + float pad0; + vec3 color; + float pad1; +}; + +layout(set=0, binding=1, std430) readonly buffer instanceBuffer { + InstanceParams params []; +}; + +layout(location = 0) out vec3 passPosition; +layout(location = 1) out vec3 passNormal; +layout(location = 2) out vec3 passColor; + +void main() { + vec4 pos = vec4(position + params[gl_InstanceIndex].offset, 1); + + gl_Position = projectionMatrix * viewMatrix * pos; + + // transform the position correctly into view space + // and pass it to the fragment shader + passPosition = (viewMatrix * pos).xyz; + + // transform the normal correctly into view space + // and pass it to the fragment shader + mat3 normalMatrix = mat3(transpose(inverse(viewMatrix))); + passNormal = normalize(normalMatrix * normal); + + passColor = params[gl_InstanceIndex].color; +} diff --git a/demos/InstancingDemo/src/main.cpp b/demos/InstancingDemo/src/main.cpp new file mode 100644 index 0000000..a0dc75e --- /dev/null +++ b/demos/InstancingDemo/src/main.cpp @@ -0,0 +1,226 @@ + +#include <random> +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/DescriptorWrites.hpp> +#include <vkcv/Window.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/geometry/Teapot.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +struct InstanceParams { + glm::vec3 offset; + float pad0; + glm::vec3 color; + float pad1; +}; + +void generateInstances(int width, int height, int depth, + std::vector<InstanceParams>& instances) { + std::default_random_engine randomEngine; + instances.reserve(width * height * depth); + + size_t i, j, k; + for (i = 0; i < width; i++) { + for (j = 0; j < height; j++) { + for (k = 0; k < depth; k++) { + InstanceParams instance {}; + + instance.offset = glm::vec3( + (i * 3.0f) - width * 1.5f, + (j * 2.0f) - height, + (k * 2.0f) - depth + ); + + instance.color = glm::vec3( + static_cast<float>(randomEngine() % 100 + 1) / 100.0f, + static_cast<float>(randomEngine() % 100 + 1) / 100.0f, + static_cast<float>(randomEngine() % 100 + 1) / 100.0f + ); + + instances.push_back(instance); + } + } + } +} + +int main(int argc, const char** argv) { + const std::string applicationName = "Instancing Demo"; + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eTransfer }, + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } + ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 800, true); + vkcv::Window& window = core.getWindow(windowHandle); + + vkcv::camera::CameraManager cameraManager (window); + auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle).setNearFar(0.1f, 100.0f); + + vkcv::geometry::Teapot teapot (glm::vec3(0.0f), 1.0f); + + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram shaderProgram; + + compiler.compileProgram( + shaderProgram, + { + { vkcv::ShaderStage::VERTEX, "shaders/instancing.vert" }, + { vkcv::ShaderStage::FRAGMENT, "shaders/instancing.frag" } + }, + nullptr + ); + + vkcv::PassHandle renderPass = core.createPass(vkcv::PassConfig( + { + vkcv::AttachmentDescription( + core.getSwapchainFormat(window.getSwapchain()), + vkcv::AttachmentOperation::CLEAR, + vkcv::AttachmentOperation::STORE, + vk::ClearValue(vk::ClearColorValue(std::array<float, 4>{ + 1.0f, 1.0f, 1.0f, 1.0f + })) + ), + vkcv::AttachmentDescription( + vk::Format::eD32Sfloat, + vkcv::AttachmentOperation::CLEAR, + vkcv::AttachmentOperation::STORE + ) + } + )); + + const vkcv::VertexLayout vertexLayout { + vkcv::createVertexBindings(shaderProgram.getVertexAttachments()) + }; + + const vkcv::DescriptorBindings descriptorBindings = ( + shaderProgram.getReflectedDescriptors().at(0) + ); + + auto descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings); + auto descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::GraphicsPipelineHandle gfxPipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + shaderProgram, + renderPass, + vertexLayout, + { descriptorSetLayout } + ) + ); + + auto matrixBuffer = vkcv::buffer<glm::mat4>( + core, + vkcv::BufferType::UNIFORM, + 2, + vkcv::BufferMemoryType::HOST_VISIBLE + ); + + int width = 5, height = 5, depth = 5; + std::vector<InstanceParams> instances; + + vkcv::BufferHandle instanceBufferHandle; + + { + vkcv::DescriptorWrites writes; + writes.writeUniformBuffer(0, matrixBuffer.getHandle()); + core.writeDescriptorSet(descriptorSet, writes); + } + + vkcv::gui::GUI gui (core, windowHandle); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::ImageHandle depthBuffer; + + glm::mat4* mvp = matrixBuffer.map(); + + core.run([&](const vkcv::WindowHandle &windowHandle, + double t, + double dt, + uint32_t swapchainWidth, + uint32_t swapchainHeight + ) { + if ((!depthBuffer) || + (swapchainWidth != core.getImageWidth(depthBuffer)) || + (swapchainHeight != core.getImageHeight(depthBuffer))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + swapchainWidth, + swapchainHeight + ); + } + + cameraManager.update(dt); + + if (!instanceBufferHandle) { + instances.clear(); + generateInstances(width, height, depth, instances); + + auto instanceBuffer = vkcv::buffer<InstanceParams>( + core, + vkcv::BufferType::STORAGE, + instances.size() + ); + + instanceBuffer.fill(instances); + instanceBufferHandle = instanceBuffer.getHandle(); + + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(1, instanceBufferHandle); + core.writeDescriptorSet(descriptorSet, writes); + } + + mvp[0] = cameraManager.getActiveCamera().getView(); + mvp[1] = cameraManager.getActiveCamera().getProjection(); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + vkcv::InstanceDrawcall drawcall (teapot.generateVertexData(core), instances.size()); + drawcall.useDescriptorSet(0, descriptorSet); + + core.recordDrawcallsToCmdStream( + cmdStream, + gfxPipeline, + vkcv::PushConstants(0), + { drawcall }, + { + swapchainInput, + depthBuffer + }, + windowHandle + ); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + ImGui::Begin("Instances"); + + int w = width, h = height, d = depth; + + ImGui::SliderInt("Width", &w, 1, 100); + ImGui::SliderInt("Height", &h, 1, 100); + ImGui::SliderInt("Depth", &d, 1, 100); + + ImGui::End(); + gui.endGUI(); + + if ((w != width) || (h != height) || (d != depth)) { + instanceBufferHandle = vkcv::BufferHandle(); + + width = w; + height = h; + depth = d; + } + }); + + matrixBuffer.unmap(); + return 0; +} \ No newline at end of file -- GitLab