2016-02-16 15:07:25 +01:00
/*
2016-03-02 21:15:00 +01:00
* Vulkan Example - Spherical Environment Mapping , using different mat caps
2016-02-16 15:07:25 +01:00
*
2016-03-06 20:15:05 +01:00
* Use + / - / space toggle through different material captures
*
2016-02-16 15:07:25 +01:00
* Based on https : //www.clicktorelease.com/blog/creating-spherical-environment-mapping-shader
*
* Copyright ( C ) 2016 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <vector>
# define GLM_FORCE_RADIANS
2016-03-08 21:52:40 +01:00
# define GLM_FORCE_DEPTH_ZERO_TO_ONE
2016-02-16 15:07:25 +01:00
# include <glm/glm.hpp>
# include <glm/gtc/matrix_transform.hpp>
# include <glm/gtc/matrix_inverse.hpp>
# include <vulkan/vulkan.h>
# include "vulkanexamplebase.h"
2017-02-12 10:44:51 +01:00
# include "VulkanBuffer.hpp"
2017-02-11 14:18:24 +01:00
# include "VulkanTexture.hpp"
# include "VulkanModel.hpp"
2016-02-16 15:07:25 +01:00
# define VERTEX_BUFFER_BIND_ID 0
# define ENABLE_VALIDATION false
class VulkanExample : public VulkanExampleBase
{
public :
struct {
VkPipelineVertexInputStateCreateInfo inputState ;
std : : vector < VkVertexInputBindingDescription > bindingDescriptions ;
std : : vector < VkVertexInputAttributeDescription > attributeDescriptions ;
} vertices ;
2017-02-11 14:18:24 +01:00
// Vertex layout for the models
vks : : VertexLayout vertexLayout = vks : : VertexLayout ( {
vks : : VERTEX_COMPONENT_POSITION ,
vks : : VERTEX_COMPONENT_NORMAL ,
vks : : VERTEX_COMPONENT_UV ,
vks : : VERTEX_COMPONENT_COLOR ,
} ) ;
2016-02-16 15:07:25 +01:00
struct {
2017-02-11 14:18:24 +01:00
vks : : Model object ;
} models ;
2016-02-16 15:07:25 +01:00
struct {
2017-02-09 21:55:35 +01:00
vks : : Texture2DArray matCapArray ;
2016-02-16 15:07:25 +01:00
} textures ;
2017-02-12 10:44:51 +01:00
vks : : Buffer uniformBuffer ;
2016-02-16 15:07:25 +01:00
2016-12-24 12:48:01 +01:00
struct UBOVS {
2016-02-16 15:07:25 +01:00
glm : : mat4 projection ;
glm : : mat4 model ;
glm : : mat4 normal ;
glm : : mat4 view ;
2016-03-02 21:15:00 +01:00
int32_t texIndex = 0 ;
2016-02-16 15:07:25 +01:00
} uboVS ;
2016-12-24 12:48:01 +01:00
VkPipeline pipeline ;
2016-02-16 15:07:25 +01:00
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
zoom = - 0.9f ;
rotationSpeed = 0.75f ;
zoomSpeed = 0.25f ;
rotation = glm : : vec3 ( - 25.0f , 23.75f , 0.0f ) ;
2017-11-01 14:22:10 +01:00
title = " Spherical Environment Mapping " ;
settings . overlay = true ;
2016-02-16 15:07:25 +01:00
}
~ VulkanExample ( )
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
2016-12-24 12:48:01 +01:00
vkDestroyPipeline ( device , pipeline , nullptr ) ;
2016-02-16 15:07:25 +01:00
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
2017-02-11 14:18:24 +01:00
models . object . destroy ( ) ;
2016-02-16 15:07:25 +01:00
2016-12-24 12:48:01 +01:00
uniformBuffer . destroy ( ) ;
2017-02-09 21:55:35 +01:00
textures . matCapArray . destroy ( ) ;
2016-02-16 15:07:25 +01:00
}
2017-02-11 14:18:24 +01:00
void loadAssets ( )
2016-02-16 15:07:25 +01:00
{
2017-02-11 14:18:24 +01:00
models . object . loadFromFile ( getAssetPath ( ) + " models/chinesedragon.dae " , vertexLayout , 0.05f , vulkanDevice , queue ) ;
// Multiple mat caps are stored in a single texture array so they can easily be switched inside the shader just by updating the index in a uniform buffer
2017-02-09 21:55:35 +01:00
textures . matCapArray . loadFromFile ( getAssetPath ( ) + " textures/matcap_array_rgba.ktx " , VK_FORMAT_R8G8B8A8_UNORM , vulkanDevice , queue ) ;
2016-02-16 15:07:25 +01:00
}
void buildCommandBuffers ( )
{
2017-02-12 11:12:42 +01:00
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
2016-02-16 15:07:25 +01:00
VkClearValue clearValues [ 2 ] ;
clearValues [ 0 ] . color = defaultClearColor ;
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
2017-02-12 11:12:42 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks : : initializers : : renderPassBeginInfo ( ) ;
2016-02-16 15:07:25 +01:00
renderPassBeginInfo . renderPass = renderPass ;
renderPassBeginInfo . renderArea . offset . x = 0 ;
renderPassBeginInfo . renderArea . offset . y = 0 ;
renderPassBeginInfo . renderArea . extent . width = width ;
renderPassBeginInfo . renderArea . extent . height = height ;
renderPassBeginInfo . clearValueCount = 2 ;
renderPassBeginInfo . pClearValues = clearValues ;
for ( int32_t i = 0 ; i < drawCmdBuffers . size ( ) ; + + i )
{
// Set target frame buffer
renderPassBeginInfo . framebuffer = frameBuffers [ i ] ;
2016-05-15 11:20:44 +02:00
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
2016-02-16 15:07:25 +01:00
vkCmdBeginRenderPass ( drawCmdBuffers [ i ] , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
2017-02-12 11:12:42 +01:00
VkViewport viewport = vks : : initializers : : viewport ( ( float ) width , ( float ) height , 0.0f , 1.0f ) ;
2016-02-16 15:07:25 +01:00
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
2017-02-12 11:12:42 +01:00
VkRect2D scissor = vks : : initializers : : rect2D ( width , height , 0 , 0 ) ;
2016-02-16 15:07:25 +01:00
vkCmdSetScissor ( drawCmdBuffers [ i ] , 0 , 1 , & scissor ) ;
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSet , 0 , NULL ) ;
2016-12-24 12:48:01 +01:00
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline ) ;
2016-02-16 15:07:25 +01:00
VkDeviceSize offsets [ 1 ] = { 0 } ;
2017-02-11 14:18:24 +01:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & models . object . vertices . buffer , offsets ) ;
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , models . object . indices . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
2016-02-16 15:07:25 +01:00
2017-02-11 14:18:24 +01:00
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , models . object . indexCount , 1 , 0 , 0 , 0 ) ;
2016-02-16 15:07:25 +01:00
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
2016-05-15 11:20:44 +02:00
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
2016-02-16 15:07:25 +01:00
}
}
void prepareVertices ( )
{
// Binding description
vertices . bindingDescriptions . resize ( 1 ) ;
vertices . bindingDescriptions [ 0 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputBindingDescription (
2016-02-16 15:07:25 +01:00
VERTEX_BUFFER_BIND_ID ,
2017-02-11 14:18:24 +01:00
vertexLayout . stride ( ) ,
2016-02-16 15:07:25 +01:00
VK_VERTEX_INPUT_RATE_VERTEX ) ;
// Attribute descriptions
// Describes memory layout and shader positions
vertices . attributeDescriptions . resize ( 4 ) ;
// Location 0 : Position
vertices . attributeDescriptions [ 0 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription (
2016-02-16 15:07:25 +01:00
VERTEX_BUFFER_BIND_ID ,
0 ,
VK_FORMAT_R32G32B32_SFLOAT ,
0 ) ;
// Location 1 : Normal
vertices . attributeDescriptions [ 1 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription (
2016-02-16 15:07:25 +01:00
VERTEX_BUFFER_BIND_ID ,
1 ,
VK_FORMAT_R32G32B32_SFLOAT ,
sizeof ( float ) * 3 ) ;
// Location 2 : Texture coordinates
vertices . attributeDescriptions [ 2 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription (
2016-02-16 15:07:25 +01:00
VERTEX_BUFFER_BIND_ID ,
2 ,
VK_FORMAT_R32G32_SFLOAT ,
sizeof ( float ) * 6 ) ;
// Location 3 : Color
vertices . attributeDescriptions [ 3 ] =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription (
2016-02-16 15:07:25 +01:00
VERTEX_BUFFER_BIND_ID ,
3 ,
VK_FORMAT_R32G32B32_SFLOAT ,
sizeof ( float ) * 8 ) ;
2017-02-12 11:12:42 +01:00
vertices . inputState = vks : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
2016-02-16 15:07:25 +01:00
vertices . inputState . vertexBindingDescriptionCount = vertices . bindingDescriptions . size ( ) ;
vertices . inputState . pVertexBindingDescriptions = vertices . bindingDescriptions . data ( ) ;
vertices . inputState . vertexAttributeDescriptionCount = vertices . attributeDescriptions . size ( ) ;
vertices . inputState . pVertexAttributeDescriptions = vertices . attributeDescriptions . data ( ) ;
}
void setupDescriptorPool ( )
{
// Example uses one ubo and one image sampler
std : : vector < VkDescriptorPoolSize > poolSizes =
{
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 ) ,
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 1 )
2016-02-16 15:07:25 +01:00
} ;
VkDescriptorPoolCreateInfo descriptorPoolInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolCreateInfo (
2016-02-16 15:07:25 +01:00
poolSizes . size ( ) ,
poolSizes . data ( ) ,
2 ) ;
2016-05-15 11:20:44 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSetLayout ( )
{
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings =
{
// Binding 0 : Vertex shader uniform buffer
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding (
2016-02-16 15:07:25 +01:00
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
VK_SHADER_STAGE_VERTEX_BIT ,
0 ) ,
// Binding 1 : Fragment shader color map image sampler
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding (
2016-02-16 15:07:25 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
VK_SHADER_STAGE_FRAGMENT_BIT ,
1 )
} ;
VkDescriptorSetLayoutCreateInfo descriptorLayout =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutCreateInfo (
2016-02-16 15:07:25 +01:00
setLayoutBindings . data ( ) ,
setLayoutBindings . size ( ) ) ;
2016-05-15 11:20:44 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
2016-02-16 15:07:25 +01:00
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineLayoutCreateInfo (
2016-02-16 15:07:25 +01:00
& descriptorSetLayout ,
1 ) ;
2016-05-15 11:20:44 +02:00
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
2016-02-16 15:07:25 +01:00
}
void setupDescriptorSet ( )
{
VkDescriptorSetAllocateInfo allocInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetAllocateInfo (
2016-02-16 15:07:25 +01:00
descriptorPool ,
& descriptorSetLayout ,
1 ) ;
2016-05-15 11:20:44 +02:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
2016-02-16 15:07:25 +01:00
std : : vector < VkWriteDescriptorSet > writeDescriptorSets =
{
// Binding 0 : Vertex shader uniform buffer
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2016-02-16 15:07:25 +01:00
descriptorSet ,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
0 ,
2016-12-24 12:48:01 +01:00
& uniformBuffer . descriptor ) ,
2016-02-16 15:07:25 +01:00
// Binding 1 : Fragment shader image sampler
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2016-02-16 15:07:25 +01:00
descriptorSet ,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
1 ,
2016-12-24 12:48:01 +01:00
& textures . matCapArray . descriptor )
2016-02-16 15:07:25 +01:00
} ;
vkUpdateDescriptorSets ( device , writeDescriptorSets . size ( ) , writeDescriptorSets . data ( ) , 0 , NULL ) ;
}
void preparePipelines ( )
{
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineInputAssemblyStateCreateInfo (
2016-02-16 15:07:25 +01:00
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST ,
0 ,
VK_FALSE ) ;
VkPipelineRasterizationStateCreateInfo rasterizationState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineRasterizationStateCreateInfo (
2016-02-16 15:07:25 +01:00
VK_POLYGON_MODE_FILL ,
VK_CULL_MODE_BACK_BIT ,
VK_FRONT_FACE_CLOCKWISE ,
0 ) ;
VkPipelineColorBlendAttachmentState blendAttachmentState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendAttachmentState (
2016-02-16 15:07:25 +01:00
0xf ,
VK_FALSE ) ;
VkPipelineColorBlendStateCreateInfo colorBlendState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendStateCreateInfo (
2016-02-16 15:07:25 +01:00
1 ,
& blendAttachmentState ) ;
VkPipelineDepthStencilStateCreateInfo depthStencilState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDepthStencilStateCreateInfo (
2016-02-16 15:07:25 +01:00
VK_TRUE ,
VK_TRUE ,
VK_COMPARE_OP_LESS_OR_EQUAL ) ;
VkPipelineViewportStateCreateInfo viewportState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineViewportStateCreateInfo ( 1 , 1 , 0 ) ;
2016-02-16 15:07:25 +01:00
VkPipelineMultisampleStateCreateInfo multisampleState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineMultisampleStateCreateInfo (
2016-02-16 15:07:25 +01:00
VK_SAMPLE_COUNT_1_BIT ,
0 ) ;
std : : vector < VkDynamicState > dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT ,
VK_DYNAMIC_STATE_SCISSOR
} ;
VkPipelineDynamicStateCreateInfo dynamicState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDynamicStateCreateInfo (
2016-02-16 15:07:25 +01:00
dynamicStateEnables . data ( ) ,
dynamicStateEnables . size ( ) ,
0 ) ;
// Spherical environment rendering pipeline
// Load shaders
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
2016-03-25 14:12:17 +01:00
shaderStages [ 0 ] = loadShader ( getAssetPath ( ) + " shaders/sphericalenvmapping/sem.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getAssetPath ( ) + " shaders/sphericalenvmapping/sem.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
2016-02-16 15:07:25 +01:00
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineCreateInfo (
2016-02-16 15:07:25 +01:00
pipelineLayout ,
renderPass ,
0 ) ;
pipelineCreateInfo . pVertexInputState = & vertices . inputState ;
pipelineCreateInfo . pInputAssemblyState = & inputAssemblyState ;
pipelineCreateInfo . pRasterizationState = & rasterizationState ;
pipelineCreateInfo . pColorBlendState = & colorBlendState ;
pipelineCreateInfo . pMultisampleState = & multisampleState ;
pipelineCreateInfo . pViewportState = & viewportState ;
pipelineCreateInfo . pDepthStencilState = & depthStencilState ;
pipelineCreateInfo . pDynamicState = & dynamicState ;
pipelineCreateInfo . stageCount = shaderStages . size ( ) ;
pipelineCreateInfo . pStages = shaderStages . data ( ) ;
2016-12-24 12:48:01 +01:00
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipeline ) ) ;
2016-02-16 15:07:25 +01:00
}
void prepareUniformBuffers ( )
{
// Vertex shader uniform buffer block
2016-12-24 12:48:01 +01:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
2016-02-16 15:07:25 +01:00
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
2016-05-20 21:30:17 +02:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
2016-12-24 12:48:01 +01:00
& uniformBuffer ,
sizeof ( uboVS ) ) ) ;
// Map persistent
VK_CHECK_RESULT ( uniformBuffer . map ( ) ) ;
2016-02-16 15:07:25 +01:00
updateUniformBuffers ( ) ;
}
void updateUniformBuffers ( )
{
2016-03-02 21:15:00 +01:00
uboVS . projection = glm : : perspective ( glm : : radians ( 45.0f ) , ( float ) width / ( float ) height , 0.1f , 256.0f ) ;
2016-02-16 15:07:25 +01:00
uboVS . view = glm : : lookAt (
glm : : vec3 ( 0 , 0 , - zoom ) ,
glm : : vec3 ( 0 , 0 , 0 ) ,
glm : : vec3 ( 0 , 1 , 0 )
) ;
2017-09-24 18:17:07 +02:00
uboVS . model = glm : : mat4 ( 1.0f ) ;
2016-03-02 21:15:00 +01:00
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . x ) , glm : : vec3 ( 1.0f , 0.0f , 0.0f ) ) ;
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . y ) , glm : : vec3 ( 0.0f , 1.0f , 0.0f ) ) ;
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . z ) , glm : : vec3 ( 0.0f , 0.0f , 1.0f ) ) ;
2016-02-16 15:07:25 +01:00
uboVS . normal = glm : : inverseTranspose ( uboVS . view * uboVS . model ) ;
2016-12-24 12:48:01 +01:00
memcpy ( uniformBuffer . mapped , & uboVS , sizeof ( uboVS ) ) ;
2016-02-16 15:07:25 +01:00
}
2016-05-20 21:30:17 +02:00
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
2016-02-16 15:07:25 +01:00
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
2017-02-11 14:18:24 +01:00
loadAssets ( ) ;
2016-02-16 15:07:25 +01:00
prepareVertices ( ) ;
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
}
virtual void viewChanged ( )
{
updateUniformBuffers ( ) ;
}
2017-11-01 14:22:10 +01:00
virtual void OnUpdateUIOverlay ( vks : : UIOverlay * overlay )
2016-03-06 20:15:05 +01:00
{
2017-11-01 14:22:10 +01:00
if ( overlay - > header ( " Settings " ) ) {
if ( overlay - > sliderInt ( " Material cap " , & uboVS . texIndex , 0 , textures . matCapArray . layerCount ) ) {
updateUniformBuffers ( ) ;
}
2016-03-06 20:15:05 +01:00
}
}
2016-02-16 15:07:25 +01:00
} ;
2016-12-13 19:25:56 +01:00
VULKAN_EXAMPLE_MAIN ( )