diff --git a/config/Sources.cmake b/config/Sources.cmake
index db4b200fa99597af4f96432e35e80af387ae881a..6cf9c6f4663c4307ee6c9350941cbe6e2f2b5a2c 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -41,6 +41,9 @@ set(vkcv_sources
 		
 		${vkcv_source}/vkcv/BufferManager.hpp
 		${vkcv_source}/vkcv/BufferManager.cpp
+		
+		${vkcv_include}/vkcv/ImageConfig.hpp
+		${vkcv_source}/vkcv/ImageConfig.cpp
 
 		${vkcv_include}/vkcv/Image.hpp
 		${vkcv_source}/vkcv/Image.cpp
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 1a4ce7c8ca203661d6bb21fdfbde0beb8ac02b12..1cec2a7149ab17de76f390944e27941c317cd6e6 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -21,6 +21,7 @@
 #include "EventFunctionTypes.hpp"
 #include "GraphicsPipelineConfig.hpp"
 #include "Handles.hpp"
+#include "ImageConfig.hpp"
 #include "PassConfig.hpp"
 #include "PushConstants.hpp"
 #include "Result.hpp"
@@ -318,24 +319,17 @@ namespace vkcv {
 					  SamplerBorderColor borderColor = SamplerBorderColor::INT_ZERO_OPAQUE);
 
 		/**
-		 * Creates an #Image with a given format, width, height, depth
-		 * and a lot more optional parameters.
+		 * Creates an #Image with a given format, configuration
+		 * and whether a mipchain should be created.
 		 *
 		 * @param[in] format Image format
-		 * @param[in] width Image width
-		 * @param[in] height Image height
-		 * @param[in] depth Image depth
+		 * @param[in] config Image configuration
 		 * @param[in] createMipChain Flag to create a mip chain
-		 * @param[in] supportStorage Flag whether support storage
-		 * @param[in] supportColorAttachment Flag whether attachment is supported
-		 * @param[in] multisampling Multisampling
 		 * @return Image handle
 		 */
-		[[nodiscard]] ImageHandle createImage(vk::Format format, uint32_t width, uint32_t height,
-											  uint32_t depth = 1, bool createMipChain = false,
-											  bool supportStorage = false,
-											  bool supportColorAttachment = false,
-											  Multisampling multisampling = Multisampling::None);
+		[[nodiscard]] ImageHandle createImage(vk::Format format,
+											  const ImageConfig& config,
+											  bool createMipChain = false);
 
 		/**
 		 * @brief Fills the image with given data of a specified size
@@ -344,8 +338,14 @@ namespace vkcv {
 		 * @param[in] image Image handle
 		 * @param[in] data Image data pointer
 		 * @param[in] size Size of data
-		 */
-		void fillImage(const ImageHandle &image, const void* data, size_t size);
+		 * @param[in] firstLayer First image layer
+		 * @param[in] layerCount Image layer count
+		 */
+		void fillImage(const ImageHandle &image,
+					   const void* data,
+					   size_t size,
+					   uint32_t firstLayer,
+					   uint32_t layerCount);
 
 		/**
 		 * @brief Switches the images layout synchronously if possible.
diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index 69d4bc07e7f40af3d5d2802dd06576f166329427..c9c91db25fb4398439d609247d71ba6be3524464 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -10,6 +10,7 @@
 #include "BufferTypes.hpp"
 #include "Core.hpp"
 #include "Handles.hpp"
+#include "ImageConfig.hpp"
 #include "Multisampling.hpp"
 
 namespace vkcv {
@@ -111,6 +112,17 @@ namespace vkcv {
 		 * the actual number of copied bytes is min(size, imageDataSize)
 		 */
 		void fill(const void* data, size_t size = SIZE_MAX);
+		
+		/**
+		 * @brief Fills a specific image layer with data of a given
+		 * size in bytes.
+		 *
+		 * @param[in] layer Image layer destination
+		 * @param[in] data Pointer to the source data
+		 * @param[in] size Lower limit of the data size to copy in bytes,
+		 * the actual number of copied bytes is min(size, imageDataSize)
+		 */
+		void fillLayer(uint32_t layer, const void* data, size_t size = SIZE_MAX);
 
 		/**
 		 * @brief Records mip chain generation to command stream,
@@ -131,5 +143,10 @@ namespace vkcv {
 				bool createMipChain = false, bool supportStorage = false,
 				bool supportColorAttachment = false,
 				Multisampling multisampling = Multisampling::None);
+	
+	Image image(Core &core,
+				vk::Format format,
+				const ImageConfig &config,
+				bool createMipChain = false);
 
 } // namespace vkcv
diff --git a/include/vkcv/ImageConfig.hpp b/include/vkcv/ImageConfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..21e9f6d05ac254e90e29d670304df961099cecef
--- /dev/null
+++ b/include/vkcv/ImageConfig.hpp
@@ -0,0 +1,160 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/ImageConfig.hpp
+ * @brief Structure for image configuration.
+ */
+
+#include "Multisampling.hpp"
+
+namespace vkcv {
+	
+	struct ImageConfig {
+	private:
+		uint32_t m_width;
+		uint32_t m_height;
+		uint32_t m_depth;
+		
+		bool m_supportStorage;
+		bool m_supportColorAttachment;
+		bool m_cubeMapImage;
+		
+		Multisampling m_msaa;
+		
+	public:
+		/**
+		 * Constructor of the image configuration by
+		 * a given resolution.
+		 *
+		 * @param[in] width Image width
+		 * @param[in] height Image height
+		 * @param[in] depth Image depth
+		 */
+		ImageConfig(uint32_t width,
+					uint32_t height,
+					uint32_t depth = 1);
+	
+		ImageConfig(const ImageConfig &other) = default;
+		ImageConfig(ImageConfig&& other) = default;
+
+		~ImageConfig() = default;
+
+		ImageConfig& operator=(const ImageConfig &other) = default;
+		ImageConfig& operator=(ImageConfig&& other) = default;
+		
+		/**
+		 * Return the configured width of the image.
+		 *
+		 * @return Image width
+		 */
+		[[nodiscard]]
+		uint32_t getWidth() const;
+		
+		/**
+		 * Set configured width of the image.
+		 *
+		 * @param[in] width Image width
+		 */
+		void setWidth(uint32_t width);
+		
+		/**
+		 * Return the configured height of the image.
+		 *
+		 * @return Image height
+		 */
+		[[nodiscard]]
+		uint32_t getHeight() const;
+		
+		/**
+		 * Set configured height of the image.
+		 *
+		 * @param[in] height Image height
+		 */
+		void setHeight(uint32_t height);
+		
+		/**
+		 * Return the configured depth of the image.
+		 *
+		 * @return Image depth
+		 */
+		[[nodiscard]]
+		uint32_t getDepth() const;
+		
+		/**
+		 * Set configured depth of the image.
+		 *
+		 * @param[in] depth Image depth
+		 */
+		void setDepth(uint32_t depth);
+		
+		/**
+		 * Return whether the image is configured to
+		 * support storage operations.
+		 *
+		 * @return True, if it supports storage, otherwise false
+		 */
+		[[nodiscard]]
+		bool isSupportingStorage() const;
+		
+		/**
+		 * Set whether the image is configured to
+		 * support storage operations.
+		 *
+		 * @param[in] supportStorage Support storage
+		 */
+		void setSupportingStorage(bool supportStorage);
+		
+		/**
+		 * Return whether the image is configured to
+		 * support being used as color attachment.
+		 *
+		 * @return True, if it supports color attachment, otherwise false
+		 */
+		[[nodiscard]]
+		bool isSupportingColorAttachment() const;
+		
+		/**
+		 * Set whether the image is configured to
+		 * support being used as color attachment.
+		 *
+		 * @param[in] supportColorAttachment Support color attachment
+		 */
+		void setSupportingColorAttachment(bool supportColorAttachment);
+		
+		/**
+		 * Return whether the image is configured to
+		 * be a cube map.
+		 *
+		 * @return True, if the image is a cube map, otherwise false
+		 */
+		[[nodiscard]]
+		bool isCubeMapImage() const;
+		
+		/**
+		 * Set whether the image is configured to
+		 * be a cube map.
+		 *
+		 * @param[in] cubeMapImage Is cube map image
+		 */
+		void setCubeMapImage(bool cubeMapImage);
+		
+		/**
+		 * Return type of multisampling the image
+		 * is configured to use.
+		 *
+		 * @return Multisampling
+		 */
+		[[nodiscard]]
+		Multisampling getMultisampling() const;
+		
+		/**
+		 * Set the multisampling of the image
+		 * configuration.
+		 *
+		 * @param[in] msaa Multisampling
+		 */
+		void setMultisampling(Multisampling msaa);
+		
+	};
+	
+}
diff --git a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
index f31596ae80c86a432f62a50621de949961e83d56..133b380095abc6c729ef94da675cc9f394475ab4 100644
--- a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
+++ b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
@@ -294,7 +294,7 @@ namespace vkcv::effects {
 					cmdStream,
 					m_downsamplePipeline,
 					dispatch,
-					{ DescriptorSetUsage(0, mipDescriptorSets[mipLevel]) },
+					{ useDescriptorSet(0, mipDescriptorSets[mipLevel]) },
 					PushConstants(0)
 			);
 			
@@ -342,7 +342,7 @@ namespace vkcv::effects {
 					cmdStream,
 					m_upsamplePipeline,
 					dispatch,
-					{ DescriptorSetUsage(0, mipDescriptorSets[mipLevel - 1]) },
+					{ useDescriptorSet(0, mipDescriptorSets[mipLevel - 1]) },
 					PushConstants(0)
 			);
 			
@@ -388,7 +388,7 @@ namespace vkcv::effects {
 				cmdStream,
 				m_lensFlaresPipeline,
 				dispatch,
-				{ DescriptorSetUsage(0, m_lensFlaresDescriptorSet) },
+				{ useDescriptorSet(0, m_lensFlaresDescriptorSet) },
 				PushConstants(0)
 		);
 	}
@@ -455,7 +455,7 @@ namespace vkcv::effects {
 				cmdStream,
 				m_compositePipeline,
 				dispatch,
-				{ DescriptorSetUsage(0, m_compositeDescriptorSet) },
+				{ useDescriptorSet(0, m_compositeDescriptorSet) },
 				m_advanced? pushConstants : PushConstants(0)
 		);
 	}
@@ -475,15 +475,15 @@ namespace vkcv::effects {
 				static_cast<float>(m_core.getImageHeight(output)) * 0.5f
 		));
 		
+		vkcv::ImageConfig imageConfig (halfWidth, halfHeight);
+		imageConfig.setSupportingStorage(true);
+		
 		if ((!m_blurImage) ||
 			(halfWidth != m_core.getImageWidth(m_blurImage)) ||
 			(halfHeight != m_core.getImageHeight(m_blurImage))) {
 			m_blurImage = m_core.createImage(
 					m_core.getImageFormat(output),
-					halfWidth,
-					halfHeight,
-					1,
-					true,
+					imageConfig,
 					true
 			);
 			
@@ -510,10 +510,7 @@ namespace vkcv::effects {
 			(halfHeight != m_core.getImageHeight(m_flaresImage))) {
 			m_flaresImage = m_core.createImage(
 					m_core.getImageFormat(output),
-					halfWidth,
-					halfHeight,
-					1,
-					true,
+					imageConfig,
 					true
 			);
 			
diff --git a/modules/geometry/include/vkcv/geometry/Geometry.hpp b/modules/geometry/include/vkcv/geometry/Geometry.hpp
index 7924dfa570c510bddab6e0003a17ed7602290d0d..cf2b6eb5132e46883a81dc6d45eae18f51f48bea 100644
--- a/modules/geometry/include/vkcv/geometry/Geometry.hpp
+++ b/modules/geometry/include/vkcv/geometry/Geometry.hpp
@@ -24,6 +24,19 @@ namespace vkcv::geometry {
 		 */
 		glm::vec3 m_position;
 		
+	protected:
+		/**
+		 * Generate tangent from positions and uv-coordinates
+		 * for a given triangle.
+		 *
+		 * @param[in] positions Array of positions
+		 * @param[in] uvs Array of uv-coordinates
+		 * @return Calculated tangent
+		 */
+		[[nodiscard]]
+		glm::vec3 generateTangent(const std::array<glm::vec3, 3>& positions,
+								  const  std::array<glm::vec2, 3>& uvs) const;
+		
 	public:
 		/**
 		 * Constructor creating geometry by a given position
diff --git a/modules/geometry/src/vkcv/geometry/Cuboid.cpp b/modules/geometry/src/vkcv/geometry/Cuboid.cpp
index 0256261e4bc2c4dc73bc51721718e4e986905d15..73a701b2826a7694201304b029c91af3e7191e5c 100644
--- a/modules/geometry/src/vkcv/geometry/Cuboid.cpp
+++ b/modules/geometry/src/vkcv/geometry/Cuboid.cpp
@@ -41,12 +41,12 @@ namespace vkcv::geometry {
 				+0.5f, +0.5f, +0.5f,
 				
 				-0.5f, -0.5f, -0.5f,
-				-0.5f, -0.5f, +0.5f,
 				+0.5f, -0.5f, -0.5f,
+				-0.5f, -0.5f, +0.5f,
 				+0.5f, -0.5f, +0.5f,
 				-0.5f, +0.5f, -0.5f,
-				-0.5f, +0.5f, +0.5f,
 				+0.5f, +0.5f, -0.5f,
+				-0.5f, +0.5f, +0.5f,
 				+0.5f, +0.5f, +0.5f,
 				
 				-0.5f, -0.5f, -0.5f,
@@ -134,10 +134,73 @@ namespace vkcv::geometry {
 				20, 23, 21
 		};
 		
+		std::vector<glm::vec3> cuboidTangents;
+		cuboidTangents.resize(24, glm::vec3(0.0f));
+		
+		std::vector<size_t> cuboidTangentWeights;
+		cuboidTangentWeights.resize(cuboidTangents.size(), 0);
+		
+		for (size_t i = 0; i < cuboidIndices.size(); i += 3) {
+			const auto index0 = cuboidIndices[i + 0];
+			const auto index1 = cuboidIndices[i + 1];
+			const auto index2 = cuboidIndices[i + 2];
+			
+			const std::array<glm::vec3, 3> positions = {
+					glm::vec3(
+							cuboidPositions[index0 * 3 + 0],
+							cuboidPositions[index0 * 3 + 1],
+							cuboidPositions[index0 * 3 + 2]
+					),
+					glm::vec3(
+							cuboidPositions[index1 * 3 + 0],
+							cuboidPositions[index1 * 3 + 1],
+							cuboidPositions[index1 * 3 + 2]
+					),
+					glm::vec3(
+							cuboidPositions[index2 * 3 + 0],
+							cuboidPositions[index2 * 3 + 1],
+							cuboidPositions[index2 * 3 + 2]
+					)
+			};
+			
+			const std::array<glm::vec2, 3> uvs = {
+					glm::vec2(
+							cuboidUVCoords[index0 * 3 + 0],
+							cuboidUVCoords[index0 * 3 + 1]
+					),
+					glm::vec2(
+							cuboidUVCoords[index1 * 3 + 0],
+							cuboidUVCoords[index1 * 3 + 1]
+					),
+					glm::vec2(
+							cuboidUVCoords[index2 * 3 + 0],
+							cuboidUVCoords[index2 * 3 + 1]
+					)
+			};
+			
+			const glm::vec3 tangent = generateTangent(positions, uvs);
+			
+			cuboidTangents[index0] += tangent;
+			cuboidTangents[index1] += tangent;
+			cuboidTangents[index2] += tangent;
+			
+			cuboidTangentWeights[index0]++;
+			cuboidTangentWeights[index1]++;
+			cuboidTangentWeights[index2]++;
+		}
+		
+		for (size_t i = 0; i < cuboidTangents.size(); i++) {
+			if (cuboidTangentWeights[i] <= 0) {
+				continue;
+			}
+			
+			cuboidTangents[i] /= cuboidTangentWeights[i];
+		}
+		
 		const auto& position = getPosition();
 		const auto& size = getSize();
 		
-		for (size_t i = 0; i < 8; i++) {
+		for (size_t i = 0; i < 24; i++) {
 			cuboidPositions[i * 3 + 0] = cuboidPositions[i * 3 + 0] * size.x + position.x;
 			cuboidPositions[i * 3 + 1] = cuboidPositions[i * 3 + 1] * size.y + position.y;
 			cuboidPositions[i * 3 + 2] = cuboidPositions[i * 3 + 2] * size.z + position.z;
@@ -152,10 +215,14 @@ namespace vkcv::geometry {
 		auto uvBuffer = buffer<float>(core, BufferType::VERTEX, cuboidUVCoords.size());
 		uvBuffer.fill(cuboidUVCoords);
 		
+		auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, cuboidTangents.size());
+		tangentBuffer.fill(cuboidTangents);
+		
 		VertexData data ({
 			vkcv::vertexBufferBinding(positionBuffer.getHandle()),
 			vkcv::vertexBufferBinding(normalBuffer.getHandle()),
-			vkcv::vertexBufferBinding(uvBuffer.getHandle())
+			vkcv::vertexBufferBinding(uvBuffer.getHandle()),
+			vkcv::vertexBufferBinding(tangentBuffer.getHandle())
 		});
 		
 		const auto& featureManager = core.getContext().getFeatureManager();
@@ -174,7 +241,8 @@ namespace vkcv::geometry {
 			data.setIndexBuffer(indexBuffer.getHandle(), IndexBitCount::Bit8);
 			data.setCount(indexBuffer.getCount());
 		} else {
-			std::array<uint16_t, cuboidIndices.size()> cuboidIndices16;
+			std::vector<uint16_t> cuboidIndices16;
+			cuboidIndices16.resize(cuboidIndices.size());
 			
 			for (size_t i = 0; i < cuboidIndices16.size(); i++) {
 				cuboidIndices16[i] = static_cast<uint16_t>(cuboidIndices[i]);
diff --git a/modules/geometry/src/vkcv/geometry/Cylinder.cpp b/modules/geometry/src/vkcv/geometry/Cylinder.cpp
index cffdaf47fb44c916032dda8f62651c7c3d6962c6..67b62f694714e86877879f25e59d086627e95731 100644
--- a/modules/geometry/src/vkcv/geometry/Cylinder.cpp
+++ b/modules/geometry/src/vkcv/geometry/Cylinder.cpp
@@ -73,7 +73,7 @@ namespace vkcv::geometry {
 		size_t offset = 0;
 		
 		for (i = 0; i < resolution; i++) {
-			u = static_cast<float>(i) / static_cast<float>(resolution);
+			u = static_cast<float>(i) / static_cast<float>(resolution - 1);
 			phi = 2.0f * std::numbers::pi_v<float> * u;
 			
 			sinPhi = std::sin(phi);
@@ -95,18 +95,70 @@ namespace vkcv::geometry {
 				cylinderNormals.push_back(glm::vec3(sinPhi, 0.0f, cosPhi));
 				cylinderUVCoords.push_back(glm::vec2(u, v));
 				
-				cylinderIndices.push_back(2 + (offset + j * 2) % (vertexCount - 2));
-				cylinderIndices.push_back(2 + (offset + j * 2 + 4) % (vertexCount - 2));
-				cylinderIndices.push_back(j);
-				
-				cylinderIndices.push_back(2 + (offset + j * 2 + 1) % (vertexCount - 2));
-				cylinderIndices.push_back(2 + (offset + j * 2 + 5) % (vertexCount - 2));
-				cylinderIndices.push_back(2 + (offset + j * 2 + 3) % (vertexCount - 2));
+				if (j == 1) {
+					cylinderIndices.push_back(2 + (offset + j * 2) % (vertexCount - 2));
+					cylinderIndices.push_back(2 + (offset + j * 2 + 4) % (vertexCount - 2));
+					cylinderIndices.push_back(j);
+					
+					cylinderIndices.push_back(2 + (offset + j * 2 + 5) % (vertexCount - 2));
+					cylinderIndices.push_back(2 + (offset + j * 2 + 1) % (vertexCount - 2));
+					cylinderIndices.push_back(2 + (offset + j * 2 + 3) % (vertexCount - 2));
+				} else {
+					cylinderIndices.push_back(2 + (offset + j * 2 + 4) % (vertexCount - 2));
+					cylinderIndices.push_back(2 + (offset + j * 2) % (vertexCount - 2));
+					cylinderIndices.push_back(j);
+					
+					cylinderIndices.push_back(2 + (offset + j * 2 + 1) % (vertexCount - 2));
+					cylinderIndices.push_back(2 + (offset + j * 2 + 5) % (vertexCount - 2));
+					cylinderIndices.push_back(2 + (offset + j * 2 + 3) % (vertexCount - 2));
+				}
 			}
 			
 			offset += 4;
 		}
 		
+		std::vector<glm::vec3> cylinderTangents;
+		cylinderTangents.resize(cylinderVertices.size(), glm::vec3(0.0f));
+		
+		std::vector<size_t> cylinderTangentWeights;
+		cylinderTangentWeights.resize(cylinderTangents.size(), 0);
+		
+		for (i = 0; i < cylinderIndices.size(); i += 3) {
+			const auto index0 = cylinderIndices[i + 0];
+			const auto index1 = cylinderIndices[i + 1];
+			const auto index2 = cylinderIndices[i + 2];
+			
+			const std::array<glm::vec3, 3> positions = {
+					cylinderVertices[index0],
+					cylinderVertices[index1],
+					cylinderVertices[index2]
+			};
+			
+			const std::array<glm::vec2, 3> uvs = {
+					cylinderUVCoords[index0],
+					cylinderUVCoords[index1],
+					cylinderUVCoords[index2]
+			};
+			
+			const glm::vec3 tangent = generateTangent(positions, uvs);
+			
+			cylinderTangents[index0] += tangent;
+			cylinderTangents[index1] += tangent;
+			cylinderTangents[index2] += tangent;
+			
+			cylinderTangentWeights[index0]++;
+			cylinderTangentWeights[index1]++;
+			cylinderTangentWeights[index2]++;
+		}
+		
+		for (i = 0; i < cylinderTangents.size(); i++) {
+			if (cylinderTangentWeights[i] <= 0) {
+				continue;
+			}
+			
+			cylinderTangents[i] /= cylinderTangentWeights[i];
+		}
+		
 		auto positionBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, cylinderVertices.size());
 		positionBuffer.fill(cylinderVertices);
 		
@@ -116,13 +168,17 @@ namespace vkcv::geometry {
 		auto uvBuffer = buffer<glm::vec2>(core, BufferType::VERTEX, cylinderUVCoords.size());
 		uvBuffer.fill(cylinderUVCoords);
 		
+		auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, cylinderTangents.size());
+		tangentBuffer.fill(cylinderTangents);
+		
 		auto indexBuffer = buffer<uint32_t>(core, BufferType::INDEX, cylinderIndices.size());
 		indexBuffer.fill(cylinderIndices);
 		
 		VertexData data ({
 			vkcv::vertexBufferBinding(positionBuffer.getHandle()),
 			vkcv::vertexBufferBinding(normalBuffer.getHandle()),
-			vkcv::vertexBufferBinding(uvBuffer.getHandle())
+			vkcv::vertexBufferBinding(uvBuffer.getHandle()),
+			vkcv::vertexBufferBinding(tangentBuffer.getHandle())
 		});
 		
 		data.setIndexBuffer(indexBuffer.getHandle(), IndexBitCount::Bit32);
diff --git a/modules/geometry/src/vkcv/geometry/Geometry.cpp b/modules/geometry/src/vkcv/geometry/Geometry.cpp
index ccdc45fc1be7301691f1ec14e705a0f68d83fe88..feaa84a04f2e180b08fd92977d6494ac997af6b2 100644
--- a/modules/geometry/src/vkcv/geometry/Geometry.cpp
+++ b/modules/geometry/src/vkcv/geometry/Geometry.cpp
@@ -14,4 +14,18 @@ namespace vkcv::geometry {
 		m_position = position;
 	}
 	
+	glm::vec3 Geometry::generateTangent(const std::array<glm::vec3, 3>& positions,
+										const  std::array<glm::vec2, 3>& uvs) const {
+		auto delta1 = positions[1] - positions[0];
+		auto delta2 = positions[2] - positions[0];
+		
+		auto deltaUV1 = uvs[1] - uvs[0];
+		auto deltaUV2 = uvs[2] - uvs[0];
+		
+		return glm::normalize(glm::vec3(
+				delta1 * deltaUV2.y -
+				delta2 * deltaUV1.y
+		));
+	}
+	
 }
diff --git a/modules/geometry/src/vkcv/geometry/Sphere.cpp b/modules/geometry/src/vkcv/geometry/Sphere.cpp
index e7ed47dc13265dd83d186ad88834950b8c08b693..d516a1dad4e11651e4ad6d6c056dcf9442d29f18 100644
--- a/modules/geometry/src/vkcv/geometry/Sphere.cpp
+++ b/modules/geometry/src/vkcv/geometry/Sphere.cpp
@@ -79,6 +79,48 @@ namespace vkcv::geometry {
 			offset += (resolution + 1);
 		}
 		
+		std::vector<glm::vec3> sphereTangents;
+		sphereTangents.resize(sphereVertices.size(), glm::vec3(0.0f));
+		
+		std::vector<size_t> sphereTangentWeights;
+		sphereTangentWeights.resize(sphereTangents.size(), 0);
+		
+		for (i = 0; i < sphereIndices.size(); i += 3) {
+			const auto index0 = sphereIndices[i + 0];
+			const auto index1 = sphereIndices[i + 1];
+			const auto index2 = sphereIndices[i + 2];
+			
+			const std::array<glm::vec3, 3> positions = {
+					sphereVertices[index0],
+					sphereVertices[index1],
+					sphereVertices[index2]
+			};
+			
+			const std::array<glm::vec2, 3> uvs = {
+					sphereUVCoords[index0],
+					sphereUVCoords[index1],
+					sphereUVCoords[index2]
+			};
+			
+			const glm::vec3 tangent = generateTangent(positions, uvs);
+			
+			sphereTangents[index0] += tangent;
+			sphereTangents[index1] += tangent;
+			sphereTangents[index2] += tangent;
+			
+			sphereTangentWeights[index0]++;
+			sphereTangentWeights[index1]++;
+			sphereTangentWeights[index2]++;
+		}
+		
+		for (i = 0; i < sphereTangents.size(); i++) {
+			if (sphereTangentWeights[i] <= 0) {
+				continue;
+			}
+			
+			sphereTangents[i] /= sphereTangentWeights[i];
+		}
+		
 		auto positionBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, sphereVertices.size());
 		positionBuffer.fill(sphereVertices);
 		
@@ -88,13 +130,17 @@ namespace vkcv::geometry {
 		auto uvBuffer = buffer<glm::vec2>(core, BufferType::VERTEX, sphereUVCoords.size());
 		uvBuffer.fill(sphereUVCoords);
 		
+		auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, sphereTangents.size());
+		tangentBuffer.fill(sphereTangents);
+		
 		auto indexBuffer = buffer<uint32_t>(core, BufferType::INDEX, sphereIndices.size());
 		indexBuffer.fill(sphereIndices);
 		
 		VertexData data ({
 			vkcv::vertexBufferBinding(positionBuffer.getHandle()),
 			vkcv::vertexBufferBinding(normalBuffer.getHandle()),
-			vkcv::vertexBufferBinding(uvBuffer.getHandle())
+			vkcv::vertexBufferBinding(uvBuffer.getHandle()),
+			vkcv::vertexBufferBinding(tangentBuffer.getHandle())
 		});
 		
 		data.setIndexBuffer(indexBuffer.getHandle(), IndexBitCount::Bit32);
diff --git a/modules/geometry/src/vkcv/geometry/Teapot.cpp b/modules/geometry/src/vkcv/geometry/Teapot.cpp
index d1c7465ab646ba4ffcce0f356e5d4935b4e610e1..7becdef5941769c79da0b0b17a45ec3e5d0a1cb6 100644
--- a/modules/geometry/src/vkcv/geometry/Teapot.cpp
+++ b/modules/geometry/src/vkcv/geometry/Teapot.cpp
@@ -14846,6 +14846,69 @@ namespace vkcv::geometry {
 				3859, 3860, 3870, 3870, 3860, 3871
 		};
 		
+		std::vector<glm::vec3> teapotTangents;
+		teapotTangents.resize(teapotVertices.size() / 3, glm::vec3(0.0f));
+		
+		std::vector<size_t> teapotTangentWeights;
+		teapotTangentWeights.resize(teapotTangents.size(), 0);
+		
+		for (size_t i = 0; i < teapotIndices.size(); i += 3) {
+			const auto index0 = teapotIndices[i + 0];
+			const auto index1 = teapotIndices[i + 1];
+			const auto index2 = teapotIndices[i + 2];
+			
+			const std::array<glm::vec3, 3> positions = {
+					glm::vec3(
+							teapotVertices[index0 * 3 + 0],
+							teapotVertices[index0 * 3 + 1],
+							teapotVertices[index0 * 3 + 2]
+					),
+					glm::vec3(
+							teapotVertices[index1 * 3 + 0],
+							teapotVertices[index1 * 3 + 1],
+							teapotVertices[index1 * 3 + 2]
+					),
+					glm::vec3(
+							teapotVertices[index2 * 3 + 0],
+							teapotVertices[index2 * 3 + 1],
+							teapotVertices[index2 * 3 + 2]
+					)
+			};
+			
+			const std::array<glm::vec2, 3> uvs = {
+					glm::vec2(
+							teapotUVCoords[index0 * 3 + 0],
+							teapotUVCoords[index0 * 3 + 1]
+					),
+					glm::vec2(
+							teapotUVCoords[index1 * 3 + 0],
+							teapotUVCoords[index1 * 3 + 1]
+					),
+					glm::vec2(
+							teapotUVCoords[index2 * 3 + 0],
+							teapotUVCoords[index2 * 3 + 1]
+					)
+			};
+			
+			const glm::vec3 tangent = generateTangent(positions, uvs);
+			
+			teapotTangents[index0] += tangent;
+			teapotTangents[index1] += tangent;
+			teapotTangents[index2] += tangent;
+			
+			teapotTangentWeights[index0]++;
+			teapotTangentWeights[index1]++;
+			teapotTangentWeights[index2]++;
+		}
+		
+		for (size_t i = 0; i < teapotTangents.size(); i++) {
+			if (teapotTangentWeights[i] <= 0) {
+				continue;
+			}
+			
+			teapotTangents[i] /= teapotTangentWeights[i];
+		}
+		
 		const auto& position = getPosition();
 		const auto scale = getScale();
 		
@@ -14864,13 +14927,17 @@ namespace vkcv::geometry {
 		auto uvBuffer = buffer<float>(core, BufferType::VERTEX, teapotUVCoords.size());
 		uvBuffer.fill(teapotUVCoords);
 		
+		auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, teapotTangents.size());
+		tangentBuffer.fill(teapotTangents);
+		
 		auto indexBuffer = buffer<uint16_t>(core, BufferType::INDEX, teapotIndices.size());
 		indexBuffer.fill(teapotIndices);
 		
 		VertexData data ({
 			vkcv::vertexBufferBinding(positionBuffer.getHandle()),
 			vkcv::vertexBufferBinding(normalBuffer.getHandle()),
-			vkcv::vertexBufferBinding(uvBuffer.getHandle())
+			vkcv::vertexBufferBinding(uvBuffer.getHandle()),
+			vkcv::vertexBufferBinding(tangentBuffer.getHandle())
 		});
 		
 		data.setIndexBuffer(indexBuffer.getHandle());
diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
index d9ca8d9f0429960f1d43c074fb58ac6d79f6e677..bacb4ce0814ff0caec0c3d4d353ecf5019e0eda4 100644
--- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
@@ -274,11 +274,12 @@ namespace vkcv::upscaling {
 		if ((!m_intermediateImage) ||
 			(outputWidth != m_core.getImageWidth(m_intermediateImage)) ||
 			(outputHeight != m_core.getImageHeight(m_intermediateImage))) {
+			ImageConfig imageConfig (outputWidth, outputHeight);
+			imageConfig.setSupportingStorage(true);
+			
 			m_intermediateImage = m_core.createImage(
 					m_core.getImageFormat(output),
-					outputWidth, outputHeight,1,
-					false,
-					true
+					imageConfig
 			);
 			
 			m_core.prepareImageForStorage(cmdStream, m_intermediateImage);
@@ -333,7 +334,7 @@ namespace vkcv::upscaling {
 					cmdStream,
 					m_easuPipeline,
 					dispatch,
-					{vkcv::useDescriptorSet(0, m_easuDescriptorSet, { 0 })},
+					{ useDescriptorSet(0, m_easuDescriptorSet, { 0 }) },
 					PushConstants(0)
 			);
 			
@@ -353,7 +354,7 @@ namespace vkcv::upscaling {
 					cmdStream,
 					m_rcasPipeline,
 					dispatch,
-					{vkcv::useDescriptorSet(0,m_rcasDescriptorSet, { 0 })},
+					{ useDescriptorSet(0,m_rcasDescriptorSet, { 0 }) },
 					PushConstants(0)
 			);
 			
@@ -371,7 +372,7 @@ namespace vkcv::upscaling {
 					cmdStream,
 					m_easuPipeline,
 					dispatch,
-					{vkcv::useDescriptorSet(0, m_easuDescriptorSet, { 0 })},
+					{ useDescriptorSet(0, m_easuDescriptorSet, { 0 }) },
 					PushConstants(0)
 			);
 		}
diff --git a/projects/bindless_textures/resources/Szene/Szene.bin b/projects/bindless_textures/resources/Szene/Szene.bin
deleted file mode 100644
index c87d27637516b0bbf864251dd162773f5cc53e06..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/Szene/Szene.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d
-size 70328
diff --git a/projects/bindless_textures/resources/Szene/Szene.gltf b/projects/bindless_textures/resources/Szene/Szene.gltf
deleted file mode 100644
index e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/Szene/Szene.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a
-size 7421
diff --git a/projects/bindless_textures/resources/Szene/boards2_vcyc.jpg b/projects/bindless_textures/resources/Szene/boards2_vcyc.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/Szene/boards2_vcyc.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/bindless_textures/resources/Szene/boards2_vcyc_jpg.jpg b/projects/bindless_textures/resources/Szene/boards2_vcyc_jpg.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/Szene/boards2_vcyc_jpg.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/bindless_textures/resources/triangle/Triangle.bin b/projects/bindless_textures/resources/triangle/Triangle.bin
deleted file mode 100644
index 57f26ad96592b64377e6aa93823d96a94e6c5022..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/triangle/Triangle.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959
-size 104
diff --git a/projects/bindless_textures/resources/triangle/Triangle.blend b/projects/bindless_textures/resources/triangle/Triangle.blend
deleted file mode 100644
index 2421dc5e1bb029d73a9ec09cc4530c5196851fd7..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/triangle/Triangle.blend
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477
-size 758144
diff --git a/projects/bindless_textures/resources/triangle/Triangle.glb b/projects/bindless_textures/resources/triangle/Triangle.glb
deleted file mode 100644
index 4148620cd6af0dadbc791aa1c52bb5431a40884b..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/triangle/Triangle.glb
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227
-size 980
diff --git a/projects/bindless_textures/resources/triangle/Triangle.gltf b/projects/bindless_textures/resources/triangle/Triangle.gltf
deleted file mode 100644
index a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6..0000000000000000000000000000000000000000
--- a/projects/bindless_textures/resources/triangle/Triangle.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec
-size 2202
diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp
index 42af6910cd0102f01df29d470269882ebd1cb0d0..37d0c68b34eeb45560907fc43d049d61f0285c17 100644
--- a/projects/bindless_textures/src/main.cpp
+++ b/projects/bindless_textures/src/main.cpp
@@ -185,9 +185,7 @@ int main(int argc, const char** argv) {
 	vkcv::SamplerHandle sampler = vkcv::samplerLinear(core);
 	vkcv::DescriptorWrites setWrites;
 	
-	for(uint32_t i = 0; i < 6; i++)
-	{
-		
+	for(uint32_t i = 0; i < 6; i++) {
 		setWrites.writeSampledImage(
 				1,
 				texturesArray[i].getHandle(),
@@ -225,8 +223,7 @@ int main(int argc, const char** argv) {
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
 			depthBuffer = core.createImage(
 					vk::Format::eD32Sfloat,
-					swapchainWidth,
-					swapchainHeight
+					vkcv::ImageConfig(swapchainWidth, swapchainHeight)
 			);
 		}
 
diff --git a/projects/fire_works/src/main.cpp b/projects/fire_works/src/main.cpp
index 472b781905fc446c592bef74bc0262792e8c3988..0166da4b5788990a05f85da5a3b41c0e206d956a 100644
--- a/projects/fire_works/src/main.cpp
+++ b/projects/fire_works/src/main.cpp
@@ -331,11 +331,17 @@ int main(int argc, const char **argv) {
 	
 	std::array<vkcv::ImageHandle, 4> colorBuffers;
 	for (size_t i = 0; i < colorBuffers.size(); i++) {
+		vkcv::ImageConfig colorBufferConfig (
+				swapchainExtent.width,
+				swapchainExtent.height
+		);
+		
+		colorBufferConfig.setSupportingStorage(true);
+		colorBufferConfig.setSupportingColorAttachment(true);
+		
 		colorBuffers[i] = core.createImage(
 				colorFormat,
-				swapchainExtent.width,
-				swapchainExtent.height,
-				1, false, true, true
+				colorBufferConfig
 		);
 	}
 	
@@ -700,65 +706,60 @@ int main(int argc, const char **argv) {
 	std::vector<uint32_t> zeroVoxel;
 	zeroVoxel.resize(voxelWidth * voxelHeight * voxelDepth, 0);
 	
+	vkcv::ImageConfig voxelImageConfig (
+			voxelWidth,
+			voxelHeight,
+			voxelDepth
+	);
+	
+	voxelImageConfig.setSupportingStorage(true);
+	
 	vkcv::Image voxelRed = vkcv::image(
 			core,
 			vk::Format::eR32Uint,
-			voxelWidth,
-			voxelHeight,
-			voxelDepth,
-			false, true
+			voxelImageConfig
 	);
 	
 	vkcv::Image voxelGreen = vkcv::image(
 			core,
 			vk::Format::eR32Uint,
-			voxelWidth,
-			voxelHeight,
-			voxelDepth,
-			false, true
+			voxelImageConfig
 	);
 	
 	vkcv::Image voxelBlue = vkcv::image(
 			core,
 			vk::Format::eR32Uint,
-			voxelWidth,
-			voxelHeight,
-			voxelDepth,
-			false, true
+			voxelImageConfig
 	);
 	
 	vkcv::Image voxelDensity = vkcv::image(
 			core,
 			vk::Format::eR32Uint,
-			voxelWidth,
-			voxelHeight,
-			voxelDepth,
-			false, true
+			voxelImageConfig
 	);
 	
 	std::array<vkcv::ImageHandle, 2> voxelData {
 		core.createImage(
 			vk::Format::eR16G16B16A16Sfloat,
-			voxelWidth,
-			voxelHeight,
-			voxelDepth,
-			false, true
+			voxelImageConfig
 		),
 		core.createImage(
 			vk::Format::eR16G16B16A16Sfloat,
-			voxelWidth,
-			voxelHeight,
-			voxelDepth,
-			false, true
+			voxelImageConfig
 		)
 	};
 	
+	vkcv::ImageConfig voxelSamplesConfig (
+			voxelWidth,
+			voxelHeight
+	);
+	
+	voxelImageConfig.setSupportingStorage(true);
+	
 	vkcv::Image voxelSamples = vkcv::image(
 			core,
 			colorFormat,
-			voxelWidth,
-			voxelHeight,
-			1, false, true
+			voxelSamplesConfig
    	);
 	
 	vkcv::SamplerHandle voxelSampler = vkcv::samplerLinear(core, true);
@@ -934,14 +935,20 @@ int main(int argc, const char **argv) {
 			continue;
 		}
 	
-		for (size_t i = 0; i < colorBuffers.size(); i++) {
-			if ((core.getImageWidth(colorBuffers[i]) != swapchainWidth) ||
-				(core.getImageHeight(colorBuffers[i]) != swapchainHeight)) {
-				colorBuffers[i] = core.createImage(
+		for (auto& colorBuffer : colorBuffers) {
+			if ((core.getImageWidth(colorBuffer) != swapchainWidth) ||
+				(core.getImageHeight(colorBuffer) != swapchainHeight)) {
+				vkcv::ImageConfig colorBufferConfig (
+						swapchainWidth,
+						swapchainHeight
+				);
+				
+				colorBufferConfig.setSupportingStorage(true);
+				colorBufferConfig.setSupportingColorAttachment(true);
+				
+				colorBuffer = core.createImage(
 					colorFormat,
-					swapchainWidth,
-					swapchainHeight,
-					1, false, true, true
+					colorBufferConfig
 				);
 			}
 		}
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 1350156140e4946599982a5057b2bab0bab276aa..886d6e82ebf0fa68b5242624b001b20f889be22f 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -1,5 +1,4 @@
 #include <iostream>
-#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
 #include <vkcv/Image.hpp>
 #include <vkcv/Pass.hpp>
@@ -117,8 +116,10 @@ int main(int argc, const char** argv) {
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
 			depthBuffer = core.createImage(
 					vk::Format::eD32Sfloat,
-					swapchainWidth,
-					swapchainHeight
+					vkcv::ImageConfig(
+							swapchainWidth,
+							swapchainHeight
+					)
 			);
 		}
 		
diff --git a/projects/first_scene/assets/Cutlery/Cutlery_chrome_BaseColor.png b/projects/first_scene/assets/Cutlery/Cutlery_chrome_BaseColor.png
deleted file mode 100644
index 8258525f22097f2382ec5c26ad0df7eb50718642..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Cutlery_chrome_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0ce87f6407ee40ffa60983587aeb52333d59b4b1c01a53e11f4bb227ba1099d9
-size 109
diff --git a/projects/first_scene/assets/Cutlery/Cutlery_chrome_Normal.png b/projects/first_scene/assets/Cutlery/Cutlery_chrome_Normal.png
deleted file mode 100644
index 620fe7621cf35409ae8c04099dc8bc4bbc04343b..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Cutlery_chrome_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:68a0064d457a6f7994814b07d943deda778754128935689874334300ede6161d
-size 2332064
diff --git a/projects/first_scene/assets/Cutlery/Cutlery_details_BaseColor.png b/projects/first_scene/assets/Cutlery/Cutlery_details_BaseColor.png
deleted file mode 100644
index 5570e88c569036b9d00155ef6113013aa23f2503..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Cutlery_details_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:42c2715635081eb29c4489ce631798b0e9c881460efc0aa63d0e81641a0dcfe9
-size 108
diff --git a/projects/first_scene/assets/Cutlery/Cutlery_details_Normal.png b/projects/first_scene/assets/Cutlery/Cutlery_details_Normal.png
deleted file mode 100644
index d07681f5bfa25c279321c3074e21e4900c5eb0fa..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Cutlery_details_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:15b0133e140899c47ccf35b0f99a7e337e3110ae089f45d27faf9983f3e0a1f7
-size 770758
diff --git a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png b/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png
deleted file mode 100644
index 1845e8a7d586a0d23300ad9d04a82f1d5048fcf5..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ea7c82c0f9e25afa401470df1fb6903f508fa138d21ad30f57a9153b0395b198
-size 521315
diff --git a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png b/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png
deleted file mode 100644
index 1c800c0489693b66d6163bdc2156d453b68ca19b..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:119efbbc020244ff9b7ff16ac9795a6d4b1808d1b90d81d20d2c874d0dc8a924
-size 1693468
diff --git a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png b/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png
deleted file mode 100644
index 36f46ebf25158c78bc26d83860e1c08b2c9e96cf..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:99896468c7d47dd5391d585eecf149f420eca3bfec31923c21fa86c45fe02d0f
-size 108
diff --git a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png b/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png
deleted file mode 100644
index 28c205d4e70867ec58448ca8ba2c2117c611a367..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b31618aa5adce4ad476bec2c03718c5ae097250e784344f2d298b8a74c3bfd46
-size 90
diff --git a/projects/first_scene/assets/Cutlery/Plates_Ceramic_BaseColor.png b/projects/first_scene/assets/Cutlery/Plates_Ceramic_BaseColor.png
deleted file mode 100644
index e0104189a1190c160152101e9e328e81ed89121b..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Plates_Ceramic_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6eff6ccd12d8b39d60ae5ee91edd73d4d7838fcb5d9bc6ff0e671bdf009134e9
-size 109
diff --git a/projects/first_scene/assets/Cutlery/Plates_Ceramic_Normal.png b/projects/first_scene/assets/Cutlery/Plates_Ceramic_Normal.png
deleted file mode 100644
index fa13483d25595ee6b3a00e7fe6ce48aed7b6aaca..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Plates_Ceramic_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fe92c40ff4032fdaf10eeafd943657a0c6e0bfb3f38770f5654aa943a660f421
-size 59419
diff --git a/projects/first_scene/assets/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png b/projects/first_scene/assets/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png
deleted file mode 100644
index b91d0ac62fbeabbef1ed78f1343f919836cbca40..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4ca7d436a68a2a1237aee6e763b2954f01666b21f1dbd46929a322ea277483d2
-size 779227
diff --git a/projects/first_scene/assets/Cutlery/Plates_Details_Normal.png b/projects/first_scene/assets/Cutlery/Plates_Details_Normal.png
deleted file mode 100644
index 6efd967984ee2e68b89de9e471b93396c13ca69a..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/Plates_Details_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b01fc6482054c64d7407b283731e57fce0601a8db28b6781c14fae3c6b30b0fe
-size 504362
diff --git a/projects/first_scene/assets/Cutlery/ToffeeJar_Label_BaseColor.png b/projects/first_scene/assets/Cutlery/ToffeeJar_Label_BaseColor.png
deleted file mode 100644
index d0e0c4f4134cb99d3765d6f078f71efab8861bf1..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/ToffeeJar_Label_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:df138ee68c1d455652d1b9ae3dd03e93fcd2f6a0d8a1f12e3710f39143088674
-size 1593466
diff --git a/projects/first_scene/assets/Cutlery/ToffeeJar_Label_Normal.png b/projects/first_scene/assets/Cutlery/ToffeeJar_Label_Normal.png
deleted file mode 100644
index 9f310653b5211575da3cab2f6deb47ec6826a936..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/ToffeeJar_Label_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6af5da97cbb25d79aea2dde8dd71ecbd495334fe34e99497ba17821be93fd7fd
-size 2696676
diff --git a/projects/first_scene/assets/Cutlery/TransparentGlass_BaseColor.png b/projects/first_scene/assets/Cutlery/TransparentGlass_BaseColor.png
deleted file mode 100644
index 4e4f0fcb312b8f8bd0df965bfe6b8ac62ec5df4d..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/TransparentGlass_BaseColor.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2796affdfdcf6bc805176d9f85505680b5ee52eeec625e9eaeea4f0ff3854883
-size 108
diff --git a/projects/first_scene/assets/Cutlery/TransparentGlass_Normal.png b/projects/first_scene/assets/Cutlery/TransparentGlass_Normal.png
deleted file mode 100644
index 28c205d4e70867ec58448ca8ba2c2117c611a367..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/TransparentGlass_Normal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b31618aa5adce4ad476bec2c03718c5ae097250e784344f2d298b8a74c3bfd46
-size 90
diff --git a/projects/first_scene/assets/Cutlery/cutlerySzene.bin b/projects/first_scene/assets/Cutlery/cutlerySzene.bin
deleted file mode 100644
index ab9a0aa47205e8f3064d2f16a950d4733fb4f472..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/cutlerySzene.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f545b986e0a1ac5bff5d49693a52042aa37878425818f72c69c243da20d1f99d
-size 183324
diff --git a/projects/first_scene/assets/Cutlery/cutlerySzene.glb b/projects/first_scene/assets/Cutlery/cutlerySzene.glb
deleted file mode 100644
index b0c5f345aaaa3f96d7158a0992ee124aae99a69a..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/cutlerySzene.glb
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fb1bad604192ca36222c0ca485ba87b846ecbd11ee8254327e04e3c993b00116
-size 11150396
diff --git a/projects/first_scene/assets/Cutlery/cutlerySzene.gltf b/projects/first_scene/assets/Cutlery/cutlerySzene.gltf
deleted file mode 100644
index 53e339cda4511f3f1a8670b36469e184aac530e2..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Cutlery/cutlerySzene.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c77cd60e2327daca1a01044e45f2c38655f7b781bd07985fc0135328a8a96b57
-size 34312
diff --git a/projects/first_scene/assets/Szene/Szene.bin b/projects/first_scene/assets/Szene/Szene.bin
deleted file mode 100644
index c87d27637516b0bbf864251dd162773f5cc53e06..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Szene/Szene.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d
-size 70328
diff --git a/projects/first_scene/assets/Szene/Szene.gltf b/projects/first_scene/assets/Szene/Szene.gltf
deleted file mode 100644
index e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Szene/Szene.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a
-size 7421
diff --git a/projects/first_scene/assets/Szene/boards2_vcyc.jpg b/projects/first_scene/assets/Szene/boards2_vcyc.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Szene/boards2_vcyc.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/first_scene/assets/Szene/boards2_vcyc_jpg.jpg b/projects/first_scene/assets/Szene/boards2_vcyc_jpg.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/first_scene/assets/Szene/boards2_vcyc_jpg.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 9fb7247ec6da805acd2312c3889c45e4a95a3d48..1d0fd0e989c4b41d0f08feaa7f4c8ae0b8fa3d3c 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -103,8 +103,10 @@ int main(int argc, const char** argv) {
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
 			depthBuffer = core.createImage(
 					vk::Format::eD32Sfloat,
-					swapchainWidth,
-					swapchainHeight
+					vkcv::ImageConfig(
+							swapchainWidth,
+							swapchainHeight
+					)
 			);
 		}
 		
diff --git a/projects/head_demo/src/main.cpp b/projects/head_demo/src/main.cpp
index 1b6533cfdd3726ad8b1901ae5bbb800d5c65b81d..e96f22eb665e681e9d4e1b100ad47fa60df267f3 100644
--- a/projects/head_demo/src/main.cpp
+++ b/projects/head_demo/src/main.cpp
@@ -150,15 +150,23 @@ int main(int argc, const char** argv) {
 	
 	vkcv::ImageHandle depthBuffer = core.createImage(
 			vk::Format::eD32Sfloat,
+			vkcv::ImageConfig(
+					swapchainExtent.width,
+					swapchainExtent.height
+			)
+	);
+	
+	vkcv::ImageConfig colorBufferConfig (
 			swapchainExtent.width,
 			swapchainExtent.height
 	);
 	
+	colorBufferConfig.setSupportingStorage(true);
+	colorBufferConfig.setSupportingColorAttachment(true);
+	
 	vkcv::ImageHandle colorBuffer = core.createImage(
 			colorFormat,
-			swapchainExtent.width,
-			swapchainExtent.height,
-			1, false, true, true
+			colorBufferConfig
 	);
 	
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
@@ -168,14 +176,22 @@ int main(int argc, const char** argv) {
 	
 	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
 				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
-		if ((swapchainWidth != swapchainExtent.width) || ((swapchainHeight != swapchainExtent.height))) {
-			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight);
+		if ((swapchainWidth != swapchainExtent.width) ||
+			((swapchainHeight != swapchainExtent.height))) {
+			depthBuffer = core.createImage(
+					vk::Format::eD32Sfloat,
+					vkcv::ImageConfig(
+							swapchainWidth,
+							swapchainHeight
+					)
+			);
+			
+			colorBufferConfig.setWidth(swapchainWidth);
+			colorBufferConfig.setHeight(swapchainHeight);
 			
 			colorBuffer = core.createImage(
 					colorFormat,
-					swapchainExtent.width,
-					swapchainExtent.height,
-					1, false, true, true
+					colorBufferConfig
 			);
 			
 			swapchainExtent.width = swapchainWidth;
diff --git a/projects/indirect_dispatch/src/AppSetup.cpp b/projects/indirect_dispatch/src/AppSetup.cpp
index bc6090a7829944cc503eb978b9540095781c0133..5dc6c81e0e9599a561c3de1e359563c1f25334ea 100644
--- a/projects/indirect_dispatch/src/AppSetup.cpp
+++ b/projects/indirect_dispatch/src/AppSetup.cpp
@@ -313,43 +313,34 @@ AppRenderTargets createRenderTargets(vkcv::Core& core,
 			renderWidth,
 			renderHeight
 	);
+	
+	vkcv::ImageConfig depthBufferConfig (renderWidth, renderHeight);
 
 	targets.depthBuffer = core.createImage(
-			AppConfig::depthBufferFormat,
-			renderWidth,
-			renderHeight,
-			1,
-			false
+		AppConfig::depthBufferFormat,
+		depthBufferConfig
 	);
+	
+	vkcv::ImageConfig bufferConfig (renderWidth, renderHeight);
+	bufferConfig.setSupportingColorAttachment(true);
 
 	targets.colorBuffer = core.createImage(
-			AppConfig::colorBufferFormat,
-			renderWidth,
-			renderHeight,
-			1,
-			false,
-			false,
-			true
+		AppConfig::colorBufferFormat,
+		bufferConfig
 	);
 
 	targets.motionBuffer = core.createImage(
-			AppConfig::motionBufferFormat,
-			renderWidth,
-			renderHeight,
-			1,
-			false,
-			false,
-			true
+		AppConfig::motionBufferFormat,
+		bufferConfig
 	);
 	
+	vkcv::ImageConfig finalConfig (width, height);
+	finalConfig.setSupportingColorAttachment(true);
+	finalConfig.setSupportingStorage(true);
+	
 	targets.finalBuffer = core.createImage(
 			AppConfig::colorBufferFormat,
-			width,
-			height,
-			1,
-			false,
-			true,
-			true
+			finalConfig
 	);
 	
 	core.setDebugLabel(targets.depthBuffer, "Depth buffer");
diff --git a/projects/indirect_dispatch/src/MotionBlurSetup.cpp b/projects/indirect_dispatch/src/MotionBlurSetup.cpp
index 0244c4ae9519ea6c6f818e024930e70c15e7a289..7589f77b80398e0bc1d7f1aec9b9570cc20acf3f 100644
--- a/projects/indirect_dispatch/src/MotionBlurSetup.cpp
+++ b/projects/indirect_dispatch/src/MotionBlurSetup.cpp
@@ -9,51 +9,37 @@ MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t
 
 	// divide and ceil to int
 	const uint32_t motionMaxWidth  = (width  + (MotionBlurConfig::maxMotionTileSize - 1)) / MotionBlurConfig::maxMotionTileSize;
-	const uint32_t motionMaxheight = (height + (MotionBlurConfig::maxMotionTileSize - 1)) / MotionBlurConfig::maxMotionTileSize;
+	const uint32_t motionMaxHeight = (height + (MotionBlurConfig::maxMotionTileSize - 1)) / MotionBlurConfig::maxMotionTileSize;
 
+	vkcv::ImageConfig targetConfig (motionMaxWidth, motionMaxHeight);
+	targetConfig.setSupportingStorage(true);
+	
 	targets.motionMax = core.createImage(
 		MotionBlurConfig::motionVectorTileFormat,
-		motionMaxWidth,
-		motionMaxheight,
-		1,
-		false,
-		true
+		targetConfig
 	);
 
 	targets.motionMaxNeighbourhood = core.createImage(
 		MotionBlurConfig::motionVectorTileFormat,
-		motionMaxWidth,
-		motionMaxheight,
-		1,
-		false,
-		true
+		targetConfig
 	);
 
 	targets.motionMin = core.createImage(
 		MotionBlurConfig::motionVectorTileFormat,
-		motionMaxWidth,
-		motionMaxheight,
-		1,
-		false,
-		true
+		targetConfig
 	);
 
 	targets.motionMinNeighbourhood = core.createImage(
 		MotionBlurConfig::motionVectorTileFormat,
-		motionMaxWidth,
-		motionMaxheight,
-		1,
-		false,
-		true
+		targetConfig
 	);
+	
+	vkcv::ImageConfig outputConfig (width, height);
+	outputConfig.setSupportingStorage(true);
 
 	targets.outputColor = core.createImage(
 		MotionBlurConfig::outputColorFormat,
-		width,
-		height,
-		1,
-		false,
-		true
+		outputConfig
 	);
 
 	return targets;
diff --git a/projects/indirect_draw/resources/Szene/Szene.bin b/projects/indirect_draw/resources/Szene/Szene.bin
deleted file mode 100644
index c87d27637516b0bbf864251dd162773f5cc53e06..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/Szene/Szene.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d
-size 70328
diff --git a/projects/indirect_draw/resources/Szene/Szene.gltf b/projects/indirect_draw/resources/Szene/Szene.gltf
deleted file mode 100644
index e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/Szene/Szene.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a
-size 7421
diff --git a/projects/indirect_draw/resources/Szene/boards2_vcyc.jpg b/projects/indirect_draw/resources/Szene/boards2_vcyc.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/Szene/boards2_vcyc.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/indirect_draw/resources/Szene/boards2_vcyc_jpg.jpg b/projects/indirect_draw/resources/Szene/boards2_vcyc_jpg.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/Szene/boards2_vcyc_jpg.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_AmbientOcclusion.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_AmbientOcclusion.jpg
deleted file mode 100644
index 2217fb53744f9166232a40b78ee9518e04b0ded5..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/Grass001_1K_AmbientOcclusion.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f6c88f8f3facd9e91f8dd160bd8c4a602e433872ed18e08015a9fa9dfff889de
-size 901465
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Color.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Color.jpg
deleted file mode 100644
index b8aa1533ae5a023a5fc8457f30ed60efe3bda32d..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/Grass001_1K_Color.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:878b8fe4747d9ce693220edea1915de550e8f14d402d26a0915f162d40f84e91
-size 1763328
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Displacement.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Displacement.jpg
deleted file mode 100644
index 89789cba150eea7c7abbdc1851090f6021af978a..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/Grass001_1K_Displacement.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1e8300e1107bee7e681059d9da0a7e3ca422977e8b6f496e16452a4c94b3d385
-size 912347
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Normal.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Normal.jpg
deleted file mode 100644
index 3163d6391592ace10446cb71141a2192e63e9660..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/Grass001_1K_Normal.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:684426b49e841e267f12f06dc955b5b1d01b3ba3659bea0c5d73be889700929f
-size 2336471
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Roughness.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Roughness.jpg
deleted file mode 100644
index 10e6ac33badf2a4795766a66546a62c67eb8b558..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/Grass001_1K_Roughness.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d952ffb098faf9ac5eb25134eabac08f0c65d927a448b5e7b4f9c72510cfcbe0
-size 822659
diff --git a/projects/indirect_draw/resources/cube/boards2_vcyc_jpg.jpg b/projects/indirect_draw/resources/cube/boards2_vcyc_jpg.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/boards2_vcyc_jpg.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/indirect_draw/resources/cube/cube.bin b/projects/indirect_draw/resources/cube/cube.bin
deleted file mode 100644
index 3303cd8635848bee18e10ab8754d5e4e7218db92..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/cube.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4
-size 840
diff --git a/projects/indirect_draw/resources/cube/cube.blend b/projects/indirect_draw/resources/cube/cube.blend
deleted file mode 100644
index 62ccb2c742094bcfb5ed194ab905bffae86bfd65..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/cube.blend
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09
-size 774920
diff --git a/projects/indirect_draw/resources/cube/cube.blend1 b/projects/indirect_draw/resources/cube/cube.blend1
deleted file mode 100644
index 13f21dcca218d7bc7a07a8a9682b5e1d9e607736..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/cube.blend1
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b
-size 774920
diff --git a/projects/indirect_draw/resources/cube/cube.glb b/projects/indirect_draw/resources/cube/cube.glb
deleted file mode 100644
index 66a42c65e71dcf375e04cc378256024dd3c7834d..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/cube.glb
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897
-size 1194508
diff --git a/projects/indirect_draw/resources/cube/cube.gltf b/projects/indirect_draw/resources/cube/cube.gltf
deleted file mode 100644
index 428176144843dd06c78fe1d11a6392a0ea02b22d..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/cube/cube.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac
-size 2823
diff --git a/projects/indirect_draw/resources/triangle/Triangle.bin b/projects/indirect_draw/resources/triangle/Triangle.bin
deleted file mode 100644
index 57f26ad96592b64377e6aa93823d96a94e6c5022..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/triangle/Triangle.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959
-size 104
diff --git a/projects/indirect_draw/resources/triangle/Triangle.blend b/projects/indirect_draw/resources/triangle/Triangle.blend
deleted file mode 100644
index 2421dc5e1bb029d73a9ec09cc4530c5196851fd7..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/triangle/Triangle.blend
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477
-size 758144
diff --git a/projects/indirect_draw/resources/triangle/Triangle.glb b/projects/indirect_draw/resources/triangle/Triangle.glb
deleted file mode 100644
index 4148620cd6af0dadbc791aa1c52bb5431a40884b..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/triangle/Triangle.glb
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227
-size 980
diff --git a/projects/indirect_draw/resources/triangle/Triangle.gltf b/projects/indirect_draw/resources/triangle/Triangle.gltf
deleted file mode 100644
index a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6..0000000000000000000000000000000000000000
--- a/projects/indirect_draw/resources/triangle/Triangle.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec
-size 2202
diff --git a/projects/indirect_draw/src/main.cpp b/projects/indirect_draw/src/main.cpp
index 5951c8c0653011810902a6e8499d02c96d07fd1c..f90fa0892982f68fa959671b5eb6f207aac72b65 100644
--- a/projects/indirect_draw/src/main.cpp
+++ b/projects/indirect_draw/src/main.cpp
@@ -528,7 +528,13 @@ int main(int argc, const char** argv) {
 		if ((!depthBuffer) ||
 			(swapchainWidth != core.getImageWidth(depthBuffer)) ||
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
-			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight);
+			depthBuffer = core.createImage(
+					vk::Format::eD32Sfloat,
+					vkcv::ImageConfig(
+							swapchainWidth,
+							swapchainHeight
+					)
+			);
 		}
   
 		cameraManager.update(dt);
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index 512e1e6d1e6ea694289cb862ecae425b6bf8ee66..ceaeb43c891a09a7e730724bd22cdbea1b0b3f00 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -324,8 +324,10 @@ int main(int argc, const char** argv) {
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
 			depthBuffer = core.createImage(
 					vk::Format::eD32Sfloat,
-					swapchainWidth,
-					swapchainHeight
+					vkcv::ImageConfig(
+							swapchainWidth,
+							swapchainHeight
+					)
 			);
 		}
 		
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index 00bb497b9798064c4b5ac449d6bac8495a1783e8..7f9e9fb7e12dd9c5e2437f2367d0f95eea67a0de 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -208,11 +208,17 @@ int main(int argc, const char **argv) {
 
 	const auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain());
 	
+	vkcv::ImageConfig colorBufferConfig (
+			swapchainExtent.width,
+			swapchainExtent.height
+	);
+	
+	colorBufferConfig.setSupportingStorage(true);
+	colorBufferConfig.setSupportingColorAttachment(true);
+	
     vkcv::ImageHandle colorBuffer = core.createImage(
 			colorFormat,
-			swapchainExtent.width,
-			swapchainExtent.height,
-			1, false, true, true
+			colorBufferConfig
 	);
 	
 	vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core);
@@ -237,11 +243,12 @@ int main(int argc, const char **argv) {
 				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((core.getImageWidth(colorBuffer) != swapchainWidth) ||
 			(core.getImageHeight(colorBuffer) != swapchainHeight)) {
+			colorBufferConfig.setWidth(swapchainWidth);
+			colorBufferConfig.setHeight(swapchainHeight);
+			
 			colorBuffer = core.createImage(
 					colorFormat,
-					swapchainWidth,
-					swapchainHeight,
-					1, false, true, true
+					colorBufferConfig
 			);
 		}
 
diff --git a/projects/path_tracer/src/main.cpp b/projects/path_tracer/src/main.cpp
index 53f52c3d724b8db14641108d9b8a0e33e1844600..75ee36b2ac232e20d7baf29db34909dee67cca00 100644
--- a/projects/path_tracer/src/main.cpp
+++ b/projects/path_tracer/src/main.cpp
@@ -63,24 +63,23 @@ int main(int argc, const char** argv) {
 			initialHeight,
 			true
 	);
+	
+	vkcv::ImageConfig imageConfig (
+			initialWidth,
+			initialHeight
+	);
+	
+	imageConfig.setSupportingStorage(true);
 
 	// images
 	vkcv::ImageHandle outputImage = core.createImage(
 		vk::Format::eR32G32B32A32Sfloat,
-		initialWidth,
-		initialHeight,
-		1,
-		false,
-		true
+		imageConfig
 	);
 
 	vkcv::ImageHandle meanImage = core.createImage(
 		vk::Format::eR32G32B32A32Sfloat,
-		initialWidth,
-		initialHeight,
-		1,
-		false,
-		true
+		imageConfig
 	);
 
 	vkcv::shader::GLSLCompiler compiler;
@@ -248,25 +247,19 @@ int main(int argc, const char** argv) {
 	
 	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
 				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
-		if (swapchainWidth != widthPrevious || swapchainHeight != heightPrevious) {
+		if ((swapchainWidth != widthPrevious) || (swapchainHeight != heightPrevious)) {
+			imageConfig.setWidth(swapchainWidth);
+			imageConfig.setHeight(swapchainHeight);
 
 			// resize images
 			outputImage = core.createImage(
 				vk::Format::eR32G32B32A32Sfloat,
-				swapchainWidth,
-				swapchainHeight,
-				1,
-				false,
-				true
+				imageConfig
 			);
 
 			meanImage = core.createImage(
 				vk::Format::eR32G32B32A32Sfloat,
-				swapchainWidth,
-				swapchainHeight,
-				1,
-				false,
-				true
+				imageConfig
 			);
 
 			// update descriptor sets
diff --git a/projects/rtx_ambient_occlusion/src/main.cpp b/projects/rtx_ambient_occlusion/src/main.cpp
index f5d8a46feefd070967d0f68742ad0c3b2749f160..2e999baee212aa6d9da8ca03d2649bafe69eccb8 100644
--- a/projects/rtx_ambient_occlusion/src/main.cpp
+++ b/projects/rtx_ambient_occlusion/src/main.cpp
@@ -101,8 +101,10 @@ int main(int argc, const char** argv) {
 			((swapchainHeight != core.getImageHeight(depthBuffer)))) {
 			depthBuffer = core.createImage(
 					vk::Format::eD32Sfloat,
-					swapchainWidth,
-					swapchainHeight
+					vkcv::ImageConfig(
+							swapchainWidth,
+							swapchainHeight
+					)
 			);
 		}
 		
diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp
index 043d135257ce63398ec34e5f6d47e4f4ffd6c61d..72cc9995dffd4ef8c938820f2678fac29c54e5ee 100644
--- a/projects/sph/src/main.cpp
+++ b/projects/sph/src/main.cpp
@@ -222,11 +222,17 @@ int main(int argc, const char **argv) {
 
 	const auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain());
 	
+	vkcv::ImageConfig colorBufferConfig (
+			swapchainExtent.width,
+			swapchainExtent.height
+	);
+	
+	colorBufferConfig.setSupportingStorage(true);
+	colorBufferConfig.setSupportingColorAttachment(true);
+	
     vkcv::ImageHandle colorBuffer = core.createImage(
 			colorFormat,
-			swapchainExtent.width,
-			swapchainExtent.height,
-			1, false, true, true
+			colorBufferConfig
 	);
 	
 	vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core);
@@ -245,11 +251,12 @@ int main(int argc, const char **argv) {
 				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((core.getImageWidth(colorBuffer) != swapchainWidth) ||
 			(core.getImageHeight(colorBuffer) != swapchainHeight)) {
+			colorBufferConfig.setWidth(swapchainWidth);
+			colorBufferConfig.setHeight(swapchainHeight);
+			
 			colorBuffer = core.createImage(
 					colorFormat,
-					swapchainWidth,
-					swapchainHeight,
-					1, false, true, true
+					colorBufferConfig
 			);
 		}
 
diff --git a/projects/voxelization/assets/cube/boards2_vcyc_jpg.jpg b/projects/voxelization/assets/cube/boards2_vcyc_jpg.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/cube/boards2_vcyc_jpg.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/voxelization/assets/cube/cube.bin b/projects/voxelization/assets/cube/cube.bin
deleted file mode 100644
index 3303cd8635848bee18e10ab8754d5e4e7218db92..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/cube/cube.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4
-size 840
diff --git a/projects/voxelization/assets/cube/cube.blend b/projects/voxelization/assets/cube/cube.blend
deleted file mode 100644
index 62ccb2c742094bcfb5ed194ab905bffae86bfd65..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/cube/cube.blend
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09
-size 774920
diff --git a/projects/voxelization/assets/cube/cube.blend1 b/projects/voxelization/assets/cube/cube.blend1
deleted file mode 100644
index 13f21dcca218d7bc7a07a8a9682b5e1d9e607736..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/cube/cube.blend1
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b
-size 774920
diff --git a/projects/voxelization/assets/cube/cube.glb b/projects/voxelization/assets/cube/cube.glb
deleted file mode 100644
index 66a42c65e71dcf375e04cc378256024dd3c7834d..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/cube/cube.glb
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897
-size 1194508
diff --git a/projects/voxelization/assets/cube/cube.gltf b/projects/voxelization/assets/cube/cube.gltf
deleted file mode 100644
index 428176144843dd06c78fe1d11a6392a0ea02b22d..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/cube/cube.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac
-size 2823
diff --git a/projects/voxelization/assets/triangle/Triangle.bin b/projects/voxelization/assets/triangle/Triangle.bin
deleted file mode 100644
index 57f26ad96592b64377e6aa93823d96a94e6c5022..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/triangle/Triangle.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959
-size 104
diff --git a/projects/voxelization/assets/triangle/Triangle.blend b/projects/voxelization/assets/triangle/Triangle.blend
deleted file mode 100644
index 2421dc5e1bb029d73a9ec09cc4530c5196851fd7..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/triangle/Triangle.blend
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477
-size 758144
diff --git a/projects/voxelization/assets/triangle/Triangle.glb b/projects/voxelization/assets/triangle/Triangle.glb
deleted file mode 100644
index 4148620cd6af0dadbc791aa1c52bb5431a40884b..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/triangle/Triangle.glb
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227
-size 980
diff --git a/projects/voxelization/assets/triangle/Triangle.gltf b/projects/voxelization/assets/triangle/Triangle.gltf
deleted file mode 100644
index a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/triangle/Triangle.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec
-size 2202
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 381761a766ca2c6f0d9d622a9b56c183f13fd47f..6b6e2eea46dd6d0e87d2cb8e33ce4c69c4429ca1 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -387,48 +387,69 @@ int main(int argc, const char** argv) {
 	skyPipeConfig.setWritingDepth(false);
 
 	vkcv::GraphicsPipelineHandle skyPipe = core.createGraphicsPipeline(skyPipeConfig);
+	
+	vkcv::ImageConfig depthBufferConfig (
+			swapchainExtent.width,
+			swapchainExtent.height
+	);
+	
+	depthBufferConfig.setMultisampling(msaa);
 
 	// render targets
 	vkcv::ImageHandle depthBuffer = core.createImage(
 			depthBufferFormat,
+			depthBufferConfig
+	);
+	
+	const bool colorBufferRequiresStorage = !usingMsaa;
+	
+	vkcv::ImageConfig colorBufferConfig (
 			swapchainExtent.width,
-			swapchainExtent.height,
-			1, false, false, false, msaa
+			swapchainExtent.height
 	);
+	
+	colorBufferConfig.setSupportingStorage(colorBufferRequiresStorage);
+	colorBufferConfig.setSupportingColorAttachment(true);
+	colorBufferConfig.setMultisampling(msaa);
 
-    const bool colorBufferRequiresStorage = !usingMsaa;
 	vkcv::ImageHandle colorBuffer = core.createImage(
 			colorBufferFormat,
+			colorBufferConfig
+	);
+	
+	vkcv::ImageConfig resolveBufferConfig (
 			swapchainExtent.width,
-			swapchainExtent.height,
-			1, false, colorBufferRequiresStorage, true, msaa
+			swapchainExtent.height
 	);
+	
+	resolveBufferConfig.setSupportingStorage(true);
+	resolveBufferConfig.setSupportingColorAttachment(true);
 
 	vkcv::ImageHandle resolvedColorBuffer;
 	if (usingMsaa) {
 		resolvedColorBuffer = core.createImage(
 				colorBufferFormat,
-				swapchainExtent.width,
-				swapchainExtent.height,
-				1, false, true, true
+				resolveBufferConfig
 		);
-	}
-	else {
+	} else {
 		resolvedColorBuffer = colorBuffer;
 	}
 	
+	vkcv::ImageConfig swapBufferConfig (
+			swapchainExtent.width,
+			swapchainExtent.height
+	);
+	
+	swapBufferConfig.setSupportingStorage(true);
+	
 	vkcv::ImageHandle swapBuffer = core.createImage(
 			colorBufferFormat,
-			swapchainExtent.width,
-			swapchainExtent.height,
-			1, false, true
+			swapBufferConfig
 	);
 	
 	vkcv::ImageHandle swapBuffer2 = core.createImage(
 			colorBufferFormat,
-			swapchainExtent.width,
-			swapchainExtent.height,
-			1, false, true
+			swapBufferConfig
 	);
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
@@ -657,40 +678,48 @@ int main(int argc, const char** argv) {
 				core.writeDescriptorSet(materialDescriptorSets[i], setWrites);
 			}
 			
+			depthBufferConfig.setWidth(fsrWidth);
+			depthBufferConfig.setHeight(fsrHeight);
+			
 			depthBuffer = core.createImage(
 					depthBufferFormat,
-					fsrWidth, fsrHeight, 1,
-					false, false, false,
-					msaa
+					depthBufferConfig
 			);
 			
+			colorBufferConfig.setWidth(fsrWidth);
+			colorBufferConfig.setHeight(fsrHeight);
+			
 			colorBuffer = core.createImage(
 					colorBufferFormat,
-					fsrWidth, fsrHeight, 1,
-					false, colorBufferRequiresStorage, true,
-					msaa
+					colorBufferConfig
 			);
 
 			if (usingMsaa) {
+				resolveBufferConfig.setWidth(fsrWidth);
+				resolveBufferConfig.setHeight(fsrHeight);
+				
 				resolvedColorBuffer = core.createImage(
 						colorBufferFormat,
-						fsrWidth, fsrHeight, 1,
-						false, true, true
+						resolveBufferConfig
 				);
 			} else {
 				resolvedColorBuffer = colorBuffer;
 			}
 			
+			swapBufferConfig.setWidth(fsrWidth);
+			swapBufferConfig.setHeight(fsrHeight);
+			
 			swapBuffer = core.createImage(
 					colorBufferFormat,
-					fsrWidth, fsrHeight, 1,
-					false, true
+					swapBufferConfig
 			);
 			
+			swapBufferConfig.setWidth(swapchainWidth);
+			swapBufferConfig.setHeight(swapchainHeight);
+			
 			swapBuffer2 = core.createImage(
 					colorBufferFormat,
-					swapchainWidth, swapchainHeight, 1,
-					false, true
+					swapBufferConfig
 			);
 		}
 
@@ -956,8 +985,8 @@ int main(int argc, const char** argv) {
 			upscaling2.setSharpness(sharpness);
 
 			if (ImGui::Button("Reload forward pass")) {
-
 				vkcv::ShaderProgram newForwardProgram;
+				
 				compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/shader.vert"),
 					[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 					newForwardProgram.addShader(shaderStage, path);
@@ -974,8 +1003,8 @@ int main(int argc, const char** argv) {
 					forwardPipeline = newPipeline;
 				}
 			}
+			
 			if (ImGui::Button("Reload tonemapping")) {
-
 				vkcv::ShaderProgram newProgram;
 				compiler.compile(vkcv::ShaderStage::COMPUTE, std::filesystem::path("assets/shaders/tonemapping.comp"),
 					[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
@@ -991,6 +1020,7 @@ int main(int argc, const char** argv) {
 					tonemappingPipeline = newPipeline;
 				}
 			}
+			
 			ImGui::End();
 		}
 
diff --git a/projects/wobble_bobble/src/main.cpp b/projects/wobble_bobble/src/main.cpp
index 1c3eb776b43d29ffe53aa62a03e3bc06fe145767..9a5548b7189030e3a3296edb0486cef5c9225dd1 100644
--- a/projects/wobble_bobble/src/main.cpp
+++ b/projects/wobble_bobble/src/main.cpp
@@ -309,8 +309,10 @@ int main(int argc, const char **argv) {
 	
 	vkcv::ImageHandle depthBuffer = core.createImage(
 			vk::Format::eD32Sfloat,
-			swapchainExtent.width,
-			swapchainExtent.height
+			vkcv::ImageConfig(
+					swapchainExtent.width,
+					swapchainExtent.height
+			)
 	);
 	
 	vkcv::Image grid = vkcv::image(
@@ -605,8 +607,10 @@ int main(int argc, const char **argv) {
 		if ((swapchainWidth != swapchainExtent.width) || ((swapchainHeight != swapchainExtent.height))) {
 			depthBuffer = core.createImage(
 					vk::Format::eD32Sfloat,
-					swapchainWidth,
-					swapchainHeight
+					vkcv::ImageConfig(
+							swapchainWidth,
+							swapchainHeight
+					)
 			);
 			
 			swapchainExtent.width = swapchainWidth;
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 6ccf1de841996c85905689f0ef45e3dbdcf7c285..7fde9196d3187f3aafa7134325ee7d166d6cd23c 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -859,21 +859,32 @@ namespace vkcv {
 											   mipLodBias, borderColor);
 	}
 
-	ImageHandle Core::createImage(vk::Format format, uint32_t width, uint32_t height,
-								  uint32_t depth, bool createMipChain, bool supportStorage,
-								  bool supportColorAttachment, Multisampling multisampling) {
+	ImageHandle Core::createImage(vk::Format format,
+								  const ImageConfig& config,
+								  bool createMipChain) {
 		uint32_t mipCount = 1;
 		if (createMipChain) {
-			mipCount =
-				1 + (uint32_t)std::floor(std::log2(std::max(width, std::max(height, depth))));
+			mipCount = 1 + (uint32_t) std::floor(
+					std::log2(std::max(
+							config.getWidth(),
+							std::max(config.getHeight(), config.getDepth()))
+					)
+			);
 		}
 
-		return m_ImageManager->createImage(width, height, depth, format, mipCount, supportStorage,
-										   supportColorAttachment, multisampling);
+		return m_ImageManager->createImage(
+				format,
+				mipCount,
+				config
+		);
 	}
 
-	void Core::fillImage(const ImageHandle &image, const void* data, size_t size) {
-		m_ImageManager->fillImage(image, data, size);
+	void Core::fillImage(const ImageHandle &image,
+						 const void* data,
+						 size_t size,
+						 uint32_t firstLayer,
+						 uint32_t layerCount) {
+		m_ImageManager->fillImage(image, data, size, firstLayer, layerCount);
 	}
 
 	void Core::switchImageLayout(const ImageHandle &image, vk::ImageLayout layout) {
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index 6e5b5fc1b3efbbcf4203e9825230eca20cbf55e5..fc6140fe4664096145aa08551422d919bcb4dc25 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -64,7 +64,11 @@ namespace vkcv {
 	}
 
 	void Image::fill(const void* data, size_t size) {
-		m_core->fillImage(m_handle, data, size);
+		m_core->fillImage(m_handle, data, size, 0, 0);
+	}
+	
+	void Image::fillLayer(uint32_t layer, const void* data, size_t size) {
+		m_core->fillImage(m_handle, data, size, layer, 1);
 	}
 
 	void Image::recordMipChainGeneration(const vkcv::CommandStreamHandle &cmdStream,
@@ -75,9 +79,22 @@ namespace vkcv {
 	Image image(Core &core, vk::Format format, uint32_t width, uint32_t height, uint32_t depth,
 				bool createMipChain, bool supportStorage, bool supportColorAttachment,
 				Multisampling multisampling) {
-		return Image(&core,
-					 core.createImage(format, width, height, depth, createMipChain, supportStorage,
-									  supportColorAttachment, multisampling));
+		ImageConfig config (width, height, depth);
+		config.setSupportingStorage(supportStorage);
+		config.setSupportingColorAttachment(supportColorAttachment);
+		config.setMultisampling(multisampling);
+		return image(core, format, config, createMipChain);
+	}
+	
+	Image image(Core &core, vk::Format format, const ImageConfig &config, bool createMipChain) {
+		return Image(
+				&core,
+				core.createImage(
+						format,
+						config,
+						createMipChain
+				)
+		);
 	}
 
 } // namespace vkcv
diff --git a/src/vkcv/ImageConfig.cpp b/src/vkcv/ImageConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d95a7ff6bb843b4405f119d6bec0e50a06658e0e
--- /dev/null
+++ b/src/vkcv/ImageConfig.cpp
@@ -0,0 +1,80 @@
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/Image.cpp
+ * @brief Structure for image configuration
+ */
+#include "vkcv/ImageConfig.hpp"
+
+namespace vkcv {
+	
+	ImageConfig::ImageConfig(uint32_t width,
+							 uint32_t height,
+							 uint32_t depth)
+	: m_width(width),
+	  m_height(height),
+	  m_depth(depth),
+	  
+	  m_supportStorage(false),
+	  m_supportColorAttachment(false),
+	  m_cubeMapImage(false),
+	  
+	  m_msaa(Multisampling::None)
+	{}
+	
+	uint32_t ImageConfig::getWidth() const {
+		return m_width;
+	}
+	
+	void ImageConfig::setWidth(uint32_t width) {
+		m_width = width;
+	}
+	
+	uint32_t ImageConfig::getHeight() const {
+		return m_height;
+	}
+	
+	void ImageConfig::setHeight(uint32_t height) {
+		m_height = height;
+	}
+	
+	uint32_t ImageConfig::getDepth() const {
+		return m_depth;
+	}
+	
+	void ImageConfig::setDepth(uint32_t depth) {
+		m_depth = depth;
+	}
+	
+	bool ImageConfig::isSupportingStorage() const {
+		return m_supportStorage;
+	}
+	
+	void ImageConfig::setSupportingStorage(bool supportStorage) {
+		m_supportStorage = supportStorage;
+	}
+	
+	bool ImageConfig::isSupportingColorAttachment() const {
+		return m_supportColorAttachment;
+	}
+	
+	void ImageConfig::setSupportingColorAttachment(bool supportColorAttachment) {
+		m_supportColorAttachment = supportColorAttachment;
+	}
+	
+	bool ImageConfig::isCubeMapImage() const {
+		return m_cubeMapImage;
+	}
+	
+	void ImageConfig::setCubeMapImage(bool cubeMapImage) {
+		m_cubeMapImage = cubeMapImage;
+	}
+	
+	Multisampling ImageConfig::getMultisampling() const {
+		return m_msaa;
+	}
+	
+	void ImageConfig::setMultisampling(Multisampling msaa) {
+		m_msaa = msaa;
+	}
+	
+}
\ No newline at end of file
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index e4961f760513cf5377f79edeec180ca1b6595ab4..65c645897520f9e92049824d163b27a1fb5e7124 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -1,5 +1,5 @@
 /**
- * @authors Lars Hoerttrich
+ * @authors Lars Hoerttrich, Tobias Frisch
  * @file vkcv/ImageManager.cpp
  * @brief class creating and managing images
  */
@@ -13,136 +13,136 @@
 #include <algorithm>
 
 namespace vkcv {
-
+	
 	bool ImageManager::init(Core &core, BufferManager &bufferManager) {
 		if (!HandleManager<ImageEntry, ImageHandle>::init(core)) {
 			return false;
 		}
-
+		
 		m_bufferManager = &bufferManager;
 		m_swapchainImages.clear();
 		return true;
 	}
-
+	
 	uint64_t ImageManager::getIdFrom(const ImageHandle &handle) const {
 		return handle.getId();
 	}
-
+	
 	ImageHandle ImageManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
 		return ImageHandle(id, destroy);
 	}
-
+	
 	void ImageManager::destroyById(uint64_t id) {
 		auto &image = getById(id);
-
+		
 		const vk::Device &device = getCore().getContext().getDevice();
-
+		
 		for (auto &view : image.m_viewPerMip) {
 			if (view) {
 				device.destroyImageView(view);
 				view = nullptr;
 			}
 		}
-
+		
 		for (auto &view : image.m_arrayViewPerMip) {
 			if (view) {
 				device.destroyImageView(view);
 				view = nullptr;
 			}
 		}
-
+		
 		const vma::Allocator &allocator = getCore().getContext().getAllocator();
-
+		
 		if (image.m_handle) {
 			allocator.destroyImage(image.m_handle, image.m_allocation);
-
+			
 			image.m_handle = nullptr;
 			image.m_allocation = nullptr;
 		}
 	}
-
+	
 	const BufferManager &ImageManager::getBufferManager() const {
 		return *m_bufferManager;
 	}
-
+	
 	BufferManager &ImageManager::getBufferManager() {
 		return *m_bufferManager;
 	}
-
+	
 	void ImageManager::recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer,
 														   const ImageHandle &handle) {
 		auto &image = (*this) [handle];
 		recordImageLayoutTransition(handle, 0, 0, vk::ImageLayout::eGeneral, cmdBuffer);
-
+		
 		vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ?
-											  vk::ImageAspectFlagBits::eDepth :
-											  vk::ImageAspectFlagBits::eColor;
-
+										  vk::ImageAspectFlagBits::eDepth :
+										  vk::ImageAspectFlagBits::eColor;
+		
 		uint32_t srcWidth = image.m_width;
 		uint32_t srcHeight = image.m_height;
 		uint32_t srcDepth = image.m_depth;
-
+		
 		auto half = [](uint32_t in) {
 			return std::max<uint32_t>(in / 2, 1);
 		};
-
+		
 		uint32_t dstWidth = half(srcWidth);
 		uint32_t dstHeight = half(srcHeight);
 		uint32_t dstDepth = half(srcDepth);
-
+		
 		for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) {
 			uint32_t dstMip = srcMip + 1;
 			vk::ImageBlit region(
-				vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1),
-				{ vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) },
-				vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1),
-				{ vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) });
-
+					vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1),
+					{ vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) },
+					vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1),
+					{ vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) });
+			
 			cmdBuffer.blitImage(image.m_handle, vk::ImageLayout::eGeneral, image.m_handle,
 								vk::ImageLayout::eGeneral, region, vk::Filter::eLinear);
-
+			
 			srcWidth = dstWidth;
 			srcHeight = dstHeight;
 			srcDepth = dstDepth;
-
+			
 			dstWidth = half(dstWidth);
 			dstHeight = half(dstHeight);
 			dstDepth = half(dstDepth);
-
+			
 			recordImageMemoryBarrier(handle, cmdBuffer);
 		}
 	}
-
+	
 	const ImageEntry &ImageManager::operator[](const ImageHandle &handle) const {
 		if (handle.isSwapchainImage()) {
 			return m_swapchainImages [m_currentSwapchainInputImage];
 		}
-
+		
 		return HandleManager<ImageEntry, ImageHandle>::operator[](handle);
 	}
-
+	
 	ImageEntry &ImageManager::operator[](const ImageHandle &handle) {
 		if (handle.isSwapchainImage()) {
 			return m_swapchainImages [m_currentSwapchainInputImage];
 		}
-
+		
 		return HandleManager<ImageEntry, ImageHandle>::operator[](handle);
 	}
-
+	
 	ImageManager::ImageManager() noexcept :
-		HandleManager<ImageEntry, ImageHandle>(), m_bufferManager(nullptr), m_swapchainImages(),
-		m_currentSwapchainInputImage(0) {}
-
+			HandleManager<ImageEntry, ImageHandle>(), m_bufferManager(nullptr), m_swapchainImages(),
+			m_currentSwapchainInputImage(0) {}
+	
 	ImageManager::~ImageManager() noexcept {
 		clear();
-
+		
 		for (const auto &swapchainImage : m_swapchainImages) {
 			for (const auto view : swapchainImage.m_viewPerMip) {
 				getCore().getContext().getDevice().destroy(view);
 			}
 		}
 	}
-
+	
 	bool isDepthImageFormat(vk::Format format) {
 		if ((format == vk::Format::eD16Unorm) || (format == vk::Format::eD16UnormS8Uint)
 			|| (format == vk::Format::eD24UnormS8Uint) || (format == vk::Format::eD32Sfloat)
@@ -152,51 +152,53 @@ namespace vkcv {
 			return false;
 		}
 	}
-
-	ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth,
-										  vk::Format format, uint32_t mipCount, bool supportStorage,
-										  bool supportColorAttachment, Multisampling msaa) {
+	
+	ImageHandle ImageManager::createImage(vk::Format format,
+										  uint32_t mipCount,
+										  const ImageConfig& config) {
 		const vk::PhysicalDevice &physicalDevice = getCore().getContext().getPhysicalDevice();
-
 		const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format);
-
+		
 		vk::ImageCreateFlags createFlags;
-		vk::ImageUsageFlags imageUsageFlags =
-			(vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst
-			 | vk::ImageUsageFlagBits::eTransferSrc);
-
+		vk::ImageUsageFlags imageUsageFlags = (
+				vk::ImageUsageFlagBits::eSampled |
+				vk::ImageUsageFlagBits::eTransferDst |
+				vk::ImageUsageFlagBits::eTransferSrc
+		);
+		
 		vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal;
-
-		if (supportStorage) {
+		
+		if (config.isSupportingStorage()) {
 			imageUsageFlags |= vk::ImageUsageFlagBits::eStorage;
-
+			
 			if (!(formatProperties.optimalTilingFeatures
 				  & vk::FormatFeatureFlagBits::eStorageImage)) {
 				imageTiling = vk::ImageTiling::eLinear;
-
+				
 				if (!(formatProperties.linearTilingFeatures
 					  & vk::FormatFeatureFlagBits::eStorageImage))
 					return {};
 			}
 		}
-
-		if (supportColorAttachment) {
+		
+		if (config.isSupportingColorAttachment()) {
 			imageUsageFlags |= vk::ImageUsageFlagBits::eColorAttachment;
 		}
-
+		
 		const bool isDepthFormat = isDepthImageFormat(format);
-
+		
 		if (isDepthFormat) {
 			imageUsageFlags |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
 		}
-
+		
 		const vma::Allocator &allocator = getCore().getContext().getAllocator();
-
+		uint32_t requiredArrayLayers = 1;
+		
 		vk::ImageType imageType = vk::ImageType::e3D;
 		vk::ImageViewType imageViewType = vk::ImageViewType::e3D;
-
-		if (depth <= 1) {
-			if (height <= 1) {
+		
+		if (config.getDepth() <= 1) {
+			if (config.getHeight() <= 1) {
 				imageType = vk::ImageType::e1D;
 				imageViewType = vk::ImageViewType::e1D;
 			} else {
@@ -204,128 +206,191 @@ namespace vkcv {
 				imageViewType = vk::ImageViewType::e2D;
 			}
 		}
-
+		
 		if (isDepthFormat) {
 			imageType = vk::ImageType::e2D;
 			imageViewType = vk::ImageViewType::e2D;
 		}
-
+		
+		if (config.isCubeMapImage()) {
+			requiredArrayLayers = 6;
+			
+			imageViewType = vk::ImageViewType::eCube;
+			createFlags |= vk::ImageCreateFlagBits::eCubeCompatible;
+		} else
 		if (vk::ImageType::e3D == imageType) {
 			createFlags |= vk::ImageCreateFlagBits::e2DArrayCompatible;
 		}
-
+		
 		if (!formatProperties.optimalTilingFeatures) {
 			if (!formatProperties.linearTilingFeatures)
 				return {};
-
+			
 			imageTiling = vk::ImageTiling::eLinear;
 		}
-
-		const vk::ImageFormatProperties imageFormatProperties =
-			physicalDevice.getImageFormatProperties(format, imageType, imageTiling,
-													imageUsageFlags);
-
-		const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers);
-
+		
+		const vk::ImageFormatProperties imageFormatProperties = (
+				physicalDevice.getImageFormatProperties(
+						format,
+						imageType,
+						imageTiling,
+						imageUsageFlags
+				)
+		);
+		
+		const uint32_t arrayLayers = std::min<uint32_t>(
+				requiredArrayLayers,
+				imageFormatProperties.maxArrayLayers
+		);
+		
 		const vk::ImageCreateInfo imageCreateInfo(
-			createFlags, imageType, format, vk::Extent3D(width, height, depth), mipCount,
-			arrayLayers, msaaToSampleCountFlagBits(msaa), imageTiling, imageUsageFlags,
-			vk::SharingMode::eExclusive, {}, vk::ImageLayout::eUndefined);
-
+				createFlags,
+				imageType,
+				format,
+				vk::Extent3D(
+						config.getWidth(),
+						config.getHeight(),
+						config.getDepth()
+				),
+				mipCount,
+				arrayLayers,
+				msaaToSampleCountFlagBits(
+						config.getMultisampling()
+				),
+				imageTiling,
+				imageUsageFlags,
+				vk::SharingMode::eExclusive,
+				{},
+				vk::ImageLayout::eUndefined
+		);
+		
 		auto imageAllocation = allocator.createImage(
-			imageCreateInfo,
-			vma::AllocationCreateInfo(vma::AllocationCreateFlags(), vma::MemoryUsage::eGpuOnly,
-									  vk::MemoryPropertyFlagBits::eDeviceLocal,
-									  vk::MemoryPropertyFlagBits::eDeviceLocal, 0, vma::Pool(),
-									  nullptr));
-
+				imageCreateInfo,
+				vma::AllocationCreateInfo(
+						vma::AllocationCreateFlags(),
+						vma::MemoryUsage::eGpuOnly,
+						vk::MemoryPropertyFlagBits::eDeviceLocal,
+						vk::MemoryPropertyFlagBits::eDeviceLocal,
+						0,
+						vma::Pool(),
+						nullptr
+				)
+		);
+		
 		vk::Image image = imageAllocation.first;
 		vma::Allocation allocation = imageAllocation.second;
-
 		vk::ImageAspectFlags aspectFlags;
-
+		
 		if (isDepthFormat) {
 			aspectFlags = vk::ImageAspectFlagBits::eDepth;
 		} else {
 			aspectFlags = vk::ImageAspectFlagBits::eColor;
 		}
-
+		
 		const vk::Device &device = getCore().getContext().getDevice();
-
+		
 		std::vector<vk::ImageView> views;
 		std::vector<vk::ImageView> arrayViews;
-
+		
 		for (uint32_t mip = 0; mip < mipCount; mip++) {
 			const vk::ImageViewCreateInfo imageViewCreateInfo(
-				{}, image, imageViewType, format,
-				vk::ComponentMapping(
-					vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity,
-					vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity),
-				vk::ImageSubresourceRange(aspectFlags, mip, mipCount - mip, 0, arrayLayers));
-
+					{},
+					image,
+					imageViewType,
+					format,
+					vk::ComponentMapping(
+							vk::ComponentSwizzle::eIdentity,
+							vk::ComponentSwizzle::eIdentity,
+							vk::ComponentSwizzle::eIdentity,
+							vk::ComponentSwizzle::eIdentity
+					),
+					vk::ImageSubresourceRange(
+							aspectFlags,
+							mip,
+							mipCount - mip,
+							0,
+							arrayLayers
+					)
+			);
+			
 			views.push_back(device.createImageView(imageViewCreateInfo));
 		}
-
+		
 		for (uint32_t mip = 0; mip < mipCount; mip++) {
 			const vk::ImageViewCreateInfo imageViewCreateInfo(
-				{}, image, vk::ImageViewType::e2DArray, format,
-				vk::ComponentMapping(
-					vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity,
-					vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity),
-				vk::ImageSubresourceRange(aspectFlags, mip, 1, 0, arrayLayers));
-
+					{},
+					image,
+					vk::ImageViewType::e2DArray,
+					format,
+					vk::ComponentMapping(
+							vk::ComponentSwizzle::eIdentity,
+							vk::ComponentSwizzle::eIdentity,
+							vk::ComponentSwizzle::eIdentity,
+							vk::ComponentSwizzle::eIdentity
+					),
+					vk::ImageSubresourceRange(
+							aspectFlags,
+							mip,
+							1,
+							0,
+							arrayLayers
+					)
+			);
+			
 			arrayViews.push_back(device.createImageView(imageViewCreateInfo));
 		}
-
-		return add({ image, allocation,
-
-					 views, arrayViews,
-
-					 width, height, depth,
-
-					 format, arrayLayers, vk::ImageLayout::eUndefined, supportStorage });
-	}
-
-	ImageHandle ImageManager::createSwapchainImage() const {
-		return ImageHandle::createSwapchainImageHandle();
-	}
-
+		
+		return add({
+			image,
+			allocation,
+			views,
+			arrayViews,
+			config.getWidth(),
+			config.getHeight(),
+			config.getDepth(),
+			format,
+			arrayLayers,
+			vk::ImageLayout::eUndefined,
+			config.isSupportingStorage()
+		});
+	}
+	
 	vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const {
 		auto &image = (*this) [handle];
 		return image.m_handle;
 	}
-
+	
 	vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const {
 		if (handle.isSwapchainImage()) {
 			vkcv_log(LogLevel::ERROR, "Swapchain image has no memory");
 			return nullptr;
 		}
-
+		
 		auto &image = (*this) [handle];
 		const vma::Allocator &allocator = getCore().getContext().getAllocator();
-
+		
 		auto info = allocator.getAllocationInfo(image.m_allocation);
-
+		
 		return info.deviceMemory;
 	}
-
+	
 	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, size_t mipLevel,
 												   bool arrayView) const {
 		if (handle.isSwapchainImage()) {
 			return m_swapchainImages [m_currentSwapchainInputImage].m_viewPerMip [0];
 		}
-
+		
 		const auto &image = (*this) [handle];
 		const auto &views = arrayView ? image.m_arrayViewPerMip : image.m_viewPerMip;
-
+		
 		if (mipLevel >= views.size()) {
 			vkcv_log(LogLevel::ERROR, "Image does not have requested mipLevel");
 			return nullptr;
 		}
-
+		
 		return views [mipLevel];
 	}
-
+	
 	static vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageEntry &image,
 																	 uint32_t mipLevelCount,
 																	 uint32_t mipLevelOffset,
@@ -336,15 +401,17 @@ namespace vkcv {
 		} else {
 			aspectFlags = vk::ImageAspectFlagBits::eColor;
 		}
-
+		
 		const uint32_t mipLevelsMax = image.m_viewPerMip.size();
-
-		if (mipLevelOffset > mipLevelsMax)
+		
+		if (mipLevelOffset > mipLevelsMax) {
 			mipLevelOffset = mipLevelsMax;
-
-		if ((!mipLevelCount) || (mipLevelOffset + mipLevelCount > mipLevelsMax))
+		}
+		
+		if ((!mipLevelCount) || (mipLevelOffset + mipLevelCount > mipLevelsMax)) {
 			mipLevelCount = mipLevelsMax - mipLevelOffset;
-
+		}
+		
 		vk::ImageSubresourceRange imageSubresourceRange(
 				aspectFlags,
 				mipLevelOffset,
@@ -365,232 +432,271 @@ namespace vkcv {
 				imageSubresourceRange
 		);
 	}
-
+	
 	void ImageManager::switchImageLayoutImmediate(const ImageHandle &handle,
 												  vk::ImageLayout newLayout) {
 		auto &image = (*this) [handle];
 		const auto transitionBarrier = createImageLayoutTransitionBarrier(image, 0, 0, newLayout);
-
+		
 		auto &core = getCore();
 		auto stream = core.createCommandStream(QueueType::Graphics);
-
+		
 		core.recordCommandsToStream(
-			stream,
-			[transitionBarrier](const vk::CommandBuffer &commandBuffer) {
-				// TODO: precise PipelineStageFlagBits, will require a lot of context
-				commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
-											  vk::PipelineStageFlagBits::eBottomOfPipe, {}, nullptr,
-											  nullptr, transitionBarrier);
-			},
-			nullptr);
-
+				stream,
+				[transitionBarrier](const vk::CommandBuffer &commandBuffer) {
+					// TODO: precise PipelineStageFlagBits, will require a lot of context
+					commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
+												  vk::PipelineStageFlagBits::eBottomOfPipe, {}, nullptr,
+												  nullptr, transitionBarrier);
+				},
+				nullptr);
+		
 		core.submitCommandStream(stream, false);
 		image.m_layout = newLayout;
 	}
-
+	
 	void ImageManager::recordImageLayoutTransition(const ImageHandle &handle,
 												   uint32_t mipLevelCount, uint32_t mipLevelOffset,
 												   vk::ImageLayout newLayout,
 												   vk::CommandBuffer cmdBuffer) {
 		auto &image = (*this) [handle];
 		const auto transitionBarrier =
-			createImageLayoutTransitionBarrier(image, mipLevelCount, mipLevelOffset, newLayout);
-
+				createImageLayoutTransitionBarrier(image, mipLevelCount, mipLevelOffset, newLayout);
+		
 		cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
 								  vk::PipelineStageFlagBits::eAllCommands, {}, nullptr, nullptr,
 								  transitionBarrier);
-
+		
 		image.m_layout = newLayout;
 	}
-
+	
 	void ImageManager::recordImageMemoryBarrier(const ImageHandle &handle,
 												vk::CommandBuffer cmdBuffer) {
 		auto &image = (*this) [handle];
 		const auto transitionBarrier =
-			createImageLayoutTransitionBarrier(image, 0, 0, image.m_layout);
-
+				createImageLayoutTransitionBarrier(image, 0, 0, image.m_layout);
+		
 		cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
 								  vk::PipelineStageFlagBits::eAllCommands, {}, nullptr, nullptr,
 								  transitionBarrier);
 	}
-
+	
 	constexpr uint32_t getBytesPerPixel(vk::Format format) {
 		switch (format) {
-		case vk::Format::eR8Unorm:
-			return 1;
-		case vk::Format::eR16Unorm:
-			return 2;
-		case vk::Format::eR32Uint:
-		case vk::Format::eR8G8B8A8Srgb:
-		case vk::Format::eR8G8B8A8Unorm:
-			return 4;
-		case vk::Format::eR16G16B16A16Sfloat:
-			return 8;
-		case vk::Format::eR32G32B32A32Sfloat:
-			return 16;
-		default:
-			std::cerr << "Unknown image format" << std::endl;
-			return 4;
+			case vk::Format::eR8Unorm:
+				return 1;
+			case vk::Format::eR16Unorm:
+				return 2;
+			case vk::Format::eR32Uint:
+			case vk::Format::eR8G8B8A8Srgb:
+			case vk::Format::eR8G8B8A8Unorm:
+				return 4;
+			case vk::Format::eR16G16B16A16Sfloat:
+				return 8;
+			case vk::Format::eR32G32B32A32Sfloat:
+				return 16;
+			default:
+				std::cerr << "Unknown image format" << std::endl;
+				return 4;
 		}
 	}
-
-	void ImageManager::fillImage(const ImageHandle &handle, const void* data, size_t size) {
+	
+	void ImageManager::fillImage(const ImageHandle &handle,
+								 const void* data,
+								 size_t size,
+								 uint32_t firstLayer,
+								 uint32_t layerCount) {
 		if (handle.isSwapchainImage()) {
 			vkcv_log(LogLevel::ERROR, "Swapchain image cannot be filled");
 			return;
 		}
-
+		
 		auto &image = (*this) [handle];
+		const uint32_t baseArrayLayer = std::min<uint32_t>(firstLayer, image.m_layers);
+		
+		if (baseArrayLayer >= image.m_layers) {
+			return;
+		}
+		
+		uint32_t arrayLayerCount;
+		
+		if (layerCount > 0) {
+			arrayLayerCount = std::min<uint32_t>(layerCount, image.m_layers - baseArrayLayer);
+		} else {
+			arrayLayerCount = image.m_layers - baseArrayLayer;
+		}
+		
 		switchImageLayoutImmediate(handle, vk::ImageLayout::eTransferDstOptimal);
-
-		const size_t image_size =
-			(image.m_width * image.m_height * image.m_depth * getBytesPerPixel(image.m_format));
-
+		
+		const size_t image_size = (
+				image.m_width * image.m_height * image.m_depth * getBytesPerPixel(image.m_format)
+		);
+		
 		const size_t max_size = std::min(size, image_size);
-
+		
 		BufferHandle bufferHandle = getBufferManager().createBuffer(
-			TypeGuard(1), BufferType::STAGING, BufferMemoryType::DEVICE_LOCAL, max_size, false);
-
+				TypeGuard(1), BufferType::STAGING, BufferMemoryType::DEVICE_LOCAL, max_size, false
+		);
+		
 		getBufferManager().fillBuffer(bufferHandle, data, max_size, 0);
-
+		
 		vk::Buffer stagingBuffer = getBufferManager().getBuffer(bufferHandle);
-
+		
 		auto &core = getCore();
 		auto stream = core.createCommandStream(QueueType::Transfer);
-
+		
 		core.recordCommandsToStream(
-			stream,
-			[&image, &stagingBuffer](const vk::CommandBuffer &commandBuffer) {
-				vk::ImageAspectFlags aspectFlags;
-
-				if (isDepthImageFormat(image.m_format)) {
-					aspectFlags = vk::ImageAspectFlagBits::eDepth;
-				} else {
-					aspectFlags = vk::ImageAspectFlagBits::eColor;
+				stream,
+				[&image, &stagingBuffer, &baseArrayLayer, &arrayLayerCount]
+						(const vk::CommandBuffer &commandBuffer) {
+					vk::ImageAspectFlags aspectFlags;
+					
+					if (isDepthImageFormat(image.m_format)) {
+						aspectFlags = vk::ImageAspectFlagBits::eDepth;
+					} else {
+						aspectFlags = vk::ImageAspectFlagBits::eColor;
+					}
+					
+					const vk::BufferImageCopy region(
+							0,
+							0,
+							0,
+							vk::ImageSubresourceLayers(aspectFlags, 0, baseArrayLayer, arrayLayerCount),
+							vk::Offset3D(0, 0, 0),
+							vk::Extent3D(image.m_width, image.m_height, image.m_depth)
+					);
+					
+					commandBuffer.copyBufferToImage(
+							stagingBuffer,
+							image.m_handle,
+							vk::ImageLayout::eTransferDstOptimal,
+							1,
+							&region
+					);
+				},
+				[&]() {
+					switchImageLayoutImmediate(handle, vk::ImageLayout::eShaderReadOnlyOptimal);
 				}
-
-				const vk::BufferImageCopy region(
-					0, 0, 0, vk::ImageSubresourceLayers(aspectFlags, 0, 0, image.m_layers),
-					vk::Offset3D(0, 0, 0),
-					vk::Extent3D(image.m_width, image.m_height, image.m_depth));
-
-				commandBuffer.copyBufferToImage(stagingBuffer, image.m_handle,
-												vk::ImageLayout::eTransferDstOptimal, 1, &region);
-			},
-			[&]() {
-				switchImageLayoutImmediate(handle, vk::ImageLayout::eShaderReadOnlyOptimal);
-			});
-
+		);
+		
 		core.submitCommandStream(stream, false);
 	}
-
+	
 	void ImageManager::recordImageMipChainGenerationToCmdStream(
-		const vkcv::CommandStreamHandle &cmdStream, const ImageHandle &handle) {
+			const vkcv::CommandStreamHandle &cmdStream, const ImageHandle &handle) {
 		const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) {
 			recordImageMipGenerationToCmdBuffer(cmdBuffer, handle);
 		};
-
+		
 		getCore().recordCommandsToStream(cmdStream, record, nullptr);
 	}
-
+	
 	void ImageManager::recordMSAAResolve(vk::CommandBuffer cmdBuffer, const ImageHandle &src,
 										 const ImageHandle &dst) {
 		auto &srcImage = (*this) [src];
 		auto &dstImage = (*this) [dst];
-
+		
 		vk::ImageResolve region(
-			vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1),
-			vk::Offset3D(0, 0, 0),
-			vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1),
-			vk::Offset3D(0, 0, 0),
-			vk::Extent3D(dstImage.m_width, dstImage.m_height, dstImage.m_depth));
-
+				vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, srcImage.m_layers),
+				vk::Offset3D(0, 0, 0),
+				vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, dstImage.m_layers),
+				vk::Offset3D(0, 0, 0),
+				vk::Extent3D(dstImage.m_width, dstImage.m_height, dstImage.m_depth)
+		);
+		
 		recordImageLayoutTransition(src, 0, 0, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer);
 		recordImageLayoutTransition(dst, 0, 0, vk::ImageLayout::eTransferDstOptimal, cmdBuffer);
-
-		cmdBuffer.resolveImage(srcImage.m_handle, srcImage.m_layout, dstImage.m_handle,
-							   dstImage.m_layout, region);
+		
+		cmdBuffer.resolveImage(
+				srcImage.m_handle,
+				srcImage.m_layout,
+				dstImage.m_handle,
+				dstImage.m_layout,
+				region
+		);
 	}
-
+	
 	uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const {
 		auto &image = (*this) [handle];
 		return image.m_width;
 	}
-
+	
 	uint32_t ImageManager::getImageHeight(const ImageHandle &handle) const {
 		auto &image = (*this) [handle];
 		return image.m_height;
 	}
-
+	
 	uint32_t ImageManager::getImageDepth(const ImageHandle &handle) const {
 		auto &image = (*this) [handle];
 		return image.m_depth;
 	}
-
+	
 	vk::Format ImageManager::getImageFormat(const ImageHandle &handle) const {
 		auto &image = (*this) [handle];
 		return image.m_format;
 	}
-
+	
 	bool ImageManager::isImageSupportingStorage(const ImageHandle &handle) const {
 		if (handle.isSwapchainImage()) {
 			return false;
 		}
-
+		
 		auto &image = (*this) [handle];
 		return image.m_storage;
 	}
-
+	
 	uint32_t ImageManager::getImageMipCount(const ImageHandle &handle) const {
 		if (handle.isSwapchainImage()) {
 			return 1;
 		}
-
+		
 		auto &image = (*this) [handle];
 		return image.m_viewPerMip.size();
 	}
-
+	
 	uint32_t ImageManager::getImageArrayLayers(const ImageHandle &handle) const {
 		auto &image = (*this) [handle];
 		return image.m_layers;
 	}
-
+	
 	void ImageManager::setCurrentSwapchainImageIndex(int index) {
 		m_currentSwapchainInputImage = index;
 	}
-
+	
 	void ImageManager::setSwapchainImages(const std::vector<vk::Image> &images,
 										  const std::vector<vk::ImageView> &views, uint32_t width,
 										  uint32_t height, vk::Format format) {
-
+		
 		// destroy old views
 		for (const auto &image : m_swapchainImages) {
 			for (const auto &view : image.m_viewPerMip) {
 				getCore().getContext().getDevice().destroyImageView(view);
 			}
 		}
-
+		
 		assert(images.size() == views.size());
 		m_swapchainImages.clear();
 		for (size_t i = 0; i < images.size(); i++) {
-			m_swapchainImages.push_back({ images [i],
-										  nullptr,
-										  { views [i] },
-										  {},
-										  width,
-										  height,
-										  1,
-										  format,
-										  1,
-										  vk::ImageLayout::eUndefined,
-										  false });
+			m_swapchainImages.push_back({
+					images [i],
+					nullptr,
+					{ views [i] },
+					{},
+					width,
+					height,
+					1,
+					format,
+					1,
+					vk::ImageLayout::eUndefined,
+					false
+			});
 		}
 	}
-
+	
 	void ImageManager::updateImageLayoutManual(const vkcv::ImageHandle &handle,
 											   const vk::ImageLayout layout) {
 		auto &image = (*this) [handle];
 		image.m_layout = layout;
 	}
-
+	
 } // namespace vkcv
\ No newline at end of file
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index ddc44f9b519061a7881eb0904034da2276b82a12..cbb6961f915dfee1c90409af38c61738f260a525 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -10,7 +10,7 @@
 
 #include "BufferManager.hpp"
 #include "HandleManager.hpp"
-#include "vkcv/Multisampling.hpp"
+#include "vkcv/ImageConfig.hpp"
 
 namespace vkcv {
 
@@ -86,12 +86,9 @@ namespace vkcv {
 
 		~ImageManager() noexcept override;
 
-		[[nodiscard]] ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth,
-											  vk::Format format, uint32_t mipCount,
-											  bool supportStorage, bool supportColorAttachment,
-											  Multisampling msaa);
-
-		[[nodiscard]] ImageHandle createSwapchainImage() const;
+		[[nodiscard]] ImageHandle createImage(vk::Format format,
+											  uint32_t mipCount,
+											  const ImageConfig& config);
 
 		[[nodiscard]] vk::Image getVulkanImage(const ImageHandle &handle) const;
 
@@ -109,7 +106,11 @@ namespace vkcv {
 
 		void recordImageMemoryBarrier(const ImageHandle &handle, vk::CommandBuffer cmdBuffer);
 
-		void fillImage(const ImageHandle &handle, const void* data, size_t size);
+		void fillImage(const ImageHandle &handle,
+					   const void* data,
+					   size_t size,
+					   uint32_t firstLayer,
+					   uint32_t layerCount);
 
 		void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle &cmdStream,
 													  const ImageHandle &handle);
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 6925d40c5aa967ca18cd0231499248a1006d52fe..508f4de536fc193f9b213c87e7de7e3fe0679959 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -1,5 +1,5 @@
 /**
- * @authors Simeon Hermann, Leonie Franken
+ * @authors Simeon Hermann, Leonie Franken, Tobias Frisch
  * @file src/vkcv/ShaderProgram.cpp
  * @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline
  */
@@ -165,6 +165,8 @@ namespace vkcv {
 			auto insertionResult =
 				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
 			if (!insertionResult.second) {
+				insertionResult.first->second.shaderStages |= shaderStage;
+				
 				vkcv_log(LogLevel::WARNING,
 						 "Attempting to overwrite already existing binding %u at set ID %u.",
 						 bindingID, setID);
@@ -197,6 +199,8 @@ namespace vkcv {
 			auto insertionResult =
 				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
 			if (!insertionResult.second) {
+				insertionResult.first->second.shaderStages |= shaderStage;
+				
 				vkcv_log(LogLevel::WARNING,
 						 "Attempting to overwrite already existing binding %u at set ID %u.",
 						 bindingID, setID);
@@ -228,6 +232,8 @@ namespace vkcv {
 			auto insertionResult =
 				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
 			if (!insertionResult.second) {
+				insertionResult.first->second.shaderStages |= shaderStage;
+				
 				vkcv_log(LogLevel::WARNING,
 						 "Attempting to overwrite already existing binding %u at set ID %u.",
 						 bindingID, setID);
@@ -260,6 +266,8 @@ namespace vkcv {
 			auto insertionResult =
 				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
 			if (!insertionResult.second) {
+				insertionResult.first->second.shaderStages |= shaderStage;
+				
 				vkcv_log(LogLevel::WARNING,
 						 "Attempting to overwrite already existing binding %u at set ID %u.",
 						 bindingID, setID);
@@ -292,6 +300,8 @@ namespace vkcv {
 			auto insertionResult =
 				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
 			if (!insertionResult.second) {
+				insertionResult.first->second.shaderStages |= shaderStage;
+				
 				vkcv_log(LogLevel::WARNING,
 						 "Attempting to overwrite already existing binding %u at set ID %u.",
 						 bindingID, setID);
@@ -315,6 +325,8 @@ namespace vkcv {
 			auto insertionResult =
 				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
 			if (!insertionResult.second) {
+				insertionResult.first->second.shaderStages |= shaderStage;
+				
 				vkcv_log(LogLevel::WARNING,
 						 "Attempting to overwrite already existing binding %u at set ID %u.",
 						 bindingID, setID);
@@ -343,3 +355,4 @@ namespace vkcv {
 		return m_pushConstantsSize;
 	}
 } // namespace vkcv
+
diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp
index 252973f539af51e67ecb30bbe86717961c3a17d6..dc20966bafcbf3c1f4b32956555c444fbc77962e 100644
--- a/src/vkcv/VertexLayout.cpp
+++ b/src/vkcv/VertexLayout.cpp
@@ -49,8 +49,8 @@ namespace vkcv {
 		VertexBindings bindings;
 		bindings.reserve(attachments.size());
 
-		for (uint32_t i = 0; i < attachments.size(); i++) {
-			bindings.push_back(createVertexBinding(i, { attachments [i] }));
+		for (const auto& attachment : attachments) {
+			bindings.push_back(createVertexBinding(attachment.inputLocation, { attachment }));
 		}
 
 		return bindings;