diff --git a/README.md b/README.md index 79d3f468..10ef87d5 100644 --- a/README.md +++ b/README.md @@ -171,9 +171,9 @@ Implements a bloom effect to simulate glowing parts of a 3D mesh. A two pass gau

## [Deferred shading](deferred/) - + -Demonstrates the use of multiple render targets to fill a G-Buffer for deferred shading. +Demonstrates the use of multiple render targets to fill a G-Buffer for a deferred shading setup with multiple dynamic lights and normal mapped surfaces. Deferred shading collects all values (color, normal, position) into different render targets in one pass thanks to multiple render targets, and then does all shading and lighting calculations based on these in screen space, thus allowing for much more light sources than traditional forward renderers.

diff --git a/data/shaders/deferred/debug.frag b/data/shaders/deferred/debug.frag index c3a8b84a..84e983a7 100644 --- a/data/shaders/deferred/debug.frag +++ b/data/shaders/deferred/debug.frag @@ -17,6 +17,7 @@ void main() components[0] = texture(samplerPosition, inUV.st).rgb; components[1] = texture(samplerNormal, inUV.st).rgb; components[2] = texture(samplerAlbedo, inUV.st).rgb; + // Uncomment to display specular component //components[2] = vec3(texture(samplerAlbedo, inUV.st).a); // Select component depending on z coordinate of quad diff --git a/data/shaders/deferred/debug.frag.spv b/data/shaders/deferred/debug.frag.spv index 004722d4..2af6398f 100644 Binary files a/data/shaders/deferred/debug.frag.spv and b/data/shaders/deferred/debug.frag.spv differ diff --git a/data/shaders/deferred/deferred.frag b/data/shaders/deferred/deferred.frag index 6702157c..96e45909 100644 --- a/data/shaders/deferred/deferred.frag +++ b/data/shaders/deferred/deferred.frag @@ -12,12 +12,9 @@ layout (location = 0) in vec2 inUV; layout (location = 0) out vec4 outFragcolor; struct Light { - vec4 position; - vec4 color; + vec4 position; + vec3 color; float radius; - float quadraticFalloff; - float linearFalloff; - float _pad; }; layout (binding = 4) uniform UBO @@ -29,40 +26,50 @@ layout (binding = 4) uniform UBO void main() { - // Get G-Buffer values - vec3 fragPos = texture(samplerposition, inUV).rgb; - vec3 normal = texture(samplerNormal, inUV).rgb; - vec4 albedo = texture(samplerAlbedo, inUV); - - #define lightCount 5 - #define ambient 0.05 - #define specularStrength 0.15 + // Get G-Buffer values + vec3 fragPos = texture(samplerposition, inUV).rgb; + vec3 normal = texture(samplerNormal, inUV).rgb; + vec4 albedo = texture(samplerAlbedo, inUV); + + #define lightCount 6 + #define ambient 0.0 // Ambient part - vec3 fragcolor = albedo.rgb * ambient; + vec3 fragcolor = albedo.rgb * ambient; - vec3 viewVec = normalize(ubo.viewPos.xyz - fragPos); - - for(int i = 0; i < lightCount; ++i) - { - // Distance from light to fragment position - float dist = length(ubo.lights[i].position.xyz - fragPos); + for(int i = 0; i < lightCount; ++i) + { + // Vector to light + vec3 L = ubo.lights[i].position.xyz - fragPos; + // Distance from light to fragment position + float dist = length(L); + + // Viewer to fragment + vec3 V = ubo.viewPos.xyz - fragPos; + V = normalize(V); - if(dist < ubo.lights[i].radius) - { - // Get vector from current light source to fragment position - vec3 lightVec = normalize(ubo.lights[i].position.xyz - fragPos); - // Diffuse part - vec3 diffuse = max(dot(normal, lightVec), 0.0) * albedo.rgb * ubo.lights[i].color.rgb; - // Specular part (specular texture part stored in albedo alpha channel) - vec3 halfVec = normalize(lightVec + viewVec); - vec3 specular = ubo.lights[i].color.rgb * pow(max(dot(normal, halfVec), 0.0), 16.0) * albedo.a * specularStrength; - // Attenuation with linearFalloff and quadraticFalloff falloff - float attenuation = 1.0 / (1.0 + ubo.lights[i].linearFalloff * dist + ubo.lights[i].quadraticFalloff * dist * dist); - fragcolor += (diffuse + specular) * attenuation; - } - - } + //if(dist < ubo.lights[i].radius) + { + // Light to fragment + L = normalize(L); + + // Attenuation + float atten = ubo.lights[i].radius / (pow(dist, 2.0) + 1.0); + + // Diffuse part + vec3 N = normalize(normal); + float NdotL = max(0.0, dot(N, L)); + vec3 diff = ubo.lights[i].color * albedo.rgb * NdotL * atten; + + // Specular part + // Specular map values are stored in alpha of albedo mrt + vec3 R = reflect(-L, N); + float NdotR = max(0.0, dot(R, V)); + vec3 spec = ubo.lights[i].color * albedo.a * pow(NdotR, 16.0) * atten; + + fragcolor += diff + spec; + } + } outFragcolor = vec4(fragcolor, 1.0); } \ No newline at end of file diff --git a/data/shaders/deferred/deferred.frag.spv b/data/shaders/deferred/deferred.frag.spv index 2d3a7a5d..b3d447d5 100644 Binary files a/data/shaders/deferred/deferred.frag.spv and b/data/shaders/deferred/deferred.frag.spv differ diff --git a/data/shaders/deferred/mrt.frag b/data/shaders/deferred/mrt.frag index 46346360..dcdd0ce7 100644 --- a/data/shaders/deferred/mrt.frag +++ b/data/shaders/deferred/mrt.frag @@ -4,11 +4,13 @@ #extension GL_ARB_shading_language_420pack : enable layout (binding = 1) uniform sampler2D samplerColor; +layout (binding = 2) uniform sampler2D samplerNormalMap; layout (location = 0) in vec3 inNormal; layout (location = 1) in vec2 inUV; layout (location = 2) in vec3 inColor; layout (location = 3) in vec3 inWorldPos; +layout (location = 4) in vec3 inTangent; layout (location = 0) out vec4 outPosition; layout (location = 1) out vec4 outNormal; @@ -17,6 +19,14 @@ layout (location = 2) out vec4 outAlbedo; void main() { outPosition = vec4(inWorldPos, 1.0); - outNormal = vec4(inNormal, 1.0); + + // Calculate normal in tangent space + vec3 N = normalize(inNormal); + vec3 T = normalize(inTangent); + vec3 B = cross(N, T); + mat3 TBN = mat3(T, B, N); + vec3 tnorm = TBN * normalize(texture(samplerNormalMap, inUV).xyz * 2.0 - vec3(1.0)); + outNormal = vec4(tnorm, 1.0); + outAlbedo = texture(samplerColor, inUV); } \ No newline at end of file diff --git a/data/shaders/deferred/mrt.frag.spv b/data/shaders/deferred/mrt.frag.spv index 021c26e2..6ff397e8 100644 Binary files a/data/shaders/deferred/mrt.frag.spv and b/data/shaders/deferred/mrt.frag.spv differ diff --git a/data/shaders/deferred/mrt.vert b/data/shaders/deferred/mrt.vert index 65ac03b8..6c449413 100644 --- a/data/shaders/deferred/mrt.vert +++ b/data/shaders/deferred/mrt.vert @@ -7,18 +7,21 @@ layout (location = 0) in vec4 inPos; layout (location = 1) in vec2 inUV; layout (location = 2) in vec3 inColor; layout (location = 3) in vec3 inNormal; +layout (location = 4) in vec3 inTangent; layout (binding = 0) uniform UBO { mat4 projection; mat4 model; mat4 view; + vec4 instancePos[3]; } ubo; layout (location = 0) out vec3 outNormal; layout (location = 1) out vec2 outUV; layout (location = 2) out vec3 outColor; layout (location = 3) out vec3 outWorldPos; +layout (location = 4) out vec3 outTangent; out gl_PerVertex { @@ -27,13 +30,14 @@ out gl_PerVertex void main() { - gl_Position = ubo.projection * ubo.view * ubo.model * inPos; + vec4 tmpPos = inPos + ubo.instancePos[gl_InstanceIndex]; + + gl_Position = ubo.projection * ubo.view * ubo.model * tmpPos; outUV = inUV; outUV.t = 1.0 - outUV.t; // Vertex position in world space - vec4 tmpPos = inPos; outWorldPos = vec3(ubo.model * tmpPos); // GL to Vulkan coord space outWorldPos.y = -outWorldPos.y; @@ -41,6 +45,7 @@ void main() // Normal in world space mat3 mNormal = transpose(inverse(mat3(ubo.model))); outNormal = mNormal * normalize(inNormal); + outTangent = mNormal * normalize(inTangent); // Currently just vertex color outColor = inColor; diff --git a/data/shaders/deferred/mrt.vert.spv b/data/shaders/deferred/mrt.vert.spv index 684eb3c7..25d2c2da 100644 Binary files a/data/shaders/deferred/mrt.vert.spv and b/data/shaders/deferred/mrt.vert.spv differ diff --git a/deferred/deferred.cpp b/deferred/deferred.cpp index 005e86f1..834c3505 100644 --- a/deferred/deferred.cpp +++ b/deferred/deferred.cpp @@ -1,5 +1,5 @@ /* -* Vulkan Example - Deferred shading multiple render targets (aka G-Buffer) example +* Vulkan Example - Deferred shading with multiple render targets (aka G-Buffer) example * * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de * @@ -24,7 +24,7 @@ #define ENABLE_VALIDATION false // Texture properties -#define TEX_DIM 1024 +#define TEX_DIM 2048 #define TEX_FILTER VK_FILTER_LINEAR // Offscreen frame buffer properties @@ -36,20 +36,29 @@ std::vector vertexLayout = vkMeshLoader::VERTEX_LAYOUT_POSITION, vkMeshLoader::VERTEX_LAYOUT_UV, vkMeshLoader::VERTEX_LAYOUT_COLOR, - vkMeshLoader::VERTEX_LAYOUT_NORMAL + vkMeshLoader::VERTEX_LAYOUT_NORMAL, + vkMeshLoader::VERTEX_LAYOUT_TANGENT }; class VulkanExample : public VulkanExampleBase { public: - bool debugDisplay = true; + bool debugDisplay = false; struct { - vkTools::VulkanTexture colorMap; + struct { + vkTools::VulkanTexture colorMap; + vkTools::VulkanTexture normalMap; + } model; + struct { + vkTools::VulkanTexture colorMap; + vkTools::VulkanTexture normalMap; + } floor; } textures; struct { - vkMeshLoader::MeshBuffer example; + vkMeshLoader::MeshBuffer model; + vkMeshLoader::MeshBuffer floor; vkMeshLoader::MeshBuffer quad; } meshes; @@ -63,19 +72,17 @@ public: glm::mat4 projection; glm::mat4 model; glm::mat4 view; + glm::vec4 instancePos[3]; } uboVS, uboOffscreenVS; struct Light { glm::vec4 position; - glm::vec4 color; + glm::vec3 color; float radius; - float quadraticFalloff; - float linearFalloff; - float _pad; }; struct { - Light lights[5]; + Light lights[6]; glm::vec4 viewPos; } uboFragmentLights; @@ -97,7 +104,8 @@ public: } pipelineLayouts; struct { - VkDescriptorSet offscreen; + VkDescriptorSet model; + VkDescriptorSet floor; } descriptorSets; VkDescriptorSet descriptorSet; @@ -130,10 +138,15 @@ public: { zoom = -8.0f; rotation = { 0.0f, 0.0f, 0.0f }; - width = 1024; - height = 1024; enableTextOverlay = true; - title = "Vulkan Example - Deferred shading"; + title = "Vulkan Example - Deferred shading (2016 by Sascha Willems)"; + camera.type = Camera::CameraType::firstperson; + camera.movementSpeed = 5.0f; + camera.rotationSpeed = 0.25f; + camera.position = { 2.15f, 0.3f, -8.75f }; + camera.setRotation(glm::vec3(-0.75f, 12.5f, 0.0f)); + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); + paused = true; } ~VulkanExample() @@ -175,7 +188,8 @@ public: vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); // Meshes - vkMeshLoader::freeMeshBufferResources(device, &meshes.example); + vkMeshLoader::freeMeshBufferResources(device, &meshes.model); + vkMeshLoader::freeMeshBufferResources(device, &meshes.floor); vkMeshLoader::freeMeshBufferResources(device, &meshes.quad); // Uniform buffers @@ -187,7 +201,11 @@ public: vkDestroyRenderPass(device, offScreenFrameBuf.renderPass, nullptr); - textureLoader->destroyTexture(textures.colorMap); + textureLoader->destroyTexture(textures.model.colorMap); + textureLoader->destroyTexture(textures.model.normalMap); + textureLoader->destroyTexture(textures.floor.colorMap); + textureLoader->destroyTexture(textures.floor.normalMap); + vkDestroySemaphore(device, offscreenSemaphore, nullptr); } @@ -363,13 +381,13 @@ public: VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.pColorAttachments = colorReferences.data(); - subpass.colorAttachmentCount = colorReferences.size(); + subpass.colorAttachmentCount = static_cast(colorReferences.size()); subpass.pDepthStencilAttachment = &depthReference; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.pAttachments = attachmentDescs.data(); - renderPassInfo.attachmentCount = attachmentDescs.size(); + renderPassInfo.attachmentCount = static_cast(attachmentDescs.size()); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; @@ -387,7 +405,7 @@ public: fbufCreateInfo.pNext = NULL; fbufCreateInfo.renderPass = offScreenFrameBuf.renderPass; fbufCreateInfo.pAttachments = attachments.data(); - fbufCreateInfo.attachmentCount = attachments.size(); + fbufCreateInfo.attachmentCount = static_cast(attachments.size()); fbufCreateInfo.width = offScreenFrameBuf.width; fbufCreateInfo.height = offScreenFrameBuf.height; fbufCreateInfo.layers = 1; @@ -435,7 +453,7 @@ public: renderPassBeginInfo.framebuffer = offScreenFrameBuf.frameBuffer; renderPassBeginInfo.renderArea.extent.width = offScreenFrameBuf.width; renderPassBeginInfo.renderArea.extent.height = offScreenFrameBuf.height; - renderPassBeginInfo.clearValueCount = clearValues.size(); + renderPassBeginInfo.clearValueCount = static_cast(clearValues.size()); renderPassBeginInfo.pClearValues = clearValues.data(); VK_CHECK_RESULT(vkBeginCommandBuffer(offScreenCmdBuffer, &cmdBufInfo)); @@ -461,13 +479,21 @@ public: VkRect2D scissor = vkTools::initializers::rect2D(offScreenFrameBuf.width, offScreenFrameBuf.height, 0, 0); vkCmdSetScissor(offScreenCmdBuffer, 0, 1, &scissor); - vkCmdBindDescriptorSets(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.offscreen, 0, 1, &descriptorSets.offscreen, 0, NULL); vkCmdBindPipeline(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.offscreen); VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(offScreenCmdBuffer, VERTEX_BUFFER_BIND_ID, 1, &meshes.example.vertices.buf, offsets); - vkCmdBindIndexBuffer(offScreenCmdBuffer, meshes.example.indices.buf, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(offScreenCmdBuffer, meshes.example.indexCount, 1, 0, 0, 0); + + // Background + vkCmdBindDescriptorSets(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.offscreen, 0, 1, &descriptorSets.floor, 0, NULL); + vkCmdBindVertexBuffers(offScreenCmdBuffer, VERTEX_BUFFER_BIND_ID, 1, &meshes.floor.vertices.buf, offsets); + vkCmdBindIndexBuffer(offScreenCmdBuffer, meshes.floor.indices.buf, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(offScreenCmdBuffer, meshes.floor.indexCount, 1, 0, 0, 0); + + // Object + vkCmdBindDescriptorSets(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.offscreen, 0, 1, &descriptorSets.model, 0, NULL); + vkCmdBindVertexBuffers(offScreenCmdBuffer, VERTEX_BUFFER_BIND_ID, 1, &meshes.model.vertices.buf, offsets); + vkCmdBindIndexBuffer(offScreenCmdBuffer, meshes.model.indices.buf, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(offScreenCmdBuffer, meshes.model.indexCount, 3, 0, 0, 0); vkCmdEndRenderPass(offScreenCmdBuffer); @@ -487,10 +513,11 @@ public: void loadTextures() { - textureLoader->loadTexture( - getAssetPath() + "models/armor/colormap.ktx", - VK_FORMAT_BC3_UNORM_BLOCK, - &textures.colorMap); + textureLoader->loadTexture(getAssetPath() + "models/armor/colormap.ktx", VK_FORMAT_BC3_UNORM_BLOCK, &textures.model.colorMap); + textureLoader->loadTexture(getAssetPath() + "models/armor/normalmap.ktx", VK_FORMAT_BC3_UNORM_BLOCK, &textures.model.normalMap); + + textureLoader->loadTexture(getAssetPath() + "textures/pattern_35_bc3.ktx", VK_FORMAT_BC3_UNORM_BLOCK, &textures.floor.colorMap); + textureLoader->loadTexture(getAssetPath() + "textures/pattern_35_normalmap_bc3.ktx", VK_FORMAT_BC3_UNORM_BLOCK, &textures.floor.normalMap); } void reBuildCommandBuffers() @@ -564,7 +591,13 @@ public: void loadMeshes() { - loadMesh(getAssetPath() + "models/armor/armor.dae", &meshes.example, vertexLayout, 1.0f); + loadMesh(getAssetPath() + "models/armor/armor.dae", &meshes.model, vertexLayout, 1.0f); + + vkMeshLoader::MeshCreateInfo meshCreateInfo; + meshCreateInfo.scale = glm::vec3(2.0f); + meshCreateInfo.uvscale = glm::vec2(4.0f); + meshCreateInfo.center = glm::vec3(0.0f, 2.35f, 0.0f); + loadMesh(getAssetPath() + "models/plane.obj", &meshes.floor, vertexLayout, &meshCreateInfo); } void generateQuads() @@ -576,6 +609,7 @@ public: float uv[2]; float col[3]; float normal[3]; + float tangent[3]; }; std::vector vertexBuffer; @@ -614,7 +648,7 @@ public: indexBuffer.push_back(i * 4 + index); } } - meshes.quad.indexCount = indexBuffer.size(); + meshes.quad.indexCount = static_cast(indexBuffer.size()); createBuffer( VK_BUFFER_USAGE_INDEX_BUFFER_BIT, @@ -635,40 +669,47 @@ public: VK_VERTEX_INPUT_RATE_VERTEX); // Attribute descriptions - vertices.attributeDescriptions.resize(4); - // Location 0 : Position + vertices.attributeDescriptions.resize(5); + // Location 0: Position vertices.attributeDescriptions[0] = vkTools::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, 0, VK_FORMAT_R32G32B32_SFLOAT, 0); - // Location 1 : Texture coordinates + // Location 1: Texture coordinates vertices.attributeDescriptions[1] = vkTools::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, 1, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 3); - // Location 2 : Color + // Location 2: Color vertices.attributeDescriptions[2] = vkTools::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, 2, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 5); - // Location 3 : Normal + // Location 3: Normal vertices.attributeDescriptions[3] = vkTools::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, 3, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 8); + // Location 4: Tangent + vertices.attributeDescriptions[4] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 4, + VK_FORMAT_R32G32B32_SFLOAT, + sizeof(float) * 11); vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo(); - vertices.inputState.vertexBindingDescriptionCount = vertices.bindingDescriptions.size(); + vertices.inputState.vertexBindingDescriptionCount = static_cast(vertices.bindingDescriptions.size()); vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); - vertices.inputState.vertexAttributeDescriptionCount = vertices.attributeDescriptions.size(); + vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.attributeDescriptions.size()); vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); } @@ -677,14 +718,14 @@ public: std::vector poolSizes = { vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 8), - vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 8) + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 9) }; VkDescriptorPoolCreateInfo descriptorPoolInfo = vkTools::initializers::descriptorPoolCreateInfo( - poolSizes.size(), + static_cast(poolSizes.size()), poolSizes.data(), - 2); + 3); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); } @@ -724,7 +765,7 @@ public: VkDescriptorSetLayoutCreateInfo descriptorLayout = vkTools::initializers::descriptorSetLayoutCreateInfo( setLayoutBindings.data(), - setLayoutBindings.size()); + static_cast(setLayoutBindings.size())); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); @@ -741,6 +782,8 @@ public: void setupDescriptorSet() { + std::vector writeDescriptorSets; + // Textured quad descriptor set VkDescriptorSetAllocateInfo allocInfo = vkTools::initializers::descriptorSetAllocateInfo( @@ -769,8 +812,7 @@ public: offScreenFrameBuf.albedo.view, VK_IMAGE_LAYOUT_GENERAL); - std::vector writeDescriptorSets = - { + writeDescriptorSets = { // Binding 0 : Vertex shader uniform buffer vkTools::initializers::writeDescriptorSet( descriptorSet, @@ -803,33 +845,59 @@ public: &uniformData.fsLights.descriptor), }; - vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); // Offscreen (scene) - VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.offscreen)); - VkDescriptorImageInfo texDescriptorSceneColormap = - vkTools::initializers::descriptorImageInfo( - textures.colorMap.sampler, - textures.colorMap.view, - VK_IMAGE_LAYOUT_GENERAL); - - std::vector offScreenWriteDescriptorSets = + // Model + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.model)); + writeDescriptorSets = { - // Binding 0 : Vertex shader uniform buffer + // Binding 0: Vertex shader uniform buffer vkTools::initializers::writeDescriptorSet( - descriptorSets.offscreen, + descriptorSets.model, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformData.vsOffscreen.descriptor), - // Binding 1 : Scene color map + // Binding 1: Color map vkTools::initializers::writeDescriptorSet( - descriptorSets.offscreen, + descriptorSets.model, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, - &texDescriptorSceneColormap) + &textures.model.colorMap.descriptor), + // Binding 2: Normal map + vkTools::initializers::writeDescriptorSet( + descriptorSets.model, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 2, + &textures.model.normalMap.descriptor) }; - vkUpdateDescriptorSets(device, offScreenWriteDescriptorSets.size(), offScreenWriteDescriptorSets.data(), 0, NULL); + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + + // Backbround + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.floor)); + writeDescriptorSets = + { + // Binding 0: Vertex shader uniform buffer + vkTools::initializers::writeDescriptorSet( + descriptorSets.floor, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &uniformData.vsOffscreen.descriptor), + // Binding 1: Color map + vkTools::initializers::writeDescriptorSet( + descriptorSets.floor, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, + &textures.floor.colorMap.descriptor), + // Binding 2: Normal map + vkTools::initializers::writeDescriptorSet( + descriptorSets.floor, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 2, + &textures.floor.normalMap.descriptor) + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); } void preparePipelines() @@ -878,7 +946,7 @@ public: VkPipelineDynamicStateCreateInfo dynamicState = vkTools::initializers::pipelineDynamicStateCreateInfo( dynamicStateEnables.data(), - dynamicStateEnables.size(), + static_cast(dynamicStateEnables.size()), 0); // Final fullscreen pass pipeline @@ -901,7 +969,7 @@ public: pipelineCreateInfo.pViewportState = &viewportState; pipelineCreateInfo.pDepthStencilState = &depthStencilState; pipelineCreateInfo.pDynamicState = &dynamicState; - pipelineCreateInfo.stageCount = shaderStages.size(); + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); pipelineCreateInfo.pStages = shaderStages.data(); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.deferred)); @@ -930,7 +998,7 @@ public: vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE) }; - colorBlendState.attachmentCount = blendAttachmentStates.size(); + colorBlendState.attachmentCount = static_cast(blendAttachmentStates.size()); colorBlendState.pAttachments = blendAttachmentStates.data(); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.offscreen)); @@ -969,6 +1037,11 @@ public: &uniformData.fsLights.memory, &uniformData.fsLights.descriptor); + // Init some values + uboOffscreenVS.instancePos[0] = glm::vec4(0.0f); + uboOffscreenVS.instancePos[1] = glm::vec4(-4.0f, 0.0, -4.0f, 0.0f); + uboOffscreenVS.instancePos[2] = glm::vec4(4.0f, 0.0, -4.0f, 0.0f); + // Update updateUniformBuffersScreen(); updateUniformBufferDeferredMatrices(); @@ -1004,6 +1077,10 @@ public: uboOffscreenVS.model = glm::rotate(uboOffscreenVS.model, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); uboOffscreenVS.model = glm::rotate(uboOffscreenVS.model, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); + uboOffscreenVS.projection = camera.matrices.perspective; + uboOffscreenVS.view = camera.matrices.view; + uboOffscreenVS.model = glm::mat4(); + uint8_t *pData; VK_CHECK_RESULT(vkMapMemory(device, uniformData.vsOffscreen.memory, 0, sizeof(uboOffscreenVS), 0, (void **)&pData)); memcpy(pData, &uboOffscreenVS, sizeof(uboOffscreenVS)); @@ -1013,39 +1090,48 @@ public: // Update fragment shader light position uniform block void updateUniformBufferDeferredLights() { - // White light from above - uboFragmentLights.lights[0].position = glm::vec4(0.0f, 3.0f, 1.0f, 0.0f); - uboFragmentLights.lights[0].color = glm::vec4(1.5f); - uboFragmentLights.lights[0].radius = 15.0f; - uboFragmentLights.lights[0].linearFalloff = 0.3f; - uboFragmentLights.lights[0].quadraticFalloff = 0.4f; - // Red light + // White + uboFragmentLights.lights[0].position = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); + uboFragmentLights.lights[0].color = glm::vec3(1.5f); + uboFragmentLights.lights[0].radius = 15.0f * 0.25f; + // Red uboFragmentLights.lights[1].position = glm::vec4(-2.0f, 0.0f, 0.0f, 0.0f); - uboFragmentLights.lights[1].color = glm::vec4(1.5f, 0.0f, 0.0f, 0.0f); + uboFragmentLights.lights[1].color = glm::vec3(1.0f, 0.0f, 0.0f); uboFragmentLights.lights[1].radius = 15.0f; - uboFragmentLights.lights[1].linearFalloff = 0.4f; - uboFragmentLights.lights[1].quadraticFalloff = 0.3f; - // Blue light + // Blue uboFragmentLights.lights[2].position = glm::vec4(2.0f, 1.0f, 0.0f, 0.0f); - uboFragmentLights.lights[2].color = glm::vec4(0.0f, 0.0f, 2.5f, 0.0f); - uboFragmentLights.lights[2].radius = 10.0f; - uboFragmentLights.lights[2].linearFalloff = 0.45f; - uboFragmentLights.lights[2].quadraticFalloff = 0.35f; - // Belt glow - uboFragmentLights.lights[3].position = glm::vec4(0.0f, 0.7f, 0.5f, 0.0f); - uboFragmentLights.lights[3].color = glm::vec4(2.5f, 2.5f, 0.0f, 0.0f); - uboFragmentLights.lights[3].radius = 5.0f; - uboFragmentLights.lights[3].linearFalloff = 8.0f; - uboFragmentLights.lights[3].quadraticFalloff = 6.0f; - // Green light - uboFragmentLights.lights[4].position = glm::vec4(3.0f, 2.0f, 1.0f, 0.0f); - uboFragmentLights.lights[4].color = glm::vec4(0.0f, 1.5f, 0.0f, 0.0f); - uboFragmentLights.lights[4].radius = 10.0f; - uboFragmentLights.lights[4].linearFalloff = 0.8f; - uboFragmentLights.lights[4].quadraticFalloff = 0.6f; + uboFragmentLights.lights[2].color = glm::vec3(0.0f, 0.0f, 2.5f); + uboFragmentLights.lights[2].radius = 5.0f; + // Yellow + uboFragmentLights.lights[3].position = glm::vec4(0.0f, 0.9f, 0.5f, 0.0f); + uboFragmentLights.lights[3].color = glm::vec3(1.0f, 1.0f, 0.0f); + uboFragmentLights.lights[3].radius = 2.0f; + // Green + uboFragmentLights.lights[4].position = glm::vec4(0.0f, 0.5f, 0.0f, 0.0f); + uboFragmentLights.lights[4].color = glm::vec3(0.0f, 1.0f, 0.2f); + uboFragmentLights.lights[4].radius = 5.0f; + // Yellow + uboFragmentLights.lights[5].position = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f); + uboFragmentLights.lights[5].color = glm::vec3(1.0f, 0.7f, 0.3f); + uboFragmentLights.lights[5].radius = 25.0f; + + uboFragmentLights.lights[0].position.x = sin(glm::radians(360.0f * timer)) * 5.0f; + uboFragmentLights.lights[0].position.z = cos(glm::radians(360.0f * timer)) * 5.0f; + + uboFragmentLights.lights[1].position.x = -4.0f + sin(glm::radians(360.0f * timer) + 45.0f) * 2.0f; + uboFragmentLights.lights[1].position.z = 0.0f + cos(glm::radians(360.0f * timer) + 45.0f) * 2.0f; + + uboFragmentLights.lights[2].position.x = 4.0f + sin(glm::radians(360.0f * timer)) * 2.0f; + uboFragmentLights.lights[2].position.z = 0.0f + cos(glm::radians(360.0f * timer)) * 2.0f; + + uboFragmentLights.lights[4].position.x = 0.0f + sin(glm::radians(360.0f * timer + 90.0f)) * 5.0f; + uboFragmentLights.lights[4].position.z = 0.0f - cos(glm::radians(360.0f * timer + 45.0f)) * 5.0f; + + uboFragmentLights.lights[5].position.x = 0.0f + sin(glm::radians(-360.0f * timer + 135.0f)) * 10.0f; + uboFragmentLights.lights[5].position.z = 0.0f - cos(glm::radians(-360.0f * timer - 45.0f)) * 10.0f; // Current view position - uboFragmentLights.viewPos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f); + uboFragmentLights.viewPos = glm::vec4(camera.position, 0.0f) * glm::vec4(-1.0f, 1.0f, -1.0f, 1.0f); uint8_t *pData; VK_CHECK_RESULT(vkMapMemory(device, uniformData.fsLights.memory, 0, sizeof(uboFragmentLights), 0, (void **)&pData)); @@ -1117,6 +1203,7 @@ public: if (!prepared) return; draw(); + updateUniformBufferDeferredLights(); } virtual void viewChanged() @@ -1135,7 +1222,7 @@ public: { switch (keyCode) { - case 0x44: + case 0x70: case GAMEPAD_BUTTON_A: toggleDebugDisplay(); updateTextOverlay(); @@ -1146,78 +1233,19 @@ public: virtual void getOverlayText(VulkanTextOverlay *textOverlay) { #if defined(__ANDROID__) - textOverlay->addText("Press \"Button A\" to toggle render targets", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("Press \"Button A\" to toggle debug display", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); #else - textOverlay->addText("Press \"d\" to toggle render targets", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("Press \"F1\" to toggle debug display", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); #endif // Render targets if (debugDisplay) { - textOverlay->addText("World Position", (float)width * 0.25f, (float)height * 0.5f - 25.0f, VulkanTextOverlay::alignCenter); - textOverlay->addText("World normals", (float)width * 0.75f, (float)height * 0.5f - 25.0f, VulkanTextOverlay::alignCenter); - textOverlay->addText("Color", (float)width * 0.25f, (float)height - 25.0f, VulkanTextOverlay::alignCenter); + textOverlay->addText("World space position", (float)width * 0.25f, (float)height * 0.5f - 25.0f, VulkanTextOverlay::alignCenter); + textOverlay->addText("World space normals", (float)width * 0.75f, (float)height * 0.5f - 25.0f, VulkanTextOverlay::alignCenter); + textOverlay->addText("Albedo", (float)width * 0.25f, (float)height - 25.0f, VulkanTextOverlay::alignCenter); textOverlay->addText("Final image", (float)width * 0.75f, (float)height - 25.0f, VulkanTextOverlay::alignCenter); } } }; -VulkanExample *vulkanExample; - -#if defined(_WIN32) -LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (vulkanExample != NULL) - { - vulkanExample->handleMessages(hWnd, uMsg, wParam, lParam); - } - return (DefWindowProc(hWnd, uMsg, wParam, lParam)); -} -#elif defined(__linux__) && !defined(__ANDROID__) -static void handleEvent(const xcb_generic_event_t *event) -{ - if (vulkanExample != NULL) - { - vulkanExample->handleEvent(event); - } -} -#endif - -// Main entry point -#if defined(_WIN32) -// Windows entry point -int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) -#elif defined(__ANDROID__) -// Android entry point -void android_main(android_app* state) -#elif defined(__linux__) -// Linux entry point -int main(const int argc, const char *argv[]) -#endif -{ -#if defined(__ANDROID__) - // Removing this may cause the compiler to omit the main entry point - // which would make the application crash at start - app_dummy(); -#endif - vulkanExample = new VulkanExample(); -#if defined(_WIN32) - vulkanExample->setupWindow(hInstance, WndProc); -#elif defined(__ANDROID__) - // Attach vulkan example to global android application state - state->userData = vulkanExample; - state->onAppCmd = VulkanExample::handleAppCommand; - state->onInputEvent = VulkanExample::handleAppInput; - vulkanExample->androidApp = state; -#elif defined(__linux__) - vulkanExample->setupWindow(); -#endif -#if !defined(__ANDROID__) - vulkanExample->initSwapchain(); - vulkanExample->prepare(); -#endif - vulkanExample->renderLoop(); - delete(vulkanExample); -#if !defined(__ANDROID__) - return 0; -#endif -} \ No newline at end of file +VULKAN_EXAMPLE_MAIN() \ No newline at end of file diff --git a/screenshots/deferred_shading.jpg b/screenshots/deferred_shading.jpg new file mode 100644 index 00000000..5a080e6a Binary files /dev/null and b/screenshots/deferred_shading.jpg differ diff --git a/screenshots/deferred_shading.png b/screenshots/deferred_shading.png deleted file mode 100644 index 9e2053c7..00000000 Binary files a/screenshots/deferred_shading.png and /dev/null differ