diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index 8b5172cb6a4b4b63a428a5094845dc47ecd686f5..671c99925536d63e1ef87c0d069e1929e1a592b9 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 0000000000000000000000000000000000000000..1a87bbc3ab6aee83d78e25d2b3693c44fa2b16bd --- /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 0000000000000000000000000000000000000000..bc49c4415f31880353676cfabb1b55da4de74205 --- /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 0000000000000000000000000000000000000000..052705adbe68412b685c841c6e82c43057933bbd --- /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 0000000000000000000000000000000000000000..96c02743dc39aac926ffccc6c5c6dc937d51cb15 --- /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 0000000000000000000000000000000000000000..a0dc75e4acec92f3f855415ea47d638c91b6489d --- /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