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..f8be3f90caf07414bea053c0a063efb40da5016e 100644
--- a/modules/geometry/src/vkcv/geometry/Cuboid.cpp
+++ b/modules/geometry/src/vkcv/geometry/Cuboid.cpp
@@ -134,6 +134,69 @@ 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();
 		
@@ -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..79e31496b559f049fb0a9931b7e5e061fd310cb2 100644
--- a/modules/geometry/src/vkcv/geometry/Cylinder.cpp
+++ b/modules/geometry/src/vkcv/geometry/Cylinder.cpp
@@ -107,6 +107,48 @@ namespace vkcv::geometry {
 			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 +158,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());