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