Code cleanup, refactoring and simplification
This commit is contained in:
parent
0888d1c9b0
commit
47c3bd16c4
16 changed files with 500 additions and 901 deletions
|
|
@ -1,7 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* Vulkan Example - Texture loading (and display) example (including mip maps)
|
* Vulkan Example - Texture loading (and display) example (including mip maps)
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de
|
* This sample shows how to upload a 2D texture to the device and how to display it. In Vulkan this is done using images, views and samplers.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
|
||||||
*
|
*
|
||||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||||
*/
|
*/
|
||||||
|
|
@ -23,41 +25,33 @@ public:
|
||||||
// Contains all Vulkan objects that are required to store and use a texture
|
// Contains all Vulkan objects that are required to store and use a texture
|
||||||
// Note that this repository contains a texture class (VulkanTexture.hpp) that encapsulates texture loading functionality in a class that is used in subsequent demos
|
// Note that this repository contains a texture class (VulkanTexture.hpp) that encapsulates texture loading functionality in a class that is used in subsequent demos
|
||||||
struct Texture {
|
struct Texture {
|
||||||
VkSampler sampler;
|
VkSampler sampler{ VK_NULL_HANDLE };
|
||||||
VkImage image;
|
VkImage image{ VK_NULL_HANDLE };
|
||||||
VkImageLayout imageLayout;
|
VkImageLayout imageLayout;
|
||||||
VkDeviceMemory deviceMemory;
|
VkDeviceMemory deviceMemory{ VK_NULL_HANDLE };
|
||||||
VkImageView view;
|
VkImageView view{ VK_NULL_HANDLE };
|
||||||
uint32_t width, height;
|
uint32_t width{ 0 };
|
||||||
uint32_t mipLevels;
|
uint32_t height{ 0 };
|
||||||
|
uint32_t mipLevels{ 0 };
|
||||||
} texture;
|
} texture;
|
||||||
|
|
||||||
struct {
|
|
||||||
VkPipelineVertexInputStateCreateInfo inputState;
|
|
||||||
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
|
|
||||||
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
|
|
||||||
} vertices;
|
|
||||||
|
|
||||||
vks::Buffer vertexBuffer;
|
vks::Buffer vertexBuffer;
|
||||||
vks::Buffer indexBuffer;
|
vks::Buffer indexBuffer;
|
||||||
uint32_t indexCount;
|
uint32_t indexCount{ 0 };
|
||||||
|
|
||||||
vks::Buffer uniformBufferVS;
|
struct UniformData {
|
||||||
|
|
||||||
struct {
|
|
||||||
glm::mat4 projection;
|
glm::mat4 projection;
|
||||||
glm::mat4 modelView;
|
glm::mat4 modelView;
|
||||||
glm::vec4 viewPos;
|
glm::vec4 viewPos;
|
||||||
|
// This is used to change the bias for the level-of-detail (mips) in the fragment shader
|
||||||
float lodBias = 0.0f;
|
float lodBias = 0.0f;
|
||||||
} uboVS;
|
} uniformData;
|
||||||
|
vks::Buffer uniformBuffer;
|
||||||
|
|
||||||
struct {
|
VkPipeline pipeline{ VK_NULL_HANDLE };
|
||||||
VkPipeline solid;
|
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
|
||||||
} pipelines;
|
VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
|
||||||
|
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
|
||||||
VkPipelineLayout pipelineLayout;
|
|
||||||
VkDescriptorSet descriptorSet;
|
|
||||||
VkDescriptorSetLayout descriptorSetLayout;
|
|
||||||
|
|
||||||
VulkanExample() : VulkanExampleBase()
|
VulkanExample() : VulkanExampleBase()
|
||||||
{
|
{
|
||||||
|
|
@ -70,19 +64,15 @@ public:
|
||||||
|
|
||||||
~VulkanExample()
|
~VulkanExample()
|
||||||
{
|
{
|
||||||
// Clean up used Vulkan resources
|
if (device) {
|
||||||
// Note : Inherited destructor cleans up resources stored in base class
|
destroyTextureImage(texture);
|
||||||
|
vkDestroyPipeline(device, pipeline, nullptr);
|
||||||
destroyTextureImage(texture);
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
||||||
vkDestroyPipeline(device, pipelines.solid, nullptr);
|
vertexBuffer.destroy();
|
||||||
|
indexBuffer.destroy();
|
||||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
uniformBuffer.destroy();
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
}
|
||||||
|
|
||||||
vertexBuffer.destroy();
|
|
||||||
indexBuffer.destroy();
|
|
||||||
uniformBufferVS.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable physical device features required for this example
|
// Enable physical device features required for this example
|
||||||
|
|
@ -480,8 +470,8 @@ public:
|
||||||
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
|
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
|
||||||
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
||||||
|
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
|
||||||
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
|
||||||
VkDeviceSize offsets[1] = { 0 };
|
VkDeviceSize offsets[1] = { 0 };
|
||||||
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets);
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets);
|
||||||
|
|
@ -497,20 +487,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw()
|
// Creates a vertex and index buffer for a quad made of two triangles
|
||||||
{
|
// This is used to display the texture on
|
||||||
VulkanExampleBase::prepareFrame();
|
|
||||||
|
|
||||||
// Command buffer to be submitted to the queue
|
|
||||||
submitInfo.commandBufferCount = 1;
|
|
||||||
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
|
||||||
|
|
||||||
// Submit to queue
|
|
||||||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
|
||||||
|
|
||||||
VulkanExampleBase::submitFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
void generateQuad()
|
void generateQuad()
|
||||||
{
|
{
|
||||||
// Setup vertices for a single uv-mapped quad made from two triangles
|
// Setup vertices for a single uv-mapped quad made from two triangles
|
||||||
|
|
@ -526,213 +504,115 @@ public:
|
||||||
std::vector<uint32_t> indices = { 0,1,2, 2,3,0 };
|
std::vector<uint32_t> indices = { 0,1,2, 2,3,0 };
|
||||||
indexCount = static_cast<uint32_t>(indices.size());
|
indexCount = static_cast<uint32_t>(indices.size());
|
||||||
|
|
||||||
// Create buffers
|
// Create buffers and upload data to the GPU
|
||||||
// For the sake of simplicity we won't stage the vertex data to the gpu memory
|
struct StagingBuffers {
|
||||||
// Vertex buffer
|
vks::Buffer vertices;
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
vks::Buffer indices;
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
} stagingBuffers;
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&vertexBuffer,
|
// Host visible source buffers (staging)
|
||||||
vertices.size() * sizeof(Vertex),
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.vertices, vertices.size() * sizeof(Vertex), vertices.data()));
|
||||||
vertices.data()));
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.indices, indices.size() * sizeof(uint32_t), indices.data()));
|
||||||
// Index buffer
|
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
// Device local destination buffers
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertexBuffer, vertices.size() * sizeof(Vertex)));
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &indexBuffer, indices.size() * sizeof(uint32_t)));
|
||||||
&indexBuffer,
|
|
||||||
indices.size() * sizeof(uint32_t),
|
// Copy from host do device
|
||||||
indices.data()));
|
vulkanDevice->copyBuffer(&stagingBuffers.vertices, &vertexBuffer, queue);
|
||||||
|
vulkanDevice->copyBuffer(&stagingBuffers.indices, &indexBuffer, queue);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
stagingBuffers.vertices.destroy();
|
||||||
|
stagingBuffers.indices.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupVertexDescriptions()
|
void setupDescriptors()
|
||||||
{
|
{
|
||||||
// Binding description
|
// Pool
|
||||||
vertices.bindingDescriptions.resize(1);
|
std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||||
vertices.bindingDescriptions[0] =
|
|
||||||
vks::initializers::vertexInputBindingDescription(
|
|
||||||
0,
|
|
||||||
sizeof(Vertex),
|
|
||||||
VK_VERTEX_INPUT_RATE_VERTEX);
|
|
||||||
|
|
||||||
// Attribute descriptions
|
|
||||||
// Describes memory layout and shader positions
|
|
||||||
vertices.attributeDescriptions.resize(3);
|
|
||||||
// Location 0 : Position
|
|
||||||
vertices.attributeDescriptions[0] =
|
|
||||||
vks::initializers::vertexInputAttributeDescription(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
VK_FORMAT_R32G32B32_SFLOAT,
|
|
||||||
offsetof(Vertex, pos));
|
|
||||||
// Location 1 : Texture coordinates
|
|
||||||
vertices.attributeDescriptions[1] =
|
|
||||||
vks::initializers::vertexInputAttributeDescription(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
VK_FORMAT_R32G32_SFLOAT,
|
|
||||||
offsetof(Vertex, uv));
|
|
||||||
// Location 1 : Vertex normal
|
|
||||||
vertices.attributeDescriptions[2] =
|
|
||||||
vks::initializers::vertexInputAttributeDescription(
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
VK_FORMAT_R32G32B32_SFLOAT,
|
|
||||||
offsetof(Vertex, normal));
|
|
||||||
|
|
||||||
vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo();
|
|
||||||
vertices.inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.bindingDescriptions.size());
|
|
||||||
vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data();
|
|
||||||
vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.attributeDescriptions.size());
|
|
||||||
vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorPool()
|
|
||||||
{
|
|
||||||
// Example uses one ubo and one image sampler
|
|
||||||
std::vector<VkDescriptorPoolSize> poolSizes =
|
|
||||||
{
|
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
||||||
|
// The sample uses a combined image + sampler descriptor to sample the texture in the fragment shader
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
||||||
};
|
};
|
||||||
|
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
||||||
VkDescriptorPoolCreateInfo descriptorPoolInfo =
|
|
||||||
vks::initializers::descriptorPoolCreateInfo(
|
|
||||||
static_cast<uint32_t>(poolSizes.size()),
|
|
||||||
poolSizes.data(),
|
|
||||||
2);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSetLayout()
|
// Layout
|
||||||
{
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
|
|
||||||
{
|
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
vks::initializers::descriptorSetLayoutBinding(
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
VK_SHADER_STAGE_VERTEX_BIT,
|
|
||||||
0),
|
|
||||||
// Binding 1 : Fragment shader image sampler
|
// Binding 1 : Fragment shader image sampler
|
||||||
vks::initializers::descriptorSetLayoutBinding(
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
||||||
1)
|
|
||||||
};
|
};
|
||||||
|
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||||
VkDescriptorSetLayoutCreateInfo descriptorLayout =
|
|
||||||
vks::initializers::descriptorSetLayoutCreateInfo(
|
|
||||||
setLayoutBindings.data(),
|
|
||||||
static_cast<uint32_t>(setLayoutBindings.size()));
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
|
// Set
|
||||||
vks::initializers::pipelineLayoutCreateInfo(
|
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||||
&descriptorSetLayout,
|
|
||||||
1);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSet()
|
|
||||||
{
|
|
||||||
VkDescriptorSetAllocateInfo allocInfo =
|
|
||||||
vks::initializers::descriptorSetAllocateInfo(
|
|
||||||
descriptorPool,
|
|
||||||
&descriptorSetLayout,
|
|
||||||
1);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
||||||
|
|
||||||
// Setup a descriptor image info for the current texture to be used as a combined image sampler
|
// Setup a descriptor image info for the current texture to be used as a combined image sampler
|
||||||
VkDescriptorImageInfo textureDescriptor;
|
VkDescriptorImageInfo textureDescriptor;
|
||||||
textureDescriptor.imageView = texture.view; // The image's view (images are never directly accessed by the shader, but rather through views defining subresources)
|
// The image's view (images are never directly accessed by the shader, but rather through views defining subresources)
|
||||||
textureDescriptor.sampler = texture.sampler; // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.)
|
textureDescriptor.imageView = texture.view;
|
||||||
textureDescriptor.imageLayout = texture.imageLayout; // The current layout of the image (Note: Should always fit the actual use, e.g. shader read)
|
// The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.)
|
||||||
|
textureDescriptor.sampler = texture.sampler;
|
||||||
|
// The current layout of the image(Note: Should always fit the actual use, e.g.shader read)
|
||||||
|
textureDescriptor.imageLayout = texture.imageLayout;
|
||||||
|
|
||||||
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
|
||||||
{
|
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
vks::initializers::writeDescriptorSet(
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
|
||||||
descriptorSet,
|
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
0,
|
|
||||||
&uniformBufferVS.descriptor),
|
|
||||||
// Binding 1 : Fragment shader texture sampler
|
// Binding 1 : Fragment shader texture sampler
|
||||||
// Fragment shader: layout (binding = 1) uniform sampler2D samplerColor;
|
// Fragment shader: layout (binding = 1) uniform sampler2D samplerColor;
|
||||||
vks::initializers::writeDescriptorSet(
|
vks::initializers::writeDescriptorSet(descriptorSet,
|
||||||
descriptorSet,
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (as opposed to splitting image and sampler)
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (sampler and image could be split)
|
|
||||||
1, // Shader binding point 1
|
1, // Shader binding point 1
|
||||||
&textureDescriptor) // Pointer to the descriptor image for our texture
|
&textureDescriptor) // Pointer to the descriptor image for our texture
|
||||||
};
|
};
|
||||||
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void preparePipelines()
|
void preparePipelines()
|
||||||
{
|
{
|
||||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
|
// Layout
|
||||||
vks::initializers::pipelineInputAssemblyStateCreateInfo(
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
||||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||||
0,
|
|
||||||
VK_FALSE);
|
|
||||||
|
|
||||||
VkPipelineRasterizationStateCreateInfo rasterizationState =
|
// Pipeline
|
||||||
vks::initializers::pipelineRasterizationStateCreateInfo(
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
||||||
VK_POLYGON_MODE_FILL,
|
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
|
||||||
VK_CULL_MODE_NONE,
|
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
||||||
VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
|
||||||
0);
|
VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
|
||||||
|
VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
||||||
VkPipelineColorBlendAttachmentState blendAttachmentState =
|
VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
|
||||||
vks::initializers::pipelineColorBlendAttachmentState(
|
std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||||
0xf,
|
VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
|
||||||
VK_FALSE);
|
|
||||||
|
|
||||||
VkPipelineColorBlendStateCreateInfo colorBlendState =
|
|
||||||
vks::initializers::pipelineColorBlendStateCreateInfo(
|
|
||||||
1,
|
|
||||||
&blendAttachmentState);
|
|
||||||
|
|
||||||
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
|
||||||
vks::initializers::pipelineDepthStencilStateCreateInfo(
|
|
||||||
VK_TRUE,
|
|
||||||
VK_TRUE,
|
|
||||||
VK_COMPARE_OP_LESS_OR_EQUAL);
|
|
||||||
|
|
||||||
VkPipelineViewportStateCreateInfo viewportState =
|
|
||||||
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo multisampleState =
|
|
||||||
vks::initializers::pipelineMultisampleStateCreateInfo(
|
|
||||||
VK_SAMPLE_COUNT_1_BIT,
|
|
||||||
0);
|
|
||||||
|
|
||||||
std::vector<VkDynamicState> dynamicStateEnables = {
|
|
||||||
VK_DYNAMIC_STATE_VIEWPORT,
|
|
||||||
VK_DYNAMIC_STATE_SCISSOR
|
|
||||||
};
|
|
||||||
VkPipelineDynamicStateCreateInfo dynamicState =
|
|
||||||
vks::initializers::pipelineDynamicStateCreateInfo(
|
|
||||||
dynamicStateEnables.data(),
|
|
||||||
static_cast<uint32_t>(dynamicStateEnables.size()),
|
|
||||||
0);
|
|
||||||
|
|
||||||
// Load shaders
|
|
||||||
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
|
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
|
||||||
|
|
||||||
|
// Shaders
|
||||||
shaderStages[0] = loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
shaderStages[0] = loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
||||||
shaderStages[1] = loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
shaderStages[1] = loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||||
|
|
||||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
|
// Vertex input state
|
||||||
vks::initializers::pipelineCreateInfo(
|
std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
|
||||||
pipelineLayout,
|
vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX)
|
||||||
renderPass,
|
};
|
||||||
0);
|
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
|
||||||
|
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)),
|
||||||
|
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)),
|
||||||
|
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)),
|
||||||
|
};
|
||||||
|
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
|
||||||
|
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
|
||||||
|
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
|
||||||
|
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
|
||||||
|
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
|
||||||
|
|
||||||
pipelineCreateInfo.pVertexInputState = &vertices.inputState;
|
VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
|
||||||
|
pipelineCreateInfo.pVertexInputState = &vertexInputState;
|
||||||
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
||||||
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
||||||
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
||||||
|
|
@ -742,31 +622,23 @@ public:
|
||||||
pipelineCreateInfo.pDynamicState = &dynamicState;
|
pipelineCreateInfo.pDynamicState = &dynamicState;
|
||||||
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
||||||
pipelineCreateInfo.pStages = shaderStages.data();
|
pipelineCreateInfo.pStages = shaderStages.data();
|
||||||
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
|
||||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare and initialize uniform buffer containing shader uniforms
|
// Prepare and initialize uniform buffer containing shader uniforms
|
||||||
void prepareUniformBuffers()
|
void prepareUniformBuffers()
|
||||||
{
|
{
|
||||||
// Vertex shader uniform buffer block
|
// Vertex shader uniform buffer block
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(uniformData), &uniformData));
|
||||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
VK_CHECK_RESULT(uniformBuffer.map());
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&uniformBufferVS,
|
|
||||||
sizeof(uboVS),
|
|
||||||
&uboVS));
|
|
||||||
VK_CHECK_RESULT(uniformBufferVS.map());
|
|
||||||
|
|
||||||
updateUniformBuffers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUniformBuffers()
|
void updateUniformBuffers()
|
||||||
{
|
{
|
||||||
uboVS.projection = camera.matrices.perspective;
|
uniformData.projection = camera.matrices.perspective;
|
||||||
uboVS.modelView = camera.matrices.view;
|
uniformData.modelView = camera.matrices.view;
|
||||||
uboVS.viewPos = camera.viewPos;
|
uniformData.viewPos = camera.viewPos;
|
||||||
memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS));
|
memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepare()
|
void prepare()
|
||||||
|
|
@ -774,32 +646,34 @@ public:
|
||||||
VulkanExampleBase::prepare();
|
VulkanExampleBase::prepare();
|
||||||
loadTexture();
|
loadTexture();
|
||||||
generateQuad();
|
generateQuad();
|
||||||
setupVertexDescriptions();
|
|
||||||
prepareUniformBuffers();
|
prepareUniformBuffers();
|
||||||
setupDescriptorSetLayout();
|
setupDescriptors();
|
||||||
preparePipelines();
|
preparePipelines();
|
||||||
setupDescriptorPool();
|
|
||||||
setupDescriptorSet();
|
|
||||||
buildCommandBuffers();
|
buildCommandBuffers();
|
||||||
prepared = true;
|
prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
VulkanExampleBase::prepareFrame();
|
||||||
|
submitInfo.commandBufferCount = 1;
|
||||||
|
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
||||||
|
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
||||||
|
VulkanExampleBase::submitFrame();
|
||||||
|
}
|
||||||
|
|
||||||
virtual void render()
|
virtual void render()
|
||||||
{
|
{
|
||||||
if (!prepared)
|
if (!prepared)
|
||||||
return;
|
return;
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void viewChanged()
|
|
||||||
{
|
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
|
draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
||||||
{
|
{
|
||||||
if (overlay->header("Settings")) {
|
if (overlay->header("Settings")) {
|
||||||
if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, 0.0f, (float)texture.mipLevels)) {
|
if (overlay->sliderFloat("LOD bias", &uniformData.lodBias, 0.0f, (float)texture.mipLevels)) {
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,36 +134,30 @@ public:
|
||||||
VkImageView view = VK_NULL_HANDLE;
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
VkDescriptorImageInfo descriptor;
|
VkDescriptorImageInfo descriptor;
|
||||||
VkFormat format;
|
VkFormat format;
|
||||||
uint32_t width, height, depth;
|
uint32_t width{ 0 };
|
||||||
uint32_t mipLevels;
|
uint32_t height{ 0 };
|
||||||
|
uint32_t depth{ 0 };
|
||||||
|
uint32_t mipLevels{ 0 };
|
||||||
} texture;
|
} texture;
|
||||||
|
|
||||||
struct {
|
|
||||||
VkPipelineVertexInputStateCreateInfo inputState;
|
|
||||||
std::vector<VkVertexInputBindingDescription> inputBinding;
|
|
||||||
std::vector<VkVertexInputAttributeDescription> inputAttributes;
|
|
||||||
} vertices;
|
|
||||||
|
|
||||||
vks::Buffer vertexBuffer;
|
vks::Buffer vertexBuffer;
|
||||||
vks::Buffer indexBuffer;
|
vks::Buffer indexBuffer;
|
||||||
uint32_t indexCount;
|
uint32_t indexCount{ 0 };
|
||||||
|
|
||||||
vks::Buffer uniformBufferVS;
|
struct UniformData {
|
||||||
|
|
||||||
struct UboVS {
|
|
||||||
glm::mat4 projection;
|
glm::mat4 projection;
|
||||||
glm::mat4 modelView;
|
glm::mat4 modelView;
|
||||||
glm::vec4 viewPos;
|
glm::vec4 viewPos;
|
||||||
|
// The current depth level of the texture to display
|
||||||
|
// This is animated
|
||||||
float depth = 0.0f;
|
float depth = 0.0f;
|
||||||
} uboVS;
|
} uniformData;
|
||||||
|
vks::Buffer uniformBuffer;
|
||||||
|
|
||||||
struct {
|
VkPipeline pipeline{ VK_NULL_HANDLE };
|
||||||
VkPipeline solid;
|
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
|
||||||
} pipelines;
|
VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
|
||||||
|
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
|
||||||
VkPipelineLayout pipelineLayout;
|
|
||||||
VkDescriptorSet descriptorSet;
|
|
||||||
VkDescriptorSetLayout descriptorSetLayout;
|
|
||||||
|
|
||||||
VulkanExample() : VulkanExampleBase()
|
VulkanExample() : VulkanExampleBase()
|
||||||
{
|
{
|
||||||
|
|
@ -177,19 +171,15 @@ public:
|
||||||
|
|
||||||
~VulkanExample()
|
~VulkanExample()
|
||||||
{
|
{
|
||||||
// Clean up used Vulkan resources
|
if (device) {
|
||||||
// Note : Inherited destructor cleans up resources stored in base class
|
destroyTextureImage(texture);
|
||||||
|
vkDestroyPipeline(device, pipeline, nullptr);
|
||||||
destroyTextureImage(texture);
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
||||||
vkDestroyPipeline(device, pipelines.solid, nullptr);
|
vertexBuffer.destroy();
|
||||||
|
indexBuffer.destroy();
|
||||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
uniformBuffer.destroy();
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
}
|
||||||
|
|
||||||
vertexBuffer.destroy();
|
|
||||||
indexBuffer.destroy();
|
|
||||||
uniformBufferVS.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare all Vulkan resources for the 3D texture (including descriptors)
|
// Prepare all Vulkan resources for the 3D texture (including descriptors)
|
||||||
|
|
@ -312,14 +302,8 @@ public:
|
||||||
float nx = (float)x / (float)texture.width;
|
float nx = (float)x / (float)texture.width;
|
||||||
float ny = (float)y / (float)texture.height;
|
float ny = (float)y / (float)texture.height;
|
||||||
float nz = (float)z / (float)texture.depth;
|
float nz = (float)z / (float)texture.depth;
|
||||||
#define FRACTAL
|
|
||||||
#ifdef FRACTAL
|
|
||||||
float n = fractalNoise.noise(nx * noiseScale, ny * noiseScale, nz * noiseScale);
|
float n = fractalNoise.noise(nx * noiseScale, ny * noiseScale, nz * noiseScale);
|
||||||
#else
|
|
||||||
float n = 20.0 * perlinNoise.noise(nx, ny, nz);
|
|
||||||
#endif
|
|
||||||
n = n - floor(n);
|
n = n - floor(n);
|
||||||
|
|
||||||
data[x + y * texture.width + z * texture.width * texture.height] = static_cast<uint8_t>(floor(n * 255));
|
data[x + y * texture.width + z * texture.width * texture.height] = static_cast<uint8_t>(floor(n * 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -457,7 +441,7 @@ public:
|
||||||
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
||||||
|
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
||||||
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
|
||||||
VkDeviceSize offsets[1] = { 0 };
|
VkDeviceSize offsets[1] = { 0 };
|
||||||
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets);
|
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets);
|
||||||
|
|
@ -472,20 +456,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw()
|
// Creates a vertex and index buffer for a quad made of two triangles
|
||||||
{
|
// This is used to display the texture on
|
||||||
VulkanExampleBase::prepareFrame();
|
|
||||||
|
|
||||||
// Command buffer to be submitted to the queue
|
|
||||||
submitInfo.commandBufferCount = 1;
|
|
||||||
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
|
||||||
|
|
||||||
// Submit to queue
|
|
||||||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
|
||||||
|
|
||||||
VulkanExampleBase::submitFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
void generateQuad()
|
void generateQuad()
|
||||||
{
|
{
|
||||||
// Setup vertices for a single uv-mapped quad made from two triangles
|
// Setup vertices for a single uv-mapped quad made from two triangles
|
||||||
|
|
@ -501,206 +473,108 @@ public:
|
||||||
std::vector<uint32_t> indices = { 0,1,2, 2,3,0 };
|
std::vector<uint32_t> indices = { 0,1,2, 2,3,0 };
|
||||||
indexCount = static_cast<uint32_t>(indices.size());
|
indexCount = static_cast<uint32_t>(indices.size());
|
||||||
|
|
||||||
// Create buffers
|
// Create buffers and upload data to the GPU
|
||||||
// For the sake of simplicity we won't stage the vertex data to the gpu memory
|
struct StagingBuffers {
|
||||||
// Vertex buffer
|
vks::Buffer vertices;
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
vks::Buffer indices;
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
} stagingBuffers;
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&vertexBuffer,
|
// Host visible source buffers (staging)
|
||||||
vertices.size() * sizeof(Vertex),
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.vertices, vertices.size() * sizeof(Vertex), vertices.data()));
|
||||||
vertices.data()));
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.indices, indices.size() * sizeof(uint32_t), indices.data()));
|
||||||
// Index buffer
|
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
// Device local destination buffers
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertexBuffer, vertices.size() * sizeof(Vertex)));
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &indexBuffer, indices.size() * sizeof(uint32_t)));
|
||||||
&indexBuffer,
|
|
||||||
indices.size() * sizeof(uint32_t),
|
// Copy from host do device
|
||||||
indices.data()));
|
vulkanDevice->copyBuffer(&stagingBuffers.vertices, &vertexBuffer, queue);
|
||||||
|
vulkanDevice->copyBuffer(&stagingBuffers.indices, &indexBuffer, queue);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
stagingBuffers.vertices.destroy();
|
||||||
|
stagingBuffers.indices.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupVertexDescriptions()
|
void setupDescriptors()
|
||||||
{
|
{
|
||||||
// Binding description
|
// Pool
|
||||||
vertices.inputBinding.resize(1);
|
std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||||
vertices.inputBinding[0] =
|
|
||||||
vks::initializers::vertexInputBindingDescription(
|
|
||||||
0,
|
|
||||||
sizeof(Vertex),
|
|
||||||
VK_VERTEX_INPUT_RATE_VERTEX);
|
|
||||||
|
|
||||||
// Attribute descriptions
|
|
||||||
// Describes memory layout and shader positions
|
|
||||||
vertices.inputAttributes.resize(3);
|
|
||||||
// Location 0 : Position
|
|
||||||
vertices.inputAttributes[0] =
|
|
||||||
vks::initializers::vertexInputAttributeDescription(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
VK_FORMAT_R32G32B32_SFLOAT,
|
|
||||||
offsetof(Vertex, pos));
|
|
||||||
// Location 1 : Texture coordinates
|
|
||||||
vertices.inputAttributes[1] =
|
|
||||||
vks::initializers::vertexInputAttributeDescription(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
VK_FORMAT_R32G32_SFLOAT,
|
|
||||||
offsetof(Vertex, uv));
|
|
||||||
// Location 1 : Vertex normal
|
|
||||||
vertices.inputAttributes[2] =
|
|
||||||
vks::initializers::vertexInputAttributeDescription(
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
VK_FORMAT_R32G32B32_SFLOAT,
|
|
||||||
offsetof(Vertex, normal));
|
|
||||||
|
|
||||||
vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo();
|
|
||||||
vertices.inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.inputBinding.size());
|
|
||||||
vertices.inputState.pVertexBindingDescriptions = vertices.inputBinding.data();
|
|
||||||
vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.inputAttributes.size());
|
|
||||||
vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorPool()
|
|
||||||
{
|
|
||||||
// Example uses one ubo and one image sampler
|
|
||||||
std::vector<VkDescriptorPoolSize> poolSizes =
|
|
||||||
{
|
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
||||||
};
|
};
|
||||||
|
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
||||||
VkDescriptorPoolCreateInfo descriptorPoolInfo =
|
|
||||||
vks::initializers::descriptorPoolCreateInfo(
|
|
||||||
static_cast<uint32_t>(poolSizes.size()),
|
|
||||||
poolSizes.data(),
|
|
||||||
2);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSetLayout()
|
// Layout
|
||||||
{
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
|
|
||||||
{
|
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
vks::initializers::descriptorSetLayoutBinding(
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
VK_SHADER_STAGE_VERTEX_BIT,
|
|
||||||
0),
|
|
||||||
// Binding 1 : Fragment shader image sampler
|
// Binding 1 : Fragment shader image sampler
|
||||||
vks::initializers::descriptorSetLayoutBinding(
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
||||||
1)
|
|
||||||
};
|
};
|
||||||
|
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||||
VkDescriptorSetLayoutCreateInfo descriptorLayout =
|
|
||||||
vks::initializers::descriptorSetLayoutCreateInfo(
|
|
||||||
setLayoutBindings.data(),
|
|
||||||
static_cast<uint32_t>(setLayoutBindings.size()));
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
|
// Set
|
||||||
vks::initializers::pipelineLayoutCreateInfo(
|
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||||
&descriptorSetLayout,
|
|
||||||
1);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSet()
|
|
||||||
{
|
|
||||||
VkDescriptorSetAllocateInfo allocInfo =
|
|
||||||
vks::initializers::descriptorSetAllocateInfo(
|
|
||||||
descriptorPool,
|
|
||||||
&descriptorSetLayout,
|
|
||||||
1);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
||||||
|
|
||||||
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
|
// Image descriptor for the 3D texture
|
||||||
{
|
VkDescriptorImageInfo textureDescriptor =
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
vks::initializers::descriptorImageInfo(
|
||||||
vks::initializers::writeDescriptorSet(
|
texture.sampler,
|
||||||
descriptorSet,
|
texture.view,
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
texture.imageLayout);
|
||||||
0,
|
|
||||||
&uniformBufferVS.descriptor),
|
|
||||||
// Binding 1 : Fragment shader texture sampler
|
|
||||||
vks::initializers::writeDescriptorSet(
|
|
||||||
descriptorSet,
|
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
1,
|
|
||||||
&texture.descriptor)
|
|
||||||
};
|
|
||||||
|
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
|
||||||
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
|
||||||
|
// Binding 1 : Fragment shader texture sampler
|
||||||
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
|
||||||
|
};
|
||||||
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void preparePipelines()
|
void preparePipelines()
|
||||||
{
|
{
|
||||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
|
// Layout
|
||||||
vks::initializers::pipelineInputAssemblyStateCreateInfo(
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
||||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||||
0,
|
|
||||||
VK_FALSE);
|
|
||||||
|
|
||||||
VkPipelineRasterizationStateCreateInfo rasterizationState =
|
// Pipeline
|
||||||
vks::initializers::pipelineRasterizationStateCreateInfo(
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
||||||
VK_POLYGON_MODE_FILL,
|
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
|
||||||
VK_CULL_MODE_NONE,
|
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
||||||
VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
|
||||||
0);
|
VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
|
||||||
|
VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
||||||
VkPipelineColorBlendAttachmentState blendAttachmentState =
|
VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
|
||||||
vks::initializers::pipelineColorBlendAttachmentState(
|
std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||||
0xf,
|
VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
|
||||||
VK_FALSE);
|
|
||||||
|
|
||||||
VkPipelineColorBlendStateCreateInfo colorBlendState =
|
|
||||||
vks::initializers::pipelineColorBlendStateCreateInfo(
|
|
||||||
1,
|
|
||||||
&blendAttachmentState);
|
|
||||||
|
|
||||||
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
|
||||||
vks::initializers::pipelineDepthStencilStateCreateInfo(
|
|
||||||
VK_TRUE,
|
|
||||||
VK_TRUE,
|
|
||||||
VK_COMPARE_OP_LESS_OR_EQUAL);
|
|
||||||
|
|
||||||
VkPipelineViewportStateCreateInfo viewportState =
|
|
||||||
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo multisampleState =
|
|
||||||
vks::initializers::pipelineMultisampleStateCreateInfo(
|
|
||||||
VK_SAMPLE_COUNT_1_BIT,
|
|
||||||
0);
|
|
||||||
|
|
||||||
std::vector<VkDynamicState> dynamicStateEnables = {
|
|
||||||
VK_DYNAMIC_STATE_VIEWPORT,
|
|
||||||
VK_DYNAMIC_STATE_SCISSOR
|
|
||||||
};
|
|
||||||
VkPipelineDynamicStateCreateInfo dynamicState =
|
|
||||||
vks::initializers::pipelineDynamicStateCreateInfo(
|
|
||||||
dynamicStateEnables.data(),
|
|
||||||
static_cast<uint32_t>(dynamicStateEnables.size()),
|
|
||||||
0);
|
|
||||||
|
|
||||||
// Load shaders
|
|
||||||
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
|
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
|
||||||
|
|
||||||
|
// Shaders
|
||||||
shaderStages[0] = loadShader(getShadersPath() + "texture3d/texture3d.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
shaderStages[0] = loadShader(getShadersPath() + "texture3d/texture3d.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
||||||
shaderStages[1] = loadShader(getShadersPath() + "texture3d/texture3d.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
shaderStages[1] = loadShader(getShadersPath() + "texture3d/texture3d.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||||
|
|
||||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
|
// Vertex input state
|
||||||
vks::initializers::pipelineCreateInfo(
|
std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
|
||||||
pipelineLayout,
|
vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX)
|
||||||
renderPass,
|
};
|
||||||
0);
|
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
|
||||||
|
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)),
|
||||||
|
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)),
|
||||||
|
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)),
|
||||||
|
};
|
||||||
|
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
|
||||||
|
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
|
||||||
|
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
|
||||||
|
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
|
||||||
|
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
|
||||||
|
|
||||||
pipelineCreateInfo.pVertexInputState = &vertices.inputState;
|
VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
|
||||||
|
pipelineCreateInfo.pVertexInputState = &vertexInputState;
|
||||||
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
||||||
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
||||||
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
||||||
|
|
@ -710,59 +584,59 @@ public:
|
||||||
pipelineCreateInfo.pDynamicState = &dynamicState;
|
pipelineCreateInfo.pDynamicState = &dynamicState;
|
||||||
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
||||||
pipelineCreateInfo.pStages = shaderStages.data();
|
pipelineCreateInfo.pStages = shaderStages.data();
|
||||||
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
|
||||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare and initialize uniform buffer containing shader uniforms
|
// Prepare and initialize uniform buffer containing shader uniforms
|
||||||
void prepareUniformBuffers()
|
void prepareUniformBuffers()
|
||||||
{
|
{
|
||||||
// Vertex shader uniform buffer block
|
// Vertex shader uniform buffer block
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData), &uniformData));
|
||||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
VK_CHECK_RESULT(uniformBuffer.map());
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&uniformBufferVS,
|
|
||||||
sizeof(uboVS),
|
|
||||||
&uboVS));
|
|
||||||
VK_CHECK_RESULT(uniformBufferVS.map());
|
|
||||||
updateUniformBuffers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUniformBuffers()
|
void updateUniformBuffers()
|
||||||
{
|
{
|
||||||
uboVS.projection = camera.matrices.perspective;
|
uniformData.projection = camera.matrices.perspective;
|
||||||
uboVS.modelView = camera.matrices.view;
|
uniformData.modelView = camera.matrices.view;
|
||||||
uboVS.viewPos = camera.viewPos;
|
uniformData.viewPos = camera.viewPos;
|
||||||
uboVS.depth += frameTimer * 0.15f;
|
if (!paused) {
|
||||||
if (uboVS.depth > 1.0f) {
|
// Animate depth
|
||||||
uboVS.depth = uboVS.depth - 1.0f;
|
uniformData.depth += frameTimer * 0.15f;
|
||||||
|
if (uniformData.depth > 1.0f) {
|
||||||
|
uniformData.depth = uniformData.depth - 1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS));
|
memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepare()
|
void prepare()
|
||||||
{
|
{
|
||||||
VulkanExampleBase::prepare();
|
VulkanExampleBase::prepare();
|
||||||
generateQuad();
|
generateQuad();
|
||||||
setupVertexDescriptions();
|
|
||||||
prepareUniformBuffers();
|
prepareUniformBuffers();
|
||||||
prepareNoiseTexture(128, 128, 128);
|
prepareNoiseTexture(128, 128, 128);
|
||||||
setupDescriptorSetLayout();
|
setupDescriptors();
|
||||||
preparePipelines();
|
preparePipelines();
|
||||||
setupDescriptorPool();
|
|
||||||
setupDescriptorSet();
|
|
||||||
buildCommandBuffers();
|
buildCommandBuffers();
|
||||||
prepared = true;
|
prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
VulkanExampleBase::prepareFrame();
|
||||||
|
submitInfo.commandBufferCount = 1;
|
||||||
|
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
||||||
|
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
||||||
|
VulkanExampleBase::submitFrame();
|
||||||
|
}
|
||||||
|
|
||||||
virtual void render()
|
virtual void render()
|
||||||
{
|
{
|
||||||
if (!prepared)
|
if (!prepared)
|
||||||
return;
|
return;
|
||||||
|
updateUniformBuffers();
|
||||||
draw();
|
draw();
|
||||||
if (!paused) {
|
|
||||||
updateUniformBuffers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* Vulkan Example - Texture arrays and instanced rendering
|
* Vulkan Example - Texture arrays and instanced rendering
|
||||||
*
|
*
|
||||||
|
* This sample shows how to load and render a texture array. This is a single layered texture where each layer contains different image data.
|
||||||
|
* The different layers are displayed on cubes using instancing, where each instance selects a different layer from the texture
|
||||||
|
*
|
||||||
* Copyright (C) 2016-2023 Sascha Willems - www.saschawillems.de
|
* Copyright (C) 2016-2023 Sascha Willems - www.saschawillems.de
|
||||||
*
|
*
|
||||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||||
|
|
@ -23,38 +26,36 @@ class VulkanExample : public VulkanExampleBase
|
||||||
public:
|
public:
|
||||||
// Number of array layers in texture array
|
// Number of array layers in texture array
|
||||||
// Also used as instance count
|
// Also used as instance count
|
||||||
uint32_t layerCount;
|
uint32_t layerCount{ 0 };
|
||||||
vks::Texture textureArray;
|
vks::Texture textureArray;
|
||||||
|
|
||||||
vks::Buffer vertexBuffer;
|
vks::Buffer vertexBuffer;
|
||||||
vks::Buffer indexBuffer;
|
vks::Buffer indexBuffer;
|
||||||
uint32_t indexCount;
|
uint32_t indexCount{ 0 };
|
||||||
|
|
||||||
vks::Buffer uniformBufferVS;
|
// Values passed to the shader per drawn instance
|
||||||
|
struct alignas(16) PerInstanceData {
|
||||||
struct UboInstanceData {
|
|
||||||
// Model matrix
|
// Model matrix
|
||||||
glm::mat4 model;
|
glm::mat4 model;
|
||||||
// Texture array index
|
// Layer index from which this instance will sample in the fragment shader
|
||||||
// Vec4 due to padding
|
float arrayIndex{ 0 };
|
||||||
glm::vec4 arrayIndex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct UniformData {
|
||||||
// Global matrices
|
// Global matrices
|
||||||
struct {
|
struct {
|
||||||
glm::mat4 projection;
|
glm::mat4 projection;
|
||||||
glm::mat4 view;
|
glm::mat4 view;
|
||||||
} matrices;
|
} matrices;
|
||||||
// Separate data for each instance
|
// Separate data for each instance
|
||||||
UboInstanceData *instance;
|
PerInstanceData* instance{ nullptr };
|
||||||
} uboVS;
|
} uniformData;
|
||||||
|
vks::Buffer uniformBuffer;
|
||||||
|
|
||||||
|
VkPipeline pipeline{ VK_NULL_HANDLE };
|
||||||
VkPipeline pipeline;
|
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
|
||||||
VkPipelineLayout pipelineLayout;
|
VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
|
||||||
VkDescriptorSet descriptorSet;
|
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
|
||||||
VkDescriptorSetLayout descriptorSetLayout;
|
|
||||||
|
|
||||||
VulkanExample() : VulkanExampleBase()
|
VulkanExample() : VulkanExampleBase()
|
||||||
{
|
{
|
||||||
|
|
@ -67,25 +68,19 @@ public:
|
||||||
|
|
||||||
~VulkanExample()
|
~VulkanExample()
|
||||||
{
|
{
|
||||||
// Clean up used Vulkan resources
|
if (device) {
|
||||||
// Note : Inherited destructor cleans up resources stored in base class
|
vkDestroyImageView(device, textureArray.view, nullptr);
|
||||||
|
vkDestroyImage(device, textureArray.image, nullptr);
|
||||||
vkDestroyImageView(device, textureArray.view, nullptr);
|
vkDestroySampler(device, textureArray.sampler, nullptr);
|
||||||
vkDestroyImage(device, textureArray.image, nullptr);
|
vkFreeMemory(device, textureArray.deviceMemory, nullptr);
|
||||||
vkDestroySampler(device, textureArray.sampler, nullptr);
|
vkDestroyPipeline(device, pipeline, nullptr);
|
||||||
vkFreeMemory(device, textureArray.deviceMemory, nullptr);
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
||||||
vkDestroyPipeline(device, pipeline, nullptr);
|
vertexBuffer.destroy();
|
||||||
|
indexBuffer.destroy();
|
||||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
uniformBuffer.destroy();
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
delete[] uniformData.instance;
|
||||||
|
}
|
||||||
vertexBuffer.destroy();
|
|
||||||
indexBuffer.destroy();
|
|
||||||
|
|
||||||
uniformBufferVS.destroy();
|
|
||||||
|
|
||||||
delete[] uboVS.instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadTextureArray(std::string filename, VkFormat format)
|
void loadTextureArray(std::string filename, VkFormat format)
|
||||||
|
|
@ -324,6 +319,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a vertex and index buffer for a cube
|
||||||
|
// This is used to display the texture on
|
||||||
void generateCube()
|
void generateCube()
|
||||||
{
|
{
|
||||||
std::vector<Vertex> vertices = {
|
std::vector<Vertex> vertices = {
|
||||||
|
|
@ -363,49 +360,50 @@ public:
|
||||||
|
|
||||||
indexCount = static_cast<uint32_t>(indices.size());
|
indexCount = static_cast<uint32_t>(indices.size());
|
||||||
|
|
||||||
// Create buffers
|
// Create buffers and upload data to the GPU
|
||||||
// For the sake of simplicity we won't stage the vertex data to the gpu memory
|
struct StagingBuffers {
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
vks::Buffer vertices;
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
vks::Buffer indices;
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
} stagingBuffers;
|
||||||
&vertexBuffer,
|
|
||||||
vertices.size() * sizeof(Vertex),
|
// Host visible source buffers (staging)
|
||||||
vertices.data()));
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.vertices, vertices.size() * sizeof(Vertex), vertices.data()));
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.indices, indices.size() * sizeof(uint32_t), indices.data()));
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
// Device local destination buffers
|
||||||
&indexBuffer,
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertexBuffer, vertices.size() * sizeof(Vertex)));
|
||||||
indices.size() * sizeof(uint32_t),
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &indexBuffer, indices.size() * sizeof(uint32_t)));
|
||||||
indices.data()));
|
|
||||||
|
// Copy from host do device
|
||||||
|
vulkanDevice->copyBuffer(&stagingBuffers.vertices, &vertexBuffer, queue);
|
||||||
|
vulkanDevice->copyBuffer(&stagingBuffers.indices, &indexBuffer, queue);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
stagingBuffers.vertices.destroy();
|
||||||
|
stagingBuffers.indices.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDescriptorPool()
|
void setupDescriptors()
|
||||||
{
|
{
|
||||||
|
// Pool
|
||||||
std::vector<VkDescriptorPoolSize> poolSizes = {
|
std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
||||||
};
|
};
|
||||||
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSetLayout()
|
// Layout
|
||||||
{
|
|
||||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
|
||||||
// Binding 1 : Fragment shader image sampler (texture array)
|
// Binding 1 : Fragment shader image sampler
|
||||||
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
|
||||||
};
|
};
|
||||||
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
// Set
|
||||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSet()
|
|
||||||
{
|
|
||||||
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
||||||
|
|
||||||
|
|
@ -418,8 +416,8 @@ public:
|
||||||
|
|
||||||
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBufferVS.descriptor),
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
|
||||||
// Binding 1 : Fragment shader cubemap sampler
|
// Binding 1 : Fragment shader texture sampler
|
||||||
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
|
||||||
};
|
};
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
|
|
@ -427,6 +425,11 @@ public:
|
||||||
|
|
||||||
void preparePipelines()
|
void preparePipelines()
|
||||||
{
|
{
|
||||||
|
// Layout
|
||||||
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
||||||
|
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||||
|
|
||||||
|
// Pipeline
|
||||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
||||||
VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
|
VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
|
||||||
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
||||||
|
|
@ -466,64 +469,46 @@ public:
|
||||||
pipelineCI.pDynamicState = &dynamicStateCI;
|
pipelineCI.pDynamicState = &dynamicStateCI;
|
||||||
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
|
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
|
||||||
pipelineCI.pStages = shaderStages.data();
|
pipelineCI.pStages = shaderStages.data();
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepareUniformBuffers()
|
void prepareUniformBuffers()
|
||||||
{
|
{
|
||||||
uboVS.instance = new UboInstanceData[layerCount];
|
uniformData.instance = new PerInstanceData[layerCount];
|
||||||
|
|
||||||
uint32_t uboSize = sizeof(uboVS.matrices) + (MAX_LAYERS * sizeof(UboInstanceData));
|
uint32_t uboSize = sizeof(uniformData.matrices) + (MAX_LAYERS * sizeof(PerInstanceData));
|
||||||
|
|
||||||
// Vertex shader uniform buffer block
|
// Vertex shader uniform buffer block
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, uboSize));
|
||||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&uniformBufferVS,
|
|
||||||
uboSize));
|
|
||||||
|
|
||||||
// Array indices and model matrices are fixed
|
// Array indices and model matrices are fixed
|
||||||
float offset = -1.5f;
|
float offset = -1.5f;
|
||||||
float center = (layerCount*offset) / 2.0f - (offset * 0.5f);
|
float center = (layerCount*offset) / 2.0f - (offset * 0.5f);
|
||||||
for (uint32_t i = 0; i < layerCount; i++) {
|
for (uint32_t i = 0; i < layerCount; i++) {
|
||||||
// Instance model matrix
|
// Instance model matrix
|
||||||
uboVS.instance[i].model = glm::translate(glm::mat4(1.0f), glm::vec3(i * offset - center, 0.0f, 0.0f));
|
uniformData.instance[i].model = glm::translate(glm::mat4(1.0f), glm::vec3(i * offset - center, 0.0f, 0.0f));
|
||||||
uboVS.instance[i].model = glm::scale(uboVS.instance[i].model, glm::vec3(0.5f));
|
uniformData.instance[i].model = glm::scale(uniformData.instance[i].model, glm::vec3(0.5f));
|
||||||
// Instance texture array index
|
// Instance texture array index
|
||||||
uboVS.instance[i].arrayIndex.x = (float)i;
|
uniformData.instance[i].arrayIndex = (float)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update instanced part of the uniform buffer
|
// Update instanced part of the uniform buffer
|
||||||
uint8_t *pData;
|
uint8_t *pData;
|
||||||
uint32_t dataOffset = sizeof(uboVS.matrices);
|
uint32_t dataOffset = sizeof(uniformData.matrices);
|
||||||
uint32_t dataSize = layerCount * sizeof(UboInstanceData);
|
uint32_t dataSize = layerCount * sizeof(PerInstanceData);
|
||||||
VK_CHECK_RESULT(vkMapMemory(device, uniformBufferVS.memory, dataOffset, dataSize, 0, (void **)&pData));
|
VK_CHECK_RESULT(vkMapMemory(device, uniformBuffer.memory, dataOffset, dataSize, 0, (void **)&pData));
|
||||||
memcpy(pData, uboVS.instance, dataSize);
|
memcpy(pData, uniformData.instance, dataSize);
|
||||||
vkUnmapMemory(device, uniformBufferVS.memory);
|
vkUnmapMemory(device, uniformBuffer.memory);
|
||||||
|
|
||||||
// Map persistent
|
// Map persistent
|
||||||
VK_CHECK_RESULT(uniformBufferVS.map());
|
VK_CHECK_RESULT(uniformBuffer.map());
|
||||||
|
|
||||||
updateUniformBuffersCamera();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUniformBuffersCamera()
|
void updateUniformBuffersCamera()
|
||||||
{
|
{
|
||||||
uboVS.matrices.projection = camera.matrices.perspective;
|
uniformData.matrices.projection = camera.matrices.perspective;
|
||||||
uboVS.matrices.view = camera.matrices.view;
|
uniformData.matrices.view = camera.matrices.view;
|
||||||
memcpy(uniformBufferVS.mapped, &uboVS.matrices, sizeof(uboVS.matrices));
|
memcpy(uniformBuffer.mapped, &uniformData.matrices, sizeof(uniformData.matrices));
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
void prepare()
|
||||||
|
|
@ -532,26 +517,27 @@ public:
|
||||||
loadAssets();
|
loadAssets();
|
||||||
generateCube();
|
generateCube();
|
||||||
prepareUniformBuffers();
|
prepareUniformBuffers();
|
||||||
setupDescriptorSetLayout();
|
setupDescriptors();
|
||||||
preparePipelines();
|
preparePipelines();
|
||||||
setupDescriptorPool();
|
|
||||||
setupDescriptorSet();
|
|
||||||
buildCommandBuffers();
|
buildCommandBuffers();
|
||||||
prepared = true;
|
prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
VulkanExampleBase::prepareFrame();
|
||||||
|
submitInfo.commandBufferCount = 1;
|
||||||
|
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
||||||
|
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
||||||
|
VulkanExampleBase::submitFrame();
|
||||||
|
}
|
||||||
|
|
||||||
virtual void render()
|
virtual void render()
|
||||||
{
|
{
|
||||||
if (!prepared)
|
if (!prepared)
|
||||||
return;
|
return;
|
||||||
draw();
|
|
||||||
if (camera.updated)
|
|
||||||
updateUniformBuffersCamera();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void viewChanged()
|
|
||||||
{
|
|
||||||
updateUniformBuffersCamera();
|
updateUniformBuffersCamera();
|
||||||
|
draw();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* Vulkan Example - Cube map texture loading and displaying
|
* Vulkan Example - Cube map texture loading and displaying
|
||||||
*
|
*
|
||||||
|
* This sample shows how to load and render a cubemap. A cubemap is a textures that contains 6 images, one per cube face.
|
||||||
|
* The sample displays the cubemap as a skybox (background) and as a reflection on a selectable object
|
||||||
|
*
|
||||||
* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
|
* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
|
||||||
*
|
*
|
||||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||||
|
|
@ -18,36 +21,29 @@ public:
|
||||||
|
|
||||||
vks::Texture cubeMap;
|
vks::Texture cubeMap;
|
||||||
|
|
||||||
struct Meshes {
|
struct Models {
|
||||||
vkglTF::Model skybox;
|
vkglTF::Model skybox;
|
||||||
|
// The sample lets you select different models to apply the cubemap to
|
||||||
std::vector<vkglTF::Model> objects;
|
std::vector<vkglTF::Model> objects;
|
||||||
int32_t objectIndex = 0;
|
int32_t objectIndex = 0;
|
||||||
} models;
|
} models;
|
||||||
|
|
||||||
struct {
|
|
||||||
vks::Buffer object;
|
|
||||||
vks::Buffer skybox;
|
|
||||||
} uniformBuffers;
|
|
||||||
|
|
||||||
struct UBOVS {
|
struct UBOVS {
|
||||||
glm::mat4 projection;
|
glm::mat4 projection;
|
||||||
glm::mat4 modelView;
|
glm::mat4 modelView;
|
||||||
glm::mat4 inverseModelview;
|
glm::mat4 inverseModelview;
|
||||||
float lodBias = 0.0f;
|
float lodBias = 0.0f;
|
||||||
} uboVS;
|
} uboVS;
|
||||||
|
vks::Buffer uniformBuffer;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
VkPipeline skybox;
|
VkPipeline skybox{ VK_NULL_HANDLE };
|
||||||
VkPipeline reflect;
|
VkPipeline reflect{ VK_NULL_HANDLE };
|
||||||
} pipelines;
|
} pipelines;
|
||||||
|
|
||||||
struct {
|
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
|
||||||
VkDescriptorSet object;
|
VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
|
||||||
VkDescriptorSet skybox;
|
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
|
||||||
} descriptorSets;
|
|
||||||
|
|
||||||
VkPipelineLayout pipelineLayout;
|
|
||||||
VkDescriptorSetLayout descriptorSetLayout;
|
|
||||||
|
|
||||||
std::vector<std::string> objectNames;
|
std::vector<std::string> objectNames;
|
||||||
|
|
||||||
|
|
@ -63,23 +59,17 @@ public:
|
||||||
|
|
||||||
~VulkanExample()
|
~VulkanExample()
|
||||||
{
|
{
|
||||||
// Clean up used Vulkan resources
|
if (device) {
|
||||||
// Note : Inherited destructor cleans up resources stored in base class
|
vkDestroyImageView(device, cubeMap.view, nullptr);
|
||||||
|
vkDestroyImage(device, cubeMap.image, nullptr);
|
||||||
// Clean up texture resources
|
vkDestroySampler(device, cubeMap.sampler, nullptr);
|
||||||
vkDestroyImageView(device, cubeMap.view, nullptr);
|
vkFreeMemory(device, cubeMap.deviceMemory, nullptr);
|
||||||
vkDestroyImage(device, cubeMap.image, nullptr);
|
vkDestroyPipeline(device, pipelines.skybox, nullptr);
|
||||||
vkDestroySampler(device, cubeMap.sampler, nullptr);
|
vkDestroyPipeline(device, pipelines.reflect, nullptr);
|
||||||
vkFreeMemory(device, cubeMap.deviceMemory, nullptr);
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
||||||
vkDestroyPipeline(device, pipelines.skybox, nullptr);
|
uniformBuffer.destroy();
|
||||||
vkDestroyPipeline(device, pipelines.reflect, nullptr);
|
}
|
||||||
|
|
||||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
|
||||||
|
|
||||||
uniformBuffers.object.destroy();
|
|
||||||
uniformBuffers.skybox.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable physical device features required for this example
|
// Enable physical device features required for this example
|
||||||
|
|
@ -90,7 +80,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadCubemap(std::string filename, VkFormat format, bool forceLinearTiling)
|
// Loads a cubemap from a file, uploads it to the device and create all Vulkan resources required to display it
|
||||||
|
void loadCubemap(std::string filename, VkFormat format)
|
||||||
{
|
{
|
||||||
ktxResult result;
|
ktxResult result;
|
||||||
ktxTexture* ktxTexture;
|
ktxTexture* ktxTexture;
|
||||||
|
|
@ -315,16 +306,16 @@ public:
|
||||||
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
|
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
|
||||||
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
||||||
|
|
||||||
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
|
||||||
|
|
||||||
// Skybox
|
// Skybox
|
||||||
if (displaySkybox)
|
if (displaySkybox)
|
||||||
{
|
{
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.skybox, 0, NULL);
|
|
||||||
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
||||||
models.skybox.draw(drawCmdBuffers[i]);
|
models.skybox.draw(drawCmdBuffers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3D object
|
// 3D object
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.object, 0, NULL);
|
|
||||||
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
|
||||||
models.objects[models.objectIndex].draw(drawCmdBuffers[i]);
|
models.objects[models.objectIndex].draw(drawCmdBuffers[i]);
|
||||||
|
|
||||||
|
|
@ -349,111 +340,53 @@ public:
|
||||||
models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags);
|
models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags);
|
||||||
}
|
}
|
||||||
// Cubemap texture
|
// Cubemap texture
|
||||||
const bool forceLinearTiling = false;
|
loadCubemap(getAssetPath() + "textures/cubemap_yokohama_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM);
|
||||||
loadCubemap(getAssetPath() + "textures/cubemap_yokohama_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, forceLinearTiling);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDescriptorPool()
|
void setupDescriptors()
|
||||||
{
|
{
|
||||||
std::vector<VkDescriptorPoolSize> poolSizes =
|
// Pool
|
||||||
{
|
std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2),
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2)
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
||||||
};
|
};
|
||||||
|
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
||||||
VkDescriptorPoolCreateInfo descriptorPoolInfo =
|
|
||||||
vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSetLayout()
|
// Layout
|
||||||
{
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
{
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0),
|
||||||
// Binding 0 : Uniform buffer
|
|
||||||
vks::initializers::descriptorSetLayoutBinding(
|
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
||||||
0),
|
|
||||||
// Binding 1 : Fragment shader image sampler
|
// Binding 1 : Fragment shader image sampler
|
||||||
vks::initializers::descriptorSetLayoutBinding(
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
||||||
1)
|
|
||||||
};
|
};
|
||||||
|
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||||
VkDescriptorSetLayoutCreateInfo descriptorLayout =
|
|
||||||
vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
|
// Set
|
||||||
vks::initializers::pipelineLayoutCreateInfo(
|
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||||
&descriptorSetLayout,
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
||||||
1);
|
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSets()
|
|
||||||
{
|
|
||||||
// Image descriptor for the cube map texture
|
// Image descriptor for the cube map texture
|
||||||
VkDescriptorImageInfo textureDescriptor =
|
VkDescriptorImageInfo textureDescriptor = vks::initializers::descriptorImageInfo(cubeMap.sampler, cubeMap.view, cubeMap.imageLayout);
|
||||||
vks::initializers::descriptorImageInfo(
|
|
||||||
cubeMap.sampler,
|
|
||||||
cubeMap.view,
|
|
||||||
cubeMap.imageLayout);
|
|
||||||
|
|
||||||
VkDescriptorSetAllocateInfo allocInfo =
|
|
||||||
vks::initializers::descriptorSetAllocateInfo(
|
|
||||||
descriptorPool,
|
|
||||||
&descriptorSetLayout,
|
|
||||||
1);
|
|
||||||
|
|
||||||
// 3D object descriptor set
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.object));
|
|
||||||
|
|
||||||
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
|
||||||
{
|
{
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
vks::initializers::writeDescriptorSet(
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
|
||||||
descriptorSets.object,
|
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
0,
|
|
||||||
&uniformBuffers.object.descriptor),
|
|
||||||
// Binding 1 : Fragment shader cubemap sampler
|
// Binding 1 : Fragment shader cubemap sampler
|
||||||
vks::initializers::writeDescriptorSet(
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
|
||||||
descriptorSets.object,
|
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
1,
|
|
||||||
&textureDescriptor)
|
|
||||||
};
|
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
|
||||||
|
|
||||||
// Sky box descriptor set
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.skybox));
|
|
||||||
|
|
||||||
writeDescriptorSets =
|
|
||||||
{
|
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
|
||||||
vks::initializers::writeDescriptorSet(
|
|
||||||
descriptorSets.skybox,
|
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
0,
|
|
||||||
&uniformBuffers.skybox.descriptor),
|
|
||||||
// Binding 1 : Fragment shader cubemap sampler
|
|
||||||
vks::initializers::writeDescriptorSet(
|
|
||||||
descriptorSets.skybox,
|
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
1,
|
|
||||||
&textureDescriptor)
|
|
||||||
};
|
};
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void preparePipelines()
|
void preparePipelines()
|
||||||
{
|
{
|
||||||
|
// Layout
|
||||||
|
const VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
||||||
|
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
|
||||||
|
|
||||||
|
// Pipeline
|
||||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
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, 0);
|
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
|
||||||
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
||||||
|
|
@ -496,39 +429,28 @@ public:
|
||||||
// Prepare and initialize uniform buffer containing shader uniforms
|
// Prepare and initialize uniform buffer containing shader uniforms
|
||||||
void prepareUniformBuffers()
|
void prepareUniformBuffers()
|
||||||
{
|
{
|
||||||
// Object 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, &uniformBuffer, sizeof(uboVS)));
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
VK_CHECK_RESULT(uniformBuffer.map());
|
||||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&uniformBuffers.object,
|
|
||||||
sizeof(uboVS)));
|
|
||||||
|
|
||||||
// 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(uboVS)));
|
|
||||||
|
|
||||||
// Map persistent
|
|
||||||
VK_CHECK_RESULT(uniformBuffers.object.map());
|
|
||||||
VK_CHECK_RESULT(uniformBuffers.skybox.map());
|
|
||||||
|
|
||||||
updateUniformBuffers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUniformBuffers()
|
void updateUniformBuffers()
|
||||||
{
|
{
|
||||||
// 3D object
|
|
||||||
uboVS.projection = camera.matrices.perspective;
|
uboVS.projection = camera.matrices.perspective;
|
||||||
|
// Note: Both the object and skybox use the same uniform data, the translation part of the skybox is removed in the shader (see skybox.vert)
|
||||||
uboVS.modelView = camera.matrices.view;
|
uboVS.modelView = camera.matrices.view;
|
||||||
uboVS.inverseModelview = glm::inverse(camera.matrices.view);
|
uboVS.inverseModelview = glm::inverse(camera.matrices.view);
|
||||||
memcpy(uniformBuffers.object.mapped, &uboVS, sizeof(uboVS));
|
memcpy(uniformBuffer.mapped, &uboVS, sizeof(uboVS));
|
||||||
// Skybox
|
}
|
||||||
uboVS.modelView = camera.matrices.view;
|
|
||||||
// Cancel out translation
|
void prepare()
|
||||||
uboVS.modelView[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
{
|
||||||
memcpy(uniformBuffers.skybox.mapped, &uboVS, sizeof(uboVS));
|
VulkanExampleBase::prepare();
|
||||||
|
loadAssets();
|
||||||
|
prepareUniformBuffers();
|
||||||
|
setupDescriptors();
|
||||||
|
preparePipelines();
|
||||||
|
buildCommandBuffers();
|
||||||
|
prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw()
|
void draw()
|
||||||
|
|
@ -539,30 +461,12 @@ public:
|
||||||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
||||||
VulkanExampleBase::submitFrame();
|
VulkanExampleBase::submitFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepare()
|
|
||||||
{
|
|
||||||
VulkanExampleBase::prepare();
|
|
||||||
loadAssets();
|
|
||||||
prepareUniformBuffers();
|
|
||||||
setupDescriptorSetLayout();
|
|
||||||
preparePipelines();
|
|
||||||
setupDescriptorPool();
|
|
||||||
setupDescriptorSets();
|
|
||||||
buildCommandBuffers();
|
|
||||||
prepared = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void render()
|
virtual void render()
|
||||||
{
|
{
|
||||||
if (!prepared)
|
if (!prepared)
|
||||||
return;
|
return;
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void viewChanged()
|
|
||||||
{
|
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
|
draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
/*
|
/*
|
||||||
* Vulkan Example - Cube map array texture loading and displaying
|
* Vulkan Example - Cube map array texture loading and displaying
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de
|
* This sample shows how load and render an cubemap array texture. A single image contains multiple cube maps.
|
||||||
|
* The cubemap to be displayed is selected in the fragment shader
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020-2023 by Sascha Willems - www.saschawillems.de
|
||||||
*
|
*
|
||||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||||
*/
|
*/
|
||||||
|
|
@ -24,31 +27,24 @@ public:
|
||||||
int32_t objectIndex = 0;
|
int32_t objectIndex = 0;
|
||||||
} models;
|
} models;
|
||||||
|
|
||||||
struct {
|
struct UniformData {
|
||||||
vks::Buffer object;
|
|
||||||
vks::Buffer skybox;
|
|
||||||
} uniformBuffers;
|
|
||||||
|
|
||||||
struct ShaderData {
|
|
||||||
glm::mat4 projection;
|
glm::mat4 projection;
|
||||||
glm::mat4 modelView;
|
glm::mat4 modelView;
|
||||||
glm::mat4 inverseModelview;
|
glm::mat4 inverseModelview;
|
||||||
float lodBias = 0.0f;
|
float lodBias = 0.0f;
|
||||||
|
// Used by the fragment shader to select the cubemap from the array cubemap
|
||||||
int cubeMapIndex = 1;
|
int cubeMapIndex = 1;
|
||||||
} shaderData;
|
} uniformData;
|
||||||
|
vks::Buffer uniformBuffer;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
VkPipeline skybox;
|
VkPipeline skybox{ VK_NULL_HANDLE };
|
||||||
VkPipeline reflect;
|
VkPipeline reflect{ VK_NULL_HANDLE };
|
||||||
} pipelines;
|
} pipelines;
|
||||||
|
|
||||||
struct {
|
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
|
||||||
VkDescriptorSet object;
|
VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
|
||||||
VkDescriptorSet skybox;
|
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
|
||||||
} descriptorSets;
|
|
||||||
|
|
||||||
VkPipelineLayout pipelineLayout;
|
|
||||||
VkDescriptorSetLayout descriptorSetLayout;
|
|
||||||
|
|
||||||
std::vector<std::string> objectNames;
|
std::vector<std::string> objectNames;
|
||||||
|
|
||||||
|
|
@ -63,25 +59,23 @@ public:
|
||||||
|
|
||||||
~VulkanExample()
|
~VulkanExample()
|
||||||
{
|
{
|
||||||
// Clean up texture resources
|
if (device) {
|
||||||
vkDestroyImageView(device, cubeMapArray.view, nullptr);
|
vkDestroyImageView(device, cubeMapArray.view, nullptr);
|
||||||
vkDestroyImage(device, cubeMapArray.image, nullptr);
|
vkDestroyImage(device, cubeMapArray.image, nullptr);
|
||||||
vkDestroySampler(device, cubeMapArray.sampler, nullptr);
|
vkDestroySampler(device, cubeMapArray.sampler, nullptr);
|
||||||
vkFreeMemory(device, cubeMapArray.deviceMemory, nullptr);
|
vkFreeMemory(device, cubeMapArray.deviceMemory, nullptr);
|
||||||
|
vkDestroyPipeline(device, pipelines.skybox, nullptr);
|
||||||
vkDestroyPipeline(device, pipelines.skybox, nullptr);
|
vkDestroyPipeline(device, pipelines.reflect, nullptr);
|
||||||
vkDestroyPipeline(device, pipelines.reflect, nullptr);
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
||||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
uniformBuffer.destroy();
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
}
|
||||||
|
|
||||||
uniformBuffers.object.destroy();
|
|
||||||
uniformBuffers.skybox.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable physical device features required for this example
|
// Enable physical device features required for this example
|
||||||
virtual void getEnabledFeatures()
|
virtual void getEnabledFeatures()
|
||||||
{
|
{
|
||||||
|
// This sample requires support for cube map arrays
|
||||||
if (deviceFeatures.imageCubeArray) {
|
if (deviceFeatures.imageCubeArray) {
|
||||||
enabledFeatures.imageCubeArray = VK_TRUE;
|
enabledFeatures.imageCubeArray = VK_TRUE;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -93,7 +87,8 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void loadCubemapArray(std::string filename, VkFormat format, bool forceLinearTiling)
|
// Loads a cubemap array from a file, uploads it to the device and create all Vulkan resources required to display it
|
||||||
|
void loadCubemapArray(std::string filename, VkFormat format)
|
||||||
{
|
{
|
||||||
ktxResult result;
|
ktxResult result;
|
||||||
ktxTexture* ktxTexture;
|
ktxTexture* ktxTexture;
|
||||||
|
|
@ -320,16 +315,16 @@ public:
|
||||||
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
|
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
|
||||||
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
||||||
|
|
||||||
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
|
||||||
|
|
||||||
// Skybox
|
// Skybox
|
||||||
if (displaySkybox)
|
if (displaySkybox)
|
||||||
{
|
{
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.skybox, 0, NULL);
|
|
||||||
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
||||||
models.skybox.draw(drawCmdBuffers[i]);
|
models.skybox.draw(drawCmdBuffers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3D object
|
// 3D object
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.object, 0, NULL);
|
|
||||||
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
|
||||||
models.objects[models.objectIndex].draw(drawCmdBuffers[i]);
|
models.objects[models.objectIndex].draw(drawCmdBuffers[i]);
|
||||||
|
|
||||||
|
|
@ -354,66 +349,53 @@ public:
|
||||||
models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags);
|
models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags);
|
||||||
}
|
}
|
||||||
// Load the cube map array from a ktx texture file
|
// Load the cube map array from a ktx texture file
|
||||||
loadCubemapArray(getAssetPath() + "textures/cubemap_array.ktx", VK_FORMAT_R8G8B8A8_UNORM, false);
|
loadCubemapArray(getAssetPath() + "textures/cubemap_array.ktx", VK_FORMAT_R8G8B8A8_UNORM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDescriptorPool()
|
void setupDescriptors()
|
||||||
{
|
{
|
||||||
const std::vector<VkDescriptorPoolSize> poolSizes = {
|
// Pool
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2),
|
std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2)
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
||||||
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
||||||
};
|
};
|
||||||
const VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSetLayout()
|
// Layout
|
||||||
{
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||||
const std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
// Binding 0 : Uniform buffer
|
|
||||||
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_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0),
|
||||||
// Binding 1 : Fragment shader image sampler
|
// Binding 1 : Fragment shader image sampler
|
||||||
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
|
||||||
};
|
};
|
||||||
|
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||||
const VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||||
|
|
||||||
const VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
// Set
|
||||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDescriptorSets()
|
|
||||||
{
|
|
||||||
// Image descriptor for the cube map texture
|
|
||||||
VkDescriptorImageInfo textureDescriptor = vks::initializers::descriptorImageInfo(cubeMapArray.sampler, cubeMapArray.view, cubeMapArray.imageLayout);
|
|
||||||
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||||
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
||||||
|
|
||||||
|
// Image descriptor for the cube map array texture
|
||||||
|
VkDescriptorImageInfo textureDescriptor = vks::initializers::descriptorImageInfo(cubeMapArray.sampler, cubeMapArray.view, cubeMapArray.imageLayout);
|
||||||
|
|
||||||
// 3D object descriptor set
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.object));
|
|
||||||
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
|
||||||
{
|
{
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
// Binding 0 : Vertex shader uniform buffer
|
||||||
vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.object.descriptor),
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
|
||||||
// Binding 1 : Fragment shader cubemap sampler
|
// Binding 1 : Fragment shader cubemap sampler
|
||||||
vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
|
||||||
};
|
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
|
||||||
|
|
||||||
// Sky box descriptor set
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.skybox));
|
|
||||||
writeDescriptorSets =
|
|
||||||
{
|
|
||||||
// Binding 0 : Vertex shader uniform buffer
|
|
||||||
vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.skybox.descriptor),
|
|
||||||
// Binding 1 : Fragment shader cubemap sampler
|
|
||||||
vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
|
|
||||||
};
|
};
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void preparePipelines()
|
void preparePipelines()
|
||||||
{
|
{
|
||||||
|
// Layout
|
||||||
|
const VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
||||||
|
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
|
||||||
|
|
||||||
|
// Pipelines
|
||||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
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, 0);
|
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
|
||||||
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
||||||
|
|
@ -457,39 +439,28 @@ public:
|
||||||
void prepareUniformBuffers()
|
void prepareUniformBuffers()
|
||||||
{
|
{
|
||||||
// Object vertex shader uniform buffer
|
// Object vertex shader uniform buffer
|
||||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData)));
|
||||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
||||||
&uniformBuffers.object,
|
|
||||||
sizeof(ShaderData)));
|
|
||||||
|
|
||||||
// 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(ShaderData)));
|
|
||||||
|
|
||||||
// Map persistent
|
// Map persistent
|
||||||
VK_CHECK_RESULT(uniformBuffers.object.map());
|
VK_CHECK_RESULT(uniformBuffer.map());
|
||||||
VK_CHECK_RESULT(uniformBuffers.skybox.map());
|
|
||||||
|
|
||||||
updateUniformBuffers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUniformBuffers()
|
void updateUniformBuffers()
|
||||||
{
|
{
|
||||||
// 3D object
|
uniformData.projection = camera.matrices.perspective;
|
||||||
shaderData.projection = camera.matrices.perspective;
|
uniformData.modelView = camera.matrices.view;
|
||||||
shaderData.modelView = camera.matrices.view;
|
uniformData.inverseModelview = glm::inverse(camera.matrices.view);
|
||||||
shaderData.inverseModelview = glm::inverse(camera.matrices.view);
|
memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData));
|
||||||
memcpy(uniformBuffers.object.mapped, &shaderData, sizeof(ShaderData));
|
}
|
||||||
|
|
||||||
// Skybox
|
void prepare()
|
||||||
shaderData.modelView = camera.matrices.view;
|
{
|
||||||
// Cancel out translation
|
VulkanExampleBase::prepare();
|
||||||
shaderData.modelView[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
loadAssets();
|
||||||
memcpy(uniformBuffers.skybox.mapped, &shaderData, sizeof(ShaderData));
|
prepareUniformBuffers();
|
||||||
|
setupDescriptors();
|
||||||
|
preparePipelines();
|
||||||
|
buildCommandBuffers();
|
||||||
|
prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw()
|
void draw()
|
||||||
|
|
@ -501,43 +472,19 @@ public:
|
||||||
VulkanExampleBase::submitFrame();
|
VulkanExampleBase::submitFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepare()
|
|
||||||
{
|
|
||||||
VulkanExampleBase::prepare();
|
|
||||||
loadAssets();
|
|
||||||
prepareUniformBuffers();
|
|
||||||
setupDescriptorSetLayout();
|
|
||||||
preparePipelines();
|
|
||||||
setupDescriptorPool();
|
|
||||||
setupDescriptorSets();
|
|
||||||
buildCommandBuffers();
|
|
||||||
prepared = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void render()
|
virtual void render()
|
||||||
{
|
{
|
||||||
if (!prepared)
|
if (!prepared)
|
||||||
return;
|
return;
|
||||||
draw();
|
|
||||||
if (camera.updated) {
|
|
||||||
updateUniformBuffers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void viewChanged()
|
|
||||||
{
|
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
|
draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
||||||
{
|
{
|
||||||
if (overlay->header("Settings")) {
|
if (overlay->header("Settings")) {
|
||||||
if (overlay->sliderInt("Cube map", &shaderData.cubeMapIndex, 0, cubeMapArray.layerCount - 1)) {
|
overlay->sliderInt("Cube map", &uniformData.cubeMapIndex, 0, cubeMapArray.layerCount - 1);
|
||||||
updateUniformBuffers();
|
overlay->sliderFloat("LOD bias", &uniformData.lodBias, 0.0f, (float)cubeMapArray.mipLevels);
|
||||||
}
|
|
||||||
if (overlay->sliderFloat("LOD bias", &shaderData.lodBias, 0.0f, (float)cubeMapArray.mipLevels)) {
|
|
||||||
updateUniformBuffers();
|
|
||||||
}
|
|
||||||
if (overlay->comboBox("Object type", &models.objectIndex, objectNames)) {
|
if (overlay->comboBox("Object type", &models.objectIndex, objectNames)) {
|
||||||
buildCommandBuffers();
|
buildCommandBuffers();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ layout (location = 1) in vec2 inUV;
|
||||||
struct Instance
|
struct Instance
|
||||||
{
|
{
|
||||||
mat4 model;
|
mat4 model;
|
||||||
vec4 arrayIndex;
|
float arrayIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout (binding = 0) uniform UBO
|
layout (binding = 0) uniform UBO
|
||||||
|
|
@ -20,7 +20,7 @@ layout (location = 0) out vec3 outUV;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
outUV = vec3(inUV, ubo.instance[gl_InstanceIndex].arrayIndex.x);
|
outUV = vec3(inUV, ubo.instance[gl_InstanceIndex].arrayIndex);
|
||||||
mat4 modelView = ubo.view * ubo.instance[gl_InstanceIndex].model;
|
mat4 modelView = ubo.view * ubo.instance[gl_InstanceIndex].model;
|
||||||
gl_Position = ubo.projection * modelView * vec4(inPos, 1.0);
|
gl_Position = ubo.projection * modelView * vec4(inPos, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -15,5 +15,7 @@ void main()
|
||||||
outUVW = inPos;
|
outUVW = inPos;
|
||||||
// Convert cubemap coordinates into Vulkan coordinate space
|
// Convert cubemap coordinates into Vulkan coordinate space
|
||||||
outUVW.xy *= -1.0;
|
outUVW.xy *= -1.0;
|
||||||
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
|
// Remove translation from view matrix
|
||||||
|
mat4 viewMat = mat4(mat3(ubo.model));
|
||||||
|
gl_Position = ubo.projection * viewMat * vec4(inPos.xyz, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -17,5 +17,7 @@ void main()
|
||||||
{
|
{
|
||||||
outUVW = inPos;
|
outUVW = inPos;
|
||||||
outUVW.yz *= -1.0f;
|
outUVW.yz *= -1.0f;
|
||||||
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
|
// Remove translation from view matrix
|
||||||
|
mat4 viewMat = mat4(mat3(ubo.model));
|
||||||
|
gl_Position = ubo.projection * viewMat * vec4(inPos.xyz, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ struct VSInput
|
||||||
struct Instance
|
struct Instance
|
||||||
{
|
{
|
||||||
float4x4 model;
|
float4x4 model;
|
||||||
float4 arrayIndex;
|
float arrayIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UBO
|
struct UBO
|
||||||
|
|
@ -30,7 +30,7 @@ struct VSOutput
|
||||||
VSOutput main(VSInput input, uint InstanceIndex : SV_InstanceID)
|
VSOutput main(VSInput input, uint InstanceIndex : SV_InstanceID)
|
||||||
{
|
{
|
||||||
VSOutput output = (VSOutput)0;
|
VSOutput output = (VSOutput)0;
|
||||||
output.UV = float3(input.UV, ubo.instance[InstanceIndex].arrayIndex.x);
|
output.UV = float3(input.UV, ubo.instance[InstanceIndex].arrayIndex);
|
||||||
float4x4 modelView = mul(ubo.view, ubo.instance[InstanceIndex].model);
|
float4x4 modelView = mul(ubo.view, ubo.instance[InstanceIndex].model);
|
||||||
output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos, 1.0)));
|
output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos, 1.0)));
|
||||||
return output;
|
return output;
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -20,6 +20,11 @@ VSOutput main([[vk::location(0)]] float3 Pos : POSITION0)
|
||||||
output.UVW = Pos;
|
output.UVW = Pos;
|
||||||
// Convert cubemap coordinates into Vulkan coordinate space
|
// Convert cubemap coordinates into Vulkan coordinate space
|
||||||
output.UVW.xy *= -1.0;
|
output.UVW.xy *= -1.0;
|
||||||
output.Pos = mul(ubo.projection, mul(ubo.model, float4(Pos.xyz, 1.0)));
|
// Remove translation from view matrix
|
||||||
|
float4x4 viewMat = ubo.model;
|
||||||
|
viewMat[0][3] = 0.0;
|
||||||
|
viewMat[1][3] = 0.0;
|
||||||
|
viewMat[2][3] = 0.0;
|
||||||
|
output.Pos = mul(ubo.projection, mul(viewMat, float4(Pos.xyz, 1.0)));
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -22,6 +22,11 @@ VSOutput main([[vk::location(0)]] float3 Pos : POSITION0)
|
||||||
VSOutput output = (VSOutput)0;
|
VSOutput output = (VSOutput)0;
|
||||||
output.UVW = Pos;
|
output.UVW = Pos;
|
||||||
output.UVW.yz *= -1.0;
|
output.UVW.yz *= -1.0;
|
||||||
output.Pos = mul(ubo.projection, mul(ubo.model, float4(Pos.xyz, 1.0)));
|
// Remove translation from view matrix
|
||||||
|
float4x4 viewMat = ubo.model;
|
||||||
|
viewMat[0][3] = 0.0;
|
||||||
|
viewMat[1][3] = 0.0;
|
||||||
|
viewMat[2][3] = 0.0;
|
||||||
|
output.Pos = mul(ubo.projection, mul(viewMat, float4(Pos.xyz, 1.0)));
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue