diff --git a/examples/vulkanscene/vulkanscene.cpp b/examples/vulkanscene/vulkanscene.cpp index 66792164..3ccd822f 100644 --- a/examples/vulkanscene/vulkanscene.cpp +++ b/examples/vulkanscene/vulkanscene.cpp @@ -14,45 +14,37 @@ class VulkanExample : public VulkanExampleBase { public: - struct DemoModel - { + struct DemoModel { vkglTF::Model* glTF; VkPipeline *pipeline; }; std::vector demoModels; + vks::TextureCubeMap skybox; - struct { - vks::Buffer meshVS; - } uniformData; - - struct { + struct UniformData { glm::mat4 projection; glm::mat4 model; glm::mat4 normal; glm::mat4 view; glm::vec4 lightPos; - } uboVS; - - struct - { - vks::TextureCubeMap skybox; - } textures; + } uniformData; + vks::Buffer uniformBuffer; struct { - VkPipeline logos; - VkPipeline models; - VkPipeline skybox; + VkPipeline logos{ VK_NULL_HANDLE }; + VkPipeline models{ VK_NULL_HANDLE }; + VkPipeline skybox{ VK_NULL_HANDLE }; } pipelines; - VkPipelineLayout pipelineLayout; - VkDescriptorSet descriptorSet; - VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; glm::vec4 lightPos = glm::vec4(1.0f, 4.0f, 0.0f, 0.0f); VulkanExample() : VulkanExampleBase() { - title = "Vulkan Demo Scene - (c) by Sascha Willems"; + title = "Vulkan Demo Scene (c) by Sascha Willems"; camera.type = Camera::CameraType::lookat; //camera.flipY = true; camera.setPosition(glm::vec3(0.0f, 0.0f, -3.75f)); @@ -63,28 +55,25 @@ public: ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - vkDestroyPipeline(device, pipelines.logos, nullptr); - vkDestroyPipeline(device, pipelines.models, nullptr); - vkDestroyPipeline(device, pipelines.skybox, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - for (auto demoModel : demoModels) { - delete demoModel.glTF; + if (device) { + vkDestroyPipeline(device, pipelines.logos, nullptr); + vkDestroyPipeline(device, pipelines.models, nullptr); + vkDestroyPipeline(device, pipelines.skybox, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + for (auto demoModel : demoModels) { + delete demoModel.glTF; + } + uniformBuffer.destroy(); + skybox.destroy(); } - - uniformData.meshVS.destroy(); - textures.skybox.destroy(); } void loadAssets() { // Models - std::vector modelFiles = { "vulkanscenelogos.gltf", "vulkanscenebackground.gltf", "vulkanscenemodels.gltf", "cube.gltf" }; - std::vector modelPipelines = { &pipelines.logos, &pipelines.models, &pipelines.models, &pipelines.skybox }; + std::vector modelFiles = { "cube.gltf", "vulkanscenelogos.gltf", "vulkanscenebackground.gltf", "vulkanscenemodels.gltf" }; + std::vector modelPipelines = { &pipelines.skybox, &pipelines.logos, &pipelines.models, &pipelines.models }; for (auto i = 0; i < modelFiles.size(); i++) { DemoModel model; const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; @@ -94,7 +83,104 @@ public: demoModels.push_back(model); } // Textures - textures.skybox.loadFromFile(getAssetPath() + "textures/cubemap_vulkan.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + skybox.loadFromFile(getAssetPath() + "textures/cubemap_vulkan.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + } + + void setupDescriptors() + { + // Pool + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Layout + std::vector setLayoutBindings = { + // Binding 0 : Vertex shader uniform buffer + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), + // Binding 1 : Fragment shader color map image sampler + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) + }; + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); + + std::vector writeDescriptorSets = { + // Binding 0 : Vertex shader uniform buffer + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor), + // Binding 1 : Fragment shader image sampler + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &skybox.descriptor) + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); + } + + void preparePipelines() + { + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + // Pipelines + 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); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, 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 dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + std::array shaderStages; + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.stageCount = static_cast(shaderStages.size()); + pipelineCI.pStages = shaderStages.data(); + pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV, vkglTF::VertexComponent::Color });; + + // Default mesh rendering pipeline + shaderStages[0] = loadShader(getShadersPath() + "vulkanscene/mesh.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "vulkanscene/mesh.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.models)); + + // Pipeline for the logos + shaderStages[0] = loadShader(getShadersPath() + "vulkanscene/logo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "vulkanscene/logo.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.logos)); + + // Pipeline for the skybox + rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; + depthStencilState.depthWriteEnable = VK_FALSE; + shaderStages[0] = loadShader(getShadersPath() + "vulkanscene/skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "vulkanscene/skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox)); + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,&uniformBuffer, sizeof(uniformData)); + VK_CHECK_RESULT(uniformBuffer.map()); + } + + void updateUniformBuffers() + { + uniformData.projection = camera.matrices.perspective; + uniformData.view = camera.matrices.view; + uniformData.model = glm::mat4(1.0f); + uniformData.normal = glm::inverseTranspose(uniformData.view * uniformData.model); + uniformData.lightPos = lightPos; + memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData)); } void buildCommandBuffers() @@ -128,7 +214,7 @@ public: VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); 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); for (auto model : demoModels) { vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, *model.pipeline); @@ -143,150 +229,6 @@ public: } } - void setupDescriptorPool() - { - // Example uses one ubo and one image sampler - std::vector poolSizes = - { - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) - }; - - VkDescriptorPoolCreateInfo descriptorPoolInfo = - vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); - - VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - - void setupDescriptorSetLayout() - { - std::vector setLayoutBindings = - { - // Binding 0 : Vertex shader uniform buffer - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - VK_SHADER_STAGE_VERTEX_BIT, - 0), - // Binding 1 : Fragment shader color map image sampler - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - VK_SHADER_STAGE_FRAGMENT_BIT, - 1) - }; - - VkDescriptorSetLayoutCreateInfo descriptorLayout = - vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); - - VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - - VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = - vks::initializers::pipelineLayoutCreateInfo( - &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)); - - // Cube map image descriptor - VkDescriptorImageInfo texDescriptorCubeMap = - vks::initializers::descriptorImageInfo( - textures.skybox.sampler, - textures.skybox.view, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - std::vector writeDescriptorSets = - { - // Binding 0 : Vertex shader uniform buffer - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &uniformData.meshVS.descriptor), - // Binding 1 : Fragment shader image sampler - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 1, - &texDescriptorCubeMap) - }; - - vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); - } - - void preparePipelines() - { - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); - VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE,0); - VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, 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 dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables, 0); - std::array shaderStages; - VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); - pipelineCI.pInputAssemblyState = &inputAssemblyState; - pipelineCI.pRasterizationState = &rasterizationState; - pipelineCI.pColorBlendState = &colorBlendState; - pipelineCI.pMultisampleState = &multisampleState; - pipelineCI.pViewportState = &viewportState; - pipelineCI.pDepthStencilState = &depthStencilState; - pipelineCI.pDynamicState = &dynamicState; - pipelineCI.stageCount = static_cast(shaderStages.size()); - pipelineCI.pStages = shaderStages.data(); - pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV, vkglTF::VertexComponent::Color });; - - // Default mesh rendering pipeline - shaderStages[0] = loadShader(getShadersPath() + "vulkanscene/mesh.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader(getShadersPath() + "vulkanscene/mesh.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.models)); - - // Pipeline for the logos - shaderStages[0] = loadShader(getShadersPath() + "vulkanscene/logo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader(getShadersPath() + "vulkanscene/logo.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.logos)); - - // Pipeline for the sky sphere - rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; - depthStencilState.depthWriteEnable = VK_FALSE; - shaderStages[0] = loadShader(getShadersPath() + "vulkanscene/skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader(getShadersPath() + "vulkanscene/skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox)); - } - - // Prepare and initialize uniform buffer containing shader uniforms - void prepareUniformBuffers() - { - vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformData.meshVS, - sizeof(uboVS)); - VK_CHECK_RESULT(uniformData.meshVS.map()); - updateUniformBuffers(); - } - - void updateUniformBuffers() - { - uboVS.projection = camera.matrices.perspective; - uboVS.view = camera.matrices.view; - uboVS.model = glm::mat4(1.0f); - uboVS.normal = glm::inverseTranspose(uboVS.view * uboVS.model); - uboVS.lightPos = lightPos; - memcpy(uniformData.meshVS.mapped, &uboVS, sizeof(uboVS)); - } - void draw() { VulkanExampleBase::prepareFrame(); @@ -301,10 +243,8 @@ public: VulkanExampleBase::prepare(); loadAssets(); prepareUniformBuffers(); - setupDescriptorSetLayout(); + setupDescriptors(); preparePipelines(); - setupDescriptorPool(); - setupDescriptorSet(); buildCommandBuffers(); prepared = true; } @@ -313,12 +253,8 @@ public: { if (!prepared) return; - draw(); - } - - virtual void viewChanged() - { updateUniformBuffers(); + draw(); } }; diff --git a/shaders/glsl/vulkanscene/skybox.vert b/shaders/glsl/vulkanscene/skybox.vert index 232b12c0..89522cc5 100644 --- a/shaders/glsl/vulkanscene/skybox.vert +++ b/shaders/glsl/vulkanscene/skybox.vert @@ -6,6 +6,8 @@ layout (binding = 0) uniform UBO { mat4 projection; mat4 model; + mat4 normal; + mat4 view; } ubo; layout (location = 0) out vec3 outUVW; @@ -13,5 +15,7 @@ layout (location = 0) out vec3 outUVW; void main() { outUVW = inPos; - gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0); + // Remove translation from view matrix + mat4 viewMat = mat4(mat3(ubo.view)); + gl_Position = ubo.projection * viewMat * ubo.model * vec4(inPos.xyz, 1.0); } diff --git a/shaders/glsl/vulkanscene/skybox.vert.spv b/shaders/glsl/vulkanscene/skybox.vert.spv index 901a2d51..3ce15e1c 100644 Binary files a/shaders/glsl/vulkanscene/skybox.vert.spv and b/shaders/glsl/vulkanscene/skybox.vert.spv differ diff --git a/shaders/hlsl/vulkanscene/skybox.vert b/shaders/hlsl/vulkanscene/skybox.vert index ebc7739d..2b9d52ae 100644 --- a/shaders/hlsl/vulkanscene/skybox.vert +++ b/shaders/hlsl/vulkanscene/skybox.vert @@ -1,9 +1,12 @@ // Copyright 2020 Google LLC +// Copyright 2023 Sascha Willems struct UBO { float4x4 projection; float4x4 model; + float4x4 normal; + float4x4 view; }; cbuffer ubo : register(b0) { UBO ubo; } @@ -17,7 +20,14 @@ struct VSOutput VSOutput main([[vk::location(0)]] float3 Pos : POSITION0) { VSOutput output = (VSOutput)0; + + // Remove translation from view matrix + float4x4 viewMat = ubo.view; + viewMat[0][3] = 0.0; + viewMat[1][3] = 0.0; + viewMat[2][3] = 0.0; output.UVW = Pos; - output.Pos = mul(ubo.projection, mul(ubo.model, float4(Pos.xyz, 1.0))); + output.Pos = mul(ubo.projection, mul(viewMat, mul(ubo.model, float4(Pos.xyz, 1.0)))); + return output; } diff --git a/shaders/hlsl/vulkanscene/skybox.vert.spv b/shaders/hlsl/vulkanscene/skybox.vert.spv index 7981301a..0d4d3618 100644 Binary files a/shaders/hlsl/vulkanscene/skybox.vert.spv and b/shaders/hlsl/vulkanscene/skybox.vert.spv differ