From eaf76fd6e76667256a49bd735e7a0fc704d1f8e1 Mon Sep 17 00:00:00 2001 From: saschawillems Date: Thu, 1 Sep 2016 22:55:57 +0200 Subject: [PATCH] Updated compute raytrace example. Pass scene primitives via SSBOs, shader tweaks, etc. --- computeparticles/computeparticles.vcxproj | 5 + .../computeparticles.vcxproj.filters | 14 ++ computeshader/computeshader.vcxproj.filters | 3 + data/shaders/raytracing/raytracing.comp | 202 ++++++++-------- data/shaders/raytracing/raytracing.comp.spv | Bin 13416 -> 15720 bytes raytracing/raytracing.cpp | 219 +++++++++++++++--- 6 files changed, 310 insertions(+), 133 deletions(-) diff --git a/computeparticles/computeparticles.vcxproj b/computeparticles/computeparticles.vcxproj index 8650396c..9d4b51d3 100644 --- a/computeparticles/computeparticles.vcxproj +++ b/computeparticles/computeparticles.vcxproj @@ -89,6 +89,11 @@ + + + + + diff --git a/computeparticles/computeparticles.vcxproj.filters b/computeparticles/computeparticles.vcxproj.filters index 1247208e..80246e85 100644 --- a/computeparticles/computeparticles.vcxproj.filters +++ b/computeparticles/computeparticles.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + {e28680da-cc95-413d-b6f0-0e1f9967ee88} + @@ -39,4 +42,15 @@ Header Files + + + Shaders + + + Shaders + + + Shaders + + \ No newline at end of file diff --git a/computeshader/computeshader.vcxproj.filters b/computeshader/computeshader.vcxproj.filters index b36bf08e..2107681a 100644 --- a/computeshader/computeshader.vcxproj.filters +++ b/computeshader/computeshader.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + {232f5393-2624-4cd4-84ed-9fdda34ff64c} + diff --git a/data/shaders/raytracing/raytracing.comp b/data/shaders/raytracing/raytracing.comp index 1940349c..92c881a2 100644 --- a/data/shaders/raytracing/raytracing.comp +++ b/data/shaders/raytracing/raytracing.comp @@ -10,16 +10,16 @@ layout (binding = 0, rgba8) uniform writeonly image2D resultImage; #define EPSILON 0.0001 #define MAXLEN 1000.0 -#define PLANEID 1 -#define SPHERECOUNT 3 #define SHADOW 0.5 -#define RAYBOUNCES 1 +#define RAYBOUNCES 2 #define REFLECTIONSTRENGTH 0.4 +#define REFLECTIONFALLOFF 0.5 -struct Camera { - vec3 pos; - vec3 lookat; - float fov; +struct Camera +{ + vec3 pos; + vec3 lookat; + float fov; }; layout (binding = 1) uniform UBO @@ -31,50 +31,60 @@ layout (binding = 1) uniform UBO mat4 rotMat; } ubo; +struct Sphere +{ + vec3 pos; + float radius; + vec3 diffuse; + float specular; + int id; +}; + +struct Plane +{ + vec3 normal; + float distance; + vec3 diffuse; + float specular; + int id; +}; + +layout (std140, binding = 2) buffer Spheres +{ + Sphere spheres[ ]; +}; + +layout (std140, binding = 3) buffer Planes +{ + Plane planes[ ]; +}; + void reflectRay(inout vec3 rayD, in vec3 mormal) { - rayD = rayD + 2.0 * -dot(mormal, rayD) * mormal; + rayD = rayD + 2.0 * -dot(mormal, rayD) * mormal; } -// Lighting calculations +// Lighting ========================================================= float lightDiffuse(vec3 normal, vec3 lightDir) { - return clamp(dot(normal, lightDir), 0.0, 1.0); + return clamp(dot(normal, lightDir), 0.25, 1.0); } -float lightSpecular(vec3 normal, vec3 lightDir) +float lightSpecular(vec3 normal, vec3 lightDir, float specularFactor) { - vec3 viewVec = normalize(ubo.camera.pos); - vec3 halfVec = normalize(lightDir + viewVec); - return pow(clamp(dot(normal, halfVec), 0.0, 1.0), 32.0); + vec3 viewVec = normalize(ubo.camera.pos); + vec3 halfVec = normalize(lightDir + viewVec); + return pow(clamp(dot(normal, halfVec), 0.0, 1.0), specularFactor); } -// Primitives - -// Basic material description -struct Material -{ - vec3 diffuse; - vec3 specular; -}; - -// Sphere -struct Sphere -{ - int id; - vec3 pos; - float r; - Material material; -} sphere; - -Sphere spheres[SPHERECOUNT]; +// Sphere =========================================================== float sphereIntersect(in vec3 rayO, in vec3 rayD, in Sphere sphere) { vec3 oc = rayO - sphere.pos; float b = 2.0 * dot(oc, rayD); - float c = dot(oc, oc) - sphere.r*sphere.r; + float c = dot(oc, oc) - sphere.radius*sphere.radius; float h = b*b - 4.0*c; if (h < 0.0) { @@ -86,51 +96,58 @@ float sphereIntersect(in vec3 rayO, in vec3 rayD, in Sphere sphere) vec3 sphereNormal(in vec3 pos, in Sphere sphere) { - return (pos - sphere.pos) / sphere.r; + return (pos - sphere.pos) / sphere.radius; } -// Plane +// Plane =========================================================== -float planeIntersect(vec3 rayO, vec3 rayD) +float planeIntersect(vec3 rayO, vec3 rayD, Plane plane) { - return -rayO.y/rayD.y; + float d = dot(rayD, plane.normal); + + if (d == 0.0) + return 0.0; + + float t = -(plane.distance + dot(rayO, plane.normal)) / d; + + if (t < 0.0) + return 0.0; + + return t; } -vec3 planeNormal(in vec3 pos) -{ - return vec3(0.0, 1.0, 0.0); -} -int intersect(in vec3 rayO, in vec3 rayD, out float resT) +int intersect(in vec3 rayO, in vec3 rayD, inout float resT) { int id = -1; - resT = MAXLEN; - for (int i = 0; i < SPHERECOUNT; i++) + for (int i = 0; i < spheres.length(); i++) { float tSphere = sphereIntersect(rayO, rayD, spheres[i]); - if (tSphere > EPSILON) + if ((tSphere > EPSILON) && (tSphere < resT)) { id = spheres[i].id; resT = tSphere; - return id; - break; } } - - float tplane = planeIntersect(rayO, rayD); - if ((tplane > EPSILON) && (tplane < resT)) + + for (int i = 0; i < planes.length(); i++) { - id = PLANEID; - resT = tplane; - } + float tplane = planeIntersect(rayO, rayD, planes[i]); + if ((tplane > EPSILON) && (tplane < resT)) + { + id = planes[i].id; + resT = tplane; + } + } return id; } float calcShadow(in vec3 rayO, in vec3 rayD, in int id) { - for (int i = 0; i < SPHERECOUNT; i++) + //todo: avoid backprojection + for (int i = 0; i < spheres.length(); i++) { float tSphere = sphereIntersect(rayO, rayD, spheres[i]); if (tSphere > EPSILON) @@ -143,13 +160,13 @@ float calcShadow(in vec3 rayO, in vec3 rayD, in int id) vec3 fog(in float t, in vec3 color) { - return mix(color, ubo.fogColor.rgb, clamp(sqrt(t*t)/20.0, 0.0, 1.0)); + return mix(color, ubo.fogColor.rgb, clamp(sqrt(t*t)/20.0, 0.0, 1.0)); } vec3 renderScene(inout vec3 rayO, inout vec3 rayD, inout int id) { vec3 color = vec3(0.0); - float t = 0.0; + float t = MAXLEN; // Get intersected object ID int objectID = intersect(rayO, rayD, t); @@ -162,64 +179,53 @@ vec3 renderScene(inout vec3 rayO, inout vec3 rayD, inout int id) vec3 pos = rayO + t * rayD; vec3 lightVec = normalize(ubo.lightPos - pos); vec3 normal; - - if (objectID == PLANEID) + + // Planes + + // Spheres + + for (int i = 0; i < planes.length(); i++) { - normal = planeNormal(pos); - float diffuse = clamp(dot(normal, lightVec), 0.0, 1.0); - color = vec3(1.0, 1.0, 1.0) * diffuse; - } - else - { - for (int i = 0; i < SPHERECOUNT; i++) + if (objectID == planes[i].id) { - if (objectID == spheres[i].id) - { - normal = sphereNormal(pos, spheres[i]); - float diffuse = lightDiffuse(normal, lightVec); - float specular = lightSpecular(normal, lightVec); - color = diffuse * spheres[i].material.diffuse + specular * spheres[i].material.specular; - } + normal = planes[i].normal; + float diffuse = lightDiffuse(normal, lightVec); + float specular = lightSpecular(normal, lightVec, planes[i].specular); + color = diffuse * planes[i].diffuse + specular; } } + for (int i = 0; i < spheres.length(); i++) + { + if (objectID == spheres[i].id) + { + normal = sphereNormal(pos, spheres[i]); + float diffuse = lightDiffuse(normal, lightVec); + float specular = lightSpecular(normal, lightVec, spheres[i].specular); + color = diffuse * spheres[i].diffuse.rgb + specular; + } + } + + if (id == -1) + return color; + id = objectID; // Shadows - color *= calcShadow(pos, lightVec, objectID); + color *= calcShadow(pos, lightVec, objectID); // Fog color = fog(t, color); // Reflect ray for next render pass reflectRay(rayD, normal); - rayO = pos; + rayO = pos; return color; } void main() { - // Scene setup - // todo : from ubo - spheres[0].id = 2; - spheres[0].pos = vec3(-2.25, 1.0, 0.0); - spheres[0].r = 1.0; - spheres[0].material.diffuse = vec3(1.0, 0.0, 0.0); - spheres[0].material.specular = vec3(2.0); - - spheres[1].id = 3; - spheres[1].pos = vec3(0.0, 2.5, 0.0); - spheres[1].r = 1.0; - spheres[1].material.diffuse = vec3(0.0, 0.0, 1.0); - spheres[1].material.specular = vec3(2.0); - - spheres[2].id = 4; - spheres[2].pos = vec3(2.25, 1.0, 0.0); - spheres[2].r = 1.0; - spheres[2].material.diffuse = vec3(0.0, 1.0, 0.0); - spheres[2].material.specular = vec3(2.0); - ivec2 dim = imageSize(resultImage); vec2 uv = vec2(gl_GlobalInvocationID.xy) / dim; @@ -230,14 +236,16 @@ void main() int id = 0; vec3 finalColor = renderScene(rayO, rayD, id); - bool reflections = true; + const bool reflections = true; // Reflection if (reflections) { + float reflectionStrength = REFLECTIONSTRENGTH; for (int i = 0; i < RAYBOUNCES; i++) { vec3 reflectionColor = renderScene(rayO, rayD, id); - finalColor = (1.0 - REFLECTIONSTRENGTH) * finalColor + REFLECTIONSTRENGTH * mix(reflectionColor, finalColor, 1.0 - REFLECTIONSTRENGTH); + finalColor = (1.0 - reflectionStrength) * finalColor + reflectionStrength * mix(reflectionColor, finalColor, 1.0 - reflectionStrength); + reflectionStrength *= REFLECTIONFALLOFF; } } diff --git a/data/shaders/raytracing/raytracing.comp.spv b/data/shaders/raytracing/raytracing.comp.spv index 45f33ffbacfb7a020171e25693743a4044123284..fb3eabc93a35bc3748568397c79f0beb622f2c41 100644 GIT binary patch literal 15720 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 literal 13416 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 z(=G+8*$?ySQ!Ab+_rH80rTHu)-zKodestroyTexture(textureComputeTarget); } @@ -259,15 +283,113 @@ public: vkEndCommandBuffer(compute.commandBuffer); } + uint32_t currentId = 0; // Id used to identify objects by the ray tracing shader + + Sphere newSphere(glm::vec3 pos, float radius, glm::vec3 diffuse, float specular) + { + Sphere sphere; + sphere.id = currentId++; + sphere.pos = pos; + sphere.radius = radius; + sphere.diffuse = diffuse; + sphere.specular = specular; + return sphere; + } + + Plane newPlane(glm::vec3 normal, float distance, glm::vec3 diffuse, float specular) + { + Plane plane; + plane.id = currentId++; + plane.normal = normal; + plane.distance = distance; + plane.diffuse = diffuse; + plane.specular = specular; + return plane; + } + + // Setup and fill the compute shader storage buffers containing primitives for the raytraced scene + void prepareStorageBuffers() + { + // Spheres + std::vector spheres; + spheres.push_back(newSphere(glm::vec3(1.75f, -0.5f, 0.0f), 1.0f, glm::vec3(0.0f, 1.0f, 0.0f), 32.0f)); + spheres.push_back(newSphere(glm::vec3(0.0f, 1.0f, -0.5f), 1.0f, glm::vec3(0.65f, 0.77f, 0.97f), 32.0f)); + spheres.push_back(newSphere(glm::vec3(-1.75f, -0.75f, -0.5f), 1.25f, glm::vec3(0.9f, 0.76f, 0.46f), 32.0f)); +// spheres.push_back(newSphere(glm::vec3(-2.25f, -1.0f, 0.5f), 1.0f, glm::vec3(1.0f, 0.32f, 0.36f), 32.0f)); + //spheres.push_back(newSphere(glm::vec3(-2.25f, 1.0f, 0.0f), 1.0f, glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(2.0f))); + //spheres.push_back(newSphere(glm::vec3(0.f, 2.5f, 0.0f), 1.0f, glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(2.0f))); + //spheres.push_back(newSphere(glm::vec3(2.25f, 1.0f, 0.0f), 1.0f, glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(2.0f))); + VkDeviceSize storageBufferSize = spheres.size() * sizeof(Sphere); + + // Stage + vk::Buffer stagingBuffer; + + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &stagingBuffer, + storageBufferSize, + spheres.data()); + + vulkanDevice->createBuffer( + // The SSBO will be used as a storage buffer for the compute pipeline and as a vertex buffer in the graphics pipeline + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &compute.storageBuffers.spheres, + storageBufferSize); + + // Copy to staging buffer + VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + VkBufferCopy copyRegion = {}; + copyRegion.size = storageBufferSize; + vkCmdCopyBuffer(copyCmd, stagingBuffer.buffer, compute.storageBuffers.spheres.buffer, 1, ©Region); + VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); + + stagingBuffer.destroy(); + + // Planes + std::vector planes; + const float roomDim = 4.0f; + planes.push_back(newPlane(glm::vec3(0.0f, 1.0f, 0.0f), roomDim, glm::vec3(1.0f), 32.0f)); + planes.push_back(newPlane(glm::vec3(0.0f, -1.0f, 0.0f), roomDim, glm::vec3(1.0f), 32.0f)); + planes.push_back(newPlane(glm::vec3(0.0f, 0.0f, 1.0f), roomDim, glm::vec3(1.0f), 32.0f)); + planes.push_back(newPlane(glm::vec3(0.0f, 0.0f, -1.0f), roomDim, glm::vec3(0.0f), 32.0f)); + planes.push_back(newPlane(glm::vec3(-1.0f, 0.0f, 0.0f), roomDim, glm::vec3(1.0f, 0.0f, 0.0f), 32.0f)); + planes.push_back(newPlane(glm::vec3(1.0f, 0.0f, 0.0f), roomDim, glm::vec3(0.0f, 1.0f, 0.0f), 32.0f)); + storageBufferSize = planes.size() * sizeof(Plane); + + // Stage + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &stagingBuffer, + storageBufferSize, + planes.data()); + + vulkanDevice->createBuffer( + // The SSBO will be used as a storage buffer for the compute pipeline and as a vertex buffer in the graphics pipeline + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &compute.storageBuffers.planes, + storageBufferSize); + + // Copy to staging buffer + copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + copyRegion.size = storageBufferSize; + vkCmdCopyBuffer(copyCmd, stagingBuffer.buffer, compute.storageBuffers.planes.buffer, 1, ©Region); + VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); + + stagingBuffer.destroy(); + } + void setupDescriptorPool() { std::vector poolSizes = { - vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), - // Graphics pipeline uses image samplers for display - vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4), - // Compute pipeline uses storage images image loads and stores - vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1), + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), // Compute UBO + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4), // Graphics image samplers + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1), // Storage image for ray traced image output + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2), // Storage buffer for the scene primitives }; VkDescriptorPoolCreateInfo descriptorPoolInfo = @@ -426,16 +548,26 @@ public: vkGetDeviceQueue(device, vulkanDevice->queueFamilyIndices.compute, 0, &compute.queue); std::vector setLayoutBindings = { - // Binding 0 : Sampled image (write) + // Binding 0: Storage image (raytraced output) vkTools::initializers::descriptorSetLayoutBinding( VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT, 0), - // Binding 1 : Uniform buffer block + // Binding 1: Uniform buffer block vkTools::initializers::descriptorSetLayoutBinding( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT, - 1) + 1), + // Binding 1: Shader storage buffer for the spheres + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + VK_SHADER_STAGE_COMPUTE_BIT, + 2), + // Binding 1: Shader storage buffer for the planes + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + VK_SHADER_STAGE_COMPUTE_BIT, + 3) }; VkDescriptorSetLayoutCreateInfo descriptorLayout = @@ -462,18 +594,30 @@ public: std::vector computeWriteDescriptorSets = { - // Binding 0 : Output storage image + // Binding 0: Output storage image vkTools::initializers::writeDescriptorSet( compute.descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 0, &textureComputeTarget.descriptor), - // Binding 1 : Uniform buffer block + // Binding 1: Uniform buffer block vkTools::initializers::writeDescriptorSet( compute.descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, - &uniformDataCompute.descriptor) + &compute.uniformBuffer.descriptor), + // Binding 2: Shader storage buffer for the spheres + vkTools::initializers::writeDescriptorSet( + compute.descriptorSet, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 2, + &compute.storageBuffers.spheres.descriptor), + // Binding 2: Shader storage buffer for the planes + vkTools::initializers::writeDescriptorSet( + compute.descriptorSet, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 3, + &compute.storageBuffers.planes.descriptor) }; vkUpdateDescriptorSets(device, computeWriteDescriptorSets.size(), computeWriteDescriptorSets.data(), 0, NULL); @@ -517,22 +661,24 @@ public: // Compute shader parameter uniform buffer block vulkanDevice->createBuffer( VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformDataCompute, - sizeof(uboCompute)); + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &compute.uniformBuffer, + sizeof(compute.ubo)); updateUniformBuffers(); } void updateUniformBuffers() { - uboCompute.lightPos.x = 0.0f + sin(glm::radians(timer * 360.0f)) * 2.0f; - uboCompute.lightPos.y = 5.0f; - uboCompute.lightPos.z = 1.0f; - uboCompute.lightPos.z = 0.0f + cos(glm::radians(timer * 360.0f)) * 2.0f; - VK_CHECK_RESULT(uniformDataCompute.map()); - memcpy(uniformDataCompute.mapped, &uboCompute, sizeof(uboCompute)); - uniformDataCompute.unmap(); + compute.ubo.lightPos.x = 0.0f + sin(glm::radians(timer * 360.0f)) * cos(glm::radians(timer * 360.0f)) * 2.0f; + compute.ubo.lightPos.y = 0.0f + sin(glm::radians(timer * 360.0f)) * 2.0f; + compute.ubo.lightPos.z = 0.0f + cos(glm::radians(timer * 360.0f)) * 2.0f; + + compute.ubo.lightPos.y = 2.0f; + + VK_CHECK_RESULT(compute.uniformBuffer.map()); + memcpy(compute.uniformBuffer.mapped, &compute.ubo, sizeof(compute.ubo)); + compute.uniformBuffer.unmap(); } void draw() @@ -547,7 +693,7 @@ public: VulkanExampleBase::submitFrame(); // Submit compute commands - // Use a fence to ensure that compute command buffer has finished executin before using it again + // Use a fence to ensure that compute command buffer has finished executing before using it again vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX); vkResetFences(device, 1, &compute.fence); @@ -561,6 +707,7 @@ public: void prepare() { VulkanExampleBase::prepare(); + prepareStorageBuffers(); prepareUniformBuffers(); prepareTextureTarget(&textureComputeTarget, TEX_DIM, TEX_DIM, VK_FORMAT_R8G8B8A8_UNORM); setupDescriptorSetLayout(); @@ -585,7 +732,7 @@ public: virtual void viewChanged() { - uboCompute.aspectRatio = (float)width / (float)height; + compute.ubo.aspectRatio = (float)width / (float)height; updateUniformBuffers(); } };