diff --git a/android/raytracing/.gitignore b/android/raytracing/.gitignore
new file mode 100644
index 00000000..7a5d249c
--- /dev/null
+++ b/android/raytracing/.gitignore
@@ -0,0 +1,10 @@
+/assets/
+/res/
+/bin/
+/libs/
+/obj/
+/build.xml
+/local.properties
+/project.properties
+/proguard-project.txt
+*.apk
\ No newline at end of file
diff --git a/android/raytracing/AndroidManifest.xml b/android/raytracing/AndroidManifest.xml
new file mode 100644
index 00000000..beb3615a
--- /dev/null
+++ b/android/raytracing/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/raytracing/build.bat b/android/raytracing/build.bat
new file mode 100644
index 00000000..be5e1960
--- /dev/null
+++ b/android/raytracing/build.bat
@@ -0,0 +1,17 @@
+cd jni
+call ndk-build
+if %ERRORLEVEL% EQU 0 (
+ echo ndk-build has failed, build cancelled
+ cd..
+
+ mkdir "assets\shaders\raytracing"
+ xcopy "..\..\data\shaders\raytracing\*.spv" "assets\shaders\raytracing" /Y
+
+ mkdir "res\drawable"
+ xcopy "..\..\android\images\icon.png" "res\drawable" /Y
+
+ call ant debug -Dout.final.file=vulkanRaytracing.apk
+) ELSE (
+ echo error : ndk-build failed with errors!
+ cd..
+)
diff --git a/android/raytracing/jni/Android.mk b/android/raytracing/jni/Android.mk
new file mode 100644
index 00000000..327b28ea
--- /dev/null
+++ b/android/raytracing/jni/Android.mk
@@ -0,0 +1,47 @@
+LOCAL_PATH := $(call my-dir)/../../raytracing
+
+# assimp
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := assimp
+LOCAL_SRC_FILES := $(LOCAL_PATH)/../../libs/assimp/$(TARGET_ARCH_ABI)/libassimp.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+# vulkan example
+
+DATADIR := $(LOCAL_PATH)/../../data
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := vulkanRaytracing
+
+PROJECT_FILES := $(wildcard $(LOCAL_PATH)/../../raytracing/*.cpp)
+PROJECT_FILES += $(wildcard $(LOCAL_PATH)/../../base/*.cpp)
+
+LOCAL_CPPFLAGS := -std=c++11
+LOCAL_CPPFLAGS += -D__STDC_LIMIT_MACROS
+LOCAL_CPPFLAGS += -DVK_NO_PROTOTYPES
+LOCAL_CPPFLAGS += -DVK_USE_PLATFORM_ANDROID_KHR
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../external/
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../external/glm
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../external/gli
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../external/assimp
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../base/
+#LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../base/android
+
+LOCAL_SRC_FILES := $(PROJECT_FILES)
+
+LOCAL_LDLIBS := -landroid -llog -lz
+
+LOCAL_DISABLE_FORMAT_STRING_CHECKS := true
+
+LOCAL_STATIC_LIBRARIES += android_native_app_glue
+LOCAL_STATIC_LIBRARIES += cpufeatures
+LOCAL_STATIC_LIBRARIES += libassimp
+
+include $(BUILD_SHARED_LIBRARY)
+
+$(call import-module, android/native_app_glue)
+$(call import-module, android/cpufeatures)
diff --git a/android/raytracing/jni/Application.mk b/android/raytracing/jni/Application.mk
new file mode 100644
index 00000000..62020feb
--- /dev/null
+++ b/android/raytracing/jni/Application.mk
@@ -0,0 +1,5 @@
+APP_PLATFORM := android-19
+APP_ABI := armeabi-v7a
+APP_STL := c++_static
+APP_CPPFLAGS := -std=c++11
+NDK_TOOLCHAIN_VERSION := clang
diff --git a/data/shaders/raytracing/generate-spirv.bat b/data/shaders/raytracing/generate-spirv.bat
new file mode 100644
index 00000000..5266a7f9
--- /dev/null
+++ b/data/shaders/raytracing/generate-spirv.bat
@@ -0,0 +1,3 @@
+glslangvalidator -V texture.frag -o texture.frag.spv
+glslangvalidator -V texture.vert -o texture.vert.spv
+glslangvalidator -V raytracing.comp -o raytracing.comp.spv
\ No newline at end of file
diff --git a/data/shaders/raytracing/raytracing.comp b/data/shaders/raytracing/raytracing.comp
new file mode 100644
index 00000000..f35e61a6
--- /dev/null
+++ b/data/shaders/raytracing/raytracing.comp
@@ -0,0 +1,187 @@
+// Shader is looseley based on the ray tracing coding session by Inigo Quilez (www.iquilezles.org)
+
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (local_size_x =16, local_size_y = 16) in;
+layout (binding = 0, rgba8) uniform image2D resultImage;
+
+#define MAXLEN 1000.0
+#define PLANEID 1
+#define SPHERECOUNT 3
+
+struct Camera {
+ vec3 pos;
+ vec3 lookat;
+ float fov;
+};
+
+layout (binding = 1) uniform UBO
+{
+ vec3 lightPos;
+ float aspectRatio;
+ vec4 fogColor;
+ Camera camera;
+ mat4 rotMat;
+} ubo;
+
+// Lighting calculations
+
+float lightDiffuse(vec3 normal, vec3 lightDir)
+{
+ return clamp(dot(normal, lightDir), 0.0, 1.0);
+}
+
+float lightSpecular(vec3 normal, vec3 lightDir)
+{
+ vec3 viewVec = normalize(ubo.camera.pos);
+ vec3 halfVec = normalize(lightDir + viewVec);
+ return pow(clamp(dot(normal, halfVec), 0.0, 1.0), 16.0);
+}
+
+// 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];
+
+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 h = b*b - 4.0*c;
+ if (h < 0.0)
+ {
+ return -1.0;
+ }
+ float t = (-b - sqrt(h)) / 2.0;
+ return t;
+}
+
+vec3 sphereNormal(in vec3 pos, in Sphere sphere)
+{
+ return (pos - sphere.pos) / sphere.r;
+}
+
+// Plane
+
+float planeIntersect(vec3 rayO, vec3 rayD)
+{
+ return -rayO.y/rayD.y;
+}
+
+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 id = -1;
+ resT = MAXLEN;
+
+ float tplane = planeIntersect(rayO, rayD);
+ if ((tplane > 0.0) && (tplane < resT))
+ {
+ id = PLANEID;
+ resT = tplane;
+ }
+
+ for (int i = 0; i < SPHERECOUNT; i++)
+ {
+ float tSphere = sphereIntersect(rayO, rayD, spheres[i]);
+ if (tSphere > 0.0)
+ {
+ id = spheres[i].id;
+ resT = tSphere;
+ break;
+ }
+ }
+
+ return id;
+}
+
+vec3 fog(in float t, in vec3 color)
+{
+ return mix(color, ubo.fogColor.rgb, clamp(sqrt(t*t)/10.0, 0.0, 1.0));
+}
+
+void main()
+{
+ // Scene setup
+ 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(1.0, 1.0, 1.0);
+
+ spheres[1].id = 3;
+ spheres[1].pos = vec3(2.25, 1.0, 0.0);
+ spheres[1].r = 1.0;
+ spheres[1].material.diffuse = vec3(0.0, 1.0, 0.0);
+ spheres[1].material.specular = vec3(1.0, 1.0, 1.0);
+
+ spheres[2].id = 4;
+ spheres[2].pos = vec3(0.0, 1.0, 0.0);
+ spheres[2].r = 1.0;
+ spheres[2].material.diffuse = vec3(0.0, 0.0, 1.0);
+ spheres[2].material.specular = vec3(1.0, 1.0, 1.0);
+
+ ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 dim = imageSize(resultImage);
+ vec2 uv = vec2(gl_GlobalInvocationID.xy)/dim;
+
+ vec3 rayO = ubo.camera.pos;
+ vec3 rayD = normalize(vec3((-1.0 + 2.0 * uv) * vec2(ubo.aspectRatio, 1.0), -1.0));
+
+ // Get intersected object ID
+ float t;
+ int objectID = intersect(rayO, rayD, t);
+
+ vec3 color = vec3(0.0);
+
+ if (objectID == PLANEID)
+ {
+ vec3 pos = rayO + t * rayD;
+ vec3 normal = planeNormal(pos);
+ vec3 lightVec = normalize(ubo.lightPos - 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 == spheres[i].id)
+ {
+ vec3 pos = rayO + t * rayD;
+ vec3 lightVec = normalize(ubo.lightPos - pos);
+ vec3 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;
+ }
+ }
+ }
+
+ color = fog(t, color);
+
+ imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), vec4(color, 0.0));
+}
\ No newline at end of file
diff --git a/data/shaders/raytracing/raytracing.comp.spv b/data/shaders/raytracing/raytracing.comp.spv
new file mode 100644
index 00000000..88cd2719
Binary files /dev/null and b/data/shaders/raytracing/raytracing.comp.spv differ
diff --git a/data/shaders/raytracing/texture.frag b/data/shaders/raytracing/texture.frag
new file mode 100644
index 00000000..389026a7
--- /dev/null
+++ b/data/shaders/raytracing/texture.frag
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 0) uniform sampler2D samplerColor;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main()
+{
+ outFragColor = texture(samplerColor, vec2(inUV.s, 1.0 - inUV.t));
+}
\ No newline at end of file
diff --git a/data/shaders/raytracing/texture.frag.spv b/data/shaders/raytracing/texture.frag.spv
new file mode 100644
index 00000000..c8595438
Binary files /dev/null and b/data/shaders/raytracing/texture.frag.spv differ
diff --git a/data/shaders/raytracing/texture.vert b/data/shaders/raytracing/texture.vert
new file mode 100644
index 00000000..2b8a0e9e
--- /dev/null
+++ b/data/shaders/raytracing/texture.vert
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec2 inUV;
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+ outUV = inUV;
+ gl_Position = vec4(inPos.xyz, 1.0);
+}
diff --git a/data/shaders/raytracing/texture.vert.spv b/data/shaders/raytracing/texture.vert.spv
new file mode 100644
index 00000000..0350aaa0
Binary files /dev/null and b/data/shaders/raytracing/texture.vert.spv differ
diff --git a/raytracing/raytracing.cpp b/raytracing/raytracing.cpp
new file mode 100644
index 00000000..067e1041
--- /dev/null
+++ b/raytracing/raytracing.cpp
@@ -0,0 +1,794 @@
+/*
+* Vulkan Example - Compute shader ray tracing
+*
+* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
+*
+* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#define GLM_FORCE_RADIANS
+#define GLM_FORCE_DEPTH_ZERO_TO_ONE
+#include
+#include
+
+#include
+#include "vulkanexamplebase.h"
+
+#define VERTEX_BUFFER_BIND_ID 0
+#define ENABLE_VALIDATION false
+
+#define TEX_DIM 2048
+
+// Vertex layout for this example
+struct Vertex {
+ float pos[3];
+ float uv[2];
+};
+
+class VulkanExample : public VulkanExampleBase
+{
+private:
+ vkTools::VulkanTexture textureComputeTarget;
+public:
+ struct {
+ VkPipelineVertexInputStateCreateInfo inputState;
+ std::vector bindingDescriptions;
+ std::vector attributeDescriptions;
+ } vertices;
+
+ struct {
+ vkMeshLoader::MeshBuffer quad;
+ } meshes;
+
+ vkTools::UniformData uniformDataCompute;
+
+ struct {
+ glm::vec3 lightPos;
+ // Aspect ratio of the viewport
+ float aspectRatio;
+ glm::vec4 fogColor = glm::vec4(0.025f, 0.025f, 0.025f, 0.0f);
+ struct {
+ glm::vec3 pos = glm::vec3(0.0f, 1.0f, 4.0f);
+ glm::vec3 lookat = glm::vec3(0.0f, 0.5f, 0.0f);
+ float fov = 10.0f;
+ } camera;
+ } uboCompute;
+
+ struct {
+ VkPipeline display;
+ VkPipeline compute;
+ } pipelines;
+
+ int vertexBufferSize;
+
+ VkQueue computeQueue;
+ VkCommandBuffer computeCmdBuffer;
+ VkPipelineLayout computePipelineLayout;
+ VkDescriptorSet computeDescriptorSet;
+ VkDescriptorSetLayout computeDescriptorSetLayout;
+ VkDescriptorPool computeDescriptorPool;
+
+ VkPipelineLayout pipelineLayout;
+ VkDescriptorSet descriptorSetPostCompute;
+ VkDescriptorSetLayout descriptorSetLayout;
+
+ VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
+ {
+ zoom = -2.0f;
+ title = "Vulkan Example - Compute shader ray tracing";
+ uboCompute.aspectRatio = (float)width / (float)height;
+ paused = true;
+ }
+
+ ~VulkanExample()
+ {
+ // Clean up used Vulkan resources
+ // Note : Inherited destructor cleans up resources stored in base class
+
+ vkDestroyPipeline(device, pipelines.display, nullptr);
+ vkDestroyPipeline(device, pipelines.compute, nullptr);
+
+ vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
+ vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
+
+ vkMeshLoader::freeMeshBufferResources(device, &meshes.quad);
+
+ vkTools::destroyUniformData(device, &uniformDataCompute);
+
+ vkFreeCommandBuffers(device, cmdPool, 1, &computeCmdBuffer);
+
+ textureLoader->destroyTexture(textureComputeTarget);
+ }
+
+ // Prepare a texture target that is used to store compute shader calculations
+ void prepareTextureTarget(vkTools::VulkanTexture *tex, uint32_t width, uint32_t height, VkFormat format)
+ {
+ // Get device properties for the requested texture format
+ VkFormatProperties formatProperties;
+ vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties);
+ // Check if requested image format supports image storage operations
+ assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
+
+ // Prepare blit target texture
+ tex->width = width;
+ tex->height = height;
+
+ VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo();
+ imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageCreateInfo.format = format;
+ imageCreateInfo.extent = { width, height, 1 };
+ imageCreateInfo.mipLevels = 1;
+ imageCreateInfo.arrayLayers = 1;
+ imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ // Image will be sampled in the fragment shader and used as storage target in the compute shader
+ imageCreateInfo.usage =
+ VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_STORAGE_BIT;
+ imageCreateInfo.flags = 0;
+
+ VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo();
+ VkMemoryRequirements memReqs;
+
+ vkTools::checkResult(vkCreateImage(device, &imageCreateInfo, nullptr, &tex->image));
+ vkGetImageMemoryRequirements(device, tex->image, &memReqs);
+ memAllocInfo.allocationSize = memReqs.size;
+ getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex);
+ vkTools::checkResult(vkAllocateMemory(device, &memAllocInfo, nullptr, &tex->deviceMemory));
+ vkTools::checkResult(vkBindImageMemory(device, tex->image, tex->deviceMemory, 0));
+
+ tex->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ vkTools::setImageLayout(
+ setupCmdBuffer, tex->image,
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_PREINITIALIZED,
+ tex->imageLayout);
+
+ // Create sampler
+ VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo();
+ sampler.magFilter = VK_FILTER_LINEAR;
+ sampler.minFilter = VK_FILTER_LINEAR;
+ sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler.addressModeV = sampler.addressModeU;
+ sampler.addressModeW = sampler.addressModeU;
+ sampler.mipLodBias = 0.0f;
+ sampler.maxAnisotropy = 0;
+ sampler.compareOp = VK_COMPARE_OP_NEVER;
+ sampler.minLod = 0.0f;
+ sampler.maxLod = 0.0f;
+ sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+ vkTools::checkResult(vkCreateSampler(device, &sampler, nullptr, &tex->sampler));
+
+ // Create image view
+ VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo();
+ view.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ view.format = format;
+ view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
+ view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+ view.image = tex->image;
+ vkTools::checkResult(vkCreateImageView(device, &view, nullptr, &tex->view));
+ }
+
+ void buildCommandBuffers()
+ {
+ // Destroy command buffers if already present
+ if (!checkCommandBuffers())
+ {
+ destroyCommandBuffers();
+ createCommandBuffers();
+ }
+
+ VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
+
+ VkClearValue clearValues[2];
+ clearValues[0].color = defaultClearColor;
+ clearValues[0].color = { {0.0f, 0.0f, 0.2f, 0.0f} };
+ clearValues[1].depthStencil = { 1.0f, 0 };
+
+ VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo();
+ renderPassBeginInfo.renderPass = renderPass;
+ renderPassBeginInfo.renderArea.offset.x = 0;
+ renderPassBeginInfo.renderArea.offset.y = 0;
+ renderPassBeginInfo.renderArea.extent.width = width;
+ renderPassBeginInfo.renderArea.extent.height = height;
+ renderPassBeginInfo.clearValueCount = 2;
+ renderPassBeginInfo.pClearValues = clearValues;
+
+ VkResult err;
+
+ for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
+ {
+ // Set target frame buffer
+ renderPassBeginInfo.framebuffer = frameBuffers[i];
+
+ err = vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo);
+ assert(!err);
+
+ // Image memory barrier to make sure that compute
+ // shader writes are finished before sampling
+ // from the texture
+ VkImageMemoryBarrier imageMemoryBarrier = {};
+ imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imageMemoryBarrier.pNext = NULL;
+ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
+ imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
+ imageMemoryBarrier.image = textureComputeTarget.image;
+ imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
+ vkCmdPipelineBarrier(
+ drawCmdBuffers[i],
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_FLAGS_NONE,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &imageMemoryBarrier);
+
+ vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
+ vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
+
+ VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0);
+ vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
+
+ VkDeviceSize offsets[1] = { 0 };
+ vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.quad.vertices.buf, offsets);
+ vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.quad.indices.buf, 0, VK_INDEX_TYPE_UINT32);
+
+ // Display ray traced image generated by compute shader as a full screen quad
+
+ vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSetPostCompute, 0, NULL);
+ vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.display);
+
+ vkCmdDrawIndexed(drawCmdBuffers[i], meshes.quad.indexCount, 1, 0, 0, 0);
+
+ vkCmdEndRenderPass(drawCmdBuffers[i]);
+
+ err = vkEndCommandBuffer(drawCmdBuffers[i]);
+ assert(!err);
+ }
+
+ }
+
+ void buildComputeCommandBuffer()
+ {
+ VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
+
+ VkResult err = vkBeginCommandBuffer(computeCmdBuffer, &cmdBufInfo);
+ assert(!err);
+
+ vkCmdBindPipeline(computeCmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelines.compute);
+ vkCmdBindDescriptorSets(computeCmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &computeDescriptorSet, 0, 0);
+
+ vkCmdDispatch(computeCmdBuffer, textureComputeTarget.width / 16, textureComputeTarget.height / 16, 1);
+
+ vkEndCommandBuffer(computeCmdBuffer);
+ }
+
+ void draw()
+ {
+ VkResult err;
+
+ // Get next image in the swap chain (back/front buffer)
+ err = swapChain.acquireNextImage(semaphores.presentComplete, ¤tBuffer);
+ assert(!err);
+
+ submitPostPresentBarrier(swapChain.buffers[currentBuffer].image);
+
+ // Command buffer to be sumitted to the queue
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
+
+ // Submit to queue
+ err = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
+ assert(!err);
+
+ submitPrePresentBarrier(swapChain.buffers[currentBuffer].image);
+
+ err = swapChain.queuePresent(queue, currentBuffer, semaphores.renderComplete);
+ assert(!err);
+
+ err = vkQueueWaitIdle(queue);
+ assert(!err);
+
+ // Compute
+ VkSubmitInfo computeSubmitInfo = vkTools::initializers::submitInfo();
+ computeSubmitInfo.commandBufferCount = 1;
+ computeSubmitInfo.pCommandBuffers = &computeCmdBuffer;
+
+ err = vkQueueSubmit(computeQueue, 1, &computeSubmitInfo, VK_NULL_HANDLE);
+ assert(!err);
+
+ err = vkQueueWaitIdle(computeQueue);
+ assert(!err);
+ }
+
+ // Setup vertices for a single uv-mapped quad
+ void generateQuad()
+ {
+#define dim 1.0f
+ std::vector vertexBuffer =
+ {
+ { { dim, dim, 0.0f }, { 1.0f, 1.0f } },
+ { { -dim, dim, 0.0f }, { 0.0f, 1.0f } },
+ { { -dim, -dim, 0.0f }, { 0.0f, 0.0f } },
+ { { dim, -dim, 0.0f }, { 1.0f, 0.0f } }
+ };
+#undef dim
+
+ createBuffer(
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ vertexBuffer.size() * sizeof(Vertex),
+ vertexBuffer.data(),
+ &meshes.quad.vertices.buf,
+ &meshes.quad.vertices.mem);
+
+ // Setup indices
+ std::vector indexBuffer = { 0,1,2, 2,3,0 };
+ meshes.quad.indexCount = indexBuffer.size();
+
+ createBuffer(
+ VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+ indexBuffer.size() * sizeof(uint32_t),
+ indexBuffer.data(),
+ &meshes.quad.indices.buf,
+ &meshes.quad.indices.mem);
+ }
+
+ void setupVertexDescriptions()
+ {
+ // Binding description
+ vertices.bindingDescriptions.resize(1);
+ vertices.bindingDescriptions[0] =
+ vkTools::initializers::vertexInputBindingDescription(
+ VERTEX_BUFFER_BIND_ID,
+ sizeof(Vertex),
+ VK_VERTEX_INPUT_RATE_VERTEX);
+
+ // Attribute descriptions
+ // Describes memory layout and shader positions
+ vertices.attributeDescriptions.resize(2);
+ // Location 0 : Position
+ vertices.attributeDescriptions[0] =
+ vkTools::initializers::vertexInputAttributeDescription(
+ VERTEX_BUFFER_BIND_ID,
+ 0,
+ VK_FORMAT_R32G32B32_SFLOAT,
+ 0);
+ // Location 1 : Texture coordinates
+ vertices.attributeDescriptions[1] =
+ vkTools::initializers::vertexInputAttributeDescription(
+ VERTEX_BUFFER_BIND_ID,
+ 1,
+ VK_FORMAT_R32G32_SFLOAT,
+ sizeof(float) * 3);
+
+ // Assign to vertex buffer
+ vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo();
+ 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()
+ {
+ 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),
+ };
+
+ VkDescriptorPoolCreateInfo descriptorPoolInfo =
+ vkTools::initializers::descriptorPoolCreateInfo(
+ poolSizes.size(),
+ poolSizes.data(),
+ 3);
+
+ VkResult vkRes = vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool);
+ assert(!vkRes);
+ }
+
+ void setupDescriptorSetLayout()
+ {
+ std::vector setLayoutBindings =
+ {
+ // Binding 0 : Fragment shader image sampler
+ vkTools::initializers::descriptorSetLayoutBinding(
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ 0)
+ };
+
+ VkDescriptorSetLayoutCreateInfo descriptorLayout =
+ vkTools::initializers::descriptorSetLayoutCreateInfo(
+ setLayoutBindings.data(),
+ setLayoutBindings.size());
+
+ VkResult err = vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout);
+ assert(!err);
+
+ VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
+ vkTools::initializers::pipelineLayoutCreateInfo(
+ &descriptorSetLayout,
+ 1);
+
+ err = vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout);
+ assert(!err);
+ }
+
+ void setupDescriptorSet()
+ {
+ VkDescriptorSetAllocateInfo allocInfo =
+ vkTools::initializers::descriptorSetAllocateInfo(
+ descriptorPool,
+ &descriptorSetLayout,
+ 1);
+
+ VkResult vkRes = vkAllocateDescriptorSets(device, &allocInfo, &descriptorSetPostCompute);
+ assert(!vkRes);
+
+ // Image descriptor for the color map texture
+ VkDescriptorImageInfo texDescriptor =
+ vkTools::initializers::descriptorImageInfo(
+ textureComputeTarget.sampler,
+ textureComputeTarget.view,
+ VK_IMAGE_LAYOUT_GENERAL);
+
+ std::vector writeDescriptorSets =
+ {
+ // Binding 0 : Fragment shader texture sampler
+ vkTools::initializers::writeDescriptorSet(
+ descriptorSetPostCompute,
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ 0,
+ &texDescriptor)
+ };
+
+ vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
+ }
+
+ // Create a separate command buffer for compute commands
+ void createComputeCommandBuffer()
+ {
+ VkCommandBufferAllocateInfo cmdBufAllocateInfo =
+ vkTools::initializers::commandBufferAllocateInfo(
+ cmdPool,
+ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ 1);
+
+ VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &computeCmdBuffer);
+ assert(!vkRes);
+ }
+
+ void preparePipelines()
+ {
+ VkResult err;
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
+ vkTools::initializers::pipelineInputAssemblyStateCreateInfo(
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ 0,
+ VK_FALSE);
+
+ VkPipelineRasterizationStateCreateInfo rasterizationState =
+ vkTools::initializers::pipelineRasterizationStateCreateInfo(
+ VK_POLYGON_MODE_FILL,
+ VK_CULL_MODE_NONE,
+ VK_FRONT_FACE_COUNTER_CLOCKWISE,
+ 0);
+
+ VkPipelineColorBlendAttachmentState blendAttachmentState =
+ vkTools::initializers::pipelineColorBlendAttachmentState(
+ 0xf,
+ VK_FALSE);
+
+ VkPipelineColorBlendStateCreateInfo colorBlendState =
+ vkTools::initializers::pipelineColorBlendStateCreateInfo(
+ 1,
+ &blendAttachmentState);
+
+ VkPipelineDepthStencilStateCreateInfo depthStencilState =
+ vkTools::initializers::pipelineDepthStencilStateCreateInfo(
+ VK_TRUE,
+ VK_TRUE,
+ VK_COMPARE_OP_LESS_OR_EQUAL);
+
+ VkPipelineViewportStateCreateInfo viewportState =
+ vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
+
+ VkPipelineMultisampleStateCreateInfo multisampleState =
+ vkTools::initializers::pipelineMultisampleStateCreateInfo(
+ VK_SAMPLE_COUNT_1_BIT,
+ 0);
+
+ std::vector dynamicStateEnables = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR
+ };
+ VkPipelineDynamicStateCreateInfo dynamicState =
+ vkTools::initializers::pipelineDynamicStateCreateInfo(
+ dynamicStateEnables.data(),
+ dynamicStateEnables.size(),
+ 0);
+
+ // Display pipeline
+ std::array shaderStages;
+
+ shaderStages[0] = loadShader(getAssetPath() + "shaders/raytracing/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
+ shaderStages[1] = loadShader(getAssetPath() + "shaders/raytracing/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
+
+ VkGraphicsPipelineCreateInfo pipelineCreateInfo =
+ vkTools::initializers::pipelineCreateInfo(
+ 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();
+ pipelineCreateInfo.renderPass = renderPass;
+
+ err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.display);
+ assert(!err);
+ }
+
+ // Prepare the compute pipeline that generates the ray traced image
+ void prepareCompute()
+ {
+ std::vector setLayoutBindings = {
+ // Binding 0 : Sampled image (write)
+ vkTools::initializers::descriptorSetLayoutBinding(
+ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ VK_SHADER_STAGE_COMPUTE_BIT,
+ 0),
+ // Binding 1 : Uniform buffer block
+ vkTools::initializers::descriptorSetLayoutBinding(
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ VK_SHADER_STAGE_COMPUTE_BIT,
+ 1)
+ };
+
+ VkDescriptorSetLayoutCreateInfo descriptorLayout =
+ vkTools::initializers::descriptorSetLayoutCreateInfo(
+ setLayoutBindings.data(),
+ setLayoutBindings.size());
+
+ VkResult err = vkCreateDescriptorSetLayout(
+ device,
+ &descriptorLayout,
+ nullptr,
+ &computeDescriptorSetLayout);
+ assert(!err);
+
+ VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
+ vkTools::initializers::pipelineLayoutCreateInfo(
+ &computeDescriptorSetLayout,
+ 1);
+
+ err = vkCreatePipelineLayout(
+ device,
+ &pPipelineLayoutCreateInfo,
+ nullptr,
+ &computePipelineLayout);
+ assert(!err);
+
+ VkDescriptorSetAllocateInfo allocInfo =
+ vkTools::initializers::descriptorSetAllocateInfo(
+ descriptorPool,
+ &computeDescriptorSetLayout,
+ 1);
+
+ err = vkAllocateDescriptorSets(device, &allocInfo, &computeDescriptorSet);
+ assert(!err);
+
+ std::vector computeTexDescriptors =
+ {
+ vkTools::initializers::descriptorImageInfo(
+ VK_NULL_HANDLE,
+ textureComputeTarget.view,
+ VK_IMAGE_LAYOUT_GENERAL)
+ };
+
+ std::vector computeWriteDescriptorSets =
+ {
+ // Binding 0 : Output storage image
+ vkTools::initializers::writeDescriptorSet(
+ computeDescriptorSet,
+ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ 0,
+ &computeTexDescriptors[0]),
+ // Binding 1 : Uniform buffer block
+ vkTools::initializers::writeDescriptorSet(
+ computeDescriptorSet,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ 1,
+ &uniformDataCompute.descriptor)
+ };
+
+ vkUpdateDescriptorSets(device, computeWriteDescriptorSets.size(), computeWriteDescriptorSets.data(), 0, NULL);
+
+
+ // Create compute shader pipelines
+ VkComputePipelineCreateInfo computePipelineCreateInfo =
+ vkTools::initializers::computePipelineCreateInfo(
+ computePipelineLayout,
+ 0);
+
+ computePipelineCreateInfo.stage = loadShader(getAssetPath() + "shaders/raytracing/raytracing.comp.spv", VK_SHADER_STAGE_COMPUTE_BIT);
+ vkTools::checkResult(vkCreateComputePipelines(device, pipelineCache, 1, &computePipelineCreateInfo, nullptr, &pipelines.compute));
+ }
+
+ // Prepare and initialize uniform buffer containing shader uniforms
+ void prepareUniformBuffers()
+ {
+ // Vertex shader uniform buffer block
+ createBuffer(
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ sizeof(uboCompute),
+ &uboCompute,
+ &uniformDataCompute.buffer,
+ &uniformDataCompute.memory,
+ &uniformDataCompute.descriptor);
+
+ updateUniformBuffers();
+ }
+
+ void updateUniformBuffers()
+ {
+ uboCompute.lightPos.x = 0.0f;
+ uboCompute.lightPos.y = 1.0f;
+ uboCompute.lightPos.z = 1.5f;
+ uint8_t *pData;
+ vkTools::checkResult(vkMapMemory(device, uniformDataCompute.memory, 0, sizeof(uboCompute), 0, (void **)&pData));
+ memcpy(pData, &uboCompute, sizeof(uboCompute));
+ vkUnmapMemory(device, uniformDataCompute.memory);
+ }
+
+ // Find and create a compute capable device queue
+ void getComputeQueue()
+ {
+ uint32_t queueIndex = 0;
+ uint32_t queueCount;
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
+ assert(queueCount >= 1);
+
+ std::vector queueProps;
+ queueProps.resize(queueCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
+
+ for (queueIndex = 0; queueIndex < queueCount; queueIndex++)
+ {
+ if (queueProps[queueIndex].queueFlags & VK_QUEUE_COMPUTE_BIT)
+ break;
+ }
+ assert(queueIndex < queueCount);
+
+ VkDeviceQueueCreateInfo queueCreateInfo = {};
+ queueCreateInfo.queueFamilyIndex = queueIndex;
+ queueCreateInfo.queueCount = 1;
+ vkGetDeviceQueue(device, queueIndex, 0, &computeQueue);
+ }
+
+ void prepare()
+ {
+ VulkanExampleBase::prepare();
+ generateQuad();
+ getComputeQueue();
+ createComputeCommandBuffer();
+ setupVertexDescriptions();
+ prepareUniformBuffers();
+ prepareTextureTarget(
+ &textureComputeTarget,
+ TEX_DIM,
+ TEX_DIM,
+ VK_FORMAT_R8G8B8A8_UNORM);
+ setupDescriptorSetLayout();
+ preparePipelines();
+ setupDescriptorPool();
+ setupDescriptorSet();
+ prepareCompute();
+ buildCommandBuffers();
+ buildComputeCommandBuffer();
+ prepared = true;
+ }
+
+ virtual void render()
+ {
+ if (!prepared)
+ return;
+ vkDeviceWaitIdle(device);
+ draw();
+ vkDeviceWaitIdle(device);
+ if (!paused)
+ {
+ updateUniformBuffers();
+ }
+ }
+
+ virtual void viewChanged()
+ {
+ updateUniformBuffers();
+ }
+};
+
+VulkanExample *vulkanExample;
+
+#if defined(_WIN32)
+LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (vulkanExample != NULL)
+ {
+ vulkanExample->handleMessages(hWnd, uMsg, wParam, lParam);
+ }
+ return (DefWindowProc(hWnd, uMsg, wParam, lParam));
+}
+#elif defined(__linux__) && !defined(__ANDROID__)
+static void handleEvent(const xcb_generic_event_t *event)
+{
+ if (vulkanExample != NULL)
+ {
+ vulkanExample->handleEvent(event);
+ }
+}
+#endif
+
+// Main entry point
+#if defined(_WIN32)
+// Windows entry point
+int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
+#elif defined(__ANDROID__)
+// Android entry point
+void android_main(android_app* state)
+#elif defined(__linux__)
+// Linux entry point
+int main(const int argc, const char *argv[])
+#endif
+{
+#if defined(__ANDROID__)
+ // Removing this may cause the compiler to omit the main entry point
+ // which would make the application crash at start
+ app_dummy();
+#endif
+ vulkanExample = new VulkanExample();
+#if defined(_WIN32)
+ vulkanExample->setupWindow(hInstance, WndProc);
+#elif defined(__ANDROID__)
+ // Attach vulkan example to global android application state
+ state->userData = vulkanExample;
+ state->onAppCmd = VulkanExample::handleAppCommand;
+ state->onInputEvent = VulkanExample::handleAppInput;
+ vulkanExample->androidApp = state;
+#elif defined(__linux__)
+ vulkanExample->setupWindow();
+#endif
+#if !defined(__ANDROID__)
+ vulkanExample->initSwapchain();
+ vulkanExample->prepare();
+#endif
+ vulkanExample->renderLoop();
+ delete(vulkanExample);
+#if !defined(__ANDROID__)
+ return 0;
+#endif
+}
diff --git a/raytracing/raytracing.vcxproj b/raytracing/raytracing.vcxproj
new file mode 100644
index 00000000..5bfc8967
--- /dev/null
+++ b/raytracing/raytracing.vcxproj
@@ -0,0 +1,96 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {8B1C24E5-CC00-484C-9F6F-8FFCBDA3AA30}
+ Win32Proj
+ 8.1
+
+
+
+ Application
+ true
+ v140
+
+
+ Application
+ false
+ v140
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\bin\
+ $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName)
+
+
+ true
+ $(SolutionDir)\bin\
+ $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName)
+
+
+
+ WIN32;_DEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+ Level3
+ ProgramDatabase
+ Disabled
+ ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ true
+ Windows
+ ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies)
+
+
+
+
+ WIN32;NDEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+ ProgramDatabase
+ ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories)
+ MaxSpeed
+
+
+ true
+ Windows
+ true
+ true
+ ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/raytracing/raytracing.vcxproj.filters b/raytracing/raytracing.vcxproj.filters
new file mode 100644
index 00000000..8c9d76b7
--- /dev/null
+++ b/raytracing/raytracing.vcxproj.filters
@@ -0,0 +1,42 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/vulkanExamples.sln b/vulkanExamples.sln
index 89a5801f..7518d8fc 100644
--- a/vulkanExamples.sln
+++ b/vulkanExamples.sln
@@ -59,6 +59,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shadowmapping", "shadowmapp
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "particlefire", "particlefire\particlefire.vcxproj", "{A8DDE46D-0C36-49E5-83CB-19FF69493FA0}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "raytracing", "raytracing\raytracing.vcxproj", "{8B1C24E5-CC00-484C-9F6F-8FFCBDA3AA30}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -177,6 +179,10 @@ Global
{A8DDE46D-0C36-49E5-83CB-19FF69493FA0}.Debug|x64.Build.0 = Debug|x64
{A8DDE46D-0C36-49E5-83CB-19FF69493FA0}.Release|x64.ActiveCfg = Release|x64
{A8DDE46D-0C36-49E5-83CB-19FF69493FA0}.Release|x64.Build.0 = Release|x64
+ {8B1C24E5-CC00-484C-9F6F-8FFCBDA3AA30}.Debug|x64.ActiveCfg = Debug|x64
+ {8B1C24E5-CC00-484C-9F6F-8FFCBDA3AA30}.Debug|x64.Build.0 = Debug|x64
+ {8B1C24E5-CC00-484C-9F6F-8FFCBDA3AA30}.Release|x64.ActiveCfg = Release|x64
+ {8B1C24E5-CC00-484C-9F6F-8FFCBDA3AA30}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE