diff --git a/data/models/venus.fbx b/data/models/venus.fbx new file mode 100644 index 00000000..81888603 Binary files /dev/null and b/data/models/venus.fbx differ diff --git a/data/shaders/pbribl/pbribl.frag b/data/shaders/pbribl/pbribl.frag new file mode 100644 index 00000000..624a88a7 --- /dev/null +++ b/data/shaders/pbribl/pbribl.frag @@ -0,0 +1,92 @@ +// Phyiscally based rendering using IBL +// Based on http://www.trentreed.net/blog/physically-based-shading-and-image-based-lighting/ + +#version 450 + +layout (location = 0) in vec3 inWorldPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; + +layout (binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; + vec3 camPos; +} ubo; + +layout (binding = 1) uniform UBOShared { + float exposure; + float gamma; +} uboShared; + +layout(push_constant) uniform PushConsts { + layout(offset = 12) float roughness; + layout(offset = 16) float metallic; + layout(offset = 20) float specular; + layout(offset = 24) float r; + layout(offset = 28) float g; + layout(offset = 32) float b; +} material; + +layout (binding = 2) uniform samplerCube radianceMap; +layout (binding = 3) uniform samplerCube irradianceMap; + +layout (location = 0) out vec4 outColor; + +// From http://filmicgames.com/archives/75 +vec3 Uncharted2Tonemap( vec3 x ) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; +} + +// Environment BRDF approximation from https://www.unrealengine.com/blog/physically-based-shading-on-mobile +vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV) +{ + vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022); + vec4 c1 = vec4(1, 0.0425, 1.04, -0.04); + vec4 r = Roughness * c0 + c1; + float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; + vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw; + return SpecularColor * AB.x + AB.y; +} + +void main() +{ + vec3 N = normalize(inNormal); + vec3 V = normalize(ubo.camPos - inWorldPos); + vec3 R = reflect(-V, N); + + vec3 baseColor = vec3(material.r, material.g, material.b); + + // Diffuse and specular color from material color and metallic factor + vec3 diffuseColor = baseColor - baseColor * material.metallic; + vec3 specularColor = mix(vec3(material.specular), baseColor, material.metallic); + + // Cube map sampling + ivec2 cubedim = textureSize(radianceMap, 0); + int numMipLevels = int(log2(max(cubedim.s, cubedim.y))); + float mipLevel = numMipLevels - 1.0 + log2(material.roughness); + vec3 radianceSample = pow(textureLod(radianceMap, R, mipLevel).rgb, vec3(2.2f)); + vec3 irradianceSample = pow(texture(irradianceMap, N).rgb, vec3(2.2f)); + + vec3 reflection = EnvBRDFApprox(specularColor, pow(material.roughness, 1.0f), clamp(dot(N, V), 0.0, 1.0)); + + // Combine specular IBL and BRDF + vec3 diffuse = diffuseColor * irradianceSample; + vec3 specular = radianceSample * reflection; + vec3 color = diffuse + specular; + + // Tone mapping + color = Uncharted2Tonemap( color * uboShared.exposure ); + color = color * (1.0f / Uncharted2Tonemap(vec3(11.2f))); + // Gamma correction + color = pow(color, vec3(1.0f / uboShared.gamma)); + + outColor = vec4( color, 1.0 ); +} \ No newline at end of file diff --git a/data/shaders/pbribl/pbribl.frag.spv b/data/shaders/pbribl/pbribl.frag.spv new file mode 100644 index 00000000..ce21aca0 Binary files /dev/null and b/data/shaders/pbribl/pbribl.frag.spv differ diff --git a/data/shaders/pbribl/pbribl.vert b/data/shaders/pbribl/pbribl.vert new file mode 100644 index 00000000..bc741abd --- /dev/null +++ b/data/shaders/pbribl/pbribl.vert @@ -0,0 +1,38 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 model; + mat4 view; + vec3 camPos; +} ubo; + +layout (location = 0) out vec3 outWorldPos; +layout (location = 1) out vec3 outNormal; +layout (location = 2) out vec2 outUV; + +layout(push_constant) uniform PushConsts { + vec3 objPos; +} pushConsts; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + vec3 locPos = vec3(ubo.model * vec4(inPos, 1.0)); + outWorldPos = locPos + pushConsts.objPos; + outNormal = mat3(ubo.model) * inNormal; + outUV = inUV; + gl_Position = ubo.projection * ubo.view * vec4(outWorldPos, 1.0); +} diff --git a/data/shaders/pbribl/pbribl.vert.spv b/data/shaders/pbribl/pbribl.vert.spv new file mode 100644 index 00000000..a9abfe39 Binary files /dev/null and b/data/shaders/pbribl/pbribl.vert.spv differ diff --git a/data/shaders/pbribl/skybox.frag b/data/shaders/pbribl/skybox.frag new file mode 100644 index 00000000..c7f5834d --- /dev/null +++ b/data/shaders/pbribl/skybox.frag @@ -0,0 +1,38 @@ +#version 450 + +layout (binding = 2) uniform samplerCube samplerEnv; + +layout (location = 0) in vec3 inUVW; + +layout (location = 0) out vec4 outColor; + +layout (binding = 1) uniform UBOShared { + float exposure; + float gamma; +} uboShared; + +// From http://filmicworlds.com/blog/filmic-tonemapping-operators/ +vec3 Uncharted2Tonemap(vec3 color) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + float W = 11.2; + return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F; +} + +void main() +{ + vec3 color = pow(texture(samplerEnv, inUVW).rgb, vec3(2.2)); + + color = Uncharted2Tonemap(color * uboShared.exposure); + color = color * (1.0 / Uncharted2Tonemap(vec3(11.2))); + + // gamma correction + color = pow(color, vec3(1.0 / uboShared.gamma)); + + outColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/data/shaders/pbribl/skybox.frag.spv b/data/shaders/pbribl/skybox.frag.spv new file mode 100644 index 00000000..caeed8df Binary files /dev/null and b/data/shaders/pbribl/skybox.frag.spv differ diff --git a/data/shaders/pbribl/skybox.vert b/data/shaders/pbribl/skybox.vert new file mode 100644 index 00000000..785a3010 --- /dev/null +++ b/data/shaders/pbribl/skybox.vert @@ -0,0 +1,27 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 model; +} ubo; + +layout (location = 0) out vec3 outUVW; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + outUVW = inPos; + gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0); +} diff --git a/data/shaders/pbribl/skybox.vert.spv b/data/shaders/pbribl/skybox.vert.spv new file mode 100644 index 00000000..a6d9ade4 Binary files /dev/null and b/data/shaders/pbribl/skybox.vert.spv differ diff --git a/data/textures/hamarikyu_bridge_irradiance_cube.ktx b/data/textures/hamarikyu_bridge_irradiance_cube.ktx new file mode 100644 index 00000000..b449886b Binary files /dev/null and b/data/textures/hamarikyu_bridge_irradiance_cube.ktx differ diff --git a/data/textures/hamarikyu_bridge_radiance_cube.ktx b/data/textures/hamarikyu_bridge_radiance_cube.ktx new file mode 100644 index 00000000..27d6b8f7 Binary files /dev/null and b/data/textures/hamarikyu_bridge_radiance_cube.ktx differ diff --git a/pbribl/pbribl.cpp b/pbribl/pbribl.cpp new file mode 100644 index 00000000..305b9f1e --- /dev/null +++ b/pbribl/pbribl.cpp @@ -0,0 +1,583 @@ +/* +* Vulkan Example - Physical based rendering with image based lighting +* +* Copyright (C) 2017 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include + +#include +#include "vulkanexamplebase.h" +#include "VulkanBuffer.hpp" +#include "VulkanTexture.hpp" +#include "VulkanModel.hpp" + +#define VERTEX_BUFFER_BIND_ID 0 +#define ENABLE_VALIDATION false +#define GRID_DIM 7 +#define OBJ_DIM 0.05f + +struct Material { + float roughness; + float metallic; + float specular; + float r,g,b; // Color components as single floats because we use push constants + std::string name; + Material() {}; + Material(std::string n, glm::vec3 c, float r, float m) : name(n), roughness(r), metallic(m), r(c.r), g(c.g), b(c.b) { specular = 0.8f; }; +}; + +class VulkanExample : public VulkanExampleBase +{ +public: + bool displaySkybox = true; + + struct Textures { + vks::TextureCubeMap radianceMap; + vks::TextureCubeMap irradianceMap; + } textures; + + // Vertex layout for the models + vks::VertexLayout vertexLayout = vks::VertexLayout({ + vks::VERTEX_COMPONENT_POSITION, + vks::VERTEX_COMPONENT_NORMAL, + vks::VERTEX_COMPONENT_UV, + }); + + struct Meshes { + vks::Model skybox; + std::vector objects; + uint32_t objectIndex = 3; + } models; + + struct { + vks::Buffer object; + vks::Buffer skybox; + vks::Buffer params; + } uniformBuffers; + + struct UBOMatrices { + glm::mat4 projection; + glm::mat4 model; + glm::mat4 view; + glm::vec3 camPos; + } uboMatrices; + + struct UBOParams { + float exposure = 10.0f; + float gamma = 2.2f; + } uboParams; + + struct { + VkPipeline skybox; + VkPipeline pbr; + } pipelines; + + struct { + VkDescriptorSet object; + VkDescriptorSet skybox; + } descriptorSets; + + VkPipelineLayout pipelineLayout; + VkDescriptorSetLayout descriptorSetLayout; + + // Default materials to select from + std::vector materials; + int32_t materialIndex = 0; + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + title = "Vulkan Example - Physical based rendering"; + enableTextOverlay = true; + camera.type = Camera::CameraType::firstperson; + camera.setPosition(glm::vec3(13.0f, 8.0f, -10.0f)); + camera.setRotation(glm::vec3(-31.75f, 45.0f, 0.0f)); + camera.movementSpeed = 4.0f; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); + camera.rotationSpeed = 0.25f; + width = 1920; + height = 1080; + + // Setup some default materials (source: https://seblagarde.wordpress.com/2011/08/17/feeding-a-physical-based-lighting-mode/) + materials.push_back(Material("Gold", glm::vec3(1.0f, 0.765557f, 0.336057f), 0.1f, 1.0f)); + materials.push_back(Material("Copper", glm::vec3(0.955008f, 0.637427f, 0.538163f), 0.1f, 1.0f)); + materials.push_back(Material("Chromium", glm::vec3(0.549585f, 0.556114f, 0.554256f), 0.1f, 1.0f)); + materials.push_back(Material("Nickel", glm::vec3(0.659777f, 0.608679f, 0.525649f), 0.1f, 1.0f)); + materials.push_back(Material("Titanium", glm::vec3(0.541931f, 0.496791f, 0.449419f), 0.1f, 1.0f)); + materials.push_back(Material("Cobalt", glm::vec3(0.662124f, 0.654864f, 0.633732f), 0.1f, 1.0f)); + materials.push_back(Material("Platinum", glm::vec3(0.672411f, 0.637331f, 0.585456f), 0.1f, 1.0f)); + // Testing materials + materials.push_back(Material("White", glm::vec3(1.0f), 0.1f, 1.0f)); + materials.push_back(Material("Red", glm::vec3(1.0f, 0.0f, 0.0f), 0.1f, 1.0f)); + materials.push_back(Material("Blue", glm::vec3(0.0f, 0.0f, 1.0f), 0.1f, 1.0f)); + materials.push_back(Material("Black", glm::vec3(0.0f), 0.1f, 1.0f)); + + materialIndex = 4; + } + + ~VulkanExample() + { + vkDestroyPipeline(device, pipelines.skybox, nullptr); + vkDestroyPipeline(device, pipelines.pbr, nullptr); + + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + + for (auto& model : models.objects) { + model.destroy(); + } + models.skybox.destroy(); + + uniformBuffers.object.destroy(); + uniformBuffers.skybox.destroy(); + uniformBuffers.params.destroy(); + textures.radianceMap.destroy(); + textures.irradianceMap.destroy(); + } + + void reBuildCommandBuffers() + { + if (!checkCommandBuffers()) + { + destroyCommandBuffers(); + createCommandBuffers(); + } + buildCommandBuffers(); + } + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = { { 0.1f, 0.1f, 0.1f, 1.0f } }; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + // Set target frame buffer + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + VkDeviceSize offsets[1] = { 0 }; + + // Skybox + if (displaySkybox) + { + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.skybox, 0, NULL); + vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.skybox.vertices.buffer, offsets); + vkCmdBindIndexBuffer(drawCmdBuffers[i], models.skybox.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); + vkCmdDrawIndexed(drawCmdBuffers[i], models.skybox.indexCount, 1, 0, 0, 0); + } + + // Objects + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.object, 0, NULL); + vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.objects[models.objectIndex].vertices.buffer, offsets); + vkCmdBindIndexBuffer(drawCmdBuffers[i], models.objects[models.objectIndex].indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.pbr); + + Material mat = materials[materialIndex]; + +//#define SINGLE_MESH 1 +#ifdef SINGLE_MESH + mat.metallic = 1.0; + mat.roughness = 0.1; + + uint32_t objcount = 10; + for (uint32_t x = 0; x < objcount; x++) { + glm::vec3 pos = glm::vec3(float(x - (objcount / 2.0f)) * 2.5f, 0.0f, 0.0f); + mat.roughness = glm::clamp((float)x / (float)objcount, 0.005f, 1.0f); + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::vec3), &pos); + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(glm::vec3), sizeof(Material), &mat); + vkCmdDrawIndexed(drawCmdBuffers[i], models.objects[models.objectIndex].indexCount, 1, 0, 0, 0); + } +#else + for (uint32_t y = 0; y < GRID_DIM; y++) { + for (uint32_t x = 0; x < GRID_DIM; x++) { + glm::vec3 pos = glm::vec3(float(x - (GRID_DIM / 2.0f)) * 2.5f, 0.0f, float(y - (GRID_DIM / 2.0f)) * 2.5f); + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::vec3), &pos); + mat.metallic = (float)x / (float)(GRID_DIM - 1); + mat.roughness = (float)y / (float)(GRID_DIM - 1); + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(glm::vec3), sizeof(Material), &mat); + vkCmdDrawIndexed(drawCmdBuffers[i], models.objects[models.objectIndex].indexCount, 1, 0, 0, 0); + } + } +#endif + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void loadAssets() + { + // Skybox + models.skybox.loadFromFile(getAssetPath() + "models/cube.obj", vertexLayout, 1.0f, vulkanDevice, queue); + // Objects + std::vector filenames = { "geosphere.obj", "teapot.dae", "torusknot.obj", "venus.fbx" }; + for (auto file : filenames) { + vks::Model model; + model.loadFromFile(getAssetPath() + "models/" + file, vertexLayout, OBJ_DIM * (file == "venus.fbx" ? 3.0f : 1.0f), vulkanDevice, queue); + models.objects.push_back(model); + } + // Radiance and irradiance cube maps for image-based-lighting + // HDR images from http://www.hdrlabs.com/sibl/archive.html, converted to radiance and irradiance maps with https://github.com/dariomanesku/cmft + textures.radianceMap.loadFromFile(getAssetPath() + "textures/hamarikyu_bridge_radiance_cube.ktx", VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); + textures.irradianceMap.loadFromFile(getAssetPath() + "textures/hamarikyu_bridge_irradiance_cube.ktx", VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); + } + + void setupDescriptorSetLayout() + { + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0), + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 2), + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 3), + }; + + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = + vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + + std::vector pushConstantRanges = { + vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(glm::vec3), 0), + vks::initializers::pushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(Material), sizeof(glm::vec3)), + }; + + pipelineLayoutCreateInfo.pushConstantRangeCount = 2; + pipelineLayoutCreateInfo.pPushConstantRanges = pushConstantRanges.data(); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + } + + void setupDescriptorSets() + { + // Descriptor Pool + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6) + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = + vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); + + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Descriptor sets + + VkDescriptorSetAllocateInfo allocInfo = + vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + + // 3D object descriptor set + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.object)); + + std::vector writeDescriptorSets = { + vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.object.descriptor), + vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &uniformBuffers.params.descriptor), + vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &textures.radianceMap.descriptor), + vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3, &textures.irradianceMap.descriptor), + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + + // Sky box descriptor set + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.skybox)); + + writeDescriptorSets = { + vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.skybox.descriptor), + vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &uniformBuffers.params.descriptor), + vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &textures.radianceMap.descriptor), + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + } + + void preparePipelines() + { + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = + vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + + VkPipelineRasterizationStateCreateInfo rasterizationState = + vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE); + + VkPipelineColorBlendAttachmentState blendAttachmentState = + vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + + VkPipelineColorBlendStateCreateInfo colorBlendState = + vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = + vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL); + + VkPipelineViewportStateCreateInfo viewportState = + vks::initializers::pipelineViewportStateCreateInfo(1, 1); + + VkPipelineMultisampleStateCreateInfo multisampleState = + vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicState = + vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = + vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass); + + std::array shaderStages; + + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfo.pStages = shaderStages.data(); + + // Vertex bindings an attributes + // Binding description + std::vector vertexInputBindings = { + vks::initializers::vertexInputBindingDescription(0, vertexLayout.stride(), VK_VERTEX_INPUT_RATE_VERTEX), + }; + + // Attribute descriptions + std::vector vertexInputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position + vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3), // Normal + vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 5), // UV + }; + + VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo(); + vertexInputState.vertexBindingDescriptionCount = static_cast(vertexInputBindings.size()); + vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data(); + vertexInputState.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); + vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data(); + + pipelineCreateInfo.pVertexInputState = &vertexInputState; + + // Skybox pipeline (background cube) + shaderStages[0] = loadShader(getAssetPath() + "shaders/pbribl/skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/pbribl/skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.skybox)); + + // PBR pipeline + shaderStages[0] = loadShader(getAssetPath() + "shaders/pbribl/pbribl.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/pbribl/pbribl.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + // Enable depth test and write + depthStencilState.depthWriteEnable = VK_TRUE; + depthStencilState.depthTestEnable = VK_TRUE; + // Flip cull mode + rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.pbr)); + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + // Objact vertex shader uniform buffer + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.object, + sizeof(uboMatrices))); + + // Skybox vertex shader uniform buffer + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.skybox, + sizeof(uboMatrices))); + + // Shared parameter uniform buffer + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.params, + sizeof(uboParams))); + + // Map persistent + VK_CHECK_RESULT(uniformBuffers.object.map()); + VK_CHECK_RESULT(uniformBuffers.skybox.map()); + VK_CHECK_RESULT(uniformBuffers.params.map()); + + updateUniformBuffers(); + updateParams(); + } + + void updateUniformBuffers() + { + // 3D object + uboMatrices.projection = camera.matrices.perspective; + uboMatrices.view = camera.matrices.view; + uboMatrices.model = glm::rotate(glm::mat4(), glm::radians(-90.0f + (models.objectIndex == 1 ? 45.0f : 0.0f)), glm::vec3(0.0f, 1.0f, 0.0f)); + uboMatrices.camPos = camera.position * -1.0f; + memcpy(uniformBuffers.object.mapped, &uboMatrices, sizeof(uboMatrices)); + + // Skybox + uboMatrices.model = glm::mat4(glm::mat3(camera.matrices.view)); + memcpy(uniformBuffers.skybox.mapped, &uboMatrices, sizeof(uboMatrices)); + } + + void updateParams() + { + memcpy(uniformBuffers.params.mapped, &uboParams, sizeof(uboParams)); + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + VulkanExampleBase::submitFrame(); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + prepareUniformBuffers(); + setupDescriptorSetLayout(); + preparePipelines(); + setupDescriptorSets(); + buildCommandBuffers(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + } + + virtual void viewChanged() + { + updateUniformBuffers(); + updateTextOverlay(); + } + + void toggleSkyBox() + { + displaySkybox = !displaySkybox; + reBuildCommandBuffers(); + } + + void toggleObject() + { + models.objectIndex++; + if (models.objectIndex >= static_cast(models.objects.size())) + { + models.objectIndex = 0; + } + updateUniformBuffers(); + reBuildCommandBuffers(); + } + + void toggleMaterial(int32_t dir) + { + materialIndex += dir; + if (materialIndex < 0) { + materialIndex = static_cast(materials.size()) - 1; + } + if (materialIndex > static_cast(materials.size()) - 1) { + materialIndex = 0; + } + reBuildCommandBuffers(); + updateTextOverlay(); + } + + void changeExposure(float delta) + { + uboParams.exposure += delta; + if (uboParams.exposure < 0.01f) { + uboParams.exposure = 0.01f; + } + updateParams(); + updateTextOverlay(); + } + + virtual void keyPressed(uint32_t keyCode) + { + switch (keyCode) + { + case KEY_F2: + case GAMEPAD_BUTTON_A: + toggleSkyBox(); + break; + case KEY_SPACE: + case GAMEPAD_BUTTON_X: + toggleObject(); + break; + case KEY_KPADD: + case GAMEPAD_BUTTON_R1: + toggleMaterial(1); + break; + case KEY_KPSUB: + case GAMEPAD_BUTTON_L1: + toggleMaterial(-1); + break; + case KEY_F3: + changeExposure(-0.1f); + break; + case KEY_F4: + changeExposure(0.1f); + break; + + } + } + + virtual void getOverlayText(VulkanTextOverlay *textOverlay) + { +#if defined(__ANDROID__) + textOverlay->addText("\"Button A\" to toggle skybox", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("\"Button X\" to toggle object", 5.0f, 100.0f, VulkanTextOverlay::alignLeft); +#else + textOverlay->addText("Base material: " + materials[materialIndex].name + " (+/-)", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("Exposure = " + std::to_string(uboParams.exposure) + " (F3/F4)", 5.0f, 100.0f, VulkanTextOverlay::alignLeft); + //textOverlay->addText("\"F2\" to toggle skybox", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + //textOverlay->addText("\"space\" to toggle object", 5.0f, 100.0f, VulkanTextOverlay::alignLeft); +#endif + } +}; + +VULKAN_EXAMPLE_MAIN() \ No newline at end of file diff --git a/pbribl/pbribl.vcxproj b/pbribl/pbribl.vcxproj new file mode 100644 index 00000000..30ed2a01 --- /dev/null +++ b/pbribl/pbribl.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {659987E9-863C-4B9B-A3D4-CBA7D67A9516} + Win32Proj + 8.1 + + + + Application + true + v140 + + + Application + false + v140 + + + + + + + + + + + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + + WIN32;_DEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + true + Windows + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + WIN32;NDEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + + + true + Windows + true + true + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pbribl/pbribl.vcxproj.filters b/pbribl/pbribl.vcxproj.filters new file mode 100644 index 00000000..aa086bea --- /dev/null +++ b/pbribl/pbribl.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {3adf072b-32a9-412a-8eb5-5329a601df1a} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + \ No newline at end of file diff --git a/vulkanExamples.sln b/vulkanExamples.sln index 79f9f5a1..808643e4 100644 --- a/vulkanExamples.sln +++ b/vulkanExamples.sln @@ -139,6 +139,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hdr", "hdr\hdr.vcxproj", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pbr", "pbr\pbr.vcxproj", "{92B2640A-0CC5-48EA-B34C-520BA13938D1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pbribl", "pbribl\pbribl.vcxproj", "{659987E9-863C-4B9B-A3D4-CBA7D67A9516}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -341,6 +343,10 @@ Global {92B2640A-0CC5-48EA-B34C-520BA13938D1}.Debug|x64.Build.0 = Debug|x64 {92B2640A-0CC5-48EA-B34C-520BA13938D1}.Release|x64.ActiveCfg = Release|x64 {92B2640A-0CC5-48EA-B34C-520BA13938D1}.Release|x64.Build.0 = Release|x64 + {659987E9-863C-4B9B-A3D4-CBA7D67A9516}.Debug|x64.ActiveCfg = Debug|x64 + {659987E9-863C-4B9B-A3D4-CBA7D67A9516}.Debug|x64.Build.0 = Debug|x64 + {659987E9-863C-4B9B-A3D4-CBA7D67A9516}.Release|x64.ActiveCfg = Release|x64 + {659987E9-863C-4B9B-A3D4-CBA7D67A9516}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE