diff --git a/.gitignore b/.gitignore index c4ab876b..8c318e38 100644 --- a/.gitignore +++ b/.gitignore @@ -240,4 +240,6 @@ android/.idea/** libs/vulkan/*.so -.vscode/* \ No newline at end of file +.vscode/* + +__pycache__** \ No newline at end of file diff --git a/README.md b/README.md index 1a11894c..85fc514e 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Once built, examples can be run from the bin directory. The list of available co -vs, --vsync: Enable V-Sync -f, --fullscreen: Start in fullscreen mode -w, --width: Set window width - -s, --shaders: Select shader type to use (glsl or hlsl) + -s, --shaders: Select shader type to use (glsl, slang, hlsl) -g, --gpu: Select GPU to run on -gl, --listgpus: Display a list of available Vulkan devices -b, --benchmark: Run example in benchmark mode @@ -83,7 +83,7 @@ Note that some examples require specific device features, and if you are on a mu ## Shaders -Vulkan consumes shaders in an intermediate representation called SPIR-V. This makes it possible to use different shader languages by compiling them to that bytecode format. The primary shader language used here is [GLSL](shaders/glsl) but most samples also come with [HLSL](shaders/hlsl) shader sources. +Vulkan consumes shaders in an intermediate representation called SPIR-V. This makes it possible to use different shader languages by compiling them to that bytecode format. The primary shader language used here is [GLSL](shaders/glsl), most samples also come with [slang](shaders/slang/) and [HLSL](shaders/hlsl) shader sources, making it easy to compare the differences between those shading languages. ## A note on synchronization diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index 3584eddb..b4ad3a69 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -89,6 +89,15 @@ VkResult VulkanExampleBase::createInstance() } } + // Shaders generated by Slang require a certain SPIR-V environment that can't be satisfied by Vulkan 1.0, so we need to expliclity up that to at least 1.1 and enable some required extensions + if (shaderDir == "slang") { + if (apiVersion < VK_API_VERSION_1_1) { + apiVersion = VK_API_VERSION_1_1; + } + enabledDeviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); + } + VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = name.c_str(); diff --git a/examples/computecullandlod/computecullandlod.cpp b/examples/computecullandlod/computecullandlod.cpp index 0aa566cb..6cb5ae9e 100644 --- a/examples/computecullandlod/computecullandlod.cpp +++ b/examples/computecullandlod/computecullandlod.cpp @@ -391,8 +391,8 @@ public: vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(vkglTF::Vertex, color)), // Location 2: Texture coordinates // Per-Instance attributes // These are fetched for each instance rendered - vks::initializers::vertexInputAttributeDescription(1, 4, VK_FORMAT_R32G32B32_SFLOAT, offsetof(InstanceData, pos)), // Location 4: Position - vks::initializers::vertexInputAttributeDescription(1, 5, VK_FORMAT_R32_SFLOAT, offsetof(InstanceData, scale)), // Location 5: Scale + vks::initializers::vertexInputAttributeDescription(1, 3, VK_FORMAT_R32G32B32_SFLOAT, offsetof(InstanceData, pos)), // Location 4: Position + vks::initializers::vertexInputAttributeDescription(1, 4, VK_FORMAT_R32_SFLOAT, offsetof(InstanceData, scale)), // Location 5: Scale }; inputState.pVertexBindingDescriptions = bindingDescriptions.data(); inputState.pVertexAttributeDescriptions = attributeDescriptions.data(); diff --git a/examples/computeheadless/computeheadless.cpp b/examples/computeheadless/computeheadless.cpp index 334504fd..d0805037 100644 --- a/examples/computeheadless/computeheadless.cpp +++ b/examples/computeheadless/computeheadless.cpp @@ -82,6 +82,8 @@ public: VkDebugReportCallbackEXT debugReportCallback{}; + std::string shaderDir = "glsl"; + VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkBuffer *buffer, VkDeviceMemory *memory, VkDeviceSize size, void *data = nullptr) { // Create the buffer handle @@ -132,11 +134,19 @@ public: vks::android::loadVulkanLibrary(); #endif + if (commandLineParser.isSet("shaders")) { + shaderDir = commandLineParser.getValueAsString("shaders", "glsl"); + } + VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Vulkan headless example"; appInfo.pEngineName = "VulkanExample"; appInfo.apiVersion = VK_API_VERSION_1_0; + // Shaders generated by Slang require a certain SPIR-V environment that can't be satisfied by Vulkan 1.0, so we need to expliclity up that to at least 1.1 and enable some required extensions + if (shaderDir == "slang") { + appInfo.apiVersion = VK_API_VERSION_1_1; + } /* Vulkan instance creation (without surface extensions) @@ -159,7 +169,7 @@ public: bool layersAvailable = true; for (auto layerName : validationLayers) { bool layerAvailable = false; - for (auto instanceLayer : instanceLayers) { + for (auto& instanceLayer : instanceLayers) { if (strcmp(instanceLayer.layerName, layerName) == 0) { layerAvailable = true; break; @@ -260,8 +270,15 @@ public: deviceCreateInfo.queueCreateInfoCount = 1; deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; std::vector deviceExtensions = {}; + + // Shaders generated by Slang require a certain SPIR-V environment that can't be satisfied by Vulkan 1.0, so we need to expliclity up that to at least 1.1 and enable some required extensions + if (shaderDir == "slang") { + deviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); + deviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); + } + #if (defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT)) && defined(VK_KHR_portability_subset) - // SRS - When running on macOS with MoltenVK and VK_KHR_portability_subset is defined and supported by the device, enable the extension + // When running on macOS with MoltenVK and VK_KHR_portability_subset is defined and supported by the device, enable the extension uint32_t deviceExtCount = 0; vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtCount, nullptr); if (deviceExtCount > 0) @@ -410,10 +427,6 @@ public: VkSpecializationMapEntry specializationMapEntry = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t)); VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(SpecializationData), &specializationData); - std::string shaderDir = "glsl"; - if (commandLineParser.isSet("shaders")) { - shaderDir = commandLineParser.getValueAsString("shaders", "glsl"); - } const std::string shadersPath = getShaderBasePath() + shaderDir + "/computeheadless/"; VkPipelineShaderStageCreateInfo shaderStage = {}; diff --git a/examples/renderheadless/renderheadless.cpp b/examples/renderheadless/renderheadless.cpp index 6b2fa20e..59dbbac1 100644 --- a/examples/renderheadless/renderheadless.cpp +++ b/examples/renderheadless/renderheadless.cpp @@ -97,6 +97,8 @@ public: VkDebugReportCallbackEXT debugReportCallback{}; + std::string shaderDir = "glsl"; + uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties deviceMemoryProperties; vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); @@ -163,11 +165,19 @@ public: vks::android::loadVulkanLibrary(); #endif + if (commandLineParser.isSet("shaders")) { + shaderDir = commandLineParser.getValueAsString("shaders", "glsl"); + } + VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Vulkan headless example"; appInfo.pEngineName = "VulkanExample"; appInfo.apiVersion = VK_API_VERSION_1_0; + // Shaders generated by Slang require a certain SPIR-V environment that can't be satisfied by Vulkan 1.0, so we need to expliclity up that to at least 1.1 and enable some required extensions + if (shaderDir == "slang") { + appInfo.apiVersion = VK_API_VERSION_1_1; + } /* Vulkan instance creation (without surface extensions) @@ -190,7 +200,7 @@ public: bool layersAvailable = true; for (auto layerName : validationLayers) { bool layerAvailable = false; - for (auto instanceLayer : instanceLayers) { + for (auto& instanceLayer : instanceLayers) { if (strcmp(instanceLayer.layerName, layerName) == 0) { layerAvailable = true; break; @@ -290,8 +300,15 @@ public: deviceCreateInfo.queueCreateInfoCount = 1; deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; std::vector deviceExtensions = {}; + + // Shaders generated by Slang require a certain SPIR-V environment that can't be satisfied by Vulkan 1.0, so we need to expliclity up that to at least 1.1 and enable some required extensions + if (shaderDir == "slang") { + deviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); + deviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); + } + #if (defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT)) && defined(VK_KHR_portability_subset) - // SRS - When running on macOS with MoltenVK and VK_KHR_portability_subset is defined and supported by the device, enable the extension + // When running on macOS with MoltenVK and VK_KHR_portability_subset is defined and supported by the device, enable the extension uint32_t deviceExtCount = 0; vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtCount, nullptr); if (deviceExtCount > 0) @@ -643,7 +660,6 @@ public: pipelineCreateInfo.pVertexInputState = &vertexInputState; - std::string shaderDir = "glsl"; if (commandLineParser.isSet("shaders")) { shaderDir = commandLineParser.getValueAsString("shaders", "glsl"); } diff --git a/examples/shadowmappingcascade/shadowmappingcascade.cpp b/examples/shadowmappingcascade/shadowmappingcascade.cpp index 9133a03c..55c6328b 100644 --- a/examples/shadowmappingcascade/shadowmappingcascade.cpp +++ b/examples/shadowmappingcascade/shadowmappingcascade.cpp @@ -1,10 +1,8 @@ /* Vulkan Example - Cascaded shadow mapping for directional light sources - Copyright by Sascha Willems - www.saschawillems.de + Copyright (c) 2016-2025 by Sascha Willems - www.saschawillems.de This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ -/* This example implements projective cascaded shadow mapping. This technique splits up the camera frustum into multiple frustums with each getting its own full-res shadow map, implemented as a layered depth-only image. The shader then selects the proper shadow map layer depending on what split of the frustum the depth value @@ -175,7 +173,7 @@ public: vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); // Floor - vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); models.terrain.draw(commandBuffer, vkglTF::RenderFlags::BindImages, pipelineLayout); // Trees @@ -189,7 +187,7 @@ public: for (auto& position : positions) { pushConstBlock.position = glm::vec4(position, 0.0f); - vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); // This will also bind the texture images to set 1 models.tree.draw(commandBuffer, vkglTF::RenderFlags::BindImages, pipelineLayout); } @@ -413,7 +411,7 @@ public: vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.debugShadowMap); PushConstBlock pushConstBlock = {}; pushConstBlock.cascadeIndex = displayDepthMapCascadeIndex; - vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); } @@ -490,7 +488,7 @@ public: // Shared pipeline layout (scene and depth map debug display) { - VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0); + VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PushConstBlock), 0); std::array setLayouts = { descriptorSetLayout, vkglTF::descriptorSetLayoutImage }; VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast(setLayouts.size())); pipelineLayoutCreateInfo.pushConstantRangeCount = 1; @@ -500,7 +498,7 @@ public: // Depth pass pipeline layout { - VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0); + VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PushConstBlock), 0); std::array setLayouts = { descriptorSetLayout, vkglTF::descriptorSetLayoutImage }; VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast(setLayouts.size())); pipelineLayoutCreateInfo.pushConstantRangeCount = 1; diff --git a/shaders/README.md b/shaders/README.md index 3787f3d8..cba13b69 100644 --- a/shaders/README.md +++ b/shaders/README.md @@ -1,3 +1,9 @@ # Shaders -This folder contains the shaders used by the samples. Source files are available as GLSL and HLSL and also come with precompiled SPIR-V files that are consumed by the samples. To recompile shaders you can use the `compileshaders.py` scripts in the respective folders or any other means that can generate Vulkan SPIR-V from GLSL or HLSL. One such option is [this extension for Visual Studio](https://github.com/SaschaWillems/SPIRV-VSExtension). \ No newline at end of file +This folder contains the shaders used by the samples. Source files are available in GLSL, HLSL and [slang](https://shader-slang.org/) and also come with precompiled SPIR-V files that are consumed by the samples. To recompile shaders you can use the `compileshaders.py` scripts in the respective folders or any other means that can generate Vulkan SPIR-V from GLSL, HLSL or slang. One such option is [this extension for Visual Studio](https://github.com/SaschaWillems/SPIRV-VSExtension). + +Note that not all samples may come with all shading language variants. So some samples that have GLSL source files might not come with HLSL and/or slang source files. + +A note for using **slang** shaders: These require a different SPIR-V environment than glsl/hlsl. When selecting slang shaders, the base requirement for all samples is raised to at least Vulkan 1.1 with the SPIRV 1.4 extension. + +If you want to compile **slang** shaders to SPIR-V, please use the latest release from [here](https://github.com/shader-slang/slang/releases) to get the latest bug fixes and features required for some of the samples. \ No newline at end of file diff --git a/shaders/glsl/computecullandlod/cull.comp.spv b/shaders/glsl/computecullandlod/cull.comp.spv index a90380f9..b8b6bb0c 100644 Binary files a/shaders/glsl/computecullandlod/cull.comp.spv and b/shaders/glsl/computecullandlod/cull.comp.spv differ diff --git a/shaders/glsl/computecullandlod/indirectdraw.vert b/shaders/glsl/computecullandlod/indirectdraw.vert index 4087e1bb..4335228b 100644 --- a/shaders/glsl/computecullandlod/indirectdraw.vert +++ b/shaders/glsl/computecullandlod/indirectdraw.vert @@ -6,8 +6,8 @@ layout (location = 1) in vec3 inNormal; layout (location = 2) in vec3 inColor; // Instanced attributes -layout (location = 4) in vec3 instancePos; -layout (location = 5) in float instanceScale; +layout (location = 3) in vec3 instancePos; +layout (location = 4) in float instanceScale; layout (binding = 0) uniform UBO { diff --git a/shaders/glsl/computecullandlod/indirectdraw.vert.spv b/shaders/glsl/computecullandlod/indirectdraw.vert.spv index bcbf6927..d94a9ed2 100644 Binary files a/shaders/glsl/computecullandlod/indirectdraw.vert.spv and b/shaders/glsl/computecullandlod/indirectdraw.vert.spv differ diff --git a/shaders/hlsl/computecullandlod/indirectdraw.vert b/shaders/hlsl/computecullandlod/indirectdraw.vert index 9893ba84..06bcbe1b 100644 --- a/shaders/hlsl/computecullandlod/indirectdraw.vert +++ b/shaders/hlsl/computecullandlod/indirectdraw.vert @@ -6,8 +6,8 @@ struct VSInput [[vk::location(1)]] float3 Normal : NORMAL0; [[vk::location(2)]] float3 Color : COLOR0; // Instanced attributes -[[vk::location(4)]] float3 instancePos : TEXCOORD0; -[[vk::location(5)]] float instanceScale : TEXCOORD1; +[[vk::location(3)]] float3 instancePos : TEXCOORD0; +[[vk::location(4)]] float instanceScale : TEXCOORD1; }; struct UBO diff --git a/shaders/hlsl/computecullandlod/indirectdraw.vert.spv b/shaders/hlsl/computecullandlod/indirectdraw.vert.spv index e76bf181..63b0ea60 100644 Binary files a/shaders/hlsl/computecullandlod/indirectdraw.vert.spv and b/shaders/hlsl/computecullandlod/indirectdraw.vert.spv differ diff --git a/shaders/slang/_rename.py b/shaders/slang/_rename.py new file mode 100644 index 00000000..bce6121e --- /dev/null +++ b/shaders/slang/_rename.py @@ -0,0 +1,86 @@ +# Copyright (C) 2025 by Sascha Willems - www.saschawillems.de +# This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +from shutil import move + +# To match required file names to fother shading languages that don't support multiple entry points, shader files may need to be renamed for some samples +def checkRenameFiles(samplename): + mappings = {} + match samplename: + case "displacement": + mappings = { + "displacement.vert.spv": "base.vert.spv", + "displacement.frag.spv": "base.frag.spv", + } + case "geometryshader": + mappings = { + "normaldebug.vert.spv": "base.vert.spv", + "normaldebug.frag.spv": "base.frag.spv", + } + case "graphicspipelinelibrary": + mappings = { + "uber.vert.spv": "shared.vert.spv", + } + case "raytracingbasic": + mappings = { + "raytracingbasic.rchit.spv": "closesthit.rchit.spv", + "raytracingbasic.rmiss.spv": "miss.rmiss.spv", + "raytracingbasic.rgen.spv": "raygen.rgen.spv", + } + case "raytracingcallable": + mappings = { + "raytracingcallable.rchit.spv": "closesthit.rchit.spv", + "raytracingcallable.rmiss.spv": "miss.rmiss.spv", + "raytracingcallable.rgen.spv": "raygen.rgen.spv", + } + case "raytracinggltf": + mappings = { + "raytracinggltf.rchit.spv": "closesthit.rchit.spv", + "raytracinggltf.rmiss.spv": "miss.rmiss.spv", + "raytracinggltf.rgen.spv": "raygen.rgen.spv", + "raytracinggltf.rahit.spv": "anyhit.rahit.spv", + } + case "raytracingpositionfetch": + mappings = { + "raytracingpositionfetch.rchit.spv": "closesthit.rchit.spv", + "raytracingpositionfetch.rmiss.spv": "miss.rmiss.spv", + "raytracingpositionfetch.rgen.spv": "raygen.rgen.spv", + } + case "raytracingreflections": + mappings = { + "raytracingreflections.rchit.spv": "closesthit.rchit.spv", + "raytracingreflections.rmiss.spv": "miss.rmiss.spv", + "raytracingreflections.rgen.spv": "raygen.rgen.spv", + } + case "raytracingsbtdata": + mappings = { + "raytracingsbtdata.rchit.spv": "closesthit.rchit.spv", + "raytracingsbtdata.rmiss.spv": "miss.rmiss.spv", + "raytracingsbtdata.rgen.spv": "raygen.rgen.spv", + } + case "raytracingshadows": + mappings = { + "raytracingshadows.rchit.spv": "closesthit.rchit.spv", + "raytracingshadows.rmiss.spv": "miss.rmiss.spv", + "raytracingshadows.rgen.spv": "raygen.rgen.spv", + } + case "raytracingtextures": + mappings = { + "raytracingtextures.rchit.spv": "closesthit.rchit.spv", + "raytracingtextures.rmiss.spv": "miss.rmiss.spv", + "raytracingtextures.rgen.spv": "raygen.rgen.spv", + "raytracingtextures.rahit.spv": "anyhit.rahit.spv", + } + case "raytracingintersection": + mappings = { + "raytracingintersection.rchit.spv": "closesthit.rchit.spv", + "raytracingintersection.rmiss.spv": "miss.rmiss.spv", + "raytracingintersection.rgen.spv": "raygen.rgen.spv", + "raytracingintersection.rint.spv": "intersection.rint.spv", + } + case "viewportarray": + mappings = { + "scene.geom.spv": "multiview.geom.spv", + } + for x, y in mappings.items(): + move(samplename + "\\" + x, samplename + "\\" + y) diff --git a/shaders/slang/base/uioverlay.frag.spv b/shaders/slang/base/uioverlay.frag.spv new file mode 100644 index 00000000..9909fbc8 Binary files /dev/null and b/shaders/slang/base/uioverlay.frag.spv differ diff --git a/shaders/slang/base/uioverlay.slang b/shaders/slang/base/uioverlay.slang new file mode 100644 index 00000000..d59b67b6 --- /dev/null +++ b/shaders/slang/base/uioverlay.slang @@ -0,0 +1,43 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +Sampler2D fontTexture; + +struct VSInput +{ + float2 Pos : POSITION0; + float2 UV; + float4 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float4 Color; +}; + +struct PushConstants +{ + float2 scale; + float2 translate; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform PushConstants pushConstants) +{ + VSOutput output; + output.Pos = float4(input.Pos * pushConstants.scale + pushConstants.translate, 0.0, 1.0); + output.UV = input.UV; + output.Color = input.Color; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return input.Color * fontTexture.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/base/uioverlay.vert.spv b/shaders/slang/base/uioverlay.vert.spv new file mode 100644 index 00000000..6eacc026 Binary files /dev/null and b/shaders/slang/base/uioverlay.vert.spv differ diff --git a/shaders/slang/bloom/colorpass.frag.spv b/shaders/slang/bloom/colorpass.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/bloom/colorpass.frag.spv differ diff --git a/shaders/slang/bloom/colorpass.slang b/shaders/slang/bloom/colorpass.slang new file mode 100644 index 00000000..40ada51e --- /dev/null +++ b/shaders/slang/bloom/colorpass.slang @@ -0,0 +1,45 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +Sampler2D colorMapSampler; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, input.Pos))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 1); +} \ No newline at end of file diff --git a/shaders/slang/bloom/colorpass.vert.spv b/shaders/slang/bloom/colorpass.vert.spv new file mode 100644 index 00000000..cf45c930 Binary files /dev/null and b/shaders/slang/bloom/colorpass.vert.spv differ diff --git a/shaders/slang/bloom/gaussblur.frag.spv b/shaders/slang/bloom/gaussblur.frag.spv new file mode 100644 index 00000000..ad8c7f46 Binary files /dev/null and b/shaders/slang/bloom/gaussblur.frag.spv differ diff --git a/shaders/slang/bloom/gaussblur.slang b/shaders/slang/bloom/gaussblur.slang new file mode 100644 index 00000000..e381a27a --- /dev/null +++ b/shaders/slang/bloom/gaussblur.slang @@ -0,0 +1,63 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float blurScale; + float blurStrength; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; + +[[SpecializationConstant]] const int blurdirection = 0; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float weight[5]; + weight[0] = 0.227027; + weight[1] = 0.1945946; + weight[2] = 0.1216216; + weight[3] = 0.054054; + weight[4] = 0.016216; + + float2 textureSize; + samplerColor.GetDimensions(textureSize.x, textureSize.y); + float2 tex_offset = 1.0 / textureSize * ubo.blurScale; // gets size of single texel + float3 result = samplerColor.Sample(input.UV).rgb * weight[0]; // current fragment's contribution + for(int i = 1; i < 5; ++i) + { + if (blurdirection == 1) + { + // H + result += samplerColor.Sample(input.UV + float2(tex_offset.x * i, 0.0)).rgb * weight[i] * ubo.blurScale; + result += samplerColor.Sample(input.UV - float2(tex_offset.x * i, 0.0)).rgb * weight[i] * ubo.blurScale; + } + else + { + // V + result += samplerColor.Sample(input.UV + float2(0.0, tex_offset.y * i)).rgb * weight[i] * ubo.blurScale; + result += samplerColor.Sample(input.UV - float2(0.0, tex_offset.y * i)).rgb * weight[i] * ubo.blurScale; + } + } + return float4(result, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/bloom/gaussblur.vert.spv b/shaders/slang/bloom/gaussblur.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/bloom/gaussblur.vert.spv differ diff --git a/shaders/slang/bloom/phongpass.frag.spv b/shaders/slang/bloom/phongpass.frag.spv new file mode 100644 index 00000000..ae4d137d Binary files /dev/null and b/shaders/slang/bloom/phongpass.frag.spv differ diff --git a/shaders/slang/bloom/phongpass.slang b/shaders/slang/bloom/phongpass.slang new file mode 100644 index 00000000..8d581753 --- /dev/null +++ b/shaders/slang/bloom/phongpass.slang @@ -0,0 +1,70 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +Sampler2D colorMapSampler; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, input.Pos))); + + float3 lightPos = float3(-5.0, -5.0, 0.0); + float4 pos = mul(ubo.view, mul(ubo.model, input.Pos)); + output.Normal = mul((float4x3)mul(ubo.view, ubo.model), input.Normal).xyz; + output.LightVec = lightPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 ambient = float3(0.0f, 0.0f, 0.0f); + + // Adjust light calculations for glow color + if ((input.Color.r >= 0.9) || (input.Color.g >= 0.9) || (input.Color.b >= 0.9)) + { + ambient = input.Color * 0.25; + } + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 8.0) * float3(0.75f, 0.75f, 0.75f); + return float4(ambient + diffuse + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/bloom/phongpass.vert.spv b/shaders/slang/bloom/phongpass.vert.spv new file mode 100644 index 00000000..b606f32c Binary files /dev/null and b/shaders/slang/bloom/phongpass.vert.spv differ diff --git a/shaders/slang/bloom/skybox.frag.spv b/shaders/slang/bloom/skybox.frag.spv new file mode 100644 index 00000000..71e6a035 Binary files /dev/null and b/shaders/slang/bloom/skybox.frag.spv differ diff --git a/shaders/slang/bloom/skybox.slang b/shaders/slang/bloom/skybox.slang new file mode 100644 index 00000000..3b21695b --- /dev/null +++ b/shaders/slang/bloom/skybox.slang @@ -0,0 +1,41 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +} + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +SamplerCube samplerCubeMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UVW = input.Pos; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerCubeMap.Sample(input.UVW); +} \ No newline at end of file diff --git a/shaders/slang/bloom/skybox.vert.spv b/shaders/slang/bloom/skybox.vert.spv new file mode 100644 index 00000000..65dde5e3 Binary files /dev/null and b/shaders/slang/bloom/skybox.vert.spv differ diff --git a/shaders/slang/bufferdeviceaddress/cube.frag.spv b/shaders/slang/bufferdeviceaddress/cube.frag.spv new file mode 100644 index 00000000..534ec32d Binary files /dev/null and b/shaders/slang/bufferdeviceaddress/cube.frag.spv differ diff --git a/shaders/slang/bufferdeviceaddress/cube.slang b/shaders/slang/bufferdeviceaddress/cube.slang new file mode 100644 index 00000000..4e86556a --- /dev/null +++ b/shaders/slang/bufferdeviceaddress/cube.slang @@ -0,0 +1,54 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +Sampler2D samplerColorMap; + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct MatrixReference { + float4x4 matrix; +}; + +struct PushConsts { + // Pointer to the buffer with the scene's MVP matrix + ConstBufferPointer sceneDataReference; + // Pointer to the buffer for the data for each model + ConstBufferPointer modelDataReference; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform PushConsts pushConstants) +{ + MatrixReference sceneData = pushConstants.sceneDataReference.get(); + MatrixReference modelData = pushConstants.modelDataReference.get(); + + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(sceneData.matrix, mul(modelData.matrix, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/bufferdeviceaddress/cube.vert.spv b/shaders/slang/bufferdeviceaddress/cube.vert.spv new file mode 100644 index 00000000..200698c5 Binary files /dev/null and b/shaders/slang/bufferdeviceaddress/cube.vert.spv differ diff --git a/shaders/slang/compileshaders.py b/shaders/slang/compileshaders.py new file mode 100644 index 00000000..c2e20149 --- /dev/null +++ b/shaders/slang/compileshaders.py @@ -0,0 +1,132 @@ +# Copyright (C) 2025 by Sascha Willems - www.saschawillems.de +# This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +import argparse +import fileinput +import os +import subprocess +import sys +from _rename import * + +parser = argparse.ArgumentParser(description='Compile all slang shaders') +parser.add_argument('--slangc', type=str, help='path to slangc executable') +parser.add_argument('--sample', type=str, help='can be used to compile shaders for a single sample only') +args = parser.parse_args() + +def findCompiler(): + def isExe(path): + return os.path.isfile(path) and os.access(path, os.X_OK) + + if args.slangc != None and isExe(args.slangc): + return args.slangc + + exe_name = "slangc" + if os.name == "nt": + exe_name += ".exe" + + for exe_dir in os.environ["PATH"].split(os.pathsep): + full_path = os.path.join(exe_dir, exe_name) + if isExe(full_path): + return full_path + + sys.exit("Could not find slangc executable on PATH, and was not specified with --slangc") + +def getShaderStages(filename): + stages = [] + with open(filename) as f: + filecontent = f.read() + if '[shader("vertex")]' in filecontent: + stages.append("vertex") + if '[shader("fragment")]' in filecontent: + stages.append("fragment") + if '[shader("raygeneration")]' in filecontent: + stages.append("raygeneration") + if '[shader("miss")]' in filecontent: + stages.append("miss") + if '[shader("closesthit")]' in filecontent: + stages.append("closesthit") + if '[shader("callable")]' in filecontent: + stages.append("callable") + if '[shader("intersection")]' in filecontent: + stages.append("intersection") + if '[shader("anyhit")]' in filecontent: + stages.append("anyhit") + if '[shader("compute")]' in filecontent: + stages.append("compute") + if '[shader("amplification")]' in filecontent: + stages.append("amplification") + if '[shader("mesh")]' in filecontent: + stages.append("mesh") + if '[shader("geometry")]' in filecontent: + stages.append("geometry") + if '[shader("hull")]' in filecontent: + stages.append("hull") + if '[shader("domain")]' in filecontent: + stages.append("domain") + f.close() + return stages + +compiler_path = findCompiler() + +print("Found slang compiler at %s", compiler_path) + +compile_single_sample = "" +if args.sample != None: + compile_single_sample = args.sample + if (not os.path.isdir(compile_single_sample)): + print("ERROR: No directory found with name %s" % compile_single_sample) + exit(-1) + +dir_path = os.path.dirname(os.path.realpath(__file__)) +dir_path = dir_path.replace('\\', '/') +for root, dirs, files in os.walk(dir_path): + folder_name = os.path.basename(root) + if (compile_single_sample != "" and folder_name != compile_single_sample): + continue + for file in files: + if file.endswith(".slang"): + input_file = os.path.join(root, file) + # Slang can store multiple shader stages in a single file, we need to split into separate SPIR-V files for the sample framework + stages = getShaderStages(input_file) + print("Compiling %s" % input_file) + output_base_file_name = input_file + for stage in stages: + entry_point = stage + "Main" + output_ext = "" + match stage: + case "vertex": + output_ext = ".vert" + case "fragment": + output_ext = ".frag" + case "raygeneration": + output_ext = ".rgen" + case "miss": + output_ext = ".rmiss" + case "closesthit": + output_ext = ".rchit" + case "callable": + output_ext = ".rcall" + case "intersection": + output_ext = ".rint" + case "anyhit": + output_ext = ".rahit" + case "compute": + output_ext = ".comp" + case "mesh": + output_ext = ".mesh" + case "amplification": + output_ext = ".task" + case "geometry": + output_ext = ".geom" + case "hull": + output_ext = ".tesc" + case "domain": + output_ext = ".tese" + output_file = output_base_file_name + output_ext + ".spv" + output_file = output_file.replace(".slang", "") + print(output_file) + res = subprocess.call("%s %s -profile spirv_1_4 -matrix-layout-column-major -target spirv -o %s -entry %s -stage %s -warnings-disable 39001" % (compiler_path, input_file, output_file, entry_point, stage), shell=True) + if res != 0: + print("Error %s", res) + sys.exit(res) + checkRenameFiles(folder_name) \ No newline at end of file diff --git a/shaders/slang/computecloth/cloth.comp.spv b/shaders/slang/computecloth/cloth.comp.spv new file mode 100644 index 00000000..5f668990 Binary files /dev/null and b/shaders/slang/computecloth/cloth.comp.spv differ diff --git a/shaders/slang/computecloth/cloth.frag.spv b/shaders/slang/computecloth/cloth.frag.spv new file mode 100644 index 00000000..0cf50a1c Binary files /dev/null and b/shaders/slang/computecloth/cloth.frag.spv differ diff --git a/shaders/slang/computecloth/cloth.slang b/shaders/slang/computecloth/cloth.slang new file mode 100644 index 00000000..80e53606 --- /dev/null +++ b/shaders/slang/computecloth/cloth.slang @@ -0,0 +1,190 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float4 lightPos; +}; +[[vk::binding(0,0)]] ConstantBuffer ubo; +[[vk::binding(1,0)]] Sampler2D samplerColor; + +struct Particle { + float4 pos; + float4 vel; + float4 uv; + float4 normal; +}; + +[[vk::binding(0,0)]] StructuredBuffer particleIn; +[[vk::binding(1,0)]] RWStructuredBuffer particleOut; + +struct UBOCompute +{ + float deltaT; + float particleMass; + float springStiffness; + float damping; + float restDistH; + float restDistV; + float restDistD; + float sphereRadius; + float4 spherePos; + float4 gravity; + int2 particleCount; +}; +[[vk::binding(2, 0)]] ConstantBuffer params; + +float3 springForce(float3 p0, float3 p1, float restDist) +{ + float3 dist = p0 - p1; + return normalize(dist) * params.springStiffness * (length(dist) - restDist); +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.Normal = input.Normal.xyz; + float4 eyePos = mul(ubo.modelview, float4(input.Pos.x, input.Pos.y, input.Pos.z, 1.0)); + output.Pos = mul(ubo.projection, eyePos); + float4 pos = float4(input.Pos, 1.0); + float3 lPos = ubo.lightPos.xyz; + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 color = samplerColor.Sample(input.UV).rgb; + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.15) * float3(1, 1, 1); + float3 specular = pow(max(dot(R, V), 0.0), 8.0) * float3(0.2, 0.2, 0.2); + return float4(diffuse * color.rgb + specular, 1.0); +} + +[shader("compute")] +[numthreads(10, 10, 1)] +void computeMain(uint3 id: SV_DispatchThreadID, uniform uint calculateNormals) +{ + uint index = id.y * params.particleCount.x + id.x; + if (index > params.particleCount.x * params.particleCount.y) + return; + + // Initial force from gravity + float3 force = params.gravity.xyz * params.particleMass; + + float3 pos = particleIn[index].pos.xyz; + float3 vel = particleIn[index].vel.xyz; + + // Spring forces from neighboring particles + // left + if (id.x > 0) { + force += springForce(particleIn[index-1].pos.xyz, pos, params.restDistH); + } + // right + if (id.x < params.particleCount.x - 1) { + force += springForce(particleIn[index + 1].pos.xyz, pos, params.restDistH); + } + // upper + if (id.y < params.particleCount.y - 1) { + force += springForce(particleIn[index + params.particleCount.x].pos.xyz, pos, params.restDistV); + } + // lower + if (id.y > 0) { + force += springForce(particleIn[index - params.particleCount.x].pos.xyz, pos, params.restDistV); + } + // upper-left + if ((id.x > 0) && (id.y < params.particleCount.y - 1)) { + force += springForce(particleIn[index + params.particleCount.x - 1].pos.xyz, pos, params.restDistD); + } + // lower-left + if ((id.x > 0) && (id.y > 0)) { + force += springForce(particleIn[index - params.particleCount.x - 1].pos.xyz, pos, params.restDistD); + } + // upper-right + if ((id.x < params.particleCount.x - 1) && (id.y < params.particleCount.y - 1)) { + force += springForce(particleIn[index + params.particleCount.x + 1].pos.xyz, pos, params.restDistD); + } + // lower-right + if ((id.x < params.particleCount.x - 1) && (id.y > 0)) { + force += springForce(particleIn[index - params.particleCount.x + 1].pos.xyz, pos, params.restDistD); + } + + force += (-params.damping * vel); + + // Integrate + float3 f = force * (1.0 / params.particleMass); + particleOut[index].pos = float4(pos + vel * params.deltaT + 0.5 * f * params.deltaT * params.deltaT, 1.0); + particleOut[index].vel = float4(vel + f * params.deltaT, 0.0); + + // Sphere collision + float3 sphereDist = particleOut[index].pos.xyz - params.spherePos.xyz; + if (length(sphereDist) < params.sphereRadius + 0.01) { + // If the particle is inside the sphere, push it to the outer radius + particleOut[index].pos.xyz = params.spherePos.xyz + normalize(sphereDist) * (params.sphereRadius + 0.01); + // Cancel out velocity + particleOut[index].vel = float4(0, 0, 0, 0); + } + + // Normals + if (calculateNormals == 1) { + float3 normal = float3(0, 0, 0); + float3 a, b, c; + if (id.y > 0) { + if (id.x > 0) { + a = particleIn[index - 1].pos.xyz - pos; + b = particleIn[index - params.particleCount.x - 1].pos.xyz - pos; + c = particleIn[index - params.particleCount.x].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + if (id.x < params.particleCount.x - 1) { + a = particleIn[index - params.particleCount.x].pos.xyz - pos; + b = particleIn[index - params.particleCount.x + 1].pos.xyz - pos; + c = particleIn[index + 1].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + } + if (id.y < params.particleCount.y - 1) { + if (id.x > 0) { + a = particleIn[index + params.particleCount.x].pos.xyz - pos; + b = particleIn[index + params.particleCount.x - 1].pos.xyz - pos; + c = particleIn[index - 1].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + if (id.x < params.particleCount.x - 1) { + a = particleIn[index + 1].pos.xyz - pos; + b = particleIn[index + params.particleCount.x + 1].pos.xyz - pos; + c = particleIn[index + params.particleCount.x].pos.xyz - pos; + normal += cross(a,b) + cross(b,c); + } + } + particleOut[index].normal = float4(normalize(normal), 0.0f); + } +} \ No newline at end of file diff --git a/shaders/slang/computecloth/cloth.vert.spv b/shaders/slang/computecloth/cloth.vert.spv new file mode 100644 index 00000000..38bd3b78 Binary files /dev/null and b/shaders/slang/computecloth/cloth.vert.spv differ diff --git a/shaders/slang/computecloth/sphere.frag.spv b/shaders/slang/computecloth/sphere.frag.spv new file mode 100644 index 00000000..c06d4608 Binary files /dev/null and b/shaders/slang/computecloth/sphere.frag.spv differ diff --git a/shaders/slang/computecloth/sphere.slang b/shaders/slang/computecloth/sphere.slang new file mode 100644 index 00000000..99959727 --- /dev/null +++ b/shaders/slang/computecloth/sphere.slang @@ -0,0 +1,55 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + float4 eyePos = mul(ubo.modelview, float4(input.Pos.x, input.Pos.y, input.Pos.z, 1.0)); + output.Pos = mul(ubo.projection, eyePos); + float4 pos = float4(input.Pos, 1.0); + float3 lPos = ubo.lightPos.xyz; + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + output.Normal = input.Normal; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 color = float3(0.5, 0.5, 0.5); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.15); + float3 specular = pow(max(dot(R, V), 0.0), 32.0); + return float4(diffuse * color.rgb + specular, 1.0); +} diff --git a/shaders/slang/computecloth/sphere.vert.spv b/shaders/slang/computecloth/sphere.vert.spv new file mode 100644 index 00000000..377fb433 Binary files /dev/null and b/shaders/slang/computecloth/sphere.vert.spv differ diff --git a/shaders/slang/computecullandlod/cull.comp.spv b/shaders/slang/computecullandlod/cull.comp.spv new file mode 100644 index 00000000..d004e4ff Binary files /dev/null and b/shaders/slang/computecullandlod/cull.comp.spv differ diff --git a/shaders/slang/computecullandlod/cull.slang b/shaders/slang/computecullandlod/cull.slang new file mode 100644 index 00000000..16e1089e --- /dev/null +++ b/shaders/slang/computecullandlod/cull.slang @@ -0,0 +1,115 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#define MAX_LOD_LEVEL_COUNT 6 +[[SpecializationConstant]] const int MAX_LOD_LEVEL = 5; + +struct InstanceData +{ + float3 pos; + float scale; +}; +StructuredBuffer instances; + +// Same layout as VkDrawIndexedIndirectCommand +struct IndexedIndirectCommand +{ + uint indexCount; + uint instanceCount; + uint firstIndex; + uint vertexOffset; + uint firstInstance; +}; +RWStructuredBuffer indirectDraws; + +// Binding 2: Uniform block object with matrices +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float4 cameraPos; + float4 frustumPlanes[6]; +}; +ConstantBuffer ubo; + +// Binding 3: Indirect draw stats +struct UBOOut +{ + uint drawCount; + uint lodCount[MAX_LOD_LEVEL_COUNT]; +}; +RWStructuredBuffer uboOut; + +// Binding 4: level-of-detail information +struct LOD +{ + uint firstIndex; + uint indexCount; + float distance; + float _pad0; +}; +StructuredBuffer lods; + +bool frustumCheck(float4 pos, float radius) +{ + // Check sphere against frustum planes + for (int i = 0; i < 6; i++) + { + if (dot(pos, ubo.frustumPlanes[i]) + radius < 0.0) + { + return false; + } + } + return true; +} + +[shader("compute")] +[numthreads(16, 1, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + uint idx = GlobalInvocationID.x; + uint temp; + + // Clear stats on first invocation + if (idx == 0) + { + InterlockedExchange(uboOut[0].drawCount, 0, temp); + for (uint i = 0; i < MAX_LOD_LEVEL + 1; i++) + { + InterlockedExchange(uboOut[0].lodCount[i], 0, temp); + } + } + + float4 pos = float4(instances[idx].pos.xyz, 1.0); + + // Check if object is within current viewing frustum + if (frustumCheck(pos, 1.0)) + { + indirectDraws[idx].instanceCount = 1; + + // Increase number of indirect draw counts + InterlockedAdd(uboOut[0].drawCount, 1, temp); + + // Select appropriate LOD level based on distance to camera + uint lodLevel = MAX_LOD_LEVEL; + for (uint i = 0; i < MAX_LOD_LEVEL; i++) + { + if (distance(instances[idx].pos.xyz, ubo.cameraPos.xyz) < lods[i].distance) + { + lodLevel = i; + break; + } + } + indirectDraws[idx].firstIndex = lods[lodLevel].firstIndex; + indirectDraws[idx].indexCount = lods[lodLevel].indexCount; + // Update stats + InterlockedAdd(uboOut[0].lodCount[lodLevel], 1, temp); + } + else + { + indirectDraws[idx].instanceCount = 0; + } +} \ No newline at end of file diff --git a/shaders/slang/computecullandlod/indirectdraw.frag.spv b/shaders/slang/computecullandlod/indirectdraw.frag.spv new file mode 100644 index 00000000..c9d4cf3f Binary files /dev/null and b/shaders/slang/computecullandlod/indirectdraw.frag.spv differ diff --git a/shaders/slang/computecullandlod/indirectdraw.slang b/shaders/slang/computecullandlod/indirectdraw.slang new file mode 100644 index 00000000..d051bd6c --- /dev/null +++ b/shaders/slang/computecullandlod/indirectdraw.slang @@ -0,0 +1,56 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos : POSITION0; + float3 Normal; + float3 Color; + // Instanced attributes + float3 instancePos; + float instanceScale; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Normal = input.Normal; + float4 pos = float4((input.Pos.xyz * input.instanceScale) + input.instancePos, 1.0); + output.Pos = mul(ubo.projection, mul(ubo.modelview, pos)); + float4 wPos = mul(ubo.modelview, float4(pos.xyz, 1.0)); + float4 lPos = float4(0.0, 10.0, 50.0, 1.0); + output.LightVec = lPos.xyz - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 ambient = float3(0.25, 0.25, 0.25); + float3 diffuse = max(dot(N, L), 0.0).xxx; + return float4((ambient + diffuse) * input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/computecullandlod/indirectdraw.vert.spv b/shaders/slang/computecullandlod/indirectdraw.vert.spv new file mode 100644 index 00000000..795c4dcb Binary files /dev/null and b/shaders/slang/computecullandlod/indirectdraw.vert.spv differ diff --git a/shaders/slang/computeheadless/headless.comp.spv b/shaders/slang/computeheadless/headless.comp.spv new file mode 100644 index 00000000..83f37309 Binary files /dev/null and b/shaders/slang/computeheadless/headless.comp.spv differ diff --git a/shaders/slang/computeheadless/headless.slang b/shaders/slang/computeheadless/headless.slang new file mode 100644 index 00000000..7f5db02a --- /dev/null +++ b/shaders/slang/computeheadless/headless.slang @@ -0,0 +1,34 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +RWStructuredBuffer values; + +[[SpecializationConstant]] const uint BUFFER_ELEMENTS = 32; + +uint fibonacci(uint n) { + if(n <= 1){ + return n; + } + uint curr = 1; + uint prev = 1; + for(uint i = 2; i < n; ++i) { + uint temp = curr; + curr += prev; + prev = temp; + } + return curr; +} + +[numthreads(1, 1, 1)] +[shader("compute")] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + uint index = GlobalInvocationID.x; + if (index >= BUFFER_ELEMENTS) + return; + values[index] = fibonacci(values[index]); +} + diff --git a/shaders/slang/computenbody/particle.frag.spv b/shaders/slang/computenbody/particle.frag.spv new file mode 100644 index 00000000..f55ec7bb Binary files /dev/null and b/shaders/slang/computenbody/particle.frag.spv differ diff --git a/shaders/slang/computenbody/particle.slang b/shaders/slang/computenbody/particle.slang new file mode 100644 index 00000000..15724781 --- /dev/null +++ b/shaders/slang/computenbody/particle.slang @@ -0,0 +1,56 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float4 Vel; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float PSize : SV_PointSize; + float GradientPos; + float2 CenterPos; + float PointSize; +}; + +Sampler2D samplerColorMap; +Sampler2D samplerGradientRamp; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float2 screendim; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + const float spriteSize = 0.005 * input.Pos.w; // Point size influenced by mass (stored in input.Pos.w); + + float4 eyePos = mul(ubo.modelview, float4(input.Pos.x, input.Pos.y, input.Pos.z, 1.0)); + float4 projectedCorner = mul(ubo.projection, float4(0.5 * spriteSize, 0.5 * spriteSize, eyePos.z, eyePos.w)); + output.PSize = output.PointSize = clamp(ubo.screendim.x * projectedCorner.x / projectedCorner.w, 1.0, 128.0); + + output.Pos = mul(ubo.projection, eyePos); + output.CenterPos = ((output.Pos.xy / output.Pos.w) + 1.0) * 0.5 * ubo.screendim; + + output.GradientPos = input.Vel.w; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 color = samplerGradientRamp.Sample(float2(input.GradientPos, 0.0)).rgb; + float2 PointCoord = (input.Pos.xy - input.CenterPos.xy) / input.PointSize + 0.5; + return float4(samplerColorMap.Sample(PointCoord).rgb * color, 1); +} diff --git a/shaders/slang/computenbody/particle.vert.spv b/shaders/slang/computenbody/particle.vert.spv new file mode 100644 index 00000000..6c165347 Binary files /dev/null and b/shaders/slang/computenbody/particle.vert.spv differ diff --git a/shaders/slang/computenbody/particle_calculate.comp.spv b/shaders/slang/computenbody/particle_calculate.comp.spv new file mode 100644 index 00000000..8df61f01 Binary files /dev/null and b/shaders/slang/computenbody/particle_calculate.comp.spv differ diff --git a/shaders/slang/computenbody/particle_calculate.slang b/shaders/slang/computenbody/particle_calculate.slang new file mode 100644 index 00000000..e4ec9499 --- /dev/null +++ b/shaders/slang/computenbody/particle_calculate.slang @@ -0,0 +1,77 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Particle +{ + float4 pos; + float4 vel; +}; +// Binding 0 : Position storage buffer +RWStructuredBuffer particles; + +struct UBO +{ + float deltaT; + int particleCount; + float gravity; + float power; + float soften; +}; +ConstantBuffer ubo; + +#define MAX_SHARED_DATA_SIZE 1024 +[[SpecializationConstant]] const int SHARED_DATA_SIZE = 512; +[[SpecializationConstant]] const float GRAVITY = 0.002; +[[SpecializationConstant]] const float POWER = 0.75; +[[SpecializationConstant]] const float SOFTEN = 0.0075; + +// Share data between computer shader invocations to speed up caluclations +groupshared float4 sharedData[MAX_SHARED_DATA_SIZE]; + +[shader("compute")] +[numthreads(256, 1, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID, uint3 LocalInvocationID : SV_GroupThreadID) +{ + // Current SSBO index + uint index = GlobalInvocationID.x; + if (index >= ubo.particleCount) + return; + + float4 position = particles[index].pos; + float4 velocity = particles[index].vel; + float4 acceleration = float4(0, 0, 0, 0); + + for (int i = 0; i < ubo.particleCount; i += SHARED_DATA_SIZE) + { + if (i + LocalInvocationID.x < ubo.particleCount) + { + sharedData[LocalInvocationID.x] = particles[i + LocalInvocationID.x].pos; + } + else + { + sharedData[LocalInvocationID.x] = float4(0, 0, 0, 0); + } + + GroupMemoryBarrierWithGroupSync(); + + for (int j = 0; j < 256; j++) + { + float4 other = sharedData[j]; + float3 len = other.xyz - position.xyz; + acceleration.xyz += ubo.gravity * len * other.w / pow(dot(len, len) + ubo.soften, ubo.power); + } + + GroupMemoryBarrierWithGroupSync(); + } + + particles[index].vel.xyz += ubo.deltaT * acceleration.xyz; + + // Gradient texture position + particles[index].vel.w += 0.1 * ubo.deltaT; + if (particles[index].vel.w > 1.0) { + particles[index].vel.w -= 1.0; + } +} \ No newline at end of file diff --git a/shaders/slang/computenbody/particle_integrate.comp.spv b/shaders/slang/computenbody/particle_integrate.comp.spv new file mode 100644 index 00000000..eb5178ea Binary files /dev/null and b/shaders/slang/computenbody/particle_integrate.comp.spv differ diff --git a/shaders/slang/computenbody/particle_integrate.slang b/shaders/slang/computenbody/particle_integrate.slang new file mode 100644 index 00000000..484fe5d9 --- /dev/null +++ b/shaders/slang/computenbody/particle_integrate.slang @@ -0,0 +1,31 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Particle +{ + float4 pos; + float4 vel; +}; +// Binding 0 : Position storage buffer +RWStructuredBuffer particles; + +struct UBO +{ + float deltaT; + int particleCount; +}; +ConstantBuffer ubo; + +[shader("compute")] +[numthreads(256, 1, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + int index = int(GlobalInvocationID.x); + float4 position = particles[index].pos; + float4 velocity = particles[index].vel; + position += ubo.deltaT * velocity; + particles[index].pos = position; +} \ No newline at end of file diff --git a/shaders/slang/computeparticles/particle.comp.spv b/shaders/slang/computeparticles/particle.comp.spv new file mode 100644 index 00000000..739f548d Binary files /dev/null and b/shaders/slang/computeparticles/particle.comp.spv differ diff --git a/shaders/slang/computeparticles/particle.frag.spv b/shaders/slang/computeparticles/particle.frag.spv new file mode 100644 index 00000000..0a5b51c5 Binary files /dev/null and b/shaders/slang/computeparticles/particle.frag.spv differ diff --git a/shaders/slang/computeparticles/particle.slang b/shaders/slang/computeparticles/particle.slang new file mode 100644 index 00000000..9eee978a --- /dev/null +++ b/shaders/slang/computeparticles/particle.slang @@ -0,0 +1,120 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[[vk::binding(0, 0)]] Sampler2D samplerColorMap; +[[vk::binding(1, 0)]] Sampler2D samplerGradientRamp; + +struct VSInput +{ + float2 Pos : POSITION0; + float4 GradientPos : POSITION1; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float PointSize : SV_PointSize; + float4 Color : COLOR0; + float GradientPos : POSITION0; +}; + +struct Particle +{ + float2 pos; + float2 vel; + float4 gradientPos; +}; +// Binding 0 : Position storage buffer +[[vk::binding(0, 0)]] RWStructuredBuffer particles; + +struct UBO +{ + float deltaT; + float destX; + float destY; + int particleCount; +}; +[[vk::binding(1, 0)]] ConstantBuffer ubo; + +struct PushConsts +{ + float2 screendim; +}; + +float2 attraction(float2 pos, float2 attractPos) +{ + float2 delta = attractPos - pos; + const float damp = 0.5; + float dDampedDot = dot(delta, delta) + damp; + float invDist = 1.0f / sqrt(dDampedDot); + float invDistCubed = invDist*invDist*invDist; + return delta * invDistCubed * 0.0035; +} + +float2 repulsion(float2 pos, float2 attractPos) +{ + float2 delta = attractPos - pos; + float targetDistance = sqrt(dot(delta, delta)); + return delta * (1.0 / (targetDistance * targetDistance * targetDistance)) * -0.000035; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.PointSize = 8.0; + output.Color = float4(0.035, 0.035, 0.035, 0.035); + output.GradientPos = input.GradientPos.x; + output.Pos = float4(input.Pos.xy, 1.0, 1.0); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input, float2 pointCoord: SV_PointCoord) +{ + float3 color = samplerGradientRamp.Sample(float2(input.GradientPos, 0.0)).rgb; + return float4(samplerColorMap.Sample(pointCoord).rgb * color, 1.0); +} + +[shader("compute")] +[numthreads(256, 1, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + // Current SSBO index + uint index = GlobalInvocationID.x; + // Don't try to write beyond particle count + if (index >= ubo.particleCount) { + return; + } + + // Read position and velocity + float2 vVel = particles[index].vel.xy; + float2 vPos = particles[index].pos.xy; + + float2 destPos = float2(ubo.destX, ubo.destY); + + float2 delta = destPos - vPos; + float targetDistance = sqrt(dot(delta, delta)); + vVel += repulsion(vPos, destPos.xy) * 0.05; + + // Move by velocity + vPos += vVel * ubo.deltaT; + + // collide with boundary + if ((vPos.x < -1.0) || (vPos.x > 1.0) || (vPos.y < -1.0) || (vPos.y > 1.0)) { + vVel = (-vVel * 0.1) + attraction(vPos, destPos) * 12; + } else { + particles[index].pos.xy = vPos; + } + + // Write back + particles[index].vel.xy = vVel; + particles[index].gradientPos.x += 0.02 * ubo.deltaT; + if (particles[index].gradientPos.x > 1.0) { + particles[index].gradientPos.x -= 1.0; + } +} + diff --git a/shaders/slang/computeparticles/particle.vert.spv b/shaders/slang/computeparticles/particle.vert.spv new file mode 100644 index 00000000..e77a6d94 Binary files /dev/null and b/shaders/slang/computeparticles/particle.vert.spv differ diff --git a/shaders/slang/computeraytracing/raytracing.comp.spv b/shaders/slang/computeraytracing/raytracing.comp.spv new file mode 100644 index 00000000..59229188 Binary files /dev/null and b/shaders/slang/computeraytracing/raytracing.comp.spv differ diff --git a/shaders/slang/computeraytracing/raytracing.slang b/shaders/slang/computeraytracing/raytracing.slang new file mode 100644 index 00000000..6e55885c --- /dev/null +++ b/shaders/slang/computeraytracing/raytracing.slang @@ -0,0 +1,258 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +// Shader is looseley based on the ray tracing coding session by Inigo Quilez (www.iquilezles.org) + +#define EPSILON 0.0001 +#define MAXLEN 1000.0 +#define SHADOW 0.5 +#define RAYBOUNCES 2 +#define REFLECTIONS true +#define REFLECTIONSTRENGTH 0.4 +#define REFLECTIONFALLOFF 0.5 + +#define SceneObjectTypeSphere 0 +#define SceneObjectTypePlane 1 + +RWTexture2D resultImage; + +struct Camera +{ + float3 pos; + float3 lookat; + float fov; +}; + +struct UBO +{ + float3 lightPos; + float aspectRatio; + float4 fogColor; + Camera camera; + float4x4 rotMat; +}; +ConstantBuffer ubo; + +struct SceneObject +{ + float4 objectProperties; + float3 diffuse; + float specular; + int id; + int objectType; +}; +StructuredBuffer sceneObjects; + +void reflectRay(inout float3 rayD, in float3 mormal) +{ + rayD = rayD + 2.0 * -dot(mormal, rayD) * mormal; +} + +// Lighting ========================================================= + +float lightDiffuse(float3 normal, float3 lightDir) +{ + return clamp(dot(normal, lightDir), 0.1, 1.0); +} + +float lightSpecular(float3 normal, float3 lightDir, float specularFactor) +{ + float3 viewVec = normalize(ubo.camera.pos); + float3 halfVec = normalize(lightDir + viewVec); + return pow(clamp(dot(normal, halfVec), 0.0, 1.0), specularFactor); +} + +// Sphere =========================================================== + +float sphereIntersect(in float3 rayO, in float3 rayD, in SceneObject sphere) +{ + float3 oc = rayO - sphere.objectProperties.xyz; + float b = 2.0 * dot(oc, rayD); + float c = dot(oc, oc) - sphere.objectProperties.w * sphere.objectProperties.w; + float h = b*b - 4.0*c; + if (h < 0.0) + { + return -1.0; + } + float t = (-b - sqrt(h)) / 2.0; + + return t; +} + +float3 sphereNormal(in float3 pos, in SceneObject sphere) +{ + return (pos - sphere.objectProperties.xyz) / sphere.objectProperties.w; +} + +// Plane =========================================================== + +float planeIntersect(float3 rayO, float3 rayD, SceneObject plane) +{ + float d = dot(rayD, plane.objectProperties.xyz); + + if (d == 0.0) + return 0.0; + + float t = -(plane.objectProperties.w + dot(rayO, plane.objectProperties.xyz)) / d; + + if (t < 0.0) + return 0.0; + + return t; +} + + +int intersect(in float3 rayO, in float3 rayD, inout float resT) +{ + int id = -1; + float t = MAXLEN; + + uint sceneObjectsLength; + uint sceneObjectsStride; + sceneObjects.GetDimensions(sceneObjectsLength, sceneObjectsStride); + + for (int i = 0; i < sceneObjectsLength; i++) { + // Sphere + if (sceneObjects[i].objectType == SceneObjectTypeSphere) { + t = sphereIntersect(rayO, rayD, sceneObjects[i]); + } + // Plane + if (sceneObjects[i].objectType == SceneObjectTypePlane) { + t = planeIntersect(rayO, rayD, sceneObjects[i]); + } + if ((t > EPSILON) && (t < resT)) + { + id = sceneObjects[i].id; + resT = t; + } + } + + return id; +} + +float calcShadow(in float3 rayO, in float3 rayD, in int objectId, inout float t) +{ + uint sceneObjectsLength; + uint sceneObjectsStride; + sceneObjects.GetDimensions(sceneObjectsLength, sceneObjectsStride); + + for (int i = 0; i < sceneObjectsLength; i++) { + if (sceneObjects[i].id == objectId) + continue; + + float tLoc = MAXLEN; + + // Sphere + if (sceneObjects[i].objectType == SceneObjectTypeSphere) + { + tLoc = sphereIntersect(rayO, rayD, sceneObjects[i]); + } + // Plane + if (sceneObjects[i].objectType == SceneObjectTypePlane) + { + tLoc = planeIntersect(rayO, rayD, sceneObjects[i]); + } + if ((tLoc > EPSILON) && (tLoc < t)) + { + t = tLoc; + return SHADOW; + } + } + return 1.0; +} + +float3 fog(in float t, in float3 color) +{ + return lerp(color, ubo.fogColor.rgb, clamp(sqrt(t*t)/20.0, 0.0, 1.0)); +} + +float3 renderScene(inout float3 rayO, inout float3 rayD, inout int id) +{ + float3 color = float3(0, 0, 0); + float t = MAXLEN; + + // Get intersected object ID + int objectID = intersect(rayO, rayD, t); + + if (objectID == -1) + { + return color; + } + + float3 pos = rayO + t * rayD; + float3 lightVec = normalize(ubo.lightPos - pos); + float3 normal; + + uint sceneObjectsLength; + uint sceneObjectsStride; + sceneObjects.GetDimensions(sceneObjectsLength, sceneObjectsStride); + + for (int i = 0; i < sceneObjectsLength; i++) { + if (objectID == sceneObjects[i].id) + { + // Sphere + if (sceneObjects[i].objectType == SceneObjectTypeSphere) { + normal = sphereNormal(pos, sceneObjects[i]); + } + // Plane + if (sceneObjects[i].objectType == SceneObjectTypePlane) { + normal = sceneObjects[i].objectProperties.xyz; + } + // Lighting + float diffuse = lightDiffuse(normal, lightVec); + float specular = lightSpecular(normal, lightVec, sceneObjects[i].specular); + color = diffuse * sceneObjects[i].diffuse + specular; + } + } + + if (id == -1) + return color; + + id = objectID; + + // Shadows + t = length(ubo.lightPos - pos); + color *= calcShadow(pos, lightVec, id, t); + + // Fog + color = fog(t, color); + + // Reflect ray for next render pass + reflectRay(rayD, normal); + rayO = pos; + + return color; +} + +[shader("compute")] +[numthreads(16, 16, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + int2 dim; + resultImage.GetDimensions(dim.x, dim.y); + float2 uv = float2(GlobalInvocationID.xy) / dim; + + float3 rayO = ubo.camera.pos; + float3 rayD = normalize(float3((-1.0 + 2.0 * uv) * float2(ubo.aspectRatio, 1.0), -1.0)); + + // Basic color path + int id = 0; + float3 finalColor = renderScene(rayO, rayD, id); + + // Reflection + if (REFLECTIONS) + { + float reflectionStrength = REFLECTIONSTRENGTH; + for (int i = 0; i < RAYBOUNCES; i++) + { + float3 reflectionColor = renderScene(rayO, rayD, id); + finalColor = (1.0 - reflectionStrength) * finalColor + reflectionStrength * lerp(reflectionColor, finalColor, 1.0 - reflectionStrength); + reflectionStrength *= REFLECTIONFALLOFF; + } + } + + resultImage[int2(GlobalInvocationID.xy)] = float4(finalColor, 0.0); +} \ No newline at end of file diff --git a/shaders/slang/computeraytracing/texture.frag.spv b/shaders/slang/computeraytracing/texture.frag.spv new file mode 100644 index 00000000..2aab8ce9 Binary files /dev/null and b/shaders/slang/computeraytracing/texture.frag.spv differ diff --git a/shaders/slang/computeraytracing/texture.slang b/shaders/slang/computeraytracing/texture.slang new file mode 100644 index 00000000..0b126565 --- /dev/null +++ b/shaders/slang/computeraytracing/texture.slang @@ -0,0 +1,28 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + + struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f + -1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColor.Sample(float2(input.UV.x, 1.0 - input.UV.y)); +} \ No newline at end of file diff --git a/shaders/slang/computeraytracing/texture.vert.spv b/shaders/slang/computeraytracing/texture.vert.spv new file mode 100644 index 00000000..3ca929cc Binary files /dev/null and b/shaders/slang/computeraytracing/texture.vert.spv differ diff --git a/shaders/slang/computeshader/edgedetect.comp.spv b/shaders/slang/computeshader/edgedetect.comp.spv new file mode 100644 index 00000000..5ee96ec4 Binary files /dev/null and b/shaders/slang/computeshader/edgedetect.comp.spv differ diff --git a/shaders/slang/computeshader/edgedetect.slang b/shaders/slang/computeshader/edgedetect.slang new file mode 100644 index 00000000..ecbb5bf3 --- /dev/null +++ b/shaders/slang/computeshader/edgedetect.slang @@ -0,0 +1,34 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import shared; + +[shader("compute")] +[numthreads(16, 16, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float imageData[9]; + // Fetch neighbouring texels + int n = -1; + for (int i=-1; i<2; ++i) + { + for(int j=-1; j<2; ++j) + { + n++; + float3 rgb = inputImage[uint2(GlobalInvocationID.x + i, GlobalInvocationID.y + j)].rgb; + imageData[n] = (rgb.r + rgb.g + rgb.b) / 3.0; + } + } + + float kernel[9]; + kernel[0] = -1.0/8.0; kernel[1] = -1.0/8.0; kernel[2] = -1.0/8.0; + kernel[3] = -1.0/8.0; kernel[4] = 1.0; kernel[5] = -1.0/8.0; + kernel[6] = -1.0/8.0; kernel[7] = -1.0/8.0; kernel[8] = -1.0/8.0; + + float4 res = float4(conv(kernel, imageData, 0.1, 0.0).xxx, 1.0); + + resultImage[int2(GlobalInvocationID.xy)] = res; +} diff --git a/shaders/slang/computeshader/emboss.comp.spv b/shaders/slang/computeshader/emboss.comp.spv new file mode 100644 index 00000000..7f4bf29e Binary files /dev/null and b/shaders/slang/computeshader/emboss.comp.spv differ diff --git a/shaders/slang/computeshader/emboss.slang b/shaders/slang/computeshader/emboss.slang new file mode 100644 index 00000000..6bd00f63 --- /dev/null +++ b/shaders/slang/computeshader/emboss.slang @@ -0,0 +1,34 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import shared; + +[shader("compute")] +[numthreads(16, 16, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float imageData[9]; + // Fetch neighbouring texels + int n = -1; + for (int i=-1; i<2; ++i) + { + for(int j=-1; j<2; ++j) + { + n++; + float3 rgb = inputImage[uint2(GlobalInvocationID.x + i, GlobalInvocationID.y + j)].rgb; + imageData[n] = (rgb.r + rgb.g + rgb.b) / 3.0; + } + } + + float kernel[9]; + kernel[0] = -1.0; kernel[1] = 0.0; kernel[2] = 0.0; + kernel[3] = 0.0; kernel[4] = -1.0; kernel[5] = 0.0; + kernel[6] = 0.0; kernel[7] = 0.0; kernel[8] = 2.0; + + float4 res = float4(conv(kernel, imageData, 1.0, 0.50).xxx, 1.0); + + resultImage[int2(GlobalInvocationID.xy)] = res; +} \ No newline at end of file diff --git a/shaders/slang/computeshader/shared.slang b/shaders/slang/computeshader/shared.slang new file mode 100644 index 00000000..54828404 --- /dev/null +++ b/shaders/slang/computeshader/shared.slang @@ -0,0 +1,20 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +module shared; + +public Texture2D inputImage; +public RWTexture2D resultImage; + +public float conv(in float kernel[9], in float data[9], in float denom, in float offset) +{ + float res = 0.0; + for (int i=0; i<9; ++i) + { + res += kernel[i] * data[i]; + } + return saturate(res/denom + offset); +} \ No newline at end of file diff --git a/shaders/slang/computeshader/sharpen.comp.spv b/shaders/slang/computeshader/sharpen.comp.spv new file mode 100644 index 00000000..255e0f80 Binary files /dev/null and b/shaders/slang/computeshader/sharpen.comp.spv differ diff --git a/shaders/slang/computeshader/sharpen.slang b/shaders/slang/computeshader/sharpen.slang new file mode 100644 index 00000000..0fa15f37 --- /dev/null +++ b/shaders/slang/computeshader/sharpen.slang @@ -0,0 +1,43 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import shared; + +[shader("compute")] +[numthreads(16, 16, 1)] +void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float r[9]; + float g[9]; + float b[9]; + + // Fetch neighbouring texels + int n = -1; + for (int i=-1; i<2; ++i) + { + for(int j=-1; j<2; ++j) + { + n++; + float3 rgb = inputImage[uint2(GlobalInvocationID.x + i, GlobalInvocationID.y + j)].rgb; + r[n] = rgb.r; + g[n] = rgb.g; + b[n] = rgb.b; + } + } + + float kernel[9]; + kernel[0] = -1.0; kernel[1] = -1.0; kernel[2] = -1.0; + kernel[3] = -1.0; kernel[4] = 9.0; kernel[5] = -1.0; + kernel[6] = -1.0; kernel[7] = -1.0; kernel[8] = -1.0; + + float4 res = float4( + conv(kernel, r, 1.0, 0.0), + conv(kernel, g, 1.0, 0.0), + conv(kernel, b, 1.0, 0.0), + 1.0); + + resultImage[int2(GlobalInvocationID.xy)] = res; +} \ No newline at end of file diff --git a/shaders/slang/computeshader/texture.frag.spv b/shaders/slang/computeshader/texture.frag.spv new file mode 100644 index 00000000..a08f8ff1 Binary files /dev/null and b/shaders/slang/computeshader/texture.frag.spv differ diff --git a/shaders/slang/computeshader/texture.slang b/shaders/slang/computeshader/texture.slang new file mode 100644 index 00000000..dceb0669 --- /dev/null +++ b/shaders/slang/computeshader/texture.slang @@ -0,0 +1,41 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColor.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/computeshader/texture.vert.spv b/shaders/slang/computeshader/texture.vert.spv new file mode 100644 index 00000000..4d759a9a Binary files /dev/null and b/shaders/slang/computeshader/texture.vert.spv differ diff --git a/shaders/slang/conditionalrender/model.frag.spv b/shaders/slang/conditionalrender/model.frag.spv new file mode 100644 index 00000000..6cbde14f Binary files /dev/null and b/shaders/slang/conditionalrender/model.frag.spv differ diff --git a/shaders/slang/conditionalrender/model.slang b/shaders/slang/conditionalrender/model.slang new file mode 100644 index 00000000..fa4b8f9f --- /dev/null +++ b/shaders/slang/conditionalrender/model.slang @@ -0,0 +1,66 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +struct Node +{ + float4x4 transform; +}; +[[vk::binding(0,1)]] ConstantBuffer node; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4 baseColorFactor) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = baseColorFactor.rgb; + float4 pos = float4(input.Pos, 1.0); + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, mul(node.transform, pos)))); + + output.Normal = mul((float4x3)mul(ubo.view, mul(ubo.model, node.transform)), input.Normal).xyz; + + float4 localpos = mul(ubo.view, mul(ubo.model, mul(node.transform, pos))); + float3 lightPos = float3(10.0f, -10.0f, 10.0f); + output.LightVec = lightPos.xyz - localpos.xyz; + output.ViewVec = -localpos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 ambient = float3(0.1, 0.1, 0.1); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4((ambient + diffuse) * input.Color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/conditionalrender/model.vert.spv b/shaders/slang/conditionalrender/model.vert.spv new file mode 100644 index 00000000..a68320f1 Binary files /dev/null and b/shaders/slang/conditionalrender/model.vert.spv differ diff --git a/shaders/slang/conservativeraster/fullscreen.frag.spv b/shaders/slang/conservativeraster/fullscreen.frag.spv new file mode 100644 index 00000000..a96badf3 Binary files /dev/null and b/shaders/slang/conservativeraster/fullscreen.frag.spv differ diff --git a/shaders/slang/conservativeraster/fullscreen.slang b/shaders/slang/conservativeraster/fullscreen.slang new file mode 100644 index 00000000..d9705f60 --- /dev/null +++ b/shaders/slang/conservativeraster/fullscreen.slang @@ -0,0 +1,28 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColor.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/conservativeraster/fullscreen.vert.spv b/shaders/slang/conservativeraster/fullscreen.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/conservativeraster/fullscreen.vert.spv differ diff --git a/shaders/slang/conservativeraster/triangle.frag.spv b/shaders/slang/conservativeraster/triangle.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/conservativeraster/triangle.frag.spv differ diff --git a/shaders/slang/conservativeraster/triangle.slang b/shaders/slang/conservativeraster/triangle.slang new file mode 100644 index 00000000..bc26b8f7 --- /dev/null +++ b/shaders/slang/conservativeraster/triangle.slang @@ -0,0 +1,38 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ +struct VSInput +{ + float3 Pos; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 1); +} \ No newline at end of file diff --git a/shaders/slang/conservativeraster/triangle.vert.spv b/shaders/slang/conservativeraster/triangle.vert.spv new file mode 100644 index 00000000..7a9b3bbe Binary files /dev/null and b/shaders/slang/conservativeraster/triangle.vert.spv differ diff --git a/shaders/slang/conservativeraster/triangleoverlay.frag.spv b/shaders/slang/conservativeraster/triangleoverlay.frag.spv new file mode 100644 index 00000000..5cbccf9c Binary files /dev/null and b/shaders/slang/conservativeraster/triangleoverlay.frag.spv differ diff --git a/shaders/slang/conservativeraster/triangleoverlay.slang b/shaders/slang/conservativeraster/triangleoverlay.slang new file mode 100644 index 00000000..6915ce36 --- /dev/null +++ b/shaders/slang/conservativeraster/triangleoverlay.slang @@ -0,0 +1,11 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[shader("fragment")] +float4 fragmentMain() +{ + return float4(1.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/debugprintf/toon.frag.spv b/shaders/slang/debugprintf/toon.frag.spv new file mode 100644 index 00000000..d2367292 Binary files /dev/null and b/shaders/slang/debugprintf/toon.frag.spv differ diff --git a/shaders/slang/debugprintf/toon.slang b/shaders/slang/debugprintf/toon.slang new file mode 100644 index 00000000..1c638bcb --- /dev/null +++ b/shaders/slang/debugprintf/toon.slang @@ -0,0 +1,73 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + // Output the vertex position using debug printf + printf("Position = %v4f", pos); + + output.Normal = mul((float4x3)ubo.model, input.Normal).xyz; + float3 lPos = mul((float4x3)ubo.model, ubo.lightPos.xyz).xyz; + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Desaturate color + float3 color = float3(lerp(input.Color, dot(float3(0.2126, 0.7152, 0.0722), input.Color).xxx, 0.65)); + + // High ambient colors because mesh materials are pretty dark + float3 ambient = color * float3(1.0, 1.0, 1.0); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * color; + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + + float intensity = dot(N, L); + float shade = 1.0; + shade = intensity < 0.5 ? 0.75 : shade; + shade = intensity < 0.35 ? 0.6 : shade; + shade = intensity < 0.25 ? 0.5 : shade; + shade = intensity < 0.1 ? 0.25 : shade; + + return float4(input.Color * 3.0 * shade, 1); +} \ No newline at end of file diff --git a/shaders/slang/debugprintf/toon.vert.spv b/shaders/slang/debugprintf/toon.vert.spv new file mode 100644 index 00000000..dda273ae Binary files /dev/null and b/shaders/slang/debugprintf/toon.vert.spv differ diff --git a/shaders/slang/debugutils/colorpass.frag.spv b/shaders/slang/debugutils/colorpass.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/debugutils/colorpass.frag.spv differ diff --git a/shaders/slang/debugutils/colorpass.slang b/shaders/slang/debugutils/colorpass.slang new file mode 100644 index 00000000..7061a628 --- /dev/null +++ b/shaders/slang/debugutils/colorpass.slang @@ -0,0 +1,41 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, input.Pos)); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/debugutils/colorpass.vert.spv b/shaders/slang/debugutils/colorpass.vert.spv new file mode 100644 index 00000000..d74fcd9e Binary files /dev/null and b/shaders/slang/debugutils/colorpass.vert.spv differ diff --git a/shaders/slang/debugutils/postprocess.frag.spv b/shaders/slang/debugutils/postprocess.frag.spv new file mode 100644 index 00000000..59b020b2 Binary files /dev/null and b/shaders/slang/debugutils/postprocess.frag.spv differ diff --git a/shaders/slang/debugutils/postprocess.slang b/shaders/slang/debugutils/postprocess.slang new file mode 100644 index 00000000..a1bce2f7 --- /dev/null +++ b/shaders/slang/debugutils/postprocess.slang @@ -0,0 +1,55 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[[vk::binding(1, 0)]] Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * float2(2.0f, 2.0f) + float2(-1.0f, -1.0f), 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Single pass gauss blur + + const float2 texOffset = float2(0.01, 0.01); + + float2 tc0 = input.UV + float2(-texOffset.x, -texOffset.y); + float2 tc1 = input.UV + float2( 0.0, -texOffset.y); + float2 tc2 = input.UV + float2(+texOffset.x, -texOffset.y); + float2 tc3 = input.UV + float2(-texOffset.x, 0.0); + float2 tc4 = input.UV + float2( 0.0, 0.0); + float2 tc5 = input.UV + float2(+texOffset.x, 0.0); + float2 tc6 = input.UV + float2(-texOffset.x, +texOffset.y); + float2 tc7 = input.UV + float2( 0.0, +texOffset.y); + float2 tc8 = input.UV + float2(+texOffset.x, +texOffset.y); + + float4 col0 = samplerColor.Sample(tc0); + float4 col1 = samplerColor.Sample(tc1); + float4 col2 = samplerColor.Sample(tc2); + float4 col3 = samplerColor.Sample(tc3); + float4 col4 = samplerColor.Sample(tc4); + float4 col5 = samplerColor.Sample(tc5); + float4 col6 = samplerColor.Sample(tc6); + float4 col7 = samplerColor.Sample(tc7); + float4 col8 = samplerColor.Sample(tc8); + + float4 sum = (1.0 * col0 + 2.0 * col1 + 1.0 * col2 + + 2.0 * col3 + 4.0 * col4 + 2.0 * col5 + + 1.0 * col6 + 2.0 * col7 + 1.0 * col8) / 16.0; + return float4(sum.rgb, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/debugutils/postprocess.vert.spv b/shaders/slang/debugutils/postprocess.vert.spv new file mode 100644 index 00000000..f1935d3d Binary files /dev/null and b/shaders/slang/debugutils/postprocess.vert.spv differ diff --git a/shaders/slang/debugutils/toon.frag.spv b/shaders/slang/debugutils/toon.frag.spv new file mode 100644 index 00000000..669f325d Binary files /dev/null and b/shaders/slang/debugutils/toon.frag.spv differ diff --git a/shaders/slang/debugutils/toon.slang b/shaders/slang/debugutils/toon.slang new file mode 100644 index 00000000..8d69bee0 --- /dev/null +++ b/shaders/slang/debugutils/toon.slang @@ -0,0 +1,71 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float4x3)ubo.model, input.Normal).xyz; + float3 lPos = mul((float4x3)ubo.model, ubo.lightPos.xyz).xyz; + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Desaturate color + float3 color = float3(lerp(input.Color, dot(float3(0.2126,0.7152,0.0722), input.Color).xxx, 0.65)); + + // High ambient colors because mesh materials are pretty dark + float3 ambient = color * float3(1.0, 1.0, 1.0); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * color; + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + + float intensity = dot(N,L); + float shade = 1.0; + shade = intensity < 0.5 ? 0.75 : shade; + shade = intensity < 0.35 ? 0.6 : shade; + shade = intensity < 0.25 ? 0.5 : shade; + shade = intensity < 0.1 ? 0.25 : shade; + + return float4(input.Color * 3.0 * shade, 1); +} \ No newline at end of file diff --git a/shaders/slang/debugutils/toon.vert.spv b/shaders/slang/debugutils/toon.vert.spv new file mode 100644 index 00000000..0dd9a7e0 Binary files /dev/null and b/shaders/slang/debugutils/toon.vert.spv differ diff --git a/shaders/slang/deferred/deferred.frag.spv b/shaders/slang/deferred/deferred.frag.spv new file mode 100644 index 00000000..1ca9d175 Binary files /dev/null and b/shaders/slang/deferred/deferred.frag.spv differ diff --git a/shaders/slang/deferred/deferred.slang b/shaders/slang/deferred/deferred.slang new file mode 100644 index 00000000..e02ef0d5 --- /dev/null +++ b/shaders/slang/deferred/deferred.slang @@ -0,0 +1,110 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[[vk::binding(1, 0)]] Sampler2D samplerposition; +[[vk::binding(2, 0)]] Sampler2D samplerNormal; +[[vk::binding(3, 0)]] Sampler2D samplerAlbedo; + +struct Light { + float4 position; + float3 color; + float radius; +}; + +struct UBO +{ + Light lights[6]; + float4 viewPos; + int displayDebugTarget; +}; +[[vk::binding(4, 0)]] ConstantBuffer ubo; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Get G-Buffer values + float3 fragPos = samplerposition.Sample(input.UV).rgb; + float3 normal = samplerNormal.Sample(input.UV).rgb; + float4 albedo = samplerAlbedo.Sample(input.UV); + + float3 fragcolor; + + // Debug display + if (ubo.displayDebugTarget > 0) { + switch (ubo.displayDebugTarget) { + case 1: + fragcolor.rgb = fragPos; + break; + case 2: + fragcolor.rgb = normal; + break; + case 3: + fragcolor.rgb = albedo.rgb; + break; + case 4: + fragcolor.rgb = albedo.aaa; + break; + } + return float4(fragcolor, 1.0); + } + + #define lightCount 6 + #define ambient 0.0 + + // Ambient part + fragcolor = albedo.rgb * ambient; + + for(int i = 0; i < lightCount; ++i) + { + // Vector to light + float3 L = ubo.lights[i].position.xyz - fragPos; + // Distance from light to fragment position + float dist = length(L); + + // Viewer to fragment + float3 V = ubo.viewPos.xyz - fragPos; + V = normalize(V); + + //if(dist < ubo.lights[i].radius) + { + // Light to fragment + L = normalize(L); + + // Attenuation + float atten = ubo.lights[i].radius / (pow(dist, 2.0) + 1.0); + + // Diffuse part + float3 N = normalize(normal); + float NdotL = max(0.0, dot(N, L)); + float3 diff = ubo.lights[i].color * albedo.rgb * NdotL * atten; + + // Specular part + // Specular map values are stored in alpha of albedo mrt + float3 R = reflect(-L, N); + float NdotR = max(0.0, dot(R, V)); + float3 spec = ubo.lights[i].color * albedo.a * pow(NdotR, 16.0) * atten; + + fragcolor += diff + spec; + } + } + + return float4(fragcolor, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/deferred/deferred.vert.spv b/shaders/slang/deferred/deferred.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/deferred/deferred.vert.spv differ diff --git a/shaders/slang/deferred/mrt.frag.spv b/shaders/slang/deferred/mrt.frag.spv new file mode 100644 index 00000000..f3ebda26 Binary files /dev/null and b/shaders/slang/deferred/mrt.frag.spv differ diff --git a/shaders/slang/deferred/mrt.slang b/shaders/slang/deferred/mrt.slang new file mode 100644 index 00000000..8df7a701 --- /dev/null +++ b/shaders/slang/deferred/mrt.slang @@ -0,0 +1,83 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[[vk::binding(1, 0)]] Sampler2D samplerColor; +[[vk::binding(2, 0)]] Sampler2D samplerNormalMap; + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; + float3 Normal; + float3 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 Color; + float3 WorldPos; + float3 Tangent; +}; + +struct FSOutput +{ + float4 Position; + float4 Normal; + float4 Albedo; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; + float4 instancePos[3]; +}; +[[vk::binding(0, 0)]] ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uint InstanceIndex: SV_InstanceID) +{ + VSOutput output; + float4 tmpPos = input.Pos + ubo.instancePos[InstanceIndex]; + + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, tmpPos))); + + output.UV = input.UV; + + // Vertex position in world space + output.WorldPos = mul(ubo.model, tmpPos).xyz; + + // Normal in world space + output.Normal = normalize(input.Normal); + output.Tangent = normalize(input.Tangent); + + // Currently just vertex color + output.Color = input.Color; + return output; +} + +[shader("fragment")] +FSOutput fragmentMain(VSOutput input) +{ + FSOutput output; + output.Position = float4(input.WorldPos, 1.0); + + // Calculate normal in tangent space + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent); + float3 B = cross(N, T); + float3x3 TBN = float3x3(T, B, N); + float3 tnorm = mul(normalize(samplerNormalMap.Sample(input.UV).xyz * 2.0 - float3(1.0, 1.0, 1.0)), TBN); + output.Normal = float4(tnorm, 1.0); + + output.Albedo = samplerColor.Sample(input.UV); + return output; +} \ No newline at end of file diff --git a/shaders/slang/deferred/mrt.vert.spv b/shaders/slang/deferred/mrt.vert.spv new file mode 100644 index 00000000..92230a26 Binary files /dev/null and b/shaders/slang/deferred/mrt.vert.spv differ diff --git a/shaders/slang/deferredmultisampling/deferred.frag.spv b/shaders/slang/deferredmultisampling/deferred.frag.spv new file mode 100644 index 00000000..fa6ba70a Binary files /dev/null and b/shaders/slang/deferredmultisampling/deferred.frag.spv differ diff --git a/shaders/slang/deferredmultisampling/deferred.slang b/shaders/slang/deferredmultisampling/deferred.slang new file mode 100644 index 00000000..81d3b3bc --- /dev/null +++ b/shaders/slang/deferredmultisampling/deferred.slang @@ -0,0 +1,140 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[[vk::binding(1, 0)]] Texture2DMS samplerPosition; +[[vk::binding(2, 0)]] Texture2DMS samplerNormal; +[[vk::binding(3, 0)]] Texture2DMS samplerAlbedo; + +struct Light { + float4 position; + float3 color; + float radius; +}; + +struct UBO +{ + Light lights[6]; + float4 viewPos; + int displayDebugTarget; +}; +[[vk::binding(4, 0)]] ConstantBuffer ubo; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[[SpecializationConstant]] const int NUM_SAMPLES = 8; +#define NUM_LIGHTS 6 + +// Manual resolve for MSAA samples +float4 resolve(Texture2DMS tex, int2 uv) +{ + float4 result = float4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < NUM_SAMPLES; i++) + { + uint status = 0; + float4 val = tex.Load(uv, i, int2(0, 0), status); + result += val; + } + // Average resolved samples + return result / float(NUM_SAMPLES); +} + +float3 calculateLighting(float3 pos, float3 normal, float4 albedo) +{ + float3 result = float3(0.0, 0.0, 0.0); + + for (int i = 0; i < NUM_LIGHTS; ++i) + { + // Vector to light + float3 L = ubo.lights[i].position.xyz - pos; + // Distance from light to fragment position + float dist = length(L); + + // Viewer to fragment + float3 V = ubo.viewPos.xyz - pos; + V = normalize(V); + + // Light to fragment + L = normalize(L); + + // Attenuation + float atten = ubo.lights[i].radius / (pow(dist, 2.0) + 1.0); + + // Diffuse part + float3 N = normalize(normal); + float NdotL = max(0.0, dot(N, L)); + float3 diff = ubo.lights[i].color * albedo.rgb * NdotL * atten; + + // Specular part + float3 R = reflect(-L, N); + float NdotR = max(0.0, dot(R, V)); + float3 spec = ubo.lights[i].color * albedo.a * pow(NdotR, 8.0) * atten; + + result += diff + spec; + } + return result; +} + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + int2 attDim; int sampleCount; + samplerPosition.GetDimensions(attDim.x, attDim.y, sampleCount); + int2 UV = int2(input.UV * attDim); + + float3 fragColor; + uint status = 0; + + // Debug display + if (ubo.displayDebugTarget > 0) { + switch (ubo.displayDebugTarget) { + case 1: + fragColor.rgb = samplerPosition.Load(UV, 0, int2(0, 0), status).rgb; + break; + case 2: + fragColor.rgb = samplerNormal.Load(UV, 0, int2(0, 0), status).rgb; + break; + case 3: + fragColor.rgb = samplerAlbedo.Load(UV, 0, int2(0, 0), status).rgb; + break; + case 4: + fragColor.rgb = samplerAlbedo.Load(UV, 0, int2(0, 0), status).aaa; + break; + } + return float4(fragColor, 1.0); + } + +#define ambient 0.15 + + // Ambient part + float4 alb = resolve(samplerAlbedo, UV); + fragColor = float3(0.0, 0.0, 0.0); + + // Calualte lighting for every MSAA sample + for (int i = 0; i < NUM_SAMPLES; i++) + { + float3 pos = samplerPosition.Load(UV, i, int2(0, 0), status).rgb; + float3 normal = samplerNormal.Load(UV, i, int2(0, 0), status).rgb; + float4 albedo = samplerAlbedo.Load(UV, i, int2(0, 0), status); + fragColor += calculateLighting(pos, normal, albedo); + } + + fragColor = (alb.rgb * ambient) + fragColor / float(NUM_SAMPLES); + + return float4(fragColor, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/deferredmultisampling/deferred.vert.spv b/shaders/slang/deferredmultisampling/deferred.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/deferredmultisampling/deferred.vert.spv differ diff --git a/shaders/slang/deferredmultisampling/mrt.frag.spv b/shaders/slang/deferredmultisampling/mrt.frag.spv new file mode 100644 index 00000000..f3ebda26 Binary files /dev/null and b/shaders/slang/deferredmultisampling/mrt.frag.spv differ diff --git a/shaders/slang/deferredmultisampling/mrt.slang b/shaders/slang/deferredmultisampling/mrt.slang new file mode 100644 index 00000000..146c4fd2 --- /dev/null +++ b/shaders/slang/deferredmultisampling/mrt.slang @@ -0,0 +1,83 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; + float3 Normal; + float3 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 Color; + float3 WorldPos; + float3 Tangent; +}; + +struct FSOutput +{ + float4 Position; + float4 Normal; + float4 Albedo; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; + float4 instancePos[3]; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; +Sampler2D samplerNormalMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uint InstanceIndex: SV_InstanceID) +{ + VSOutput output; + float4 tmpPos = input.Pos + ubo.instancePos[InstanceIndex]; + + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, tmpPos))); + + output.UV = input.UV; + + // Vertex position in world space + output.WorldPos = mul(ubo.model, tmpPos).xyz; + + // Normal in world space + output.Normal = normalize(input.Normal); + output.Tangent = normalize(input.Tangent); + + // Currently just vertex color + output.Color = input.Color; + return output; +} + +[shader("fragment")] +FSOutput fragmentMain(VSOutput input) +{ + FSOutput output; + output.Position = float4(input.WorldPos, 1.0); + + // Calculate normal in tangent space + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent); + float3 B = cross(N, T); + float3x3 TBN = float3x3(T, B, N); + float3 tnorm = mul(normalize(samplerNormalMap.Sample(input.UV).xyz * 2.0 - float3(1.0, 1.0, 1.0)), TBN); + output.Normal = float4(tnorm, 1.0); + + output.Albedo = samplerColor.Sample(input.UV); + return output; +} \ No newline at end of file diff --git a/shaders/slang/deferredmultisampling/mrt.vert.spv b/shaders/slang/deferredmultisampling/mrt.vert.spv new file mode 100644 index 00000000..92230a26 Binary files /dev/null and b/shaders/slang/deferredmultisampling/mrt.vert.spv differ diff --git a/shaders/slang/deferredshadows/deferred.frag.spv b/shaders/slang/deferredshadows/deferred.frag.spv new file mode 100644 index 00000000..0ee9c595 Binary files /dev/null and b/shaders/slang/deferredshadows/deferred.frag.spv differ diff --git a/shaders/slang/deferredshadows/deferred.slang b/shaders/slang/deferredshadows/deferred.slang new file mode 100644 index 00000000..f7759227 --- /dev/null +++ b/shaders/slang/deferredshadows/deferred.slang @@ -0,0 +1,187 @@ +// Copyright 2020 Google LLC + +#define LIGHT_COUNT 3 +#define SHADOW_FACTOR 0.25 +#define AMBIENT_LIGHT 0.1 +#define USE_PCF + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[[vk::binding(1, 0)]] Sampler2D samplerPosition; +[[vk::binding(2, 0)]] Sampler2D samplerNormal; +[[vk::binding(3, 0)]] Sampler2D samplerAlbedo; + +struct Light +{ + float4 position; + float4 target; + float4 color; + float4x4 viewMatrix; +}; + +struct UBO +{ + float4 viewPos; + Light lights[LIGHT_COUNT]; + int useShadows; + int displayDebugTarget; +}; +[[vk::binding(4, 0)]] ConstantBuffer ubo; + +// Depth from the light's point of view +// layout (binding = 5) uniform sampler2DShadow samplerShadowMap; +[[vk::binding(5, 0)]] Sampler2DArray samplerShadowMap; + +float textureProj(float4 P, float layer, float2 offset) +{ + float shadow = 1.0; + float4 shadowCoord = P / P.w; + shadowCoord.xy = shadowCoord.xy * 0.5 + 0.5; + + if (shadowCoord.z > -1.0 && shadowCoord.z < 1.0) + { + float dist = samplerShadowMap.Sample(float3(shadowCoord.xy + offset, layer)).r; + if (shadowCoord.w > 0.0 && dist < shadowCoord.z) + { + shadow = SHADOW_FACTOR; + } + } + return shadow; +} + +float filterPCF(float4 sc, float layer) +{ + int2 texDim; int elements; int levels; + samplerShadowMap.GetDimensions(0, texDim.x, texDim.y, elements, levels); + float scale = 1.5; + float dx = scale * 1.0 / float(texDim.x); + float dy = scale * 1.0 / float(texDim.y); + + float shadowFactor = 0.0; + int count = 0; + int range = 1; + + for (int x = -range; x <= range; x++) + { + for (int y = -range; y <= range; y++) + { + shadowFactor += textureProj(sc, layer, float2(dx*x, dy*y)); + count++; + } + + } + return shadowFactor / count; +} + +float3 shadow(float3 fragcolor, float3 fragPos) +{ + for (int i = 0; i < LIGHT_COUNT; ++i) + { + float4 shadowClip = mul(ubo.lights[i].viewMatrix, float4(fragPos.xyz, 1.0)); + + float shadowFactor; + #ifdef USE_PCF + shadowFactor= filterPCF(shadowClip, i); + #else + shadowFactor = textureProj(shadowClip, i, float2(0.0, 0.0)); + #endif + + fragcolor *= shadowFactor; + } + return fragcolor; +} + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Get G-Buffer values + float3 fragPos = samplerPosition.Sample(input.UV).rgb; + float3 normal = samplerNormal.Sample(input.UV).rgb; + float4 albedo = samplerAlbedo.Sample(input.UV); + + float3 fragcolor; + + // Debug display + if (ubo.displayDebugTarget > 0) { + switch (ubo.displayDebugTarget) { + case 1: + fragcolor.rgb = shadow(float3(1.0, 1.0, 1.0), fragPos); + break; + case 2: + fragcolor.rgb = fragPos; + break; + case 3: + fragcolor.rgb = normal; + break; + case 4: + fragcolor.rgb = albedo.rgb; + break; + case 5: + fragcolor.rgb = albedo.aaa; + break; + } + return float4(fragcolor, 1.0); + } + + // Ambient part + fragcolor = albedo.rgb * AMBIENT_LIGHT; + + float3 N = normalize(normal); + + for(int i = 0; i < LIGHT_COUNT; ++i) + { + // Vector to light + float3 L = ubo.lights[i].position.xyz - fragPos; + // Distance from light to fragment position + float dist = length(L); + L = normalize(L); + + // Viewer to fragment + float3 V = ubo.viewPos.xyz - fragPos; + V = normalize(V); + + float lightCosInnerAngle = cos(radians(15.0)); + float lightCosOuterAngle = cos(radians(25.0)); + float lightRange = 100.0; + + // Direction vector from source to target + float3 dir = normalize(ubo.lights[i].position.xyz - ubo.lights[i].target.xyz); + + // Dual cone spot light with smooth transition between inner and outer angle + float cosDir = dot(L, dir); + float spotEffect = smoothstep(lightCosOuterAngle, lightCosInnerAngle, cosDir); + float heightAttenuation = smoothstep(lightRange, 0.0f, dist); + + // Diffuse lighting + float NdotL = max(0.0, dot(N, L)); + float3 diff = NdotL.xxx; + + // Specular lighting + float3 R = reflect(-L, N); + float NdotR = max(0.0, dot(R, V)); + float3 spec = (pow(NdotR, 16.0) * albedo.a * 2.5).xxx; + + fragcolor += float3((diff + spec) * spotEffect * heightAttenuation) * ubo.lights[i].color.rgb * albedo.rgb; + } + + // Shadow calculations in a separate pass + if (ubo.useShadows > 0) + { + fragcolor = shadow(fragcolor, fragPos); + } + + return float4(fragcolor, 1); +} \ No newline at end of file diff --git a/shaders/slang/deferredshadows/deferred.vert.spv b/shaders/slang/deferredshadows/deferred.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/deferredshadows/deferred.vert.spv differ diff --git a/shaders/slang/deferredshadows/mrt.frag.spv b/shaders/slang/deferredshadows/mrt.frag.spv new file mode 100644 index 00000000..f3ebda26 Binary files /dev/null and b/shaders/slang/deferredshadows/mrt.frag.spv differ diff --git a/shaders/slang/deferredshadows/mrt.slang b/shaders/slang/deferredshadows/mrt.slang new file mode 100644 index 00000000..53b42587 --- /dev/null +++ b/shaders/slang/deferredshadows/mrt.slang @@ -0,0 +1,83 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; + float3 Normal; + float3 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 Color; + float3 WorldPos; + float3 Tangent; +}; + +struct FSOutput +{ + float4 Position : SV_TARGET0; + float4 Normal : SV_TARGET1; + float4 Albedo : SV_TARGET2; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; + float4 instancePos[3]; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; +Sampler2D samplerNormalMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uint InstanceIndex: SV_InstanceID) +{ + VSOutput output; + float4 tmpPos = input.Pos + ubo.instancePos[InstanceIndex]; + + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, tmpPos))); + + output.UV = input.UV; + + // Vertex position in world space + output.WorldPos = mul(ubo.model, tmpPos).xyz; + + // Normal in world space + output.Normal = normalize(input.Normal); + output.Tangent = normalize(input.Tangent); + + // Currently just vertex color + output.Color = input.Color; + return output; +} + +[shader("fragment")] +FSOutput fragmentMain(VSOutput input) +{ + FSOutput output; + output.Position = float4(input.WorldPos, 1.0); + + // Calculate normal in tangent space + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent); + float3 B = cross(N, T); + float3x3 TBN = float3x3(T, B, N); + float3 tnorm = mul(normalize(samplerNormalMap.Sample(input.UV).xyz * 2.0 - float3(1.0, 1.0, 1.0)), TBN); + output.Normal = float4(tnorm, 1.0); + + output.Albedo = samplerColor.Sample(input.UV); + return output; +} \ No newline at end of file diff --git a/shaders/slang/deferredshadows/mrt.vert.spv b/shaders/slang/deferredshadows/mrt.vert.spv new file mode 100644 index 00000000..92230a26 Binary files /dev/null and b/shaders/slang/deferredshadows/mrt.vert.spv differ diff --git a/shaders/slang/deferredshadows/shadow.geom.spv b/shaders/slang/deferredshadows/shadow.geom.spv new file mode 100644 index 00000000..2d99b8e5 Binary files /dev/null and b/shaders/slang/deferredshadows/shadow.geom.spv differ diff --git a/shaders/slang/deferredshadows/shadow.slang b/shaders/slang/deferredshadows/shadow.slang new file mode 100644 index 00000000..68ada4a9 --- /dev/null +++ b/shaders/slang/deferredshadows/shadow.slang @@ -0,0 +1,57 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#define LIGHT_COUNT 3 + +struct VSInput +{ + float4 Pos; +} + +struct VSOutput +{ + float4 Pos : SV_POSITION; + int InstanceIndex; +}; + +struct GSOutput +{ + float4 Pos : SV_POSITION; + int Layer : SV_RenderTargetArrayIndex; +}; + +struct UBO +{ + float4x4 mvp[LIGHT_COUNT]; + float4 instancePos[3]; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uint InstanceIndex: SV_InstanceID) +{ + VSOutput output; + output.InstanceIndex = InstanceIndex; + output.Pos = input.Pos; + return output; +} + +[shader("geometry")] +[maxvertexcount(3)] +[instance(3)] +void geometryMain(triangle VSOutput input[3], uint InvocationID : SV_GSInstanceID, inout TriangleStream outStream) +{ + float4 instancedPos = ubo.instancePos[input[0].InstanceIndex]; + for (int i = 0; i < 3; i++) + { + float4 tmpPos = input[i].Pos + instancedPos; + GSOutput output; + output.Pos = mul(ubo.mvp[InvocationID], tmpPos); + output.Layer = InvocationID; + outStream.Append( output ); + } + outStream.RestartStrip(); +} \ No newline at end of file diff --git a/shaders/slang/deferredshadows/shadow.vert.spv b/shaders/slang/deferredshadows/shadow.vert.spv new file mode 100644 index 00000000..eed17f3e Binary files /dev/null and b/shaders/slang/deferredshadows/shadow.vert.spv differ diff --git a/shaders/slang/descriptorbuffer/cube.frag.spv b/shaders/slang/descriptorbuffer/cube.frag.spv new file mode 100644 index 00000000..1e7ca802 Binary files /dev/null and b/shaders/slang/descriptorbuffer/cube.frag.spv differ diff --git a/shaders/slang/descriptorbuffer/cube.slang b/shaders/slang/descriptorbuffer/cube.slang new file mode 100644 index 00000000..7d6e3ad3 --- /dev/null +++ b/shaders/slang/descriptorbuffer/cube.slang @@ -0,0 +1,51 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; +}; + +struct UBOCamera { + float4x4 projection; + float4x4 view; +}; +ConstantBuffer uboCamera; + +struct UBOModel { + float4x4 local; +}; +[[vk::binding(0, 1)]] ConstantBuffer uboModel; + +[[vk::binding(0, 2)]] Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(uboCamera.projection, mul(uboCamera.view, mul(uboModel.local, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/descriptorbuffer/cube.vert.spv b/shaders/slang/descriptorbuffer/cube.vert.spv new file mode 100644 index 00000000..bbc25b87 Binary files /dev/null and b/shaders/slang/descriptorbuffer/cube.vert.spv differ diff --git a/shaders/slang/descriptorindexing/descriptorindexing.frag.spv b/shaders/slang/descriptorindexing/descriptorindexing.frag.spv new file mode 100644 index 00000000..5f2ad97c Binary files /dev/null and b/shaders/slang/descriptorindexing/descriptorindexing.frag.spv differ diff --git a/shaders/slang/descriptorindexing/descriptorindexing.slang b/shaders/slang/descriptorindexing/descriptorindexing.slang new file mode 100644 index 00000000..5c25ea88 --- /dev/null +++ b/shaders/slang/descriptorindexing/descriptorindexing.slang @@ -0,0 +1,43 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos : POSITION0; + float2 UV; + int TextureIndex; +}; + +struct Matrices { + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer matrices; +Sampler2D textures[]; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + int TextureIndex; + float2 UV; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.TextureIndex = input.TextureIndex; + output.Pos = mul(matrices.projection, mul(matrices.view, mul(matrices.model, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return textures[NonUniformResourceIndex(input.TextureIndex)].Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/descriptorindexing/descriptorindexing.vert.spv b/shaders/slang/descriptorindexing/descriptorindexing.vert.spv new file mode 100644 index 00000000..1e80db21 Binary files /dev/null and b/shaders/slang/descriptorindexing/descriptorindexing.vert.spv differ diff --git a/shaders/slang/descriptorsets/cube.frag.spv b/shaders/slang/descriptorsets/cube.frag.spv new file mode 100644 index 00000000..2aaac7d7 Binary files /dev/null and b/shaders/slang/descriptorsets/cube.frag.spv differ diff --git a/shaders/slang/descriptorsets/cube.slang b/shaders/slang/descriptorsets/cube.slang new file mode 100644 index 00000000..8a8b0ec9 --- /dev/null +++ b/shaders/slang/descriptorsets/cube.slang @@ -0,0 +1,51 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; +}; + +// Slang auto generates bindings by order of descriptors +// So the UBO is bound to slot 0, the sampler to slot 1 +// due to their order in the shader + +struct UBOMatrices { + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer uboMatrices; + +Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(uboMatrices.projection, mul(uboMatrices.view, mul(uboMatrices.model, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/descriptorsets/cube.vert.spv b/shaders/slang/descriptorsets/cube.vert.spv new file mode 100644 index 00000000..398cf230 Binary files /dev/null and b/shaders/slang/descriptorsets/cube.vert.spv differ diff --git a/shaders/slang/displacement/base.frag.spv b/shaders/slang/displacement/base.frag.spv new file mode 100644 index 00000000..128751c6 Binary files /dev/null and b/shaders/slang/displacement/base.frag.spv differ diff --git a/shaders/slang/displacement/base.vert.spv b/shaders/slang/displacement/base.vert.spv new file mode 100644 index 00000000..b50c5a1e Binary files /dev/null and b/shaders/slang/displacement/base.vert.spv differ diff --git a/shaders/slang/displacement/displacement.slang b/shaders/slang/displacement/displacement.slang new file mode 100644 index 00000000..ec8f171c --- /dev/null +++ b/shaders/slang/displacement/displacement.slang @@ -0,0 +1,118 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct HSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct DSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 EyePos; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; + float tessAlpha; + float tessStrength; + float tessLevel; +}; +ConstantBuffer ubo; + +Sampler2D samplerColorAndDisplacementMap; + +struct ConstantsHSOutput +{ + float TessLevelOuter[3] : SV_TessFactor; + float TessLevelInner[2] : SV_InsideTessFactor; +}; + +ConstantsHSOutput ConstantsHS() +{ + ConstantsHSOutput output; + output.TessLevelInner[0] = ubo.tessLevel; + output.TessLevelInner[1] = ubo.tessLevel; + output.TessLevelOuter[0] = ubo.tessLevel; + output.TessLevelOuter[1] = ubo.tessLevel; + output.TessLevelOuter[2] = ubo.tessLevel; + return output; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = float4(input.Pos.xyz, 1.0); + output.UV = input.UV; + output.Normal = input.Normal; + return output; +} + +[shader("hull")] +[domain("tri")] +[partitioning("integer")] +[outputtopology("triangle_cw")] +[outputcontrolpoints(3)] +[patchconstantfunc("ConstantsHS")] +[maxtessfactor(20.0f)] +HSOutput hullMain(InputPatch patch, uint InvocationID: SV_OutputControlPointID) +{ + HSOutput output; + output.Pos = patch[InvocationID].Pos; + output.Normal = patch[InvocationID].Normal; + output.UV = patch[InvocationID].UV; + return output; +} + +[shader("domain")] +[domain("tri")] +DSOutput domainMain(ConstantsHSOutput input, float3 TessCoord: SV_DomainLocation, const OutputPatch patch) +{ + DSOutput output; + output.Pos = (TessCoord.x * patch[0].Pos) + (TessCoord.y * patch[1].Pos) + (TessCoord.z * patch[2].Pos); + output.UV = (TessCoord.x * patch[0].UV) + (TessCoord.y * patch[1].UV) + (TessCoord.z * patch[2].UV); + output.Normal = TessCoord.x * patch[0].Normal + TessCoord.y * patch[1].Normal + TessCoord.z * patch[2].Normal; + output.Pos.xyz += normalize(output.Normal) * (max(samplerColorAndDisplacementMap.SampleLevel(output.UV.xy, 0).a, 0.0) * ubo.tessStrength); + output.EyePos = output.Pos.xyz; + output.LightVec = normalize(ubo.lightPos.xyz - output.EyePos); + output.Pos = mul(ubo.projection, mul(ubo.model, output.Pos)); + return output; +} + +[shader("fragment")] +float4 fragmentMain(DSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(float3(1.0, 1.0, 1.0)); + float3 Eye = normalize(-input.EyePos); + float3 Reflected = normalize(reflect(-input.LightVec, input.Normal)); + float4 IAmbient = float4(0.0, 0.0, 0.0, 1.0); + float4 IDiffuse = float4(1.0, 1.0, 1.0, 1.0) * max(dot(input.Normal, input.LightVec), 0.0); + return float4((IAmbient + IDiffuse) * float4(samplerColorAndDisplacementMap.Sample(input.UV).rgb, 1.0)); +} diff --git a/shaders/slang/displacement/displacement.tesc.spv b/shaders/slang/displacement/displacement.tesc.spv new file mode 100644 index 00000000..593d7b18 Binary files /dev/null and b/shaders/slang/displacement/displacement.tesc.spv differ diff --git a/shaders/slang/displacement/displacement.tese.spv b/shaders/slang/displacement/displacement.tese.spv new file mode 100644 index 00000000..3c4bfd59 Binary files /dev/null and b/shaders/slang/displacement/displacement.tese.spv differ diff --git a/shaders/slang/distancefieldfonts/bitmap.frag.spv b/shaders/slang/distancefieldfonts/bitmap.frag.spv new file mode 100644 index 00000000..da5638c3 Binary files /dev/null and b/shaders/slang/distancefieldfonts/bitmap.frag.spv differ diff --git a/shaders/slang/distancefieldfonts/bitmap.slang b/shaders/slang/distancefieldfonts/bitmap.slang new file mode 100644 index 00000000..59849e00 --- /dev/null +++ b/shaders/slang/distancefieldfonts/bitmap.slang @@ -0,0 +1,40 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColor.Sample(input.UV).aaaa; +} \ No newline at end of file diff --git a/shaders/slang/distancefieldfonts/bitmap.vert.spv b/shaders/slang/distancefieldfonts/bitmap.vert.spv new file mode 100644 index 00000000..4d759a9a Binary files /dev/null and b/shaders/slang/distancefieldfonts/bitmap.vert.spv differ diff --git a/shaders/slang/distancefieldfonts/sdf.frag.spv b/shaders/slang/distancefieldfonts/sdf.frag.spv new file mode 100644 index 00000000..663a9d57 Binary files /dev/null and b/shaders/slang/distancefieldfonts/sdf.frag.spv differ diff --git a/shaders/slang/distancefieldfonts/sdf.slang b/shaders/slang/distancefieldfonts/sdf.slang new file mode 100644 index 00000000..2d3bb235 --- /dev/null +++ b/shaders/slang/distancefieldfonts/sdf.slang @@ -0,0 +1,56 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; +}; + + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 outlineColor; + float outlineWidth; + float outline; +}; +ConstantBuffer ubo; +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float dist = samplerColor.Sample(input.UV).a; + float smoothWidth = fwidth(dist); + float alpha = smoothstep(0.5 - smoothWidth, 0.5 + smoothWidth, dist); + float3 rgb = alpha.xxx; + + if (ubo.outline > 0.0) + { + float w = 1.0 - ubo.outlineWidth; + alpha = smoothstep(w - smoothWidth, w + smoothWidth, dist); + rgb += lerp(alpha.xxx, ubo.outlineColor.rgb, alpha); + } + + return float4(rgb, alpha); +} \ No newline at end of file diff --git a/shaders/slang/distancefieldfonts/sdf.vert.spv b/shaders/slang/distancefieldfonts/sdf.vert.spv new file mode 100644 index 00000000..931059c7 Binary files /dev/null and b/shaders/slang/distancefieldfonts/sdf.vert.spv differ diff --git a/shaders/slang/dynamicrendering/texture.frag.spv b/shaders/slang/dynamicrendering/texture.frag.spv new file mode 100644 index 00000000..72086329 Binary files /dev/null and b/shaders/slang/dynamicrendering/texture.frag.spv differ diff --git a/shaders/slang/dynamicrendering/texture.slang b/shaders/slang/dynamicrendering/texture.slang new file mode 100644 index 00000000..baed6d6d --- /dev/null +++ b/shaders/slang/dynamicrendering/texture.slang @@ -0,0 +1,65 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 viewPos; +}; +ConstantBuffer ubo; + +[[vk::binding(0, 1)]] Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + + float3 worldPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lightPos = float3(0.0, 0.0, 0.0); + float3 lPos = mul((float3x3)ubo.model, lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = ubo.viewPos.xyz - pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColor.Sample(input.UV); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float specular = pow(max(dot(R, V), 0.0), 16.0) * color.a; + + return float4(diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/dynamicrendering/texture.vert.spv b/shaders/slang/dynamicrendering/texture.vert.spv new file mode 100644 index 00000000..e2f8ec88 Binary files /dev/null and b/shaders/slang/dynamicrendering/texture.vert.spv differ diff --git a/shaders/slang/dynamicuniformbuffer/base.frag.spv b/shaders/slang/dynamicuniformbuffer/base.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/dynamicuniformbuffer/base.frag.spv differ diff --git a/shaders/slang/dynamicuniformbuffer/base.slang b/shaders/slang/dynamicuniformbuffer/base.slang new file mode 100644 index 00000000..ec8e444d --- /dev/null +++ b/shaders/slang/dynamicuniformbuffer/base.slang @@ -0,0 +1,47 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +struct UboView +{ + float4x4 projection; + float4x4 view; +}; +ConstantBuffer uboView; + +struct UboInstance +{ + float4x4 model; +}; +ConstantBuffer uboInstance; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + float4x4 modelView = mul(uboView.view, uboInstance.model); + float3 worldPos = mul(modelView, float4(input.Pos, 1.0)).xyz; + output.Pos = mul(uboView.projection, mul(modelView, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/dynamicuniformbuffer/base.vert.spv b/shaders/slang/dynamicuniformbuffer/base.vert.spv new file mode 100644 index 00000000..dceceb81 Binary files /dev/null and b/shaders/slang/dynamicuniformbuffer/base.vert.spv differ diff --git a/shaders/slang/gears/gears.frag.spv b/shaders/slang/gears/gears.frag.spv new file mode 100644 index 00000000..e68f1691 Binary files /dev/null and b/shaders/slang/gears/gears.frag.spv differ diff --git a/shaders/slang/gears/gears.slang b/shaders/slang/gears/gears.slang new file mode 100644 index 00000000..5c15ba63 --- /dev/null +++ b/shaders/slang/gears/gears.slang @@ -0,0 +1,58 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 EyePos; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4 lightpos; + float4x4 model[3]; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uint InstanceIndex: SV_StartInstanceLocation) +{ + VSOutput output; + output.Normal = normalize(mul((float3x3)transpose(ubo.model[InstanceIndex]), input.Normal).xyz); + output.Color = input.Color; + float4x4 modelView = mul(ubo.view, ubo.model[InstanceIndex]); + float4 pos = mul(modelView, float4(input.Pos, 1.0)); + output.EyePos = mul(modelView, pos).xyz; + float4 lightPos = mul(float4(ubo.lightpos.xyz, 1.0), modelView); + output.LightVec = normalize(lightPos.xyz - output.EyePos); + output.Pos = mul(ubo.projection, pos); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 eye = normalize(-input.EyePos); + float3 reflected = normalize(reflect(-input.LightVec, input.Normal)); + + float4 ambient = float4(0.2, 0.2, 0.2, 1.0); + float4 diffuse = float4(0.5, 0.5, 0.5, 0.5) * max(dot(input.Normal, input.LightVec), 0.0); + float4 specular = float4(0.5, 0.5, 0.5, 1.0) * pow(max(dot(reflected, eye), 0.0), 0.8) * 0.25; + + return float4((ambient + diffuse) * float4(input.Color, 1.0) + specular); +} \ No newline at end of file diff --git a/shaders/slang/gears/gears.vert.spv b/shaders/slang/gears/gears.vert.spv new file mode 100644 index 00000000..229baf7c Binary files /dev/null and b/shaders/slang/gears/gears.vert.spv differ diff --git a/shaders/slang/geometryshader/base.frag.spv b/shaders/slang/geometryshader/base.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/geometryshader/base.frag.spv differ diff --git a/shaders/slang/geometryshader/base.vert.spv b/shaders/slang/geometryshader/base.vert.spv new file mode 100644 index 00000000..9f78b1f8 Binary files /dev/null and b/shaders/slang/geometryshader/base.vert.spv differ diff --git a/shaders/slang/geometryshader/mesh.frag.spv b/shaders/slang/geometryshader/mesh.frag.spv new file mode 100644 index 00000000..6cbde14f Binary files /dev/null and b/shaders/slang/geometryshader/mesh.frag.spv differ diff --git a/shaders/slang/geometryshader/mesh.slang b/shaders/slang/geometryshader/mesh.slang new file mode 100644 index 00000000..fbe1e977 --- /dev/null +++ b/shaders/slang/geometryshader/mesh.slang @@ -0,0 +1,58 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, input.Pos)); + + float4 pos = mul(ubo.model, float4(input.Pos.xyz, 1.0)); + output.Normal = mul((float4x3)ubo.model, input.Normal).xyz; + + float3 lightPos = float3(1.0f, -1.0f, 1.0f); + output.LightVec = lightPos.xyz - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 ambient = float3(0.1, 0.1, 0.1); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4((ambient + diffuse) * input.Color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/geometryshader/mesh.vert.spv b/shaders/slang/geometryshader/mesh.vert.spv new file mode 100644 index 00000000..6b136734 Binary files /dev/null and b/shaders/slang/geometryshader/mesh.vert.spv differ diff --git a/shaders/slang/geometryshader/normaldebug.geom.spv b/shaders/slang/geometryshader/normaldebug.geom.spv new file mode 100644 index 00000000..dd1ba7a6 Binary files /dev/null and b/shaders/slang/geometryshader/normaldebug.geom.spv differ diff --git a/shaders/slang/geometryshader/normaldebug.slang b/shaders/slang/geometryshader/normaldebug.slang new file mode 100644 index 00000000..9b244d8d --- /dev/null +++ b/shaders/slang/geometryshader/normaldebug.slang @@ -0,0 +1,68 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : POSITION0; + float3 Normal; +}; + +struct GSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Pos = float4(input.Pos.xyz, 1.0); + return output; +} + +[shader("geometry")] +[maxvertexcount(6)] +void geometryMain(triangle VSOutput input[3], inout LineStream outStream) +{ + float normalLength = 0.02; + for(int i=0; i<3; i++) + { + float3 pos = input[i].Pos.xyz; + float3 normal = input[i].Normal.xyz; + + GSOutput output = (GSOutput)0; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(pos, 1.0))); + output.Color = float3(1.0, 0.0, 0.0); + outStream.Append( output ); + + output.Pos = mul(ubo.projection, mul(ubo.model, float4(pos + normal * normalLength, 1.0))); + output.Color = float3(0.0, 0.0, 1.0); + outStream.Append( output ); + + outStream.RestartStrip(); + } +} + +[shader("fragment")] +float4 fragmentMain(GSOutput input) +{ + return float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/gltfloading/mesh.frag.spv b/shaders/slang/gltfloading/mesh.frag.spv new file mode 100644 index 00000000..f19c72a0 Binary files /dev/null and b/shaders/slang/gltfloading/mesh.frag.spv differ diff --git a/shaders/slang/gltfloading/mesh.slang b/shaders/slang/gltfloading/mesh.slang new file mode 100644 index 00000000..7205d38f --- /dev/null +++ b/shaders/slang/gltfloading/mesh.slang @@ -0,0 +1,64 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4 lightPos; + float4 viewPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 modelMat) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(modelMat, float4(input.Pos.xyz, 1.0)))); + + float4 pos = mul(ubo.view, mul(modelMat, float4(input.Pos.xyz, 1.0))); + output.Normal = mul((float3x3)ubo.view, input.Normal); + output.LightVec = ubo.lightPos.xyz - pos.xyz; + output.ViewVec = ubo.viewPos.xyz - pos.xyz; + return output; +} + +[[vk::binding(0, 1)]] Sampler2D samplerColorMap; + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4(diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/gltfloading/mesh.vert.spv b/shaders/slang/gltfloading/mesh.vert.spv new file mode 100644 index 00000000..8af66eb7 Binary files /dev/null and b/shaders/slang/gltfloading/mesh.vert.spv differ diff --git a/shaders/slang/gltfscenerendering/scene.frag.spv b/shaders/slang/gltfscenerendering/scene.frag.spv new file mode 100644 index 00000000..e36d3923 Binary files /dev/null and b/shaders/slang/gltfscenerendering/scene.frag.spv differ diff --git a/shaders/slang/gltfscenerendering/scene.slang b/shaders/slang/gltfscenerendering/scene.slang new file mode 100644 index 00000000..b9ed1049 --- /dev/null +++ b/shaders/slang/gltfscenerendering/scene.slang @@ -0,0 +1,86 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; + float4 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; + float4 Tangent; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4 lightPos; + float4 viewPos; +}; +ConstantBuffer ubo; + +[[vk::binding(0, 1)]] Sampler2D samplerColorMap; +[[vk::binding(1, 1)]] Sampler2D samplerNormalMap; + +[SpecializationConstant] const bool ALPHA_MASK = false; +[SpecializationConstant] const float ALPHA_MASK_CUTOFF = 0.0; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 modelMat) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Tangent = input.Tangent; + + float4x4 modelView = mul(ubo.view, modelMat); + + output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos.xyz, 1.0))); + + output.Normal = mul((float3x3)modelMat, input.Normal); + float4 pos = mul(modelMat, float4(input.Pos, 1.0)); + output.LightVec = ubo.lightPos.xyz - pos.xyz; + output.ViewVec = ubo.viewPos.xyz - pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); + + if (ALPHA_MASK) { + if (color.a < ALPHA_MASK_CUTOFF) { + discard; + } + } + + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent.xyz); + float3 B = cross(input.Normal, input.Tangent.xyz) * input.Tangent.w; + float3x3 TBN = float3x3(T, B, N); + N = mul(normalize(samplerNormalMap.Sample(input.UV).xyz * 2.0 - float3(1.0, 1.0, 1.0)), TBN); + + const float ambient = 0.1; + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), ambient).rrr; + float3 specular = pow(max(dot(R, V), 0.0), 32.0); + return float4(diffuse * color.rgb + specular, color.a); +} \ No newline at end of file diff --git a/shaders/slang/gltfscenerendering/scene.vert.spv b/shaders/slang/gltfscenerendering/scene.vert.spv new file mode 100644 index 00000000..ecfc1b21 Binary files /dev/null and b/shaders/slang/gltfscenerendering/scene.vert.spv differ diff --git a/shaders/slang/gltfskinning/skinnedmodel.frag.spv b/shaders/slang/gltfskinning/skinnedmodel.frag.spv new file mode 100644 index 00000000..9dc302a9 Binary files /dev/null and b/shaders/slang/gltfskinning/skinnedmodel.frag.spv differ diff --git a/shaders/slang/gltfskinning/skinnedmodel.slang b/shaders/slang/gltfskinning/skinnedmodel.slang new file mode 100644 index 00000000..2d98836d --- /dev/null +++ b/shaders/slang/gltfskinning/skinnedmodel.slang @@ -0,0 +1,78 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; + float4 JointIndices; + float4 JointWeights; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4 lightPos; + float4x4 inverse; +}; +ConstantBuffer uboScene; + +[[vk::binding(0, 1)]] StructuredBuffer jointMatrices; + +[[vk::binding(0, 2)]] Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 modelMat) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + + // Calculate skinned matrix from weights and joint indices of the current vertex + float4x4 skinMat = + input.JointWeights.x * jointMatrices[int(input.JointIndices.x)] + + input.JointWeights.y * jointMatrices[int(input.JointIndices.y)] + + input.JointWeights.z * jointMatrices[int(input.JointIndices.z)] + + input.JointWeights.w * jointMatrices[int(input.JointIndices.w)]; + + output.Pos = mul(uboScene.projection, mul(uboScene.view, mul(modelMat, mul(skinMat, float4(input.Pos, 1.0))))); + output.Normal = mul((float3x3)modelMat, mul((float3x3)skinMat, input.Normal)); + + float4 pos = mul(uboScene.view, float4(input.Pos, 1.0)); + float3 lPos = mul(float3x3(uboScene.view), uboScene.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4(diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/gltfskinning/skinnedmodel.vert.spv b/shaders/slang/gltfskinning/skinnedmodel.vert.spv new file mode 100644 index 00000000..6c681788 Binary files /dev/null and b/shaders/slang/gltfskinning/skinnedmodel.vert.spv differ diff --git a/shaders/slang/graphicspipelinelibrary/shared.vert.spv b/shaders/slang/graphicspipelinelibrary/shared.vert.spv new file mode 100644 index 00000000..c5f07810 Binary files /dev/null and b/shaders/slang/graphicspipelinelibrary/shared.vert.spv differ diff --git a/shaders/slang/graphicspipelinelibrary/uber.frag.spv b/shaders/slang/graphicspipelinelibrary/uber.frag.spv new file mode 100644 index 00000000..b230418b Binary files /dev/null and b/shaders/slang/graphicspipelinelibrary/uber.frag.spv differ diff --git a/shaders/slang/graphicspipelinelibrary/uber.slang b/shaders/slang/graphicspipelinelibrary/uber.slang new file mode 100644 index 00000000..0b582f54 --- /dev/null +++ b/shaders/slang/graphicspipelinelibrary/uber.slang @@ -0,0 +1,99 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +// We use this constant to control the flow of the shader depending on the +// lighting model selected at pipeline creation time +[[SpecializationConstant]] const int LIGHTING_MODEL = 0; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lPos = mul((float3x3)ubo.model, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 outColor = float3(0.0); + + switch (LIGHTING_MODEL) { + case 0: // Phong + { + float3 ambient = input.Color * float3(0.25, 0.25, 0.25); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 32.0) * float3(0.75); + outColor = ambient + diffuse * 1.75 + specular; + break; + } + case 1: // Toon + { + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float intensity = dot(N, L); + float3 color; + if (intensity > 0.98) + color = input.Color * 1.5; + else if (intensity > 0.9) + color = input.Color * 1.0; + else if (intensity > 0.5) + color = input.Color * 0.6; + else if (intensity > 0.25) + color = input.Color * 0.4; + else + color = input.Color * 0.2; + outColor = color; + break; + } + case 2: // No shading + { + outColor = input.Color; + break; + } + case 3: // Greyscale + outColor = dot(input.Color, float3(0.299, 0.587, 0.114)); + break; + } + + // The scene itself is a bit dark, so brigthen it up a bit + return float4(outColor * 1.25, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/hdr/bloom.frag.spv b/shaders/slang/hdr/bloom.frag.spv new file mode 100644 index 00000000..d94bf4b8 Binary files /dev/null and b/shaders/slang/hdr/bloom.frag.spv differ diff --git a/shaders/slang/hdr/bloom.slang b/shaders/slang/hdr/bloom.slang new file mode 100644 index 00000000..41a1ea6e --- /dev/null +++ b/shaders/slang/hdr/bloom.slang @@ -0,0 +1,80 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +Sampler2D samplerColor0; +Sampler2D samplerColor1; + +[[SpecializationConstant]] const int dir = 0; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // From the OpenGL Super bible + const float weights[] = { 0.0024499299678342, + 0.0043538453346397, + 0.0073599963704157, + 0.0118349786570722, + 0.0181026699707781, + 0.0263392293891488, + 0.0364543006660986, + 0.0479932050577658, + 0.0601029809166942, + 0.0715974486241365, + 0.0811305381519717, + 0.0874493212267511, + 0.0896631113333857, + 0.0874493212267511, + 0.0811305381519717, + 0.0715974486241365, + 0.0601029809166942, + 0.0479932050577658, + 0.0364543006660986, + 0.0263392293891488, + 0.0181026699707781, + 0.0118349786570722, + 0.0073599963704157, + 0.0043538453346397, + 0.0024499299678342}; + + + const float blurScale = 0.003; + const float blurStrength = 1.0; + + float ar = 1.0; + // Aspect ratio for vertical blur pass + if (dir == 1) + { + float2 ts; + samplerColor1.GetDimensions(ts.x, ts.y); + ar = ts.y / ts.x; + } + + float2 P = input.UV.yx - float2(0, (25 >> 1) * ar * blurScale); + + float4 color = float4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < 25; i++) + { + float2 dv = float2(0.0, i * blurScale) * ar; + color += samplerColor1.Sample(P + dv) * weights[i] * blurStrength; + } + + return color; +} \ No newline at end of file diff --git a/shaders/slang/hdr/bloom.vert.spv b/shaders/slang/hdr/bloom.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/hdr/bloom.vert.spv differ diff --git a/shaders/slang/hdr/composition.frag.spv b/shaders/slang/hdr/composition.frag.spv new file mode 100644 index 00000000..a96badf3 Binary files /dev/null and b/shaders/slang/hdr/composition.frag.spv differ diff --git a/shaders/slang/hdr/composition.slang b/shaders/slang/hdr/composition.slang new file mode 100644 index 00000000..d9705f60 --- /dev/null +++ b/shaders/slang/hdr/composition.slang @@ -0,0 +1,28 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColor.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/hdr/composition.vert.spv b/shaders/slang/hdr/composition.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/hdr/composition.vert.spv differ diff --git a/shaders/slang/hdr/gbuffer.frag.spv b/shaders/slang/hdr/gbuffer.frag.spv new file mode 100644 index 00000000..b42c3364 Binary files /dev/null and b/shaders/slang/hdr/gbuffer.frag.spv differ diff --git a/shaders/slang/hdr/gbuffer.slang b/shaders/slang/hdr/gbuffer.slang new file mode 100644 index 00000000..ed0017a6 --- /dev/null +++ b/shaders/slang/hdr/gbuffer.slang @@ -0,0 +1,135 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; + float3 WorldPos; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct FSOutput +{ + float4 Color0 : SV_TARGET0; + float4 Color1 : SV_TARGET1; +}; + +struct UBO { + float4x4 projection; + float4x4 modelview; + float4x4 inverseModelview; + float exposure; +}; +ConstantBuffer ubo; + +SamplerCube samplerEnvMap; + +[[SpecializationConstant]] const int objectType = 0; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UVW = input.Pos; + + switch (objectType) { + case 0: // Skybox + output.WorldPos = mul((float4x3)ubo.modelview, input.Pos).xyz; + output.Pos = mul(ubo.projection, float4(output.WorldPos, 1.0)); + break; + case 1: // Object + output.WorldPos = mul(ubo.modelview, float4(input.Pos, 1.0)).xyz; + output.Pos = mul(ubo.projection, mul(ubo.modelview, float4(input.Pos.xyz, 1.0))); + break; + } + output.WorldPos = mul(ubo.modelview, float4(input.Pos, 1.0)).xyz; + output.Normal = mul((float4x3)ubo.modelview, input.Normal).xyz; + float3 lightPos = float3(0.0f, -5.0f, 5.0f); + output.LightVec = lightPos.xyz - output.WorldPos.xyz; + output.ViewVec = -output.WorldPos.xyz; + return output; +} + +[shader("fragment")] +FSOutput fragmentMain(VSOutput input) +{ + FSOutput output; + float4 color; + float3 wcNormal; + + switch (objectType) { + case 0: // Skybox + { + float3 normal = normalize(input.UVW); + color = samplerEnvMap.Sample(normal); + } + break; + + case 1: // Reflect + { + float3 wViewVec = mul((float4x3)ubo.inverseModelview, normalize(input.ViewVec)).xyz; + float3 normal = normalize(input.Normal); + float3 wNormal = mul((float4x3)ubo.inverseModelview, normal).xyz; + + float NdotL = max(dot(normal, input.LightVec), 0.0); + + float3 eyeDir = normalize(input.ViewVec); + float3 halfVec = normalize(input.LightVec + eyeDir); + float NdotH = max(dot(normal, halfVec), 0.0); + float NdotV = max(dot(normal, eyeDir), 0.0); + float VdotH = max(dot(eyeDir, halfVec), 0.0); + + // Geometric attenuation + float NH2 = 2.0 * NdotH; + float g1 = (NH2 * NdotV) / VdotH; + float g2 = (NH2 * NdotL) / VdotH; + float geoAtt = min(1.0, min(g1, g2)); + + const float F0 = 0.6; + const float k = 0.2; + + // Fresnel (schlick approximation) + float fresnel = pow(1.0 - VdotH, 5.0); + fresnel *= (1.0 - F0); + fresnel += F0; + + float spec = (fresnel * geoAtt) / (NdotV * NdotL * float.getPi()); + + color = samplerEnvMap.Sample(reflect(-wViewVec, wNormal)); + + color = float4(color.rgb * NdotL * (k + spec * (1.0 - k)), 1.0); + } + break; + + case 2: // Refract + { + float3 wViewVec = mul((float4x3)ubo.inverseModelview, normalize(input.ViewVec)).xyz; + float3 wNormal = mul((float4x3)ubo.inverseModelview, input.Normal).xyz; + color = samplerEnvMap.Sample(refract(-wViewVec, wNormal, 1.0/1.6)); + } + break; + } + + + // Color with manual exposure into attachment 0 + output.Color0.rgb = float3(1.0, 1.0, 1.0) - exp(-color.rgb * ubo.exposure); + + // Bright parts for bloom into attachment 1 + float l = dot(output.Color0.rgb, float3(0.2126, 0.7152, 0.0722)); + float threshold = 0.75; + output.Color1.rgb = (l > threshold) ? output.Color0.rgb : float3(0.0, 0.0, 0.0); + output.Color1.a = 1.0; + return output; +} \ No newline at end of file diff --git a/shaders/slang/hdr/gbuffer.vert.spv b/shaders/slang/hdr/gbuffer.vert.spv new file mode 100644 index 00000000..679f21c1 Binary files /dev/null and b/shaders/slang/hdr/gbuffer.vert.spv differ diff --git a/shaders/slang/imgui/scene.frag.spv b/shaders/slang/imgui/scene.frag.spv new file mode 100644 index 00000000..4704b77e Binary files /dev/null and b/shaders/slang/imgui/scene.frag.spv differ diff --git a/shaders/slang/imgui/scene.slang b/shaders/slang/imgui/scene.slang new file mode 100644 index 00000000..5757b95c --- /dev/null +++ b/shaders/slang/imgui/scene.slang @@ -0,0 +1,57 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float4x3)ubo.model, input.Normal).xyz; + float3 lPos = mul((float4x3)ubo.model, ubo.lightPos.xyz).xyz; + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float diffuse = max(dot(N, L), 0.0); + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4(diffuse * input.Color + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/imgui/scene.vert.spv b/shaders/slang/imgui/scene.vert.spv new file mode 100644 index 00000000..59f4ae0d Binary files /dev/null and b/shaders/slang/imgui/scene.vert.spv differ diff --git a/shaders/slang/imgui/ui.frag.spv b/shaders/slang/imgui/ui.frag.spv new file mode 100644 index 00000000..f0054856 Binary files /dev/null and b/shaders/slang/imgui/ui.frag.spv differ diff --git a/shaders/slang/imgui/ui.slang b/shaders/slang/imgui/ui.slang new file mode 100644 index 00000000..603799e4 --- /dev/null +++ b/shaders/slang/imgui/ui.slang @@ -0,0 +1,37 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float2 Pos; + float2 UV; + float4 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float4 Color; +}; + +Sampler2D fontSampler; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float2 scale, uniform float2 translate) +{ + VSOutput output; + output.UV = input.UV; + output.Color = input.Color; + output.Pos = float4(input.Pos * scale + translate, 0.0, 1.0); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return input.Color * fontSampler.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/imgui/ui.vert.spv b/shaders/slang/imgui/ui.vert.spv new file mode 100644 index 00000000..d93356a3 Binary files /dev/null and b/shaders/slang/imgui/ui.vert.spv differ diff --git a/shaders/slang/indirectdraw/ground.frag.spv b/shaders/slang/indirectdraw/ground.frag.spv new file mode 100644 index 00000000..e7f31fa7 Binary files /dev/null and b/shaders/slang/indirectdraw/ground.frag.spv differ diff --git a/shaders/slang/indirectdraw/ground.slang b/shaders/slang/indirectdraw/ground.slang new file mode 100644 index 00000000..ac8624ac --- /dev/null +++ b/shaders/slang/indirectdraw/ground.slang @@ -0,0 +1,44 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; +}; +ConstantBuffer ubo; + +[[vk::binding(2,0)]] Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV * 32.0; + output.Pos = mul(ubo.projection, mul(ubo.modelview, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColor.Sample(input.UV); + return float4(color.rgb, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/indirectdraw/ground.vert.spv b/shaders/slang/indirectdraw/ground.vert.spv new file mode 100644 index 00000000..5c18295e Binary files /dev/null and b/shaders/slang/indirectdraw/ground.vert.spv differ diff --git a/shaders/slang/indirectdraw/indirectdraw.frag.spv b/shaders/slang/indirectdraw/indirectdraw.frag.spv new file mode 100644 index 00000000..48fdacc8 Binary files /dev/null and b/shaders/slang/indirectdraw/indirectdraw.frag.spv differ diff --git a/shaders/slang/indirectdraw/indirectdraw.slang b/shaders/slang/indirectdraw/indirectdraw.slang new file mode 100644 index 00000000..8c35d5e4 --- /dev/null +++ b/shaders/slang/indirectdraw/indirectdraw.slang @@ -0,0 +1,104 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Normal; + float2 UV; + float3 Color; + float3 instancePos; + float3 instanceRot; + float instanceScale; + int instanceTexIndex; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; +}; +ConstantBuffer ubo; + +Sampler2DArray samplerArray; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.UV = float3(input.UV, input.instanceTexIndex); + + float4x4 mx, my, mz; + + // rotate around x + float s = sin(input.instanceRot.x); + float c = cos(input.instanceRot.x); + + mx[0] = float4(c, s, 0.0, 0.0); + mx[1] = float4(-s, c, 0.0, 0.0); + mx[2] = float4(0.0, 0.0, 1.0, 0.0); + mx[3] = float4(0.0, 0.0, 0.0, 1.0); + + // rotate around y + s = sin(input.instanceRot.y); + c = cos(input.instanceRot.y); + + my[0] = float4(c, 0.0, s, 0.0); + my[1] = float4(0.0, 1.0, 0.0, 0.0); + my[2] = float4(-s, 0.0, c, 0.0); + my[3] = float4(0.0, 0.0, 0.0, 1.0); + + // rot around z + s = sin(input.instanceRot.z); + c = cos(input.instanceRot.z); + + mz[0] = float4(1.0, 0.0, 0.0, 0.0); + mz[1] = float4(0.0, c, s, 0.0); + mz[2] = float4(0.0, -s, c, 0.0); + mz[3] = float4(0.0, 0.0, 0.0, 1.0); + + float4x4 rotMat = mul(mz, mul(my, mx)); + + output.Normal = mul((float4x3)rotMat, input.Normal).xyz; + + float4 pos = mul(rotMat, float4((input.Pos.xyz * input.instanceScale) + input.instancePos, 1.0)); + + output.Pos = mul(ubo.projection, mul(ubo.modelview, pos)); + + float4 wPos = mul(ubo.modelview, float4(pos.xyz, 1.0)); + float4 lPos = float4(0.0, -5.0, 0.0, 1.0); + output.LightVec = lPos.xyz - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerArray.Sample(input.UV); + + if (color.a < 0.5) + { + clip(-1); + } + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 ambient = float3(0.65, 0.65, 0.65); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + return float4((ambient + diffuse) * color.rgb, 1.0); +} diff --git a/shaders/slang/indirectdraw/indirectdraw.vert.spv b/shaders/slang/indirectdraw/indirectdraw.vert.spv new file mode 100644 index 00000000..728a257e Binary files /dev/null and b/shaders/slang/indirectdraw/indirectdraw.vert.spv differ diff --git a/shaders/slang/indirectdraw/skysphere.frag.spv b/shaders/slang/indirectdraw/skysphere.frag.spv new file mode 100644 index 00000000..626d22b4 Binary files /dev/null and b/shaders/slang/indirectdraw/skysphere.frag.spv differ diff --git a/shaders/slang/indirectdraw/skysphere.slang b/shaders/slang/indirectdraw/skysphere.slang new file mode 100644 index 00000000..6bf4679e --- /dev/null +++ b/shaders/slang/indirectdraw/skysphere.slang @@ -0,0 +1,42 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + // Skysphere always at center, only use rotation part of modelview matrix + output.Pos = mul(ubo.projection, float4(mul((float3x3)ubo.modelview, input.Pos.xyz), 1)); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + const float4 gradientStart = float4(0.93, 0.9, 0.81, 1.0); + const float4 gradientEnd = float4(0.35, 0.5, 1.0, 1.0); + return lerp(gradientStart, gradientEnd, min(0.5 - (input.UV.y + 0.05), 0.5)/0.15 - 0.5); +} \ No newline at end of file diff --git a/shaders/slang/indirectdraw/skysphere.vert.spv b/shaders/slang/indirectdraw/skysphere.vert.spv new file mode 100644 index 00000000..899b8f3b Binary files /dev/null and b/shaders/slang/indirectdraw/skysphere.vert.spv differ diff --git a/shaders/slang/inlineuniformblocks/pbr.frag.spv b/shaders/slang/inlineuniformblocks/pbr.frag.spv new file mode 100644 index 00000000..d5587b12 Binary files /dev/null and b/shaders/slang/inlineuniformblocks/pbr.frag.spv differ diff --git a/shaders/slang/inlineuniformblocks/pbr.slang b/shaders/slang/inlineuniformblocks/pbr.slang new file mode 100644 index 00000000..825d4902 --- /dev/null +++ b/shaders/slang/inlineuniformblocks/pbr.slang @@ -0,0 +1,137 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 WorldPos; + float3 Normal; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; + float3 camPos; +}; +ConstantBuffer ubo; + +// Inline uniform block +struct UniformInline { + float roughness; + float metallic; + float r; + float g; + float b; + float ambient; +}; +[[vk::binding(0,1)]] ConstantBuffer material; + +#define PI 3.14159265359 +#define MATERIALCOLOR float3(material.r, material.g, material.b) + +// Normal Distribution function -------------------------------------- +float D_GGX(float dotNH, float roughness) +{ + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; + return (alpha2)/(PI * denom*denom); +} + +// Geometric Shadowing function -------------------------------------- +float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; +} + +// Fresnel function ---------------------------------------------------- +float3 F_Schlick(float cosTheta, float metallic) +{ + float3 F0 = lerp(float3(0.04, 0.04, 0.04), MATERIALCOLOR, metallic); // * material.specular + float3 F = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); + return F; +} + +// Specular BRDF composition -------------------------------------------- + +float3 BRDF(float3 L, float3 V, float3 N, float metallic, float roughness) +{ + // Precalculate vectors and dot products + float3 H = normalize (V + L); + float dotNV = clamp(dot(N, V), 0.0, 1.0); + float dotNL = clamp(dot(N, L), 0.0, 1.0); + float dotLH = clamp(dot(L, H), 0.0, 1.0); + float dotNH = clamp(dot(N, H), 0.0, 1.0); + + // Light color fixed + float3 lightColor = float3(1.0, 1.0, 1.0); + + float3 color = float3(0.0, 0.0, 0.0); + + if (dotNL > 0.0) + { + float rroughness = max(0.05, roughness); + // D = Normal distribution (Distribution of the microfacets) + float D = D_GGX(dotNH, rroughness); + // G = Geometric shadowing term (Microfacets shadowing) + float G = G_SchlicksmithGGX(dotNL, dotNV, rroughness); + // F = Fresnel factor (Reflectance depending on angle of incidence) + float3 F = F_Schlick(dotNV, metallic); + + float3 spec = D * F * G / (4.0 * dotNL * dotNV); + + color += spec * dotNL * lightColor; + } + + return color; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float3 objPos) +{ + VSOutput output; + float3 locPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.WorldPos = locPos + objPos; + output.Normal = mul((float4x3)ubo.model, input.Normal).xyz; + output.Pos = mul(ubo.projection, mul(ubo.view, float4(output.WorldPos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 V = normalize(ubo.camPos - input.WorldPos); + + float roughness = material.roughness; + + // Specular contribution + float3 lightPos = float3(0.0f, 0.0f, 10.0f); + float3 Lo = float3(0.0, 0.0, 0.0); + float3 L = normalize(lightPos.xyz - input.WorldPos); + Lo += BRDF(L, V, N, material.metallic, roughness); + + // Combine with ambient + float3 color = MATERIALCOLOR * material.ambient; + color += Lo; + + // Gamma correct + color = pow(color, float3(0.4545, 0.4545, 0.4545)); + + return float4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/inlineuniformblocks/pbr.vert.spv b/shaders/slang/inlineuniformblocks/pbr.vert.spv new file mode 100644 index 00000000..a5d0dc60 Binary files /dev/null and b/shaders/slang/inlineuniformblocks/pbr.vert.spv differ diff --git a/shaders/slang/inputattachments/attachmentread.frag.spv b/shaders/slang/inputattachments/attachmentread.frag.spv new file mode 100644 index 00000000..758bc4a3 Binary files /dev/null and b/shaders/slang/inputattachments/attachmentread.frag.spv differ diff --git a/shaders/slang/inputattachments/attachmentread.slang b/shaders/slang/inputattachments/attachmentread.slang new file mode 100644 index 00000000..3612f1ca --- /dev/null +++ b/shaders/slang/inputattachments/attachmentread.slang @@ -0,0 +1,52 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; +}; + +[[vk::input_attachment_index(0)]] [[vk::binding(0)]] SubpassInput inputColor; +[[vk::input_attachment_index(1)]] [[vk::binding(1)]] SubpassInput inputDepth; + +struct UBO { + float2 brightnessContrast; + float2 range; + int attachmentIndex; +}; +ConstantBuffer ubo; + +float3 brightnessContrast(float3 color, float brightness, float contrast) { + return (color - 0.5) * contrast + 0.5 + brightness; +} + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.Pos = float4(float2((VertexIndex << 1) & 2, VertexIndex & 2) * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain() +{ + // Apply brightness and contrast filer to color input + if (ubo.attachmentIndex == 0) { + // Read color from previous color input attachment + float3 color = inputColor.SubpassLoad().rgb; + return float4(brightnessContrast(color, ubo.brightnessContrast[0], ubo.brightnessContrast[1]), 1.0); + } + + // Visualize depth input range + if (ubo.attachmentIndex == 1) { + // Read depth from previous depth input attachment + float depth = inputDepth.SubpassLoad().r; + return float4((depth - ubo.range[0]) * 1.0 / (ubo.range[1] - ubo.range[0]).xxx, 1.0); + } + + return float4(1.0); +} \ No newline at end of file diff --git a/shaders/slang/inputattachments/attachmentread.vert.spv b/shaders/slang/inputattachments/attachmentread.vert.spv new file mode 100644 index 00000000..36b0be4d Binary files /dev/null and b/shaders/slang/inputattachments/attachmentread.vert.spv differ diff --git a/shaders/slang/inputattachments/attachmentwrite.frag.spv b/shaders/slang/inputattachments/attachmentwrite.frag.spv new file mode 100644 index 00000000..14f4a4f6 Binary files /dev/null and b/shaders/slang/inputattachments/attachmentwrite.frag.spv differ diff --git a/shaders/slang/inputattachments/attachmentwrite.slang b/shaders/slang/inputattachments/attachmentwrite.slang new file mode 100644 index 00000000..e27e4e0e --- /dev/null +++ b/shaders/slang/inputattachments/attachmentwrite.slang @@ -0,0 +1,56 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO { + float4x4 projection; + float4x4 model; + float4x4 view; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos, 1.0)))); + output.Color = input.Color; + output.Normal = input.Normal; + output.LightVec = float3(0.0f, 5.0f, 15.0f) - input.Pos; + output.ViewVec = -input.Pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Toon shading color attachment output + float intensity = dot(normalize(input.Normal), normalize(input.LightVec)); + float shade = 1.0; + shade = intensity < 0.5 ? 0.75 : shade; + shade = intensity < 0.35 ? 0.6 : shade; + shade = intensity < 0.25 ? 0.5 : shade; + shade = intensity < 0.1 ? 0.25 : shade; + + return float4(input.Color * 3.0 * shade, 1.0); + + // Depth attachment does not need to be explicitly written +} \ No newline at end of file diff --git a/shaders/slang/inputattachments/attachmentwrite.vert.spv b/shaders/slang/inputattachments/attachmentwrite.vert.spv new file mode 100644 index 00000000..804cdd5c Binary files /dev/null and b/shaders/slang/inputattachments/attachmentwrite.vert.spv differ diff --git a/shaders/slang/instancing/instancing.frag.spv b/shaders/slang/instancing/instancing.frag.spv new file mode 100644 index 00000000..cc5a2e2c Binary files /dev/null and b/shaders/slang/instancing/instancing.frag.spv differ diff --git a/shaders/slang/instancing/instancing.slang b/shaders/slang/instancing/instancing.slang new file mode 100644 index 00000000..8541d187 --- /dev/null +++ b/shaders/slang/instancing/instancing.slang @@ -0,0 +1,106 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos : POSITION0; + float3 Normal : NORMAL0; + float2 UV : TEXCOORD0; + float3 Color : COLOR0; + // Instanced attributes + float3 instancePos : POSITION1; + float3 instanceRot : TEXCOORD1; + float instanceScale : TEXCOORD2; + int instanceTexIndex : TEXCOORD3; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float4 lightPos; + float locSpeed; + float globSpeed; +}; +ConstantBuffer ubo; +Sampler2DArray samplerArray; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.UV = float3(input.UV, input.instanceTexIndex); + + // rotate around x + float s = sin(input.instanceRot.x + ubo.locSpeed); + float c = cos(input.instanceRot.x + ubo.locSpeed); + + float3x3 mx = { c, -s, 0.0, + s, c, 0.0, + 0.0, 0.0, 1.0 }; + + // rotate around y + s = sin(input.instanceRot.y + ubo.locSpeed); + c = cos(input.instanceRot.y + ubo.locSpeed); + + float3x3 my = { c, 0.0, -s, + 0.0, 1.0, 0.0, + s, 0.0, c }; + + // rot around z + s = sin(input.instanceRot.z + ubo.locSpeed); + c = cos(input.instanceRot.z + ubo.locSpeed); + + float3x3 mz = { 1.0, 0.0, 0.0, + 0.0, c, -s, + 0.0, s, c }; + + float3x3 rotMat = mul(mz, mul(my, mx)); + + float4x4 gRotMat; + s = sin(input.instanceRot.y + ubo.globSpeed); + c = cos(input.instanceRot.y + ubo.globSpeed); + gRotMat[0] = float4(c, 0.0, -s, 0.0); + gRotMat[1] = float4(0.0, 1.0, 0.0, 0.0); + gRotMat[2] = float4(s, 0.0, c, 0.0); + gRotMat[3] = float4(0.0, 0.0, 0.0, 1.0); + + float4 locPos = float4(mul(rotMat, input.Pos.xyz), 1.0); + float4 pos = float4((locPos.xyz * input.instanceScale) + input.instancePos, 1.0); + + output.Pos = mul(ubo.projection, mul(ubo.modelview, mul(gRotMat, pos))); + output.Normal = mul((float3x3)mul(ubo.modelview, gRotMat), mul(rotMat, input.Normal)); + + pos = mul(ubo.modelview, float4(input.Pos.xyz + input.instancePos, 1.0)); + float3 lPos = mul((float3x3)ubo.modelview, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerArray.Sample(input.UV) * float4(input.Color, 1.0); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.1) * input.Color; + float3 specular = (dot(N,L) > 0.0) ? pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75) * color.r : float3(0.0, 0.0, 0.0); + return float4(diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/instancing/instancing.vert.spv b/shaders/slang/instancing/instancing.vert.spv new file mode 100644 index 00000000..a4f2b676 Binary files /dev/null and b/shaders/slang/instancing/instancing.vert.spv differ diff --git a/shaders/slang/instancing/planet.frag.spv b/shaders/slang/instancing/planet.frag.spv new file mode 100644 index 00000000..d0d62b0e Binary files /dev/null and b/shaders/slang/instancing/planet.frag.spv differ diff --git a/shaders/slang/instancing/planet.slang b/shaders/slang/instancing/planet.slang new file mode 100644 index 00000000..ca8bb346 --- /dev/null +++ b/shaders/slang/instancing/planet.slang @@ -0,0 +1,62 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float4 lightPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.modelview, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.modelview, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.modelview, input.Normal); + float3 lPos = mul((float3x3)ubo.modelview, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0) * 1.5; + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 4.0) * float3(0.5, 0.5, 0.5) * color.r; + return float4(diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/instancing/planet.vert.spv b/shaders/slang/instancing/planet.vert.spv new file mode 100644 index 00000000..cb3103ba Binary files /dev/null and b/shaders/slang/instancing/planet.vert.spv differ diff --git a/shaders/slang/instancing/starfield.frag.spv b/shaders/slang/instancing/starfield.frag.spv new file mode 100644 index 00000000..846472b6 Binary files /dev/null and b/shaders/slang/instancing/starfield.frag.spv differ diff --git a/shaders/slang/instancing/starfield.slang b/shaders/slang/instancing/starfield.slang new file mode 100644 index 00000000..2a5fbab5 --- /dev/null +++ b/shaders/slang/instancing/starfield.slang @@ -0,0 +1,50 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#define HASHSCALE3 float3(443.897, 441.423, 437.195) +#define STARFREQUENCY 0.01 + +// Hash function by Dave Hoskins (https://www.shadertoy.com/view/4djSRW) +float hash33(float3 p3) +{ + p3 = frac(p3 * HASHSCALE3); + p3 += dot(p3, p3.yxz+float3(19.19, 19.19, 19.19)); + return frac((p3.x + p3.y)*p3.z + (p3.x+p3.z)*p3.y + (p3.y+p3.z)*p3.x); +} + +float3 starField(float3 pos) +{ + float3 color = float3(0.0, 0.0, 0.0); + float threshhold = (1.0 - STARFREQUENCY); + float rnd = hash33(pos); + if (rnd >= threshhold) + { + float starCol = pow((rnd - threshhold) / (1.0 - threshhold), 16.0); + color += starCol.xxx; + } + return color; +} + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UVW = float3((VertexIndex << 1) & 2, VertexIndex & 2, VertexIndex & 2); + output.Pos = float4(output.UVW.xy * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(starField(input.UVW), 1.0); +} \ No newline at end of file diff --git a/shaders/slang/instancing/starfield.vert.spv b/shaders/slang/instancing/starfield.vert.spv new file mode 100644 index 00000000..f60862cd Binary files /dev/null and b/shaders/slang/instancing/starfield.vert.spv differ diff --git a/shaders/slang/meshshader/meshshader.frag.spv b/shaders/slang/meshshader/meshshader.frag.spv new file mode 100644 index 00000000..3d2c3eee Binary files /dev/null and b/shaders/slang/meshshader/meshshader.frag.spv differ diff --git a/shaders/slang/meshshader/meshshader.mesh.spv b/shaders/slang/meshshader/meshshader.mesh.spv new file mode 100644 index 00000000..582ac173 Binary files /dev/null and b/shaders/slang/meshshader/meshshader.mesh.spv differ diff --git a/shaders/slang/meshshader/meshshader.slang b/shaders/slang/meshshader/meshshader.slang new file mode 100644 index 00000000..652b3411 --- /dev/null +++ b/shaders/slang/meshshader/meshshader.slang @@ -0,0 +1,71 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VertexOutput +{ + float4 position : SV_Position; + float4 color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; +}; +ConstantBuffer ubo; + +static const float4 positions[3] = { + float4( 0.0, -1.0, 0.0, 1.0), + float4(-1.0, 1.0, 0.0, 1.0), + float4( 1.0, 1.0, 0.0, 1.0) +}; + +static const float4 colors[3] = { + float4(0.0, 1.0, 0.0, 1.0), + float4(0.0, 0.0, 1.0, 1.0), + float4(1.0, 0.0, 0.0, 1.0) +}; + +struct DummyPayLoad +{ + uint dummyData; +}; + +// We don't use pay loads in this sample, but the fn call requires one +groupshared DummyPayLoad dummyPayLoad; + +[shader("amplification")] +[numthreads(1, 1, 1)] +void amplificationMain() +{ + DispatchMesh(3, 1, 1, dummyPayLoad); +} + +[shader("mesh")] +[outputtopology("triangle")] +[numthreads(1, 1, 1)] +void meshMain(out indices uint3 triangles[1], out vertices VertexOutput vertices[3], uint3 DispatchThreadID : SV_DispatchThreadID) +{ + float4x4 mvp = mul(ubo.projection, mul(ubo.view, ubo.model)); + + float4 offset = float4(0.0, 0.0, float(DispatchThreadID.x), 0.0); + + SetMeshOutputCounts(3, 1); + for (uint i = 0; i < 3; i++) { + vertices[i].position = mul(mvp, positions[i] + offset); + vertices[i].color = colors[i]; + } + + SetMeshOutputCounts(3, 1); + triangles[0] = uint3(0, 1, 2); +} + +[shader("fragment")] +float4 fragmentMain(VertexOutput input) +{ + return input.color; +} diff --git a/shaders/slang/meshshader/meshshader.task.spv b/shaders/slang/meshshader/meshshader.task.spv new file mode 100644 index 00000000..735495e2 Binary files /dev/null and b/shaders/slang/meshshader/meshshader.task.spv differ diff --git a/shaders/slang/multisampling/mesh.frag.spv b/shaders/slang/multisampling/mesh.frag.spv new file mode 100644 index 00000000..3f797682 Binary files /dev/null and b/shaders/slang/multisampling/mesh.frag.spv differ diff --git a/shaders/slang/multisampling/mesh.slang b/shaders/slang/multisampling/mesh.slang new file mode 100644 index 00000000..840c1c24 --- /dev/null +++ b/shaders/slang/multisampling/mesh.slang @@ -0,0 +1,63 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +[[vk::binding(0,1)]] Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 0.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lPos = mul((float3x3)ubo.model, ubo.lightPos.xyz); + output.LightVec = lPos - input.Pos; + output.ViewVec = -input.Pos; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.15) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4(diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/multisampling/mesh.vert.spv b/shaders/slang/multisampling/mesh.vert.spv new file mode 100644 index 00000000..b8c7e122 Binary files /dev/null and b/shaders/slang/multisampling/mesh.vert.spv differ diff --git a/shaders/slang/multithreading/phong.frag.spv b/shaders/slang/multithreading/phong.frag.spv new file mode 100644 index 00000000..eefb9ff7 Binary files /dev/null and b/shaders/slang/multithreading/phong.frag.spv differ diff --git a/shaders/slang/multithreading/phong.slang b/shaders/slang/multithreading/phong.slang new file mode 100644 index 00000000..ad82f3af --- /dev/null +++ b/shaders/slang/multithreading/phong.slang @@ -0,0 +1,54 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 mvp, uniform float3 color) +{ + VSOutput output; + if ((input.Color.r == 1.0) && (input.Color.g == 0.0) && (input.Color.b == 0.0)) + { + output.Color = color; + } + else + { + output.Color = input.Color; + } + output.Pos = mul(mvp, float4(input.Pos.xyz, 1.0)); + float4 pos = mul(mvp, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)mvp, input.Normal); + float3 lPos = float3(0.0, 0.0, 0.0); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 8.0) * float3(0.75, 0.75, 0.75); + return float4(diffuse + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/multithreading/phong.vert.spv b/shaders/slang/multithreading/phong.vert.spv new file mode 100644 index 00000000..45299783 Binary files /dev/null and b/shaders/slang/multithreading/phong.vert.spv differ diff --git a/shaders/slang/multithreading/starsphere.frag.spv b/shaders/slang/multithreading/starsphere.frag.spv new file mode 100644 index 00000000..332a5b65 Binary files /dev/null and b/shaders/slang/multithreading/starsphere.frag.spv differ diff --git a/shaders/slang/multithreading/starsphere.slang b/shaders/slang/multithreading/starsphere.slang new file mode 100644 index 00000000..2d6c492f --- /dev/null +++ b/shaders/slang/multithreading/starsphere.slang @@ -0,0 +1,58 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#define HASHSCALE3 float3(443.897, 441.423, 437.195) +#define STARFREQUENCY 0.01 + +// Hash function by Dave Hoskins (https://www.shadertoy.com/view/4djSRW) +float hash33(float3 p3) +{ + p3 = frac(p3 * HASHSCALE3); + p3 += dot(p3, p3.yxz+float3(19.19, 19.19, 19.19)); + return frac((p3.x + p3.y)*p3.z + (p3.x+p3.z)*p3.y + (p3.y+p3.z)*p3.x); +} + +float3 starField(float3 pos) +{ + float3 color = float3(0.0, 0.0, 0.0); + float threshhold = (1.0 - STARFREQUENCY); + float rnd = hash33(pos); + if (rnd >= threshhold) + { + float starCol = pow((rnd - threshhold) / (1.0 - threshhold), 16.0); + color += starCol.xxx; + } + return color; +} + +struct VSInput +{ + float3 Pos; +} + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 mvp) +{ + VSOutput output; + output.UVW = input.Pos; + output.Pos = mul(mvp, float4(input.Pos.xyz, 1.0)); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Fake atmosphere at the bottom + float3 atmosphere = clamp(float3(0.1, 0.15, 0.4) * (input.UVW.y + 0.25), 0.0, 1.0); + float3 color = starField(input.UVW) + atmosphere; + return float4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/multithreading/starsphere.vert.spv b/shaders/slang/multithreading/starsphere.vert.spv new file mode 100644 index 00000000..5d02106a Binary files /dev/null and b/shaders/slang/multithreading/starsphere.vert.spv differ diff --git a/shaders/slang/multiview/multiview.frag.spv b/shaders/slang/multiview/multiview.frag.spv new file mode 100644 index 00000000..6cbde14f Binary files /dev/null and b/shaders/slang/multiview/multiview.frag.spv differ diff --git a/shaders/slang/multiview/multiview.slang b/shaders/slang/multiview/multiview.slang new file mode 100644 index 00000000..a4edbfa5 --- /dev/null +++ b/shaders/slang/multiview/multiview.slang @@ -0,0 +1,60 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection[2]; + float4x4 modelview[2]; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uint ViewIndex: SV_ViewID) +{ + VSOutput output; + output.Color = input.Color; + output.Normal = mul((float3x3)ubo.modelview[ViewIndex], input.Normal); + + float4 pos = float4(input.Pos.xyz, 1.0); + float4 worldPos = mul(ubo.modelview[ViewIndex], pos); + + float3 lPos = mul(ubo.modelview[ViewIndex], ubo.lightPos).xyz; + output.LightVec = lPos - worldPos.xyz; + output.ViewVec = -worldPos.xyz; + + output.Pos = mul(ubo.projection[ViewIndex], worldPos); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 ambient = float3(0.1, 0.1, 0.1); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4((ambient + diffuse) * input.Color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/multiview/multiview.vert.spv b/shaders/slang/multiview/multiview.vert.spv new file mode 100644 index 00000000..5fea5f90 Binary files /dev/null and b/shaders/slang/multiview/multiview.vert.spv differ diff --git a/shaders/slang/multiview/viewdisplay.frag.spv b/shaders/slang/multiview/viewdisplay.frag.spv new file mode 100644 index 00000000..59f7d45f Binary files /dev/null and b/shaders/slang/multiview/viewdisplay.frag.spv differ diff --git a/shaders/slang/multiview/viewdisplay.slang b/shaders/slang/multiview/viewdisplay.slang new file mode 100644 index 00000000..4c982738 --- /dev/null +++ b/shaders/slang/multiview/viewdisplay.slang @@ -0,0 +1,46 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 projection[2]; + float4x4 modelview[2]; + float4 lightPos; + float distortionAlpha; +}; +ConstantBuffer ubo; + +Sampler2DArray samplerView; + +[[SpecializationConstant]] const float VIEW_LAYER = 0.0f; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + const float alpha = ubo.distortionAlpha; + + float2 p1 = float2(2.0 * input.UV - 1.0); + float2 p2 = p1 / (1.0 - alpha * length(p1)); + p2 = (p2 + 1.0) * 0.5; + + bool inside = ((p2.x >= 0.0) && (p2.x <= 1.0) && (p2.y >= 0.0) && (p2.y <= 1.0)); + return inside ? samplerView.Sample(float3(p2, VIEW_LAYER)) : float4(0.0, 0.0, 0.0, 0.0); +} \ No newline at end of file diff --git a/shaders/slang/multiview/viewdisplay.vert.spv b/shaders/slang/multiview/viewdisplay.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/multiview/viewdisplay.vert.spv differ diff --git a/shaders/slang/negativeviewportheight/quad.frag.spv b/shaders/slang/negativeviewportheight/quad.frag.spv new file mode 100644 index 00000000..a96badf3 Binary files /dev/null and b/shaders/slang/negativeviewportheight/quad.frag.spv differ diff --git a/shaders/slang/negativeviewportheight/quad.slang b/shaders/slang/negativeviewportheight/quad.slang new file mode 100644 index 00000000..1ea13bb3 --- /dev/null +++ b/shaders/slang/negativeviewportheight/quad.slang @@ -0,0 +1,30 @@ +// Copyright 2020 Google LLC + +struct VSInput +{ + float3 Pos; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.Pos = float4(input.Pos, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColor.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/negativeviewportheight/quad.vert.spv b/shaders/slang/negativeviewportheight/quad.vert.spv new file mode 100644 index 00000000..74ba57ca Binary files /dev/null and b/shaders/slang/negativeviewportheight/quad.vert.spv differ diff --git a/shaders/slang/occlusionquery/mesh.frag.spv b/shaders/slang/occlusionquery/mesh.frag.spv new file mode 100644 index 00000000..e430e888 Binary files /dev/null and b/shaders/slang/occlusionquery/mesh.frag.spv differ diff --git a/shaders/slang/occlusionquery/mesh.slang b/shaders/slang/occlusionquery/mesh.slang new file mode 100644 index 00000000..50067670 --- /dev/null +++ b/shaders/slang/occlusionquery/mesh.slang @@ -0,0 +1,67 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float Visible; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 color; + float4 lightPos; + float visible; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color * ubo.color.rgb; + output.Visible = ubo.visible; + float4x4 modelView = mul(ubo.view, ubo.model); + output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos.xyz, 1.0))); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + output.LightVec = ubo.lightPos.xyz - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + if (input.Visible > 0.0) + { + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.25) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 8.0) * float3(0.75); + return float4(diffuse + specular, 1.0); + } + else + { + return float4(float3(0.1, 0.1, 0.1), 1.0); + } +} \ No newline at end of file diff --git a/shaders/slang/occlusionquery/mesh.vert.spv b/shaders/slang/occlusionquery/mesh.vert.spv new file mode 100644 index 00000000..7ace3836 Binary files /dev/null and b/shaders/slang/occlusionquery/mesh.vert.spv differ diff --git a/shaders/slang/occlusionquery/occluder.frag.spv b/shaders/slang/occlusionquery/occluder.frag.spv new file mode 100644 index 00000000..4fae9ce5 Binary files /dev/null and b/shaders/slang/occlusionquery/occluder.frag.spv differ diff --git a/shaders/slang/occlusionquery/occluder.slang b/shaders/slang/occlusionquery/occluder.slang new file mode 100644 index 00000000..7fa118d7 --- /dev/null +++ b/shaders/slang/occlusionquery/occluder.slang @@ -0,0 +1,42 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 color; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color * ubo.color.rgb; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 0.5); +} \ No newline at end of file diff --git a/shaders/slang/occlusionquery/occluder.vert.spv b/shaders/slang/occlusionquery/occluder.vert.spv new file mode 100644 index 00000000..6797ca69 Binary files /dev/null and b/shaders/slang/occlusionquery/occluder.vert.spv differ diff --git a/shaders/slang/occlusionquery/simple.frag.spv b/shaders/slang/occlusionquery/simple.frag.spv new file mode 100644 index 00000000..5cbccf9c Binary files /dev/null and b/shaders/slang/occlusionquery/simple.frag.spv differ diff --git a/shaders/slang/occlusionquery/simple.slang b/shaders/slang/occlusionquery/simple.slang new file mode 100644 index 00000000..7cfc4bfd --- /dev/null +++ b/shaders/slang/occlusionquery/simple.slang @@ -0,0 +1,39 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain() +{ + return float4(1.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/occlusionquery/simple.vert.spv b/shaders/slang/occlusionquery/simple.vert.spv new file mode 100644 index 00000000..2300613e Binary files /dev/null and b/shaders/slang/occlusionquery/simple.vert.spv differ diff --git a/shaders/slang/offscreen/mirror.frag.spv b/shaders/slang/offscreen/mirror.frag.spv new file mode 100644 index 00000000..96a3cc8a Binary files /dev/null and b/shaders/slang/offscreen/mirror.frag.spv differ diff --git a/shaders/slang/offscreen/mirror.slang b/shaders/slang/offscreen/mirror.slang new file mode 100644 index 00000000..49cf8791 --- /dev/null +++ b/shaders/slang/offscreen/mirror.slang @@ -0,0 +1,68 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float4 ProjCoord; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.ProjCoord = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + output.Pos = output.ProjCoord; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input, bool FrontFacing : SV_IsFrontFace) +{ + float4 tmp = (1.0 / input.ProjCoord.w).xxxx; + float4 projCoord = input.ProjCoord * tmp; + + // Scale and bias + projCoord += float4(1.0, 1.0, 1.0, 1.0); + projCoord *= float4(0.5, 0.5, 0.5, 0.5); + + // Slow single pass blur + // For demonstration purposes only + const float blurSize = 1.0 / 512.0; + + float4 color = float4(float3(0.0, 0.0, 0.0), 1.); + + if (FrontFacing) + { + // Only render mirrored scene on front facing (upper) side of mirror surface + float4 reflection = float4(0.0, 0.0, 0.0, 0.0); + for (int x = -3; x <= 3; x++) + { + for (int y = -3; y <= 3; y++) + { + reflection += samplerColor.Sample(float2(projCoord.x + x * blurSize, projCoord.y + y * blurSize)) / 49.0; + } + } + color += reflection; + } + + return color; +} \ No newline at end of file diff --git a/shaders/slang/offscreen/mirror.vert.spv b/shaders/slang/offscreen/mirror.vert.spv new file mode 100644 index 00000000..051ae484 Binary files /dev/null and b/shaders/slang/offscreen/mirror.vert.spv differ diff --git a/shaders/slang/offscreen/phong.frag.spv b/shaders/slang/offscreen/phong.frag.spv new file mode 100644 index 00000000..ee0318fe Binary files /dev/null and b/shaders/slang/offscreen/phong.frag.spv differ diff --git a/shaders/slang/offscreen/phong.slang b/shaders/slang/offscreen/phong.slang new file mode 100644 index 00000000..e8743485 --- /dev/null +++ b/shaders/slang/offscreen/phong.slang @@ -0,0 +1,63 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float ClipDistance : SV_ClipDistance0; + float3 Normal; + float3 Color; + float3 EyePos; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos, 1.0)))); + output.EyePos = mul(ubo.view, mul(ubo.model, float4(input.Pos, 1.0))).xyz; + output.LightVec = normalize(ubo.lightPos.xyz - output.EyePos); + // Clip against reflection plane + float4 clipPlane = float4(0.0, 0.0, 0.0, 0.0); + output.ClipDistance = dot(float4(input.Pos, 1.0), clipPlane); + return output; +} + + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 Eye = normalize(-input.EyePos); + float3 Reflected = normalize(reflect(-input.LightVec, input.Normal)); + float4 IAmbient = float4(0.1, 0.1, 0.1, 1.0); + float4 IDiffuse = max(dot(input.Normal, input.LightVec), 0.0).xxxx; + float specular = 0.75; + float4 ISpecular = float4(0.0, 0.0, 0.0, 0.0); + if (dot(input.EyePos, input.Normal) < 0.0) + { + ISpecular = float4(0.5, 0.5, 0.5, 1.0) * pow(max(dot(Reflected, Eye), 0.0), 16.0) * specular; + } + return float4((IAmbient + IDiffuse) * float4(input.Color, 1.0) + ISpecular); +} \ No newline at end of file diff --git a/shaders/slang/offscreen/phong.vert.spv b/shaders/slang/offscreen/phong.vert.spv new file mode 100644 index 00000000..5c92cb42 Binary files /dev/null and b/shaders/slang/offscreen/phong.vert.spv differ diff --git a/shaders/slang/offscreen/quad.frag.spv b/shaders/slang/offscreen/quad.frag.spv new file mode 100644 index 00000000..a08f8ff1 Binary files /dev/null and b/shaders/slang/offscreen/quad.frag.spv differ diff --git a/shaders/slang/offscreen/quad.slang b/shaders/slang/offscreen/quad.slang new file mode 100644 index 00000000..49b0ebf7 --- /dev/null +++ b/shaders/slang/offscreen/quad.slang @@ -0,0 +1,28 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[[vk::binding(1, 0)]] Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColor.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/offscreen/quad.vert.spv b/shaders/slang/offscreen/quad.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/offscreen/quad.vert.spv differ diff --git a/shaders/slang/oit/color.frag.spv b/shaders/slang/oit/color.frag.spv new file mode 100644 index 00000000..9497a329 Binary files /dev/null and b/shaders/slang/oit/color.frag.spv differ diff --git a/shaders/slang/oit/color.slang b/shaders/slang/oit/color.slang new file mode 100644 index 00000000..7cda2767 --- /dev/null +++ b/shaders/slang/oit/color.slang @@ -0,0 +1,75 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#define MAX_FRAGMENT_COUNT 128 + +struct VSOutput +{ + float4 Pos : SV_POSITION; +}; + +struct Node +{ + float4 color; + float depth; + uint next; +}; +RWTexture2D headIndexImage; + +struct Particle +{ + float2 pos; + float2 vel; + float4 gradientPos; +}; +RWStructuredBuffer nodes; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + float2 UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + Node fragments[MAX_FRAGMENT_COUNT]; + int count = 0; + + uint nodeIdx = headIndexImage[uint2(input.Pos.xy)].r; + + while (nodeIdx != 0xffffffff && count < MAX_FRAGMENT_COUNT) + { + fragments[count] = nodes[nodeIdx]; + nodeIdx = fragments[count].next; + ++count; + } + + // Do the insertion sort + for (uint i = 1; i < count; ++i) + { + Node insert = fragments[i]; + uint j = i; + while (j > 0 && insert.depth > fragments[j - 1].depth) + { + fragments[j] = fragments[j-1]; + --j; + } + fragments[j] = insert; + } + + // Do blending + float4 color = float4(0.025, 0.025, 0.025, 1.0f); + for (uint f = 0; f < count; ++f) + { + color = lerp(color, fragments[f].color, fragments[f].color.a); + } + + return color; +} \ No newline at end of file diff --git a/shaders/slang/oit/color.vert.spv b/shaders/slang/oit/color.vert.spv new file mode 100644 index 00000000..1c5bdbdc Binary files /dev/null and b/shaders/slang/oit/color.vert.spv differ diff --git a/shaders/slang/oit/geometry.frag.spv b/shaders/slang/oit/geometry.frag.spv new file mode 100644 index 00000000..4a6c62d3 Binary files /dev/null and b/shaders/slang/oit/geometry.frag.spv differ diff --git a/shaders/slang/oit/geometry.slang b/shaders/slang/oit/geometry.slang new file mode 100644 index 00000000..1a72580d --- /dev/null +++ b/shaders/slang/oit/geometry.slang @@ -0,0 +1,75 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos : POSITION0; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; +}; + +struct RenderPassUBO +{ + float4x4 projection; + float4x4 view; +}; +ConstantBuffer renderPassUBO; + +struct GeometrySBO +{ + uint count; + uint maxNodeCount; +}; +// Binding 0 : Position storage buffer +RWStructuredBuffer geometrySBO; + +struct Node +{ + float4 color; + float depth; + uint next; +}; +RWTexture2D headIndexImage; + +RWStructuredBuffer nodes; + +struct PushConsts { + float4x4 model; + float4 color; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform PushConsts pushConsts) +{ + VSOutput output; + output.Pos = mul(renderPassUBO.projection, mul(renderPassUBO.view, mul(pushConsts.model, input.Pos))); + return output; +} + +[shader("fragment")] +[earlydepthstencil] +void fragmentMain(VSOutput input, uniform PushConsts pushConsts) +{ + // Increase the node count + uint nodeIdx; + InterlockedAdd(geometrySBO[0].count, 1, nodeIdx); + + // Check LinkedListSBO is full + if (nodeIdx < geometrySBO[0].maxNodeCount) + { + // Exchange new head index and previous head index + uint prevHeadIdx; + InterlockedExchange(headIndexImage[uint2(input.Pos.xy)], nodeIdx, prevHeadIdx); + + // Store node data + nodes[nodeIdx].color = pushConsts.color; + nodes[nodeIdx].depth = input.Pos.z; + nodes[nodeIdx].next = prevHeadIdx; + } +} \ No newline at end of file diff --git a/shaders/slang/oit/geometry.vert.spv b/shaders/slang/oit/geometry.vert.spv new file mode 100644 index 00000000..7a558843 Binary files /dev/null and b/shaders/slang/oit/geometry.vert.spv differ diff --git a/shaders/slang/parallaxmapping/parallax.frag.spv b/shaders/slang/parallaxmapping/parallax.frag.spv new file mode 100644 index 00000000..45b3cf03 Binary files /dev/null and b/shaders/slang/parallaxmapping/parallax.frag.spv differ diff --git a/shaders/slang/parallaxmapping/parallax.slang b/shaders/slang/parallaxmapping/parallax.slang new file mode 100644 index 00000000..aa0c1048 --- /dev/null +++ b/shaders/slang/parallaxmapping/parallax.slang @@ -0,0 +1,150 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct UBOScene +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 lightPos; + float4 cameraPos; +}; +ConstantBuffer uboScene; + +Sampler2D samplerColorMap; +Sampler2D samplerNormalHeightMap; + +struct UBOParams +{ + float heightScale; + float parallaxBias; + float numLayers; + int mappingMode; +}; +ConstantBuffer uboParams; + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Normal; + float4 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float3 TangentLightPos; + float3 TangentViewPos; + float3 TangentFragPos; +}; + +float2 parallaxMapping(float2 uv, float3 viewDir) +{ + float height = 1.0 - samplerNormalHeightMap.SampleLevel(uv, 0.0).a; + float2 p = viewDir.xy * (height * (uboParams.heightScale * 0.5) + uboParams.parallaxBias) / viewDir.z; + return uv - p; +} + +float2 steepParallaxMapping(float2 uv, float3 viewDir) +{ + float layerDepth = 1.0 / uboParams.numLayers; + float currLayerDepth = 0.0; + float2 deltaUV = viewDir.xy * uboParams.heightScale / (viewDir.z * uboParams.numLayers); + float2 currUV = uv; + float height = 1.0 - samplerNormalHeightMap.SampleLevel(currUV, 0.0).a; + for (int i = 0; i < uboParams.numLayers; i++) { + currLayerDepth += layerDepth; + currUV -= deltaUV; + height = 1.0 - samplerNormalHeightMap.SampleLevel(currUV, 0.0).a; + if (height < currLayerDepth) { + break; + } + } + return currUV; +} + +float2 parallaxOcclusionMapping(float2 uv, float3 viewDir) +{ + float layerDepth = 1.0 / uboParams.numLayers; + float currLayerDepth = 0.0; + float2 deltaUV = viewDir.xy * uboParams.heightScale / (viewDir.z * uboParams.numLayers); + float2 currUV = uv; + float height = 1.0 - samplerNormalHeightMap.SampleLevel(currUV, 0.0).a; + for (int i = 0; i < uboParams.numLayers; i++) { + currLayerDepth += layerDepth; + currUV -= deltaUV; + height = 1.0 - samplerNormalHeightMap.SampleLevel(currUV, 0.0).a; + if (height < currLayerDepth) { + break; + } + } + float2 prevUV = currUV + deltaUV; + float nextDepth = height - currLayerDepth; + float prevDepth = 1.0 - samplerNormalHeightMap.SampleLevel(prevUV, 0.0).a - currLayerDepth + layerDepth; + return lerp(currUV, prevUV, nextDepth / (nextDepth - prevDepth)); +} + +[shader("vertex")] + +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(uboScene.projection, mul(uboScene.view, mul(uboScene.model, float4(input.Pos, 1.0f)))); + output.UV = input.UV; + + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent.xyz); + float3 B = normalize(cross(N, T)); + float3x3 TBN = float3x3(T, B, N); + + output.TangentLightPos = mul(TBN, uboScene.lightPos.xyz); + output.TangentViewPos = mul(TBN, uboScene.cameraPos.xyz); + output.TangentFragPos = mul(TBN, mul(uboScene.model, float4(input.Pos, 1.0)).xyz); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 V = normalize(input.TangentViewPos - input.TangentFragPos); + float2 uv = input.UV; + + if (uboParams.mappingMode == 0) { + // Color only + return samplerColorMap.Sample(input.UV); + } else { + switch (uboParams.mappingMode) { + case 2: + uv = parallaxMapping(input.UV, V); + break; + case 3: + uv = steepParallaxMapping(input.UV, V); + break; + case 4: + uv = parallaxOcclusionMapping(input.UV, V); + break; + } + + // Discard fragments at texture border + if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { + clip(-1); + } + + float3 N = normalize(samplerNormalHeightMap.SampleLevel(uv, 0.0).rgb * 2.0 - 1.0); + float3 L = normalize(input.TangentLightPos - input.TangentFragPos); + float3 R = reflect(-L, N); + float3 H = normalize(L + V); + + float3 color = samplerColorMap.Sample(uv).rgb; + float3 ambient = 0.2 * color; + float3 diffuse = max(dot(L, N), 0.0) * color; + float3 specular = float3(0.15, 0.15, 0.15) * pow(max(dot(N, H), 0.0), 32.0); + + return float4(ambient + diffuse + specular, 1.0f); + } +} diff --git a/shaders/slang/parallaxmapping/parallax.vert.spv b/shaders/slang/parallaxmapping/parallax.vert.spv new file mode 100644 index 00000000..3999403a Binary files /dev/null and b/shaders/slang/parallaxmapping/parallax.vert.spv differ diff --git a/shaders/slang/particlesystem/normalmap.frag.spv b/shaders/slang/particlesystem/normalmap.frag.spv new file mode 100644 index 00000000..9be89681 Binary files /dev/null and b/shaders/slang/particlesystem/normalmap.frag.spv differ diff --git a/shaders/slang/particlesystem/normalmap.slang b/shaders/slang/particlesystem/normalmap.slang new file mode 100644 index 00000000..6b471e4b --- /dev/null +++ b/shaders/slang/particlesystem/normalmap.slang @@ -0,0 +1,98 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Normal; + float4 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float3 LightVec; + float3 LightVecB; + float3 LightDir; + float3 ViewVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 normal; + float4 lightPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerColorMap; +Sampler2D samplerNormalHeightMap; + +#define lightRadius 45.0 + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + float3 vertexPosition = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.LightDir = normalize(ubo.lightPos.xyz - vertexPosition); + + float3 biTangent = cross(input.Normal, input.Tangent.xyz); + + // Setup (t)angent-(b)inormal-(n)ormal matrix for converting + // object coordinates into tangent space + float3x3 tbnMatrix; + tbnMatrix[0] = mul((float3x3)ubo.normal, input.Tangent.xyz); + tbnMatrix[1] = mul((float3x3)ubo.normal, biTangent); + tbnMatrix[2] = mul((float3x3)ubo.normal, input.Normal); + + output.LightVec.xyz = mul(float3(ubo.lightPos.xyz - vertexPosition), tbnMatrix); + + float3 lightDist = ubo.lightPos.xyz - input.Pos; + output.LightVecB.x = dot(input.Tangent.xyz, lightDist); + output.LightVecB.y = dot(biTangent, lightDist); + output.LightVecB.z = dot(input.Normal, lightDist); + + output.ViewVec.x = dot(input.Tangent.xyz, input.Pos); + output.ViewVec.y = dot(biTangent, input.Pos); + output.ViewVec.z = dot(input.Normal, input.Pos); + + output.UV = input.UV; + + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 specularColor = float3(0.85, 0.5, 0.0); + + float invRadius = 1.0/lightRadius; + float ambient = 0.25; + + float3 rgb, normal; + + rgb = samplerColorMap.Sample(input.UV).rgb; + normal = normalize((samplerNormalHeightMap.Sample(input.UV).rgb - 0.5) * 2.0); + + float distSqr = dot(input.LightVecB, input.LightVecB); + float3 lVec = input.LightVecB * rsqrt(distSqr); + + float atten = max(clamp(1.0 - invRadius * sqrt(distSqr), 0.0, 1.0), ambient); + float diffuse = clamp(dot(lVec, normal), 0.0, 1.0); + + float3 light = normalize(-input.LightVec); + float3 view = normalize(input.ViewVec); + float3 reflectDir = reflect(-light, normal); + + float specular = pow(max(dot(view, reflectDir), 0.0), 4.0); + + return float4((rgb * atten + (diffuse * rgb + 0.5 * specular * specularColor.rgb)) * atten, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/particlesystem/normalmap.vert.spv b/shaders/slang/particlesystem/normalmap.vert.spv new file mode 100644 index 00000000..9bc1b06a Binary files /dev/null and b/shaders/slang/particlesystem/normalmap.vert.spv differ diff --git a/shaders/slang/particlesystem/particle.frag.spv b/shaders/slang/particlesystem/particle.frag.spv new file mode 100644 index 00000000..e0988cb6 Binary files /dev/null and b/shaders/slang/particlesystem/particle.frag.spv differ diff --git a/shaders/slang/particlesystem/particle.slang b/shaders/slang/particlesystem/particle.slang new file mode 100644 index 00000000..d479260c --- /dev/null +++ b/shaders/slang/particlesystem/particle.slang @@ -0,0 +1,97 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float4 Color; + float Alpha; + float Size; + float Rotation; + int Type; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float PSize : SV_PointSize; + float4 Color; + float Alpha; + int Type; + float Rotation; + float2 CenterPos; + float PointSize; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float2 viewportDim; + float pointSize; +}; +ConstantBuffer ubo; + +Sampler2D samplerSmoke; +Sampler2D samplerFire; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Alpha = input.Alpha; + output.Type = input.Type; + output.Rotation = input.Rotation; + + output.Pos = mul(ubo.projection, mul(ubo.modelview, float4(input.Pos.xyz, 1.0))); + + // Base size of the point sprites + float spriteSize = 8.0 * input.Size; + + // Scale particle size depending on camera projection + float4 eyePos = mul(ubo.modelview, float4(input.Pos.xyz, 1.0)); + float4 projectedCorner = mul(ubo.projection, float4(0.5 * spriteSize, 0.5 * spriteSize, eyePos.z, eyePos.w)); + output.PointSize = output.PSize = ubo.viewportDim.x * projectedCorner.x / projectedCorner.w; + output.CenterPos = ((output.Pos.xy / output.Pos.w) + 1.0) * 0.5 * ubo.viewportDim; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color; + float alpha = (input.Alpha <= 1.0) ? input.Alpha : 2.0 - input.Alpha; + + // Rotate texture coordinates + // Rotate UV + float rotCenter = 0.5; + float rotCos = cos(input.Rotation); + float rotSin = sin(input.Rotation); + + float2 PointCoord = (input.Pos.xy - input.CenterPos.xy) / input.PointSize + 0.5; + + float2 rotUV = float2( + rotCos * (PointCoord.x - rotCenter) + rotSin * (PointCoord.y - rotCenter) + rotCenter, + rotCos * (PointCoord.y - rotCenter) - rotSin * (PointCoord.x - rotCenter) + rotCenter); + + float4 outFragColor; + if (input.Type == 0) + { + // Flame + color = samplerFire.Sample(rotUV); + outFragColor.a = 0.0; + } + else + { + // Smoke + color = samplerSmoke.Sample(rotUV); + outFragColor.a = color.a * alpha; + } + + outFragColor.rgb = color.rgb * input.Color.rgb * alpha; + return outFragColor; +} \ No newline at end of file diff --git a/shaders/slang/particlesystem/particle.vert.spv b/shaders/slang/particlesystem/particle.vert.spv new file mode 100644 index 00000000..f0911192 Binary files /dev/null and b/shaders/slang/particlesystem/particle.vert.spv differ diff --git a/shaders/slang/pbrbasic/pbr.frag.spv b/shaders/slang/pbrbasic/pbr.frag.spv new file mode 100644 index 00000000..8e06c66a Binary files /dev/null and b/shaders/slang/pbrbasic/pbr.frag.spv differ diff --git a/shaders/slang/pbrbasic/pbr.slang b/shaders/slang/pbrbasic/pbr.slang new file mode 100644 index 00000000..242d0fc9 --- /dev/null +++ b/shaders/slang/pbrbasic/pbr.slang @@ -0,0 +1,138 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 WorldPos; + float3 Normal; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; + float3 camPos; +}; +ConstantBuffer ubo; + +struct UBOParams { + float4 lights[4]; +}; +ConstantBuffer uboParams; + +struct Material { + [[vk::offset(12)]] float roughness; + [[vk::offset(16)]] float metallic; + [[vk::offset(20)]] float r; + [[vk::offset(24)]] float g; + [[vk::offset(28)]] float b; +}; +[[vk::push_constant]] Material material; + +static const float PI = 3.14159265359; + +// Normal Distribution function -------------------------------------- +float D_GGX(float dotNH, float roughness) +{ + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; + return (alpha2)/(PI * denom*denom); +} + +// Geometric Shadowing function -------------------------------------- +float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; +} + +// Fresnel function ---------------------------------------------------- +float3 F_Schlick(float cosTheta, Material material) +{ + float3 F0 = lerp(float3(0.04, 0.04, 0.04), float3(material.r, material.g, material.b), material.metallic); // * material.specular + float3 F = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); + return F; +} + +// Specular BRDF composition -------------------------------------------- + +float3 BRDF(float3 L, float3 V, float3 N, Material material) +{ + // Precalculate vectors and dot products + float3 H = normalize (V + L); + float dotNV = clamp(dot(N, V), 0.0, 1.0); + float dotNL = clamp(dot(N, L), 0.0, 1.0); + float dotLH = clamp(dot(L, H), 0.0, 1.0); + float dotNH = clamp(dot(N, H), 0.0, 1.0); + + // Light color fixed + float3 lightColor = float3(1.0, 1.0, 1.0); + + float3 color = float3(0.0, 0.0, 0.0); + + if (dotNL > 0.0) + { + float rroughness = max(0.05, material.roughness); + // D = Normal distribution (Distribution of the microfacets) + float D = D_GGX(dotNH, material.roughness); + // G = Geometric shadowing term (Microfacets shadowing) + float G = G_SchlicksmithGGX(dotNL, dotNV, rroughness); + // F = Fresnel factor (Reflectance depending on angle of incidence) + float3 F = F_Schlick(dotNV, material); + + float3 spec = D * F * G / (4.0 * dotNL * dotNV); + + color += spec * dotNL * lightColor; + } + + return color; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float3 objPos) +{ + VSOutput output; + float3 locPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.WorldPos = locPos + objPos; + output.Normal = mul((float3x3)ubo.model, input.Normal); + output.Pos = mul(ubo.projection, mul(ubo.view, float4(output.WorldPos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 V = normalize(ubo.camPos - input.WorldPos); + + // Specular contribution + float3 Lo = float3(0.0, 0.0, 0.0); + for (int i = 0; i < 4; i++) { + float3 L = normalize(uboParams.lights[i].xyz - input.WorldPos); + Lo += BRDF(L, V, N, material); + }; + + // Combine with ambient + float3 color = float3(material.r, material.g, material.b) * 0.02; + color += Lo; + + // Gamma correct + color = pow(color, float3(0.4545, 0.4545, 0.4545)); + + return float4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pbrbasic/pbr.vert.spv b/shaders/slang/pbrbasic/pbr.vert.spv new file mode 100644 index 00000000..763c4205 Binary files /dev/null and b/shaders/slang/pbrbasic/pbr.vert.spv differ diff --git a/shaders/slang/pbribl/filtercube.slang b/shaders/slang/pbribl/filtercube.slang new file mode 100644 index 00000000..6c848459 --- /dev/null +++ b/shaders/slang/pbribl/filtercube.slang @@ -0,0 +1,25 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 mvp) +{ + VSOutput output; + output.UVW = input.Pos; + output.Pos = mul(mvp, float4(input.Pos.xyz, 1.0)); + return output; +} diff --git a/shaders/slang/pbribl/filtercube.vert.spv b/shaders/slang/pbribl/filtercube.vert.spv new file mode 100644 index 00000000..5d02106a Binary files /dev/null and b/shaders/slang/pbribl/filtercube.vert.spv differ diff --git a/shaders/slang/pbribl/genbrdflut.frag.spv b/shaders/slang/pbribl/genbrdflut.frag.spv new file mode 100644 index 00000000..a055c41e Binary files /dev/null and b/shaders/slang/pbribl/genbrdflut.frag.spv differ diff --git a/shaders/slang/pbribl/genbrdflut.slang b/shaders/slang/pbribl/genbrdflut.slang new file mode 100644 index 00000000..b40d7b16 --- /dev/null +++ b/shaders/slang/pbribl/genbrdflut.slang @@ -0,0 +1,108 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + + struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[[SpecializationConstant]] const uint NUM_SAMPLES = 1024u; + +#define PI 3.1415926536 + +// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/ +float random(float2 co) +{ + float a = 12.9898; + float b = 78.233; + float c = 43758.5453; + float dt= dot(co.xy ,float2(a,b)); + float sn= fmod(dt,3.14); + return frac(sin(sn) * c); +} + +float2 hammersley2d(uint i, uint N) +{ + // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + uint bits = (i << 16u) | (i >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + float rdi = float(bits) * 2.3283064365386963e-10; + return float2(float(i) /float(N), rdi); +} + +// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf +float3 importanceSample_GGX(float2 Xi, float roughness, float3 normal) +{ + // Maps a 2D point to a hemisphere with spread based on roughness + float alpha = roughness * roughness; + float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + float3 H = float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); + + // Tangent space + float3 up = abs(normal.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0); + float3 tangentX = normalize(cross(up, normal)); + float3 tangentY = normalize(cross(normal, tangentX)); + + // Convert to world Space + return normalize(tangentX * H.x + tangentY * H.y + normal * H.z); +} + +// Geometric Shadowing function +float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness) +{ + float k = (roughness * roughness) / 2.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; +} + +float2 BRDF(float NoV, float roughness) +{ + // Normal always points along z-axis for the 2D lookup + const float3 N = float3(0.0, 0.0, 1.0); + float3 V = float3(sqrt(1.0 - NoV*NoV), 0.0, NoV); + + float2 LUT = float2(0.0, 0.0); + for(uint i = 0u; i < NUM_SAMPLES; i++) { + float2 Xi = hammersley2d(i, NUM_SAMPLES); + float3 H = importanceSample_GGX(Xi, roughness, N); + float3 L = 2.0 * dot(V, H) * H - V; + + float dotNL = max(dot(N, L), 0.0); + float dotNV = max(dot(N, V), 0.0); + float dotVH = max(dot(V, H), 0.0); + float dotNH = max(dot(H, N), 0.0); + + if (dotNL > 0.0) { + float G = G_SchlicksmithGGX(dotNL, dotNV, roughness); + float G_Vis = (G * dotVH) / (dotNH * dotNV); + float Fc = pow(1.0 - dotVH, 5.0); + LUT += float2((1.0 - Fc) * G_Vis, Fc * G_Vis); + } + } + return LUT / float(NUM_SAMPLES); +} + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(BRDF(input.UV.x, input.UV.y), 0.0, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pbribl/genbrdflut.vert.spv b/shaders/slang/pbribl/genbrdflut.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/pbribl/genbrdflut.vert.spv differ diff --git a/shaders/slang/pbribl/irradiancecube.frag.spv b/shaders/slang/pbribl/irradiancecube.frag.spv new file mode 100644 index 00000000..8941b9b3 Binary files /dev/null and b/shaders/slang/pbribl/irradiancecube.frag.spv differ diff --git a/shaders/slang/pbribl/irradiancecube.slang b/shaders/slang/pbribl/irradiancecube.slang new file mode 100644 index 00000000..31cc96ea --- /dev/null +++ b/shaders/slang/pbribl/irradiancecube.slang @@ -0,0 +1,39 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +SamplerCube samplerEnv; + +struct PushConsts { + [[vk::offset(64)]] float deltaPhi; + [[vk::offset(68)]] float deltaTheta; +}; +[[vk::push_constant]] PushConsts consts; + +#define PI 3.1415926535897932384626433832795 + +[shader("fragment")] +float4 fragmentMain(float3 inPos) +{ + float3 N = normalize(inPos.xyz); + float3 up = float3(0.0, 1.0, 0.0); + float3 right = normalize(cross(up, N)); + up = cross(N, right); + + const float TWO_PI = PI * 2.0; + const float HALF_PI = PI * 0.5; + + float3 color = float3(0.0, 0.0, 0.0); + uint sampleCount = 0u; + for (float phi = 0.0; phi < TWO_PI; phi += consts.deltaPhi) { + for (float theta = 0.0; theta < HALF_PI; theta += consts.deltaTheta) { + float3 tempVec = cos(phi) * right + sin(phi) * up; + float3 sampleVector = cos(theta) * N + sin(theta) * tempVec; + color += samplerEnv.Sample(sampleVector).rgb * cos(theta) * sin(theta); + sampleCount++; + } + } + return float4(PI * color / float(sampleCount), 1.0); +} diff --git a/shaders/slang/pbribl/pbribl.frag.spv b/shaders/slang/pbribl/pbribl.frag.spv new file mode 100644 index 00000000..44c5bf75 Binary files /dev/null and b/shaders/slang/pbribl/pbribl.frag.spv differ diff --git a/shaders/slang/pbribl/pbribl.slang b/shaders/slang/pbribl/pbribl.slang new file mode 100644 index 00000000..6ced37ff --- /dev/null +++ b/shaders/slang/pbribl/pbribl.slang @@ -0,0 +1,193 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 WorldPos; + float3 Normal; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; + float3 camPos; +}; +ConstantBuffer ubo; + +struct UBOParams { + float4 lights[4]; + float exposure; + float gamma; +}; +ConstantBuffer uboParams; + +SamplerCube samplerIrradiance; +Sampler2D samplerBRDFLUT; +SamplerCube prefilteredMapSampler; + +struct Material { + [[vk::offset(12)]] float roughness; + [[vk::offset(16)]] float metallic; + [[vk::offset(20)]] float specular; + [[vk::offset(24)]] float r; + [[vk::offset(28)]] float g; + [[vk::offset(32)]] float b; +}; +[[vk::push_constant]] Material material; + +#define PI 3.1415926535897932384626433832795 +#define ALBEDO float3(material.r, material.g, material.b) + +// From http://filmicgames.com/archives/75 +float3 Uncharted2Tonemap(float3 x) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; +} + +// Normal Distribution function -------------------------------------- +float D_GGX(float dotNH, float roughness) +{ + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; + return (alpha2)/(PI * denom*denom); +} + +// Geometric Shadowing function -------------------------------------- +float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; +} + +// Fresnel function ---------------------------------------------------- +float3 F_Schlick(float cosTheta, float3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} +float3 F_SchlickR(float cosTheta, float3 F0, float roughness) +{ + return F0 + (max((1.0 - roughness).xxx, F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +float3 prefilteredReflection(float3 R, float roughness) +{ + const float MAX_REFLECTION_LOD = 9.0; // todo: param/const + float lod = roughness * MAX_REFLECTION_LOD; + float lodf = floor(lod); + float lodc = ceil(lod); + float3 a = prefilteredMapSampler.SampleLevel(R, lodf).rgb; + float3 b = prefilteredMapSampler.SampleLevel(R, lodc).rgb; + return lerp(a, b, lod - lodf); +} + +float3 specularContribution(float3 L, float3 V, float3 N, float3 F0, float metallic, float roughness) +{ + // Precalculate vectors and dot products + float3 H = normalize (V + L); + float dotNH = clamp(dot(N, H), 0.0, 1.0); + float dotNV = clamp(dot(N, V), 0.0, 1.0); + float dotNL = clamp(dot(N, L), 0.0, 1.0); + + // Light color fixed + float3 lightColor = float3(1.0, 1.0, 1.0); + + float3 color = float3(0.0, 0.0, 0.0); + + if (dotNL > 0.0) { + // D = Normal distribution (Distribution of the microfacets) + float D = D_GGX(dotNH, roughness); + // G = Geometric shadowing term (Microfacets shadowing) + float G = G_SchlicksmithGGX(dotNL, dotNV, roughness); + // F = Fresnel factor (Reflectance depending on angle of incidence) + float3 F = F_Schlick(dotNV, F0); + float3 spec = D * F * G / (4.0 * dotNL * dotNV + 0.001); + float3 kD = (float3(1.0, 1.0, 1.0) - F) * (1.0 - metallic); + color += (kD * ALBEDO / PI + spec) * dotNL; + } + + return color; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float3 objPos) +{ + VSOutput output; + float3 locPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.WorldPos = locPos + objPos; + output.Normal = mul((float3x3)ubo.model, input.Normal); + output.UV = input.UV; + output.UV.y = 1.0 - input.UV.y; + output.Pos = mul(ubo.projection, mul(ubo.view, float4(output.WorldPos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 V = normalize(ubo.camPos - input.WorldPos); + float3 R = reflect(-V, N); + + float metallic = material.metallic; + float roughness = material.roughness; + + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, ALBEDO, metallic); + + float3 Lo = float3(0.0, 0.0, 0.0); + for(int i = 0; i < 4; i++) { + float3 L = normalize(uboParams.lights[i].xyz - input.WorldPos); + Lo += specularContribution(L, V, N, F0, metallic, roughness); + } + + float2 brdf = samplerBRDFLUT.Sample(float2(max(dot(N, V), 0.0), roughness)).rg; + float3 reflection = prefilteredReflection(R, roughness).rgb; + float3 irradiance = samplerIrradiance.Sample(N).rgb; + + // Diffuse based on irradiance + float3 diffuse = irradiance * ALBEDO; + + float3 F = F_SchlickR(max(dot(N, V), 0.0), F0, roughness); + + // Specular reflectance + float3 specular = reflection * (F * brdf.x + brdf.y); + + // Ambient part + float3 kD = 1.0 - F; + kD *= 1.0 - metallic; + float3 ambient = (kD * diffuse + specular); + + float3 color = ambient + Lo; + + // Tone mapping + color = Uncharted2Tonemap(color * uboParams.exposure); + color = color * (1.0f / Uncharted2Tonemap((11.2f).xxx)); + // Gamma correction + color = pow(color, (1.0f / uboParams.gamma).xxx); + + return float4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pbribl/pbribl.vert.spv b/shaders/slang/pbribl/pbribl.vert.spv new file mode 100644 index 00000000..889fb594 Binary files /dev/null and b/shaders/slang/pbribl/pbribl.vert.spv differ diff --git a/shaders/slang/pbribl/prefilterenvmap.frag.spv b/shaders/slang/pbribl/prefilterenvmap.frag.spv new file mode 100644 index 00000000..1fc90a68 Binary files /dev/null and b/shaders/slang/pbribl/prefilterenvmap.frag.spv differ diff --git a/shaders/slang/pbribl/prefilterenvmap.slang b/shaders/slang/pbribl/prefilterenvmap.slang new file mode 100644 index 00000000..d33cd37c --- /dev/null +++ b/shaders/slang/pbribl/prefilterenvmap.slang @@ -0,0 +1,109 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +SamplerCube samplerEnv; + +struct PushConsts { + [[vk::offset(64)]] float roughness; + [[vk::offset(68)]] uint numSamples; +}; +[[vk::push_constant]] PushConsts consts; + +#define PI 3.1415926536 + +// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/ +float random(float2 co) +{ + float a = 12.9898; + float b = 78.233; + float c = 43758.5453; + float dt= dot(co.xy ,float2(a,b)); + float sn= fmod(dt,3.14); + return frac(sin(sn) * c); +} + +float2 hammersley2d(uint i, uint N) +{ + // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + uint bits = (i << 16u) | (i >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + float rdi = float(bits) * 2.3283064365386963e-10; + return float2(float(i) /float(N), rdi); +} + +// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf +float3 importanceSample_GGX(float2 Xi, float roughness, float3 normal) +{ + // Maps a 2D point to a hemisphere with spread based on roughness + float alpha = roughness * roughness; + float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + float3 H = float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); + + // Tangent space + float3 up = abs(normal.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0); + float3 tangentX = normalize(cross(up, normal)); + float3 tangentY = normalize(cross(normal, tangentX)); + + // Convert to world Space + return normalize(tangentX * H.x + tangentY * H.y + normal * H.z); +} + +// Normal Distribution function +float D_GGX(float dotNH, float roughness) +{ + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; + return (alpha2)/(PI * denom*denom); +} + +float3 prefilterEnvMap(float3 R, float roughness) +{ + float3 N = R; + float3 V = R; + float3 color = float3(0.0, 0.0, 0.0); + float totalWeight = 0.0; + int2 envMapDims; + samplerEnv.GetDimensions(envMapDims.x, envMapDims.y); + float envMapDim = float(envMapDims.x); + for(uint i = 0u; i < consts.numSamples; i++) { + float2 Xi = hammersley2d(i, consts.numSamples); + float3 H = importanceSample_GGX(Xi, roughness, N); + float3 L = 2.0 * dot(V, H) * H - V; + float dotNL = clamp(dot(N, L), 0.0, 1.0); + if(dotNL > 0.0) { + // Filtering based on https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/ + + float dotNH = clamp(dot(N, H), 0.0, 1.0); + float dotVH = clamp(dot(V, H), 0.0, 1.0); + + // Probability Distribution Function + float pdf = D_GGX(dotNH, roughness) * dotNH / (4.0 * dotVH) + 0.0001; + // Slid angle of current smple + float omegaS = 1.0 / (float(consts.numSamples) * pdf); + // Solid angle of 1 pixel across all cube faces + float omegaP = 4.0 * PI / (6.0 * envMapDim * envMapDim); + // Biased (+1.0) mip level for better result + float mipLevel = roughness == 0.0 ? 0.0 : max(0.5 * log2(omegaS / omegaP) + 1.0, 0.0f); + color += samplerEnv.SampleLevel(L, mipLevel).rgb * dotNL; + totalWeight += dotNL; + + } + } + return (color / totalWeight); +} + +[shader("fragment")] +float4 fragmentMain(float3 inPos) +{ + float3 N = normalize(inPos.xyz); + return float4(prefilterEnvMap(N, consts.roughness), 1.0); +} diff --git a/shaders/slang/pbribl/skybox.frag.spv b/shaders/slang/pbribl/skybox.frag.spv new file mode 100644 index 00000000..84bd3497 Binary files /dev/null and b/shaders/slang/pbribl/skybox.frag.spv differ diff --git a/shaders/slang/pbribl/skybox.slang b/shaders/slang/pbribl/skybox.slang new file mode 100644 index 00000000..809fedab --- /dev/null +++ b/shaders/slang/pbribl/skybox.slang @@ -0,0 +1,70 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +struct UBOParams { + float4 lights[4]; + float exposure; + float gamma; +}; +ConstantBuffer uboParams; + +SamplerCube samplerEnv; + +// From http://filmicworlds.com/blog/filmic-tonemapping-operators/ +float3 Uncharted2Tonemap(float3 color) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + float W = 11.2; + return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UVW = input.Pos; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 color = samplerEnv.Sample(input.UVW).rgb; + + // Tone mapping + color = Uncharted2Tonemap(color * uboParams.exposure); + color = color * (1.0f / Uncharted2Tonemap((11.2f).xxx)); + // Gamma correction + color = pow(color, (1.0f / uboParams.gamma).xxx); + + return float4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pbribl/skybox.vert.spv b/shaders/slang/pbribl/skybox.vert.spv new file mode 100644 index 00000000..57f170e7 Binary files /dev/null and b/shaders/slang/pbribl/skybox.vert.spv differ diff --git a/shaders/slang/pbrtexture/filtercube.slang b/shaders/slang/pbrtexture/filtercube.slang new file mode 100644 index 00000000..6c848459 --- /dev/null +++ b/shaders/slang/pbrtexture/filtercube.slang @@ -0,0 +1,25 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 mvp) +{ + VSOutput output; + output.UVW = input.Pos; + output.Pos = mul(mvp, float4(input.Pos.xyz, 1.0)); + return output; +} diff --git a/shaders/slang/pbrtexture/filtercube.vert.spv b/shaders/slang/pbrtexture/filtercube.vert.spv new file mode 100644 index 00000000..5d02106a Binary files /dev/null and b/shaders/slang/pbrtexture/filtercube.vert.spv differ diff --git a/shaders/slang/pbrtexture/genbrdflut.frag.spv b/shaders/slang/pbrtexture/genbrdflut.frag.spv new file mode 100644 index 00000000..a055c41e Binary files /dev/null and b/shaders/slang/pbrtexture/genbrdflut.frag.spv differ diff --git a/shaders/slang/pbrtexture/genbrdflut.slang b/shaders/slang/pbrtexture/genbrdflut.slang new file mode 100644 index 00000000..b40d7b16 --- /dev/null +++ b/shaders/slang/pbrtexture/genbrdflut.slang @@ -0,0 +1,108 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + + struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[[SpecializationConstant]] const uint NUM_SAMPLES = 1024u; + +#define PI 3.1415926536 + +// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/ +float random(float2 co) +{ + float a = 12.9898; + float b = 78.233; + float c = 43758.5453; + float dt= dot(co.xy ,float2(a,b)); + float sn= fmod(dt,3.14); + return frac(sin(sn) * c); +} + +float2 hammersley2d(uint i, uint N) +{ + // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + uint bits = (i << 16u) | (i >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + float rdi = float(bits) * 2.3283064365386963e-10; + return float2(float(i) /float(N), rdi); +} + +// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf +float3 importanceSample_GGX(float2 Xi, float roughness, float3 normal) +{ + // Maps a 2D point to a hemisphere with spread based on roughness + float alpha = roughness * roughness; + float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + float3 H = float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); + + // Tangent space + float3 up = abs(normal.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0); + float3 tangentX = normalize(cross(up, normal)); + float3 tangentY = normalize(cross(normal, tangentX)); + + // Convert to world Space + return normalize(tangentX * H.x + tangentY * H.y + normal * H.z); +} + +// Geometric Shadowing function +float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness) +{ + float k = (roughness * roughness) / 2.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; +} + +float2 BRDF(float NoV, float roughness) +{ + // Normal always points along z-axis for the 2D lookup + const float3 N = float3(0.0, 0.0, 1.0); + float3 V = float3(sqrt(1.0 - NoV*NoV), 0.0, NoV); + + float2 LUT = float2(0.0, 0.0); + for(uint i = 0u; i < NUM_SAMPLES; i++) { + float2 Xi = hammersley2d(i, NUM_SAMPLES); + float3 H = importanceSample_GGX(Xi, roughness, N); + float3 L = 2.0 * dot(V, H) * H - V; + + float dotNL = max(dot(N, L), 0.0); + float dotNV = max(dot(N, V), 0.0); + float dotVH = max(dot(V, H), 0.0); + float dotNH = max(dot(H, N), 0.0); + + if (dotNL > 0.0) { + float G = G_SchlicksmithGGX(dotNL, dotNV, roughness); + float G_Vis = (G * dotVH) / (dotNH * dotNV); + float Fc = pow(1.0 - dotVH, 5.0); + LUT += float2((1.0 - Fc) * G_Vis, Fc * G_Vis); + } + } + return LUT / float(NUM_SAMPLES); +} + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(BRDF(input.UV.x, input.UV.y), 0.0, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pbrtexture/genbrdflut.vert.spv b/shaders/slang/pbrtexture/genbrdflut.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/pbrtexture/genbrdflut.vert.spv differ diff --git a/shaders/slang/pbrtexture/irradiancecube.frag.spv b/shaders/slang/pbrtexture/irradiancecube.frag.spv new file mode 100644 index 00000000..a3dc8bf0 Binary files /dev/null and b/shaders/slang/pbrtexture/irradiancecube.frag.spv differ diff --git a/shaders/slang/pbrtexture/irradiancecube.slang b/shaders/slang/pbrtexture/irradiancecube.slang new file mode 100644 index 00000000..31cc96ea --- /dev/null +++ b/shaders/slang/pbrtexture/irradiancecube.slang @@ -0,0 +1,39 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +SamplerCube samplerEnv; + +struct PushConsts { + [[vk::offset(64)]] float deltaPhi; + [[vk::offset(68)]] float deltaTheta; +}; +[[vk::push_constant]] PushConsts consts; + +#define PI 3.1415926535897932384626433832795 + +[shader("fragment")] +float4 fragmentMain(float3 inPos) +{ + float3 N = normalize(inPos.xyz); + float3 up = float3(0.0, 1.0, 0.0); + float3 right = normalize(cross(up, N)); + up = cross(N, right); + + const float TWO_PI = PI * 2.0; + const float HALF_PI = PI * 0.5; + + float3 color = float3(0.0, 0.0, 0.0); + uint sampleCount = 0u; + for (float phi = 0.0; phi < TWO_PI; phi += consts.deltaPhi) { + for (float theta = 0.0; theta < HALF_PI; theta += consts.deltaTheta) { + float3 tempVec = cos(phi) * right + sin(phi) * up; + float3 sampleVector = cos(theta) * N + sin(theta) * tempVec; + color += samplerEnv.Sample(sampleVector).rgb * cos(theta) * sin(theta); + sampleCount++; + } + } + return float4(PI * color / float(sampleCount), 1.0); +} diff --git a/shaders/slang/pbrtexture/pbrtexture.frag.spv b/shaders/slang/pbrtexture/pbrtexture.frag.spv new file mode 100644 index 00000000..7c56a77a Binary files /dev/null and b/shaders/slang/pbrtexture/pbrtexture.frag.spv differ diff --git a/shaders/slang/pbrtexture/pbrtexture.slang b/shaders/slang/pbrtexture/pbrtexture.slang new file mode 100644 index 00000000..0d7bbef0 --- /dev/null +++ b/shaders/slang/pbrtexture/pbrtexture.slang @@ -0,0 +1,198 @@ +// Copyright 2020 Google LLC + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float4 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 WorldPos; + float3 Normal; + float2 UV; + float3 Tangent; +}; + +struct UBO { + float4x4 projection; + float4x4 model; + float4x4 view; + float3 camPos; +}; +ConstantBuffer ubo; + +struct UBOParams { + float4 lights[4]; + float exposure; + float gamma; +}; +ConstantBuffer uboParams; + +SamplerCube samplerIrradiance; +Sampler2D samplerBRDFLUT; +SamplerCube prefilteredMapSampler; + +Sampler2D albedoMapSampler; +Sampler2D normalMapSampler; +Sampler2D aoMapSampler; +Sampler2D metallicMapSampler; +Sampler2D roughnessMapSampler; + +#define PI 3.1415926535897932384626433832795 +#define ALBEDO(uv) pow(albedoMapSampler.Sample(uv).rgb, float3(2.2, 2.2, 2.2)) + +// From http://filmicgames.com/archives/75 +float3 Uncharted2Tonemap(float3 x) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; +} + +// Normal Distribution function -------------------------------------- +float D_GGX(float dotNH, float roughness) +{ + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; + return (alpha2)/(PI * denom*denom); +} + +// Geometric Shadowing function -------------------------------------- +float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; +} + +// Fresnel function ---------------------------------------------------- +float3 F_Schlick(float cosTheta, float3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} +float3 F_SchlickR(float cosTheta, float3 F0, float roughness) +{ + return F0 + (max((1.0 - roughness).xxx, F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +float3 prefilteredReflection(float3 R, float roughness) +{ + const float MAX_REFLECTION_LOD = 9.0; // todo: param/const + float lod = roughness * MAX_REFLECTION_LOD; + float lodf = floor(lod); + float lodc = ceil(lod); + float3 a = prefilteredMapSampler.SampleLevel(R, lodf).rgb; + float3 b = prefilteredMapSampler.SampleLevel(R, lodc).rgb; + return lerp(a, b, lod - lodf); +} + +float3 specularContribution(float2 inUV, float3 L, float3 V, float3 N, float3 F0, float metallic, float roughness) +{ + // Precalculate vectors and dot products + float3 H = normalize (V + L); + float dotNH = clamp(dot(N, H), 0.0, 1.0); + float dotNV = clamp(dot(N, V), 0.0, 1.0); + float dotNL = clamp(dot(N, L), 0.0, 1.0); + + // Light color fixed + float3 lightColor = float3(1.0, 1.0, 1.0); + + float3 color = float3(0.0, 0.0, 0.0); + + if (dotNL > 0.0) { + // D = Normal distribution (Distribution of the microfacets) + float D = D_GGX(dotNH, roughness); + // G = Geometric shadowing term (Microfacets shadowing) + float G = G_SchlicksmithGGX(dotNL, dotNV, roughness); + // F = Fresnel factor (Reflectance depending on angle of incidence) + float3 F = F_Schlick(dotNV, F0); + float3 spec = D * F * G / (4.0 * dotNL * dotNV + 0.001); + float3 kD = (float3(1.0, 1.0, 1.0) - F) * (1.0 - metallic); + color += (kD * ALBEDO(inUV) / PI + spec) * dotNL; + } + + return color; +} + +float3 calculateNormal(VSOutput input) +{ + float3 tangentNormal = normalMapSampler.Sample(input.UV).xyz * 2.0 - 1.0; + + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent); + float3 B = normalize(cross(N, T)); + float3x3 TBN = transpose(float3x3(T, B, N)); + + return normalize(mul(TBN, tangentNormal)); +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + float3 locPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.WorldPos = locPos; + output.Normal = mul((float3x3)ubo.model, input.Normal); + output.Tangent = mul((float3x3)ubo.model, input.Tangent.xyz); + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.view, float4(output.WorldPos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = calculateNormal(input); + float3 V = normalize(ubo.camPos - input.WorldPos); + float3 R = reflect(-V, N); + + float metallic = metallicMapSampler.Sample(input.UV).r; + float roughness = roughnessMapSampler.Sample(input.UV).r; + + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, ALBEDO(input.UV), metallic); + + float3 Lo = float3(0.0, 0.0, 0.0); + for(int i = 0; i < 4; i++) { + float3 L = normalize(uboParams.lights[i].xyz - input.WorldPos); + Lo += specularContribution(input.UV, L, V, N, F0, metallic, roughness); + } + + float2 brdf = samplerBRDFLUT.Sample(float2(max(dot(N, V), 0.0), roughness)).rg; + float3 reflection = prefilteredReflection(R, roughness).rgb; + float3 irradiance = samplerIrradiance.Sample(N).rgb; + + // Diffuse based on irradiance + float3 diffuse = irradiance * ALBEDO(input.UV); + + float3 F = F_SchlickR(max(dot(N, V), 0.0), F0, roughness); + + // Specular reflectance + float3 specular = reflection * (F * brdf.x + brdf.y); + + // Ambient part + float3 kD = 1.0 - F; + kD *= 1.0 - metallic; + float3 ambient = (kD * diffuse + specular) * aoMapSampler.Sample(input.UV).rrr; + + float3 color = ambient + Lo; + + // Tone mapping + color = Uncharted2Tonemap(color * uboParams.exposure); + color = color * (1.0f / Uncharted2Tonemap((11.2f).xxx)); + // Gamma correction + color = pow(color, (1.0f / uboParams.gamma).xxx); + + return float4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pbrtexture/pbrtexture.vert.spv b/shaders/slang/pbrtexture/pbrtexture.vert.spv new file mode 100644 index 00000000..b56bda1c Binary files /dev/null and b/shaders/slang/pbrtexture/pbrtexture.vert.spv differ diff --git a/shaders/slang/pbrtexture/prefilterenvmap.frag.spv b/shaders/slang/pbrtexture/prefilterenvmap.frag.spv new file mode 100644 index 00000000..1fc90a68 Binary files /dev/null and b/shaders/slang/pbrtexture/prefilterenvmap.frag.spv differ diff --git a/shaders/slang/pbrtexture/prefilterenvmap.slang b/shaders/slang/pbrtexture/prefilterenvmap.slang new file mode 100644 index 00000000..d33cd37c --- /dev/null +++ b/shaders/slang/pbrtexture/prefilterenvmap.slang @@ -0,0 +1,109 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +SamplerCube samplerEnv; + +struct PushConsts { + [[vk::offset(64)]] float roughness; + [[vk::offset(68)]] uint numSamples; +}; +[[vk::push_constant]] PushConsts consts; + +#define PI 3.1415926536 + +// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/ +float random(float2 co) +{ + float a = 12.9898; + float b = 78.233; + float c = 43758.5453; + float dt= dot(co.xy ,float2(a,b)); + float sn= fmod(dt,3.14); + return frac(sin(sn) * c); +} + +float2 hammersley2d(uint i, uint N) +{ + // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + uint bits = (i << 16u) | (i >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + float rdi = float(bits) * 2.3283064365386963e-10; + return float2(float(i) /float(N), rdi); +} + +// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf +float3 importanceSample_GGX(float2 Xi, float roughness, float3 normal) +{ + // Maps a 2D point to a hemisphere with spread based on roughness + float alpha = roughness * roughness; + float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + float3 H = float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); + + // Tangent space + float3 up = abs(normal.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0); + float3 tangentX = normalize(cross(up, normal)); + float3 tangentY = normalize(cross(normal, tangentX)); + + // Convert to world Space + return normalize(tangentX * H.x + tangentY * H.y + normal * H.z); +} + +// Normal Distribution function +float D_GGX(float dotNH, float roughness) +{ + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; + return (alpha2)/(PI * denom*denom); +} + +float3 prefilterEnvMap(float3 R, float roughness) +{ + float3 N = R; + float3 V = R; + float3 color = float3(0.0, 0.0, 0.0); + float totalWeight = 0.0; + int2 envMapDims; + samplerEnv.GetDimensions(envMapDims.x, envMapDims.y); + float envMapDim = float(envMapDims.x); + for(uint i = 0u; i < consts.numSamples; i++) { + float2 Xi = hammersley2d(i, consts.numSamples); + float3 H = importanceSample_GGX(Xi, roughness, N); + float3 L = 2.0 * dot(V, H) * H - V; + float dotNL = clamp(dot(N, L), 0.0, 1.0); + if(dotNL > 0.0) { + // Filtering based on https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/ + + float dotNH = clamp(dot(N, H), 0.0, 1.0); + float dotVH = clamp(dot(V, H), 0.0, 1.0); + + // Probability Distribution Function + float pdf = D_GGX(dotNH, roughness) * dotNH / (4.0 * dotVH) + 0.0001; + // Slid angle of current smple + float omegaS = 1.0 / (float(consts.numSamples) * pdf); + // Solid angle of 1 pixel across all cube faces + float omegaP = 4.0 * PI / (6.0 * envMapDim * envMapDim); + // Biased (+1.0) mip level for better result + float mipLevel = roughness == 0.0 ? 0.0 : max(0.5 * log2(omegaS / omegaP) + 1.0, 0.0f); + color += samplerEnv.SampleLevel(L, mipLevel).rgb * dotNL; + totalWeight += dotNL; + + } + } + return (color / totalWeight); +} + +[shader("fragment")] +float4 fragmentMain(float3 inPos) +{ + float3 N = normalize(inPos.xyz); + return float4(prefilterEnvMap(N, consts.roughness), 1.0); +} diff --git a/shaders/slang/pbrtexture/skybox.frag.spv b/shaders/slang/pbrtexture/skybox.frag.spv new file mode 100644 index 00000000..84bd3497 Binary files /dev/null and b/shaders/slang/pbrtexture/skybox.frag.spv differ diff --git a/shaders/slang/pbrtexture/skybox.slang b/shaders/slang/pbrtexture/skybox.slang new file mode 100644 index 00000000..809fedab --- /dev/null +++ b/shaders/slang/pbrtexture/skybox.slang @@ -0,0 +1,70 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +struct UBOParams { + float4 lights[4]; + float exposure; + float gamma; +}; +ConstantBuffer uboParams; + +SamplerCube samplerEnv; + +// From http://filmicworlds.com/blog/filmic-tonemapping-operators/ +float3 Uncharted2Tonemap(float3 color) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + float W = 11.2; + return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UVW = input.Pos; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 color = samplerEnv.Sample(input.UVW).rgb; + + // Tone mapping + color = Uncharted2Tonemap(color * uboParams.exposure); + color = color * (1.0f / Uncharted2Tonemap((11.2f).xxx)); + // Gamma correction + color = pow(color, (1.0f / uboParams.gamma).xxx); + + return float4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pbrtexture/skybox.vert.spv b/shaders/slang/pbrtexture/skybox.vert.spv new file mode 100644 index 00000000..57f170e7 Binary files /dev/null and b/shaders/slang/pbrtexture/skybox.vert.spv differ diff --git a/shaders/slang/pipelines/phong.frag.spv b/shaders/slang/pipelines/phong.frag.spv new file mode 100644 index 00000000..37d29241 Binary files /dev/null and b/shaders/slang/pipelines/phong.frag.spv differ diff --git a/shaders/slang/pipelines/phong.slang b/shaders/slang/pipelines/phong.slang new file mode 100644 index 00000000..e6e6f172 --- /dev/null +++ b/shaders/slang/pipelines/phong.slang @@ -0,0 +1,62 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lPos = mul((float3x3)ubo.model, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Desaturate color + float3 color = lerp(input.Color, dot(float3(0.2126,0.7152,0.0722), input.Color).xxx, 0.65); + + // High ambient colors because mesh materials are pretty dark + float3 ambient = color * float3(1.0, 1.0, 1.0); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * color; + float3 specular = pow(max(dot(R, V), 0.0), 32.0) * float3(0.35, 0.35, 0.35); + return float4(ambient + diffuse * 1.75 + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pipelines/phong.vert.spv b/shaders/slang/pipelines/phong.vert.spv new file mode 100644 index 00000000..c5f07810 Binary files /dev/null and b/shaders/slang/pipelines/phong.vert.spv differ diff --git a/shaders/slang/pipelines/toon.frag.spv b/shaders/slang/pipelines/toon.frag.spv new file mode 100644 index 00000000..d2367292 Binary files /dev/null and b/shaders/slang/pipelines/toon.frag.spv differ diff --git a/shaders/slang/pipelines/toon.slang b/shaders/slang/pipelines/toon.slang new file mode 100644 index 00000000..14cc13e5 --- /dev/null +++ b/shaders/slang/pipelines/toon.slang @@ -0,0 +1,70 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lPos = mul((float3x3)ubo.model, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Desaturate color + float3 color = lerp(input.Color, dot(float3(0.2126,0.7152,0.0722), input.Color).xxx, 0.65); + + // High ambient colors because mesh materials are pretty dark + float3 ambient = color * float3(1.0, 1.0, 1.0); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * color; + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + + float intensity = dot(N,L); + float shade = 1.0; + shade = intensity < 0.5 ? 0.75 : shade; + shade = intensity < 0.35 ? 0.6 : shade; + shade = intensity < 0.25 ? 0.5 : shade; + shade = intensity < 0.1 ? 0.25 : shade; + + return float4(input.Color * 3.0 * shade, 1); +} \ No newline at end of file diff --git a/shaders/slang/pipelines/toon.vert.spv b/shaders/slang/pipelines/toon.vert.spv new file mode 100644 index 00000000..c5f07810 Binary files /dev/null and b/shaders/slang/pipelines/toon.vert.spv differ diff --git a/shaders/slang/pipelines/wireframe.frag.spv b/shaders/slang/pipelines/wireframe.frag.spv new file mode 100644 index 00000000..649fef4c Binary files /dev/null and b/shaders/slang/pipelines/wireframe.frag.spv differ diff --git a/shaders/slang/pipelines/wireframe.slang b/shaders/slang/pipelines/wireframe.slang new file mode 100644 index 00000000..4482be10 --- /dev/null +++ b/shaders/slang/pipelines/wireframe.slang @@ -0,0 +1,40 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, input.Pos)); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color * 1.5, 1); +} \ No newline at end of file diff --git a/shaders/slang/pipelines/wireframe.vert.spv b/shaders/slang/pipelines/wireframe.vert.spv new file mode 100644 index 00000000..4232d9dd Binary files /dev/null and b/shaders/slang/pipelines/wireframe.vert.spv differ diff --git a/shaders/slang/pipelinestatistics/scene.frag.spv b/shaders/slang/pipelinestatistics/scene.frag.spv new file mode 100644 index 00000000..3191b732 Binary files /dev/null and b/shaders/slang/pipelinestatistics/scene.frag.spv differ diff --git a/shaders/slang/pipelinestatistics/scene.slang b/shaders/slang/pipelinestatistics/scene.slang new file mode 100644 index 00000000..781f7f7f --- /dev/null +++ b/shaders/slang/pipelinestatistics/scene.slang @@ -0,0 +1,127 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct HSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct DSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct ConstantsHSOutput +{ + float TessLevelOuter[3] : SV_TessFactor; + float TessLevelInner[2] : SV_InsideTessFactor; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float4 lightPos; +}; +ConstantBuffer ubo; + +struct PushConsts { + float3 objPos; +}; +[[vk::push_constant]] PushConsts pushConsts; + +ConstantsHSOutput ConstantsHS(InputPatch patch) +{ + ConstantsHSOutput output; + output.TessLevelInner[0] = 2.0; + output.TessLevelInner[1] = 2.0; + output.TessLevelOuter[0] = 1.0; + output.TessLevelOuter[1] = 1.0; + output.TessLevelOuter[2] = 1.0; + return output; +} + +[shader("hull")] +[domain("tri")] +[partitioning("integer")] +[outputtopology("triangle_ccw")] +[outputcontrolpoints(3)] +[patchconstantfunc("ConstantsHS")] +[maxtessfactor(20.0f)] +HSOutput hullMain(InputPatch patch, uint InvocationID: SV_OutputControlPointID) +{ + HSOutput output; + output.Pos = patch[InvocationID].Pos; + output.Normal = patch[InvocationID].Normal; + output.Color = patch[InvocationID].Color; + output.ViewVec = patch[InvocationID].ViewVec; + output.LightVec = patch[InvocationID].LightVec; + return output; +} + +[shader("domain")] +[domain("tri")] +DSOutput domainMain(ConstantsHSOutput input, float3 TessCoord: SV_DomainLocation, const OutputPatch patch) +{ + DSOutput output; + output.Pos = (TessCoord.x * patch[2].Pos) + (TessCoord.y * patch[1].Pos) + (TessCoord.z * patch[0].Pos); + output.Normal = TessCoord.x * patch[2].Normal + TessCoord.y * patch[1].Normal + TessCoord.z * patch[0].Normal; + output.ViewVec = TessCoord.x * patch[2].ViewVec + TessCoord.y * patch[1].ViewVec + TessCoord.z * patch[0].ViewVec; + output.LightVec = TessCoord.x * patch[2].LightVec + TessCoord.y * patch[1].LightVec + TessCoord.z * patch[0].LightVec; + output.Color = patch[0].Color; + return output; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + float3 locPos = mul(ubo.modelview, float4(input.Pos, 1.0)).xyz; + float3 worldPos = mul(ubo.modelview, float4(input.Pos + pushConsts.objPos, 1.0)).xyz; + output.Pos = mul(ubo.projection, float4(worldPos, 1.0)); + float4 pos = mul(ubo.modelview, float4(worldPos, 1.0)); + output.Normal = mul((float3x3)ubo.modelview, input.Normal); + output.LightVec = ubo.lightPos.xyz - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 8.0) * float3(0.75, 0.75, 0.75); + return float4(diffuse + specular, 0.5); +} \ No newline at end of file diff --git a/shaders/slang/pipelinestatistics/scene.tesc.spv b/shaders/slang/pipelinestatistics/scene.tesc.spv new file mode 100644 index 00000000..321b6d58 Binary files /dev/null and b/shaders/slang/pipelinestatistics/scene.tesc.spv differ diff --git a/shaders/slang/pipelinestatistics/scene.tese.spv b/shaders/slang/pipelinestatistics/scene.tese.spv new file mode 100644 index 00000000..06ca6acf Binary files /dev/null and b/shaders/slang/pipelinestatistics/scene.tese.spv differ diff --git a/shaders/slang/pipelinestatistics/scene.vert.spv b/shaders/slang/pipelinestatistics/scene.vert.spv new file mode 100644 index 00000000..019aef42 Binary files /dev/null and b/shaders/slang/pipelinestatistics/scene.vert.spv differ diff --git a/shaders/slang/pushconstants/pushconstants.frag.spv b/shaders/slang/pushconstants/pushconstants.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/pushconstants/pushconstants.frag.spv differ diff --git a/shaders/slang/pushconstants/pushconstants.slang b/shaders/slang/pushconstants/pushconstants.slang new file mode 100644 index 00000000..8b318b88 --- /dev/null +++ b/shaders/slang/pushconstants/pushconstants.slang @@ -0,0 +1,44 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos : POSITION0; + float3 Normal; + float3 Color; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; +}; +ConstantBuffer ubo; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +// Uniform entry-point parameters are automatically bound to push constants by slang +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4 pushColor, uniform float4 pushPosition) +{ + VSOutput output; + output.Color = input.Color * pushColor.rgb; + float3 locPos = float3(mul(ubo.model, float4(input.Pos.xyz, 1.0)).xyz); + float3 worldPos = locPos + pushPosition.xyz; + output.Pos = mul(ubo.projection, mul(ubo.view, float4(worldPos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pushconstants/pushconstants.vert.spv b/shaders/slang/pushconstants/pushconstants.vert.spv new file mode 100644 index 00000000..cebd2a2f Binary files /dev/null and b/shaders/slang/pushconstants/pushconstants.vert.spv differ diff --git a/shaders/slang/pushdescriptors/cube.frag.spv b/shaders/slang/pushdescriptors/cube.frag.spv new file mode 100644 index 00000000..26d7f3dc Binary files /dev/null and b/shaders/slang/pushdescriptors/cube.frag.spv differ diff --git a/shaders/slang/pushdescriptors/cube.slang b/shaders/slang/pushdescriptors/cube.slang new file mode 100644 index 00000000..372c5eaa --- /dev/null +++ b/shaders/slang/pushdescriptors/cube.slang @@ -0,0 +1,51 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; +}; + +struct UBOCamera { + float4x4 projection; + float4x4 view; +}; +ConstantBuffer uboCamera; + +struct UBOModel { + float4x4 local; +}; +ConstantBuffer uboModel; + +Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(uboCamera.projection, mul(uboCamera.view, mul(uboModel.local, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/pushdescriptors/cube.vert.spv b/shaders/slang/pushdescriptors/cube.vert.spv new file mode 100644 index 00000000..fe201558 Binary files /dev/null and b/shaders/slang/pushdescriptors/cube.vert.spv differ diff --git a/shaders/slang/radialblur/colorpass.frag.spv b/shaders/slang/radialblur/colorpass.frag.spv new file mode 100644 index 00000000..137bb8aa Binary files /dev/null and b/shaders/slang/radialblur/colorpass.frag.spv differ diff --git a/shaders/slang/radialblur/colorpass.slang b/shaders/slang/radialblur/colorpass.slang new file mode 100644 index 00000000..c36de69e --- /dev/null +++ b/shaders/slang/radialblur/colorpass.slang @@ -0,0 +1,51 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float gradientPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerGradientRamp; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.UV = float2(ubo.gradientPos, 0.0f); + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Use max. color channel value to detect bright glow emitters + if ((input.Color.r >= 0.9) || (input.Color.g >= 0.9) || (input.Color.b >= 0.9)) + { + return float4(samplerGradientRamp.Sample(input.UV).rgb, 1); + } else { + return float4(input.Color, 1); + } +} \ No newline at end of file diff --git a/shaders/slang/radialblur/colorpass.vert.spv b/shaders/slang/radialblur/colorpass.vert.spv new file mode 100644 index 00000000..a99056dc Binary files /dev/null and b/shaders/slang/radialblur/colorpass.vert.spv differ diff --git a/shaders/slang/radialblur/phongpass.frag.spv b/shaders/slang/radialblur/phongpass.frag.spv new file mode 100644 index 00000000..d975a832 Binary files /dev/null and b/shaders/slang/radialblur/phongpass.frag.spv differ diff --git a/shaders/slang/radialblur/phongpass.slang b/shaders/slang/radialblur/phongpass.slang new file mode 100644 index 00000000..7569146d --- /dev/null +++ b/shaders/slang/radialblur/phongpass.slang @@ -0,0 +1,67 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 EyePos; + float3 LightVec; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float gradientPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerGradientRamp; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = float2(ubo.gradientPos, 0.0); + output.Pos = mul(ubo.projection, mul(ubo.model, input.Pos)); + output.EyePos = mul(ubo.model, input.Pos).xyz; + float4 lightPos = float4(0.0, 0.0, -5.0, 1.0); // * ubo.model; + output.LightVec = normalize(lightPos.xyz - input.Pos.xyz); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // No light calculations for glow color + // Use max. color channel value + // to detect bright glow emitters + if ((input.Color.r >= 0.9) || (input.Color.g >= 0.9) || (input.Color.b >= 0.9)) + { + return float4(samplerGradientRamp.Sample(input.UV).rgb, 1); + } else { + float3 Eye = normalize(-input.EyePos); + float3 Reflected = normalize(reflect(-input.LightVec, input.Normal)); + float4 IAmbient = float4(0.2, 0.2, 0.2, 1.0); + float4 IDiffuse = float4(0.5, 0.5, 0.5, 0.5) * max(dot(input.Normal, input.LightVec), 0.0); + float specular = 0.25; + float4 ISpecular = float4(0.5, 0.5, 0.5, 1.0) * pow(max(dot(Reflected, Eye), 0.0), 4.0) * specular; + return float4((IAmbient + IDiffuse) * float4(input.Color, 1.0) + ISpecular); + } +} \ No newline at end of file diff --git a/shaders/slang/radialblur/phongpass.vert.spv b/shaders/slang/radialblur/phongpass.vert.spv new file mode 100644 index 00000000..65c33a6d Binary files /dev/null and b/shaders/slang/radialblur/phongpass.vert.spv differ diff --git a/shaders/slang/radialblur/radialblur.frag.spv b/shaders/slang/radialblur/radialblur.frag.spv new file mode 100644 index 00000000..26486d4a Binary files /dev/null and b/shaders/slang/radialblur/radialblur.frag.spv differ diff --git a/shaders/slang/radialblur/radialblur.slang b/shaders/slang/radialblur/radialblur.slang new file mode 100644 index 00000000..8fefdf16 --- /dev/null +++ b/shaders/slang/radialblur/radialblur.slang @@ -0,0 +1,48 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float radialBlurScale; + float radialBlurStrength; + float2 radialOrigin; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + int2 texDim; + samplerColor.GetDimensions(texDim.x, texDim.y); + float2 radialSize = float2(1.0 / texDim.x, 1.0 / texDim.y); + float2 UV = input.UV; + float4 color = float4(0.0, 0.0, 0.0, 0.0); + UV += radialSize * 0.5 - ubo.radialOrigin; + #define samples 32 + for (int i = 0; i < samples; i++) + { + float scale = 1.0 - ubo.radialBlurScale * (float(i) / float(samples - 1)); + color += samplerColor.Sample(UV * scale + ubo.radialOrigin); + } + return (color / samples) * ubo.radialBlurStrength; +} \ No newline at end of file diff --git a/shaders/slang/radialblur/radialblur.vert.spv b/shaders/slang/radialblur/radialblur.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/radialblur/radialblur.vert.spv differ diff --git a/shaders/slang/rayquery/scene.frag.spv b/shaders/slang/rayquery/scene.frag.spv new file mode 100644 index 00000000..24fea2a3 Binary files /dev/null and b/shaders/slang/rayquery/scene.frag.spv differ diff --git a/shaders/slang/rayquery/scene.slang b/shaders/slang/rayquery/scene.slang new file mode 100644 index 00000000..5b061c62 --- /dev/null +++ b/shaders/slang/rayquery/scene.slang @@ -0,0 +1,81 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Color; + float3 Normal; +} + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; + float3 WorldPos; +} + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float3 lightPos; +}; +ConstantBuffer ubo; + +RaytracingAccelerationStructure accelStruct; + +#define ambient 0.1 + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.WorldPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.Normal = mul((float4x3)ubo.model, input.Normal).xyz; + output.LightVec = normalize(ubo.lightPos - input.Pos); + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = normalize(-reflect(L, N)); + float3 diffuse = max(dot(N, L), ambient) * input.Color; + + float4 color = float4(diffuse, 1.0); + + RayDesc rayDesc; + rayDesc.Origin = input.WorldPos; + rayDesc.Direction = L; + rayDesc.TMin = 0.01; + rayDesc.TMax = 1000.0; + + RayQuery rayQuery; + rayQuery.TraceRayInline(accelStruct, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xFF, rayDesc); + + // Traverse the acceleration structure and store information about the first intersection (if any) + rayQuery.Proceed(); + + // If the intersection has hit a triangle, the fragment is shadowed + if (rayQuery.CommittedStatus() == COMMITTED_TRIANGLE_HIT) { + color *= 0.1; + } + + return color; +} diff --git a/shaders/slang/rayquery/scene.vert.spv b/shaders/slang/rayquery/scene.vert.spv new file mode 100644 index 00000000..86b91b87 Binary files /dev/null and b/shaders/slang/rayquery/scene.vert.spv differ diff --git a/shaders/slang/raytracingbasic/closesthit.rchit.spv b/shaders/slang/raytracingbasic/closesthit.rchit.spv new file mode 100644 index 00000000..dbc3087b Binary files /dev/null and b/shaders/slang/raytracingbasic/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingbasic/miss.rmiss.spv b/shaders/slang/raytracingbasic/miss.rmiss.spv new file mode 100644 index 00000000..8d1e7b12 Binary files /dev/null and b/shaders/slang/raytracingbasic/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingbasic/raygen.rgen.spv b/shaders/slang/raytracingbasic/raygen.rgen.spv new file mode 100644 index 00000000..84a72044 Binary files /dev/null and b/shaders/slang/raytracingbasic/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingbasic/raytracingbasic.slang b/shaders/slang/raytracingbasic/raytracingbasic.slang new file mode 100644 index 00000000..49d8182d --- /dev/null +++ b/shaders/slang/raytracingbasic/raytracingbasic.slang @@ -0,0 +1,60 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; +struct CameraProperties +{ + float4x4 viewInverse; + float4x4 projInverse; +}; +ConstantBuffer cam; + +struct Attributes +{ + float2 bary; +}; + +struct Payload +{ + float3 hitValue; +}; + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter/float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(cam.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(cam.viewInverse, float4(0,0,0,1)).xyz; + rayDesc.Direction = mul(cam.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + TraceRay(accelStruct, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout Payload p, in Attributes attribs) +{ + const float3 barycentricCoords = float3(1.0f - attribs.bary.x - attribs.bary.y, attribs.bary.x, attribs.bary.y); + p.hitValue = barycentricCoords; +} + +[shader("miss")] +void missMain(inout Payload p) +{ + p.hitValue = float3(0.0, 0.0, 0.2); +} diff --git a/shaders/slang/raytracingcallable/callable1.rcall.spv b/shaders/slang/raytracingcallable/callable1.rcall.spv new file mode 100644 index 00000000..c9709d8f Binary files /dev/null and b/shaders/slang/raytracingcallable/callable1.rcall.spv differ diff --git a/shaders/slang/raytracingcallable/callable1.slang b/shaders/slang/raytracingcallable/callable1.slang new file mode 100644 index 00000000..afacce9a --- /dev/null +++ b/shaders/slang/raytracingcallable/callable1.slang @@ -0,0 +1,14 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[shader("callable")] +void callableMain(inout float3 outColor) +{ + // Generate a checker board pattern + float2 pos = float2(DispatchRaysIndex().x / 8, DispatchRaysIndex().y / 8); + float col = (pos.x + (pos.y % 2.0)) % 2.0; + outColor = float3(col, col, col); +} \ No newline at end of file diff --git a/shaders/slang/raytracingcallable/callable2.rcall.spv b/shaders/slang/raytracingcallable/callable2.rcall.spv new file mode 100644 index 00000000..282018ee Binary files /dev/null and b/shaders/slang/raytracingcallable/callable2.rcall.spv differ diff --git a/shaders/slang/raytracingcallable/callable2.slang b/shaders/slang/raytracingcallable/callable2.slang new file mode 100644 index 00000000..de51b24d --- /dev/null +++ b/shaders/slang/raytracingcallable/callable2.slang @@ -0,0 +1,11 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[shader("callable")] +void callableMain(inout float3 outColor) +{ + outColor = float3(0.0, 1.0, 0.0); +} \ No newline at end of file diff --git a/shaders/slang/raytracingcallable/callable3.rcall.spv b/shaders/slang/raytracingcallable/callable3.rcall.spv new file mode 100644 index 00000000..fa818478 Binary files /dev/null and b/shaders/slang/raytracingcallable/callable3.rcall.spv differ diff --git a/shaders/slang/raytracingcallable/callable3.slang b/shaders/slang/raytracingcallable/callable3.slang new file mode 100644 index 00000000..75ca336a --- /dev/null +++ b/shaders/slang/raytracingcallable/callable3.slang @@ -0,0 +1,14 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[shader("callable")] +void callableMain(inout float3 outColor) +{ + // Generate a checker board pattern + float2 pos = float2(DispatchRaysIndex().x / 8, DispatchRaysIndex().y / 8); + float col = pos.y % 2.0; + outColor = float3(col, col, col); +} \ No newline at end of file diff --git a/shaders/slang/raytracingcallable/closesthit.rchit.spv b/shaders/slang/raytracingcallable/closesthit.rchit.spv new file mode 100644 index 00000000..17922e44 Binary files /dev/null and b/shaders/slang/raytracingcallable/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingcallable/miss.rmiss.spv b/shaders/slang/raytracingcallable/miss.rmiss.spv new file mode 100644 index 00000000..8d1e7b12 Binary files /dev/null and b/shaders/slang/raytracingcallable/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingcallable/raygen.rgen.spv b/shaders/slang/raytracingcallable/raygen.rgen.spv new file mode 100644 index 00000000..84a72044 Binary files /dev/null and b/shaders/slang/raytracingcallable/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingcallable/raytracingcallable.slang b/shaders/slang/raytracingcallable/raytracingcallable.slang new file mode 100644 index 00000000..e125f455 --- /dev/null +++ b/shaders/slang/raytracingcallable/raytracingcallable.slang @@ -0,0 +1,68 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; +struct CameraProperties +{ + float4x4 viewInverse; + float4x4 projInverse; +}; +ConstantBuffer cam; + +struct Payload +{ + float3 hitValue; +}; + +struct CallData +{ + float3 outColor; +}; + +struct Attributes +{ + float2 bary; +}; + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(cam.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(cam.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(cam.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + TraceRay(accelStruct, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout Payload p, in Attributes attribs) +{ + // Execute the callable shader indexed by the current geometry being hit + // For our sample this means that the first callable shader in the SBT is invoked for the first triangle, the second callable shader for the second triangle, etc. + CallData callData; + CallShader(GeometryIndex(), callData); + p.hitValue = callData.outColor; +} + +[shader("miss")] +void missMain(inout Payload p) +{ + p.hitValue = float3(0.0, 0.0, 0.2); +} \ No newline at end of file diff --git a/shaders/slang/raytracinggltf/anyhit.rahit.spv b/shaders/slang/raytracinggltf/anyhit.rahit.spv new file mode 100644 index 00000000..1d38b30b Binary files /dev/null and b/shaders/slang/raytracinggltf/anyhit.rahit.spv differ diff --git a/shaders/slang/raytracinggltf/closesthit.rchit.spv b/shaders/slang/raytracinggltf/closesthit.rchit.spv new file mode 100644 index 00000000..9499a6ed Binary files /dev/null and b/shaders/slang/raytracinggltf/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracinggltf/miss.rmiss.spv b/shaders/slang/raytracinggltf/miss.rmiss.spv new file mode 100644 index 00000000..d35423b7 Binary files /dev/null and b/shaders/slang/raytracinggltf/miss.rmiss.spv differ diff --git a/shaders/slang/raytracinggltf/raygen.rgen.spv b/shaders/slang/raytracinggltf/raygen.rgen.spv new file mode 100644 index 00000000..1f69b26c Binary files /dev/null and b/shaders/slang/raytracinggltf/raygen.rgen.spv differ diff --git a/shaders/slang/raytracinggltf/raytracinggltf.slang b/shaders/slang/raytracinggltf/raytracinggltf.slang new file mode 100644 index 00000000..76f2a832 --- /dev/null +++ b/shaders/slang/raytracinggltf/raytracinggltf.slang @@ -0,0 +1,219 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Payload +{ + float3 hitValue; + uint payloadSeed; + bool shadowed; +}; + +struct GeometryNode { + ConstBufferPointer vertices; + ConstBufferPointer indices; + int textureIndexBaseColor; + int textureIndexOcclusion; +}; + +struct UBOCameraProperties { + float4x4 viewInverse; + float4x4 projInverse; + uint frame; +} + +[[vk::binding(0, 0)]] RaytracingAccelerationStructure accelStruct; +[[vk::binding(1, 0)]] RWTexture2D image; +[[vk::binding(2, 0)]] ConstantBuffer cam; +[[vk::binding(4, 0)]] StructuredBuffer geometryNodes; +[[vk::binding(5, 0)]] Sampler2D textures[]; + +struct Vertex +{ + float3 pos; + float3 normal; + float2 uv; +}; + +struct Triangle { + Vertex vertices[3]; + float3 normal; + float2 uv; +}; + +struct Attributes +{ + float2 bary; +}; + +// Tiny Encryption Algorithm +// By Fahad Zafar, Marc Olano and Aaron Curtis, see https://www.highperformancegraphics.org/previous/www_2010/media/GPUAlgorithms/HPG2010_GPUAlgorithms_Zafar.pdf +uint tea(uint val0, uint val1) +{ + uint sum = 0; + uint v0 = val0; + uint v1 = val1; + for (uint n = 0; n < 16; n++) + { + sum += 0x9E3779B9; + v0 += ((v1 << 4) + 0xA341316C) ^ (v1 + sum) ^ ((v1 >> 5) + 0xC8013EA4); + v1 += ((v0 << 4) + 0xAD90777D) ^ (v0 + sum) ^ ((v0 >> 5) + 0x7E95761E); + } + return v0; +} + +// Linear congruential generator based on the previous RNG state +// See https://en.wikipedia.org/wiki/Linear_congruential_generator +uint lcg(inout uint previous) +{ + const uint multiplier = 1664525u; + const uint increment = 1013904223u; + previous = (multiplier * previous + increment); + return previous & 0x00FFFFFF; +} + +// Generate a random float in [0, 1) given the previous RNG state +float rnd(inout uint previous) +{ + return (float(lcg(previous)) / float(0x01000000)); +} + +// This function will unpack our vertex buffer data into a single triangle and calculates uv coordinates +Triangle unpackTriangle(uint index, Attributes attribs) { + Triangle tri; + const uint triIndex = index * 3; + const uint vertexsize = 112; + + GeometryNode geometryNode = geometryNodes[GeometryIndex()]; + + // Indices indices = Indices(geometryNode.indexBufferDeviceAddress); + // Vertices vertices = Vertices(geometryNode.vertexBufferDeviceAddress); + + // Unpack vertices + // Data is packed as float4 so we can map to the glTF vertex structure from the host side + // We match vkglTF::Vertex: pos.xyz+normal.x, normalyz+uv.xy + // glm::float3 pos; + // glm::float3 normal; + // glm::float2 uv; + // ... + for (uint i = 0; i < 3; i++) { + const uint offset = geometryNode.indices[triIndex + i] * 6; + float4 d0 = geometryNode.vertices[offset + 0]; // pos.xyz, n.x + float4 d1 = geometryNode.vertices[offset + 1]; // n.yz, uv.xy + tri.vertices[i].pos = d0.xyz; + tri.vertices[i].normal = float3(d0.w, d1.xy); + tri.vertices[i].uv = float2(d1.z, d1.w); + } + // Calculate values at barycentric coordinates + float3 barycentricCoords = float3(1.0f - attribs.bary.x - attribs.bary.y, attribs.bary.x, attribs.bary.y); + tri.uv = tri.vertices[0].uv * barycentricCoords.x + tri.vertices[1].uv * barycentricCoords.y + tri.vertices[2].uv * barycentricCoords.z; + tri.normal = tri.vertices[0].normal * barycentricCoords.x + tri.vertices[1].normal * barycentricCoords.y + tri.vertices[2].normal * barycentricCoords.z; + return tri; +} + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + uint seed = tea(LaunchID.y * LaunchSize.x + LaunchID.x, cam.frame); + + float r1 = rnd(seed); + float r2 = rnd(seed); + + // Subpixel jitter: send the ray through a different position inside the pixel + // each time, to provide antialiasing. + float2 subpixel_jitter = cam.frame == 0 ? float2(0.5f, 0.5f) : float2(r1, r2); + const float2 pixelCenter = float2(LaunchID.xy) + subpixel_jitter; + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + + float4 target = mul(cam.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(cam.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(cam.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + payload.hitValue = float3(0.0); + float3 hitValues = float3(0); + + const int samples = 4; + + // Trace multiple rays for e.g. transparency + for (int smpl = 0; smpl < samples; smpl++) { + payload.payloadSeed = tea(LaunchID.y * LaunchSize.x + LaunchID.x, cam.frame); + TraceRay(accelStruct, RAY_FLAG_NONE, 0xff, 0, 0, 0, rayDesc, payload); + hitValues += payload.hitValue; + } + + float3 hitVal = hitValues / float(samples); + + if (cam.frame > 0) + { + float a = 1.0f / float(cam.frame + 1); + float3 old_color = image[int2(LaunchID.xy)].xyz; + image[int2(LaunchID.xy)] = float4(lerp(old_color, hitVal, a), 1.0f); + } + else + { + // First frame, replace the value in the buffer + image[int2(LaunchID.xy)] = float4(hitVal, 1.0f); + } +} + +[shader("closesthit")] +void closesthitMain(inout Payload payload, in Attributes attribs) +{ + Triangle tri = unpackTriangle(PrimitiveIndex(), attribs); + payload.hitValue = float3(tri.normal); + + GeometryNode geometryNode = geometryNodes[GeometryIndex()]; + + float3 color = textures[NonUniformResourceIndex(geometryNode.textureIndexBaseColor)].SampleLevel(tri.uv, 0.0).rgb; + if (geometryNode.textureIndexOcclusion > -1) { + float occlusion = textures[NonUniformResourceIndex(geometryNode.textureIndexOcclusion)].SampleLevel(tri.uv, 0.0).r; + color *= occlusion; + } + + payload.hitValue = color; + + // Shadow casting + float tmin = 0.001; + float tmax = 10000.0; + float epsilon = 0.001; + float3 origin = WorldRayOrigin() + WorldRayDirection() * RayTCurrent() + tri.normal * epsilon; + payload.shadowed = true; + float3 lightVector = float3(-5.0, -2.5, -5.0); + // Trace shadow ray and offset indices to match shadow hit/miss shader group indices + // traceRayEXT(topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT, 0xFF, 0, 0, 1, origin, tmin, lightVector, tmax, 2); + // if (shadowed) { + // hitValue *= 0.7; + // } +} + +[shader("anyhit")] +void anyhitMain(inout Payload payload, in Attributes attribs) +{ + Triangle tri = unpackTriangle(PrimitiveIndex(), attribs); + GeometryNode geometryNode = geometryNodes[GeometryIndex()]; + float4 color = textures[NonUniformResourceIndex(geometryNode.textureIndexBaseColor)].SampleLevel(tri.uv, 0.0); + // If the alpha value of the texture at the current UV coordinates is below a given threshold, we'll ignore this intersection + // That way ray traversal will be stopped and the miss shader will be invoked + if (color.a < 0.9) { + if (rnd(payload.payloadSeed) > color.a) { + IgnoreHit(); + } + } +} + +[shader("miss")] +void missMain(inout Payload payload) +{ + payload.hitValue = float3(1.0); +} \ No newline at end of file diff --git a/shaders/slang/raytracinggltf/shadow.rmiss.spv b/shaders/slang/raytracinggltf/shadow.rmiss.spv new file mode 100644 index 00000000..fdd345de Binary files /dev/null and b/shaders/slang/raytracinggltf/shadow.rmiss.spv differ diff --git a/shaders/slang/raytracinggltf/shadow.slang b/shaders/slang/raytracinggltf/shadow.slang new file mode 100644 index 00000000..481816ad --- /dev/null +++ b/shaders/slang/raytracinggltf/shadow.slang @@ -0,0 +1,18 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Payload +{ + float3 hitValue; + uint payloadSeed; + bool shadowed; +}; + +[shader("miss")] +void missMain(inout Payload payload) +{ + payload.shadowed = false; +} \ No newline at end of file diff --git a/shaders/slang/raytracingintersection/closesthit.rchit.spv b/shaders/slang/raytracingintersection/closesthit.rchit.spv new file mode 100644 index 00000000..7cec8e57 Binary files /dev/null and b/shaders/slang/raytracingintersection/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingintersection/intersection.rint.spv b/shaders/slang/raytracingintersection/intersection.rint.spv new file mode 100644 index 00000000..d06139f7 Binary files /dev/null and b/shaders/slang/raytracingintersection/intersection.rint.spv differ diff --git a/shaders/slang/raytracingintersection/miss.rmiss.spv b/shaders/slang/raytracingintersection/miss.rmiss.spv new file mode 100644 index 00000000..8d1e7b12 Binary files /dev/null and b/shaders/slang/raytracingintersection/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingintersection/raygen.rgen.spv b/shaders/slang/raytracingintersection/raygen.rgen.spv new file mode 100644 index 00000000..a49cf0da Binary files /dev/null and b/shaders/slang/raytracingintersection/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingintersection/raytracingintersection.slang b/shaders/slang/raytracingintersection/raytracingintersection.slang new file mode 100644 index 00000000..43251132 --- /dev/null +++ b/shaders/slang/raytracingintersection/raytracingintersection.slang @@ -0,0 +1,96 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Payload +{ + float3 hitValue; +} + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; + +struct UBO +{ + float4x4 viewInverse; + float4x4 projInverse; + float4 lightPos; +}; +ConstantBuffer ubo; + +struct Sphere { + float3 center; + float radius; + float4 color; +}; +StructuredBuffer spheres; + +// Ray-sphere intersection +// By Inigo Quilez, from https://iquilezles.org/articles/spherefunctions/ +float sphIntersect(const Sphere s, float3 ro, float3 rd) +{ + float3 oc = ro - s.center; + float b = dot(oc, rd); + float c = dot(oc, oc) - s.radius * s.radius; + float h = b * b - c; + if (h < 0.0) { + return -1.0; + } + h = sqrt(h); + return -b - h; +} + +[shader("intersection")] +void intersectionMain() { + Sphere sphere = spheres[PrimitiveIndex()]; + float hit = sphIntersect(sphere, WorldRayOrigin(), WorldRayDirection()); + + if (hit > 0) { + ReportHit(hit, 0, 0); + } +} + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(ubo.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(ubo.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(ubo.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + TraceRay(accelStruct, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout Payload payload) +{ + Sphere sphere = spheres[PrimitiveIndex()]; + + float3 worldPos = WorldRayOrigin() + WorldRayDirection() * RayTCurrent(); + float3 worldNormal = normalize(worldPos - sphere.center); + + // Basic lighting + float3 lightVector = normalize(ubo.lightPos.xyz); + float dot_product = max(dot(lightVector, worldNormal), 0.2); + payload.hitValue = sphere.color.rgb * dot_product; +} + +[shader("miss")] +void missMain(inout Payload payload) +{ + payload.hitValue = float3(0.0, 0.0, 0.2); +} \ No newline at end of file diff --git a/shaders/slang/raytracingpositionfetch/closesthit.rchit.spv b/shaders/slang/raytracingpositionfetch/closesthit.rchit.spv new file mode 100644 index 00000000..273d9ef9 Binary files /dev/null and b/shaders/slang/raytracingpositionfetch/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingpositionfetch/miss.rmiss.spv b/shaders/slang/raytracingpositionfetch/miss.rmiss.spv new file mode 100644 index 00000000..8d1e7b12 Binary files /dev/null and b/shaders/slang/raytracingpositionfetch/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingpositionfetch/raygen.rgen.spv b/shaders/slang/raytracingpositionfetch/raygen.rgen.spv new file mode 100644 index 00000000..a49cf0da Binary files /dev/null and b/shaders/slang/raytracingpositionfetch/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingpositionfetch/raytracingpositionfetch.slang b/shaders/slang/raytracingpositionfetch/raytracingpositionfetch.slang new file mode 100644 index 00000000..96c341e9 --- /dev/null +++ b/shaders/slang/raytracingpositionfetch/raytracingpositionfetch.slang @@ -0,0 +1,78 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Attributes +{ + float2 bary; +}; + +struct Payload +{ + [[vk::location(0)]] float3 hitValue; +}; + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; +struct UBO +{ + float4x4 viewInverse; + float4x4 projInverse; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(ubo.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(ubo.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(ubo.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + TraceRay(accelStruct, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout Payload payload, in Attributes attribs) +{ + // We need the barycentric coordinates to calculate data for the current position + const float3 barycentricCoords = float3(1.0f - attribs.bary.x - attribs.bary.y, attribs.bary.x, attribs.bary.y); + + // With VK_KHR_ray_tracing_position_fetch we can access the vertices for the hit triangle in the shader + + float3 vertexPos0 = HitTriangleVertexPosition(0); + float3 vertexPos1 = HitTriangleVertexPosition(1); + float3 vertexPos2 = HitTriangleVertexPosition(2); + float3 currentPos = vertexPos0 * barycentricCoords.x + vertexPos1 * barycentricCoords.y + vertexPos2 * barycentricCoords.z; + + // Calcualte the normal from above values + float3 normal = normalize(cross(vertexPos1 - vertexPos0, vertexPos2 - vertexPos0)); + normal = normalize(mul(float4(normal, 1.0), WorldToObject4x3())); + + // Basic lighting + float3 lightDir = normalize(ubo.lightPos.xyz - currentPos); + float diffuse = max(dot(normal, lightDir), 0.0); + + payload.hitValue.rgb = 0.1 + diffuse; +} + +[shader("miss")] +void missMain(inout Payload payload) +{ + payload.hitValue = float3(0.0, 0.0, 0.2); +} \ No newline at end of file diff --git a/shaders/slang/raytracingreflections/closesthit.rchit.spv b/shaders/slang/raytracingreflections/closesthit.rchit.spv new file mode 100644 index 00000000..af12d556 Binary files /dev/null and b/shaders/slang/raytracingreflections/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingreflections/miss.rmiss.spv b/shaders/slang/raytracingreflections/miss.rmiss.spv new file mode 100644 index 00000000..714feeb6 Binary files /dev/null and b/shaders/slang/raytracingreflections/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingreflections/raygen.rgen.spv b/shaders/slang/raytracingreflections/raygen.rgen.spv new file mode 100644 index 00000000..7712636d Binary files /dev/null and b/shaders/slang/raytracingreflections/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingreflections/raytracingreflections.slang b/shaders/slang/raytracingreflections/raytracingreflections.slang new file mode 100644 index 00000000..2e9260a9 --- /dev/null +++ b/shaders/slang/raytracingreflections/raytracingreflections.slang @@ -0,0 +1,144 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; +struct CameraProperties +{ + float4x4 viewInverse; + float4x4 projInverse; + float4 lightPos; + int vertexSize; +}; +ConstantBuffer ubo; +StructuredBuffer vertices; +StructuredBuffer indices; + +// Max. number of recursion is passed via a specialization constant +[SpecializationConstant] const int MAX_RECURSION = 0; + +struct Attributes +{ + float2 bary; +}; + +struct RayPayload +{ + float3 color; + float distance; + float3 normal; + float reflector; +}; + +struct Vertex +{ + float3 pos; + float3 normal; + float2 uv; + float4 color; + float4 _pad0; + float4 _pad1; +}; + +Vertex unpack(uint index) +{ + // Unpack the vertices from the SSBO using the glTF vertex structure + // The multiplier is the size of the vertex divided by four float components (=16 bytes) + const int m = ubo.vertexSize / 16; + + float4 d0 = vertices[m * index + 0]; + float4 d1 = vertices[m * index + 1]; + float4 d2 = vertices[m * index + 2]; + + Vertex v; + v.pos = d0.xyz; + v.normal = float3(d0.w, d1.x, d1.y); + v.color = float4(d2.x, d2.y, d2.z, 1.0); + + return v; +} + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(ubo.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(ubo.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(ubo.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + float3 color = float3(0.0, 0.0, 0.0); + + for (int i = 0; i < MAX_RECURSION; i++) { + RayPayload rayPayload; + TraceRay(accelStruct, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, rayPayload); + float3 hitColor = rayPayload.color; + + if (rayPayload.distance < 0.0f) { + color += hitColor; + break; + } else if (rayPayload.reflector == 1.0f) { + const float3 hitPos = rayDesc.Origin + rayDesc.Direction * rayPayload.distance; + rayDesc.Origin = hitPos + rayPayload.normal * 0.001f; + rayDesc.Direction = reflect(rayDesc.Direction, rayPayload.normal); + } else { + color += hitColor; + break; + } + } + + image[int2(LaunchID.xy)] = float4(color, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout RayPayload rayPayload, in Attributes attribs) +{ + uint PrimitiveID = PrimitiveIndex(); + int3 index = int3(indices[3 * PrimitiveID], indices[3 * PrimitiveID + 1], indices[3 * PrimitiveID + 2]); + + Vertex v0 = unpack(index.x); + Vertex v1 = unpack(index.y); + Vertex v2 = unpack(index.z); + + // Interpolate normal + const float3 barycentricCoords = float3(1.0f - attribs.bary.x - attribs.bary.y, attribs.bary.x, attribs.bary.y); + float3 normal = normalize(v0.normal * barycentricCoords.x + v1.normal * barycentricCoords.y + v2.normal * barycentricCoords.z); + + // Basic lighting + float3 lightVector = normalize(ubo.lightPos.xyz); + float dot_product = max(dot(lightVector, normal), 0.6); + rayPayload.color.rgb = v0.color.rgb * dot_product; + rayPayload.distance = RayTCurrent(); + rayPayload.normal = normal; + + // Objects with full white vertex color are treated as reflectors + rayPayload.reflector = ((v0.color.r == 1.0f) && (v0.color.g == 1.0f) && (v0.color.b == 1.0f)) ? 1.0f : 0.0f; +} + +[shader("miss")] +void missMain(inout RayPayload rayPayload) +{ + float3 worldRayDirection = WorldRayDirection(); + + // View-independent background gradient to simulate a basic sky background + const float3 gradientStart = float3(0.5, 0.6, 1.0); + const float3 gradientEnd = float3(1.0, 1.0, 1.0); + float3 unitDir = normalize(worldRayDirection); + float t = 0.5 * (unitDir.y + 1.0); + rayPayload.color = (1.0 - t) * gradientStart + t * gradientEnd; + + rayPayload.distance = -1.0f; + rayPayload.normal = float3(0, 0, 0); + rayPayload.reflector = 0.0f; +} \ No newline at end of file diff --git a/shaders/slang/raytracingsbtdata/closesthit.rchit.spv b/shaders/slang/raytracingsbtdata/closesthit.rchit.spv new file mode 100644 index 00000000..2271821b Binary files /dev/null and b/shaders/slang/raytracingsbtdata/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingsbtdata/miss.rmiss.spv b/shaders/slang/raytracingsbtdata/miss.rmiss.spv new file mode 100644 index 00000000..4c386b79 Binary files /dev/null and b/shaders/slang/raytracingsbtdata/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingsbtdata/raygen.rgen.spv b/shaders/slang/raytracingsbtdata/raygen.rgen.spv new file mode 100644 index 00000000..c215bd7b Binary files /dev/null and b/shaders/slang/raytracingsbtdata/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingsbtdata/raytracingsbtdata.slang b/shaders/slang/raytracingsbtdata/raytracingsbtdata.slang new file mode 100644 index 00000000..5ec0bc13 --- /dev/null +++ b/shaders/slang/raytracingsbtdata/raytracingsbtdata.slang @@ -0,0 +1,88 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Attributes +{ + float2 bary; +}; + +struct Payload +{ + float3 hitValue; +}; + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; +struct CameraProperties +{ + float4x4 viewInverse; + float4x4 projInverse; +}; +ConstantBuffer cam; + +struct SBT { + float r; + float g; + float b; +}; +[[vk::shader_record]] ConstantBuffer sbt; + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(cam.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(cam.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(cam.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + + // use border to demonstrate raygen record data + if (all(LaunchID.xy > int2(16, 16)) && all(LaunchID.xy < LaunchSize.xy - int2(16, 16))) + { + // Generate a checker board pattern to trace out rays or use hit record data + int2 pos = int2(LaunchID.xy / 16); + if (((pos.x + pos.y % 2) % 2) == 0) { + // This will set hit value to either hit or miss SBT record color + TraceRay(accelStruct, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + } + else { + // Set the hit value to the raygen SBT data + payload.hitValue = float3(sbt.r, sbt.g, sbt.b); + } + } + else { + // Set hit value to black + payload.hitValue = float3(0.0, 0.0, 0.0); + } + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout Payload payload, in Attributes attribs) +{ + // Update the hit value to the hit record SBT data associated with this + // geometry ID and ray ID + payload.hitValue = float3(sbt.r, sbt.g, sbt.g); +} + +[shader("miss")] +void missMain(inout Payload payload) +{ + // Update the hit value to the hit record SBT data associated with this + // miss record + payload.hitValue = float3(sbt.r, sbt.g, sbt.g); +} diff --git a/shaders/slang/raytracingshadows/closesthit.rchit.spv b/shaders/slang/raytracingshadows/closesthit.rchit.spv new file mode 100644 index 00000000..d3e84f32 Binary files /dev/null and b/shaders/slang/raytracingshadows/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingshadows/miss.rmiss.spv b/shaders/slang/raytracingshadows/miss.rmiss.spv new file mode 100644 index 00000000..7663e81e Binary files /dev/null and b/shaders/slang/raytracingshadows/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingshadows/payload.slang b/shaders/slang/raytracingshadows/payload.slang new file mode 100644 index 00000000..5391a15b --- /dev/null +++ b/shaders/slang/raytracingshadows/payload.slang @@ -0,0 +1,13 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +module payload; + +public struct Payload +{ + public float3 hitValue; + public bool shadowed; +}; \ No newline at end of file diff --git a/shaders/slang/raytracingshadows/raygen.rgen.spv b/shaders/slang/raytracingshadows/raygen.rgen.spv new file mode 100644 index 00000000..b0069949 Binary files /dev/null and b/shaders/slang/raytracingshadows/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingshadows/raytracingshadows.slang b/shaders/slang/raytracingshadows/raytracingshadows.slang new file mode 100644 index 00000000..a7bdb4ae --- /dev/null +++ b/shaders/slang/raytracingshadows/raytracingshadows.slang @@ -0,0 +1,117 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import payload; + +struct Attributes +{ + float2 bary; +}; + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; + +struct UBO +{ + float4x4 viewInverse; + float4x4 projInverse; + float4 lightPos; + int vertexSize; +}; +ConstantBuffer ubo; + +StructuredBuffer vertices; +StructuredBuffer indices; + +struct Vertex +{ + float3 pos; + float3 normal; + float2 uv; + float4 color; + float4 _pad0; + float4 _pad1; +}; + +Vertex unpack(uint index) +{ + // Unpack the vertices from the SSBO using the glTF vertex structure + // The multiplier is the size of the vertex divided by four float components (=16 bytes) + const int m = ubo.vertexSize / 16; + + float4 d0 = vertices[m * index + 0]; + float4 d1 = vertices[m * index + 1]; + float4 d2 = vertices[m * index + 2]; + + Vertex v; + v.pos = d0.xyz; + v.normal = float3(d0.w, d1.x, d1.y); + v.color = float4(d2.x, d2.y, d2.z, 1.0); + + return v; +} + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(ubo.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(ubo.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(ubo.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + TraceRay(accelStruct, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout Payload payload, in Attributes attribs) +{ + uint PrimitiveID = PrimitiveIndex(); + int3 index = int3(indices[3 * PrimitiveID], indices[3 * PrimitiveID + 1], indices[3 * PrimitiveID + 2]); + + Vertex v0 = unpack(index.x); + Vertex v1 = unpack(index.y); + Vertex v2 = unpack(index.z); + + // Interpolate normal + const float3 barycentricCoords = float3(1.0f - attribs.bary.x - attribs.bary.y, attribs.bary.x, attribs.bary.y); + float3 normal = normalize(v0.normal * barycentricCoords.x + v1.normal * barycentricCoords.y + v2.normal * barycentricCoords.z); + + // Basic lighting + float3 lightVector = normalize(ubo.lightPos.xyz); + float dot_product = max(dot(lightVector, normal), 0.2); + payload.hitValue = v0.color.rgb * dot_product; + + RayDesc rayDesc; + rayDesc.Origin = WorldRayOrigin() + WorldRayDirection() * RayTCurrent(); + rayDesc.Direction = lightVector; + rayDesc.TMin = 0.001; + rayDesc.TMax = 100.0; + + payload.shadowed = true; + // Offset indices to match shadow hit/miss index + TraceRay(accelStruct, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER, 0xff, 0, 0, 1, rayDesc, payload); + if (payload.shadowed) { + payload.hitValue *= 0.3; + } +} + +[shader("miss")] +void missMain(inout Payload payload) +{ + payload.hitValue = float3(0.0, 0.0, 0.2); +} \ No newline at end of file diff --git a/shaders/slang/raytracingshadows/shadow.rmiss.spv b/shaders/slang/raytracingshadows/shadow.rmiss.spv new file mode 100644 index 00000000..1f99cf01 Binary files /dev/null and b/shaders/slang/raytracingshadows/shadow.rmiss.spv differ diff --git a/shaders/slang/raytracingshadows/shadow.slang b/shaders/slang/raytracingshadows/shadow.slang new file mode 100644 index 00000000..d2149b84 --- /dev/null +++ b/shaders/slang/raytracingshadows/shadow.slang @@ -0,0 +1,14 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import payload; + +[shader("miss")] +void missMain(inout Payload payload) +{ + payload.shadowed = false; +} + diff --git a/shaders/slang/raytracingtextures/anyhit.rahit.spv b/shaders/slang/raytracingtextures/anyhit.rahit.spv new file mode 100644 index 00000000..021186f1 Binary files /dev/null and b/shaders/slang/raytracingtextures/anyhit.rahit.spv differ diff --git a/shaders/slang/raytracingtextures/closesthit.rchit.spv b/shaders/slang/raytracingtextures/closesthit.rchit.spv new file mode 100644 index 00000000..fcd3ddbb Binary files /dev/null and b/shaders/slang/raytracingtextures/closesthit.rchit.spv differ diff --git a/shaders/slang/raytracingtextures/miss.rmiss.spv b/shaders/slang/raytracingtextures/miss.rmiss.spv new file mode 100644 index 00000000..8d1e7b12 Binary files /dev/null and b/shaders/slang/raytracingtextures/miss.rmiss.spv differ diff --git a/shaders/slang/raytracingtextures/raygen.rgen.spv b/shaders/slang/raytracingtextures/raygen.rgen.spv new file mode 100644 index 00000000..9073f791 Binary files /dev/null and b/shaders/slang/raytracingtextures/raygen.rgen.spv differ diff --git a/shaders/slang/raytracingtextures/raytracingtextures.slang b/shaders/slang/raytracingtextures/raytracingtextures.slang new file mode 100644 index 00000000..469a1b5e --- /dev/null +++ b/shaders/slang/raytracingtextures/raytracingtextures.slang @@ -0,0 +1,114 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Vertex +{ + float3 pos; + float2 uv; +}; + +struct Triangle { + Vertex vertices[3]; + float2 uv; +}; + +struct BufferReferences { + // Pointer to the buffer with the scene's MVP matrix + ConstBufferPointer vertices; + // Pointer to the buffer for the data for each model + ConstBufferPointer indices; +}; +[[vk::push_constant]] BufferReferences bufferReferences; + +struct Payload +{ + float3 hitValue; +}; + +struct UBOCameraProperties { + float4x4 viewInverse; + float4x4 projInverse; +} + +RaytracingAccelerationStructure accelStruct; +RWTexture2D image; +ConstantBuffer cam; +Sampler2D samplerColor; + +struct Attributes +{ + float2 bary; +}; + +Triangle unpackTriangle(uint index, Attributes attribs) { + Triangle tri; + const uint triIndex = index * 3; + const uint vertexSize = 32; + + // Unpack vertices + // Data is packed as float4 so we can map to the glTF vertex structure from the host side + for (uint i = 0; i < 3; i++) { + const uint offset = bufferReferences.indices[triIndex + i] * (vertexSize / 16); + float4 d0 = bufferReferences.vertices[offset + 0]; // pos.xyz, n.x + float4 d1 = bufferReferences.vertices[offset + 1]; // n.yz, uv.xy + tri.vertices[i].pos = d0.xyz; + tri.vertices[i].uv = d1.zw; + } + // Calculate values at barycentric coordinates + float3 barycentricCoords = float3(1.0f - attribs.bary.x - attribs.bary.y, attribs.bary.x, attribs.bary.y); + tri.uv = tri.vertices[0].uv * barycentricCoords.x + tri.vertices[1].uv * barycentricCoords.y + tri.vertices[2].uv * barycentricCoords.z; + return tri; +} + +[shader("raygeneration")] +void raygenerationMain() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter / float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(cam.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(cam.viewInverse, float4(0, 0, 0, 1)).xyz; + rayDesc.Direction = mul(cam.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + Payload payload; + TraceRay(accelStruct, RAY_FLAG_NONE, 0xff, 0, 0, 0, rayDesc, payload); + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} + +[shader("closesthit")] +void closesthitMain(inout Payload payload, in Attributes attribs) +{ + Triangle tri = unpackTriangle(PrimitiveIndex(), attribs); + // Fetch the color for this ray hit from the texture at the current uv coordinates + float4 color = samplerColor.SampleLevel(tri.uv, 0.0); + payload.hitValue = color.rgb; +} + +[shader("anyhit")] +void anyhitMain(inout Payload payload, in Attributes attribs) +{ + Triangle tri = unpackTriangle(PrimitiveIndex(), attribs); + float4 color = samplerColor.SampleLevel(tri.uv, 0.0); + // If the alpha value of the texture at the current UV coordinates is below a given threshold, we'll ignore this intersection + // That way ray traversal will be stopped and the miss shader will be invoked + if (color.a < 0.9) { + IgnoreHit(); + } +} + +[shader("miss")] +void missMain(inout Payload payload) +{ + payload.hitValue = float3(0.0, 0.0, 0.2); +} \ No newline at end of file diff --git a/shaders/slang/renderheadless/triangle.frag.spv b/shaders/slang/renderheadless/triangle.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/renderheadless/triangle.frag.spv differ diff --git a/shaders/slang/renderheadless/triangle.slang b/shaders/slang/renderheadless/triangle.slang new file mode 100644 index 00000000..11fc0630 --- /dev/null +++ b/shaders/slang/renderheadless/triangle.slang @@ -0,0 +1,32 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 mvp) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(mvp, float4(input.Pos.xyz, 1.0)); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/renderheadless/triangle.vert.spv b/shaders/slang/renderheadless/triangle.vert.spv new file mode 100644 index 00000000..1459c82a Binary files /dev/null and b/shaders/slang/renderheadless/triangle.vert.spv differ diff --git a/shaders/slang/screenshot/mesh.frag.spv b/shaders/slang/screenshot/mesh.frag.spv new file mode 100644 index 00000000..37a0b45e Binary files /dev/null and b/shaders/slang/screenshot/mesh.frag.spv differ diff --git a/shaders/slang/screenshot/mesh.slang b/shaders/slang/screenshot/mesh.slang new file mode 100644 index 00000000..abbc7a36 --- /dev/null +++ b/shaders/slang/screenshot/mesh.slang @@ -0,0 +1,60 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + + struct VSInput + { + float3 Pos; + float3 Normal; + float3 Color; + }; + + struct VSOutput + { + float4 Pos : SV_POSITION; + float3 Color; + float3 Normal; + float3 ViewVec; + float3 LightVec; + }; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + + output.Normal = mul((float3x3)ubo.model, input.Normal); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + + const float3 lightPos = float3(1.0, -1.0, 1.0); + output.LightVec = lightPos.xyz - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + const float ambient = 0.1; + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0).rrr; + float3 specular = pow(max(dot(R, V), 0.0), 32.0); + return float4((ambient + diffuse) * input.Color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/screenshot/mesh.vert.spv b/shaders/slang/screenshot/mesh.vert.spv new file mode 100644 index 00000000..7f5e52b2 Binary files /dev/null and b/shaders/slang/screenshot/mesh.vert.spv differ diff --git a/shaders/slang/shaderobjects/phong.frag.spv b/shaders/slang/shaderobjects/phong.frag.spv new file mode 100644 index 00000000..37d29241 Binary files /dev/null and b/shaders/slang/shaderobjects/phong.frag.spv differ diff --git a/shaders/slang/shaderobjects/phong.slang b/shaders/slang/shaderobjects/phong.slang new file mode 100644 index 00000000..10e0e0d8 --- /dev/null +++ b/shaders/slang/shaderobjects/phong.slang @@ -0,0 +1,65 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float4x3)ubo.model, input.Normal).xyz; + float3 lPos = mul((float4x3)ubo.model, ubo.lightPos.xyz).xyz; + output.LightVec = lPos.xyz - pos.xyz; + output.ViewVec = -pos.xyz; + + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Desaturate color + float3 color = float3(lerp(input.Color, float3(dot(float3(0.2126, 0.7152, 0.0722), input.Color)), 0.65)); + + // High ambient colors because mesh materials are pretty dark + float3 ambient = color; + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * color; + float3 specular = pow(max(dot(R, V), 0.0), 32.0) * float3(0.35); + return float4(ambient + diffuse * 1.75 + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/shaderobjects/phong.vert.spv b/shaders/slang/shaderobjects/phong.vert.spv new file mode 100644 index 00000000..a9e98688 Binary files /dev/null and b/shaders/slang/shaderobjects/phong.vert.spv differ diff --git a/shaders/slang/shadowmapping/offscreen.frag.spv b/shaders/slang/shadowmapping/offscreen.frag.spv new file mode 100644 index 00000000..bd595cd4 Binary files /dev/null and b/shaders/slang/shadowmapping/offscreen.frag.spv differ diff --git a/shaders/slang/shadowmapping/offscreen.slang b/shaders/slang/shadowmapping/offscreen.slang new file mode 100644 index 00000000..decb80c3 --- /dev/null +++ b/shaders/slang/shadowmapping/offscreen.slang @@ -0,0 +1,23 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct UBO +{ + float4x4 depthMVP; +}; +ConstantBuffer ubo; + +[shader("vertex")] +float4 vertexMain(float3 Pos) : SV_POSITION +{ + return mul(ubo.depthMVP, float4(Pos, 1.0)); +} + +[shader("fragment")] +float4 fragmentMain() +{ + return float4(1.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/shadowmapping/offscreen.vert.spv b/shaders/slang/shadowmapping/offscreen.vert.spv new file mode 100644 index 00000000..a9c7c1db Binary files /dev/null and b/shaders/slang/shadowmapping/offscreen.vert.spv differ diff --git a/shaders/slang/shadowmapping/quad.frag.spv b/shaders/slang/shadowmapping/quad.frag.spv new file mode 100644 index 00000000..893e62cd Binary files /dev/null and b/shaders/slang/shadowmapping/quad.frag.spv differ diff --git a/shaders/slang/shadowmapping/quad.slang b/shaders/slang/shadowmapping/quad.slang new file mode 100644 index 00000000..c6237b1d --- /dev/null +++ b/shaders/slang/shadowmapping/quad.slang @@ -0,0 +1,48 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4x4 lightSpace; + float4 lightPos; + float zNear; + float zFar; +}; +ConstantBuffer ubo; +Sampler2D samplerColor; + +float LinearizeDepth(float depth) +{ + float n = ubo.zNear; + float f = ubo.zFar; + float z = depth; + return (2.0 * n) / (f + n - z * (f - n)); +} + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float depth = samplerColor.Sample(input.UV).r; + return float4((1.0 - LinearizeDepth(depth)).xxx, 1.0); +} diff --git a/shaders/slang/shadowmapping/quad.vert.spv b/shaders/slang/shadowmapping/quad.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/shadowmapping/quad.vert.spv differ diff --git a/shaders/slang/shadowmapping/scene.frag.spv b/shaders/slang/shadowmapping/scene.frag.spv new file mode 100644 index 00000000..4f8867db Binary files /dev/null and b/shaders/slang/shadowmapping/scene.frag.spv differ diff --git a/shaders/slang/shadowmapping/scene.slang b/shaders/slang/shadowmapping/scene.slang new file mode 100644 index 00000000..2f9395a6 --- /dev/null +++ b/shaders/slang/shadowmapping/scene.slang @@ -0,0 +1,116 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; + float4 ShadowCoord; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4x4 lightSpace; + float4 lightPos; + float zNear; + float zFar; +}; +ConstantBuffer ubo; +Sampler2D shadowMapSampler; + +[SpecializationConstant] const int enablePCF = 0; + +#define ambient 0.1 + +float textureProj(float4 shadowCoord, float2 off) +{ + float shadow = 1.0; + if ( shadowCoord.z > -1.0 && shadowCoord.z < 1.0 ) + { + float dist = shadowMapSampler.Sample(shadowCoord.xy + off).r; + if ( shadowCoord.w > 0.0 && dist < shadowCoord.z ) + { + shadow = ambient; + } + } + return shadow; +} + +float filterPCF(float4 sc) +{ + int2 texDim; + shadowMapSampler.GetDimensions(texDim.x, texDim.y); + float scale = 1.5; + float dx = scale * 1.0 / float(texDim.x); + float dy = scale * 1.0 / float(texDim.y); + + float shadowFactor = 0.0; + int count = 0; + int range = 1; + + for (int x = -range; x <= range; x++) + { + for (int y = -range; y <= range; y++) + { + shadowFactor += textureProj(sc, float2(dx*x, dy*y)); + count++; + } + + } + return shadowFactor / count; +} + +static const float4x4 biasMat = float4x4( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output = (VSOutput)0; + output.Color = input.Color; + output.Normal = input.Normal; + + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + output.LightVec = normalize(ubo.lightPos.xyz - input.Pos); + output.ViewVec = -pos.xyz; + + output.ShadowCoord = mul(biasMat, mul(ubo.lightSpace, mul(ubo.model, float4(input.Pos, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float shadow = (enablePCF == 1) ? filterPCF(input.ShadowCoord / input.ShadowCoord.w) : textureProj(input.ShadowCoord / input.ShadowCoord.w, float2(0.0, 0.0)); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = normalize(-reflect(L, N)); + float3 diffuse = max(dot(N, L), ambient) * input.Color; + + return float4(diffuse * shadow, 1.0); +} diff --git a/shaders/slang/shadowmapping/scene.vert.spv b/shaders/slang/shadowmapping/scene.vert.spv new file mode 100644 index 00000000..c1e316eb Binary files /dev/null and b/shaders/slang/shadowmapping/scene.vert.spv differ diff --git a/shaders/slang/shadowmappingcascade/debugshadowmap.frag.spv b/shaders/slang/shadowmappingcascade/debugshadowmap.frag.spv new file mode 100644 index 00000000..a5facaad Binary files /dev/null and b/shaders/slang/shadowmappingcascade/debugshadowmap.frag.spv differ diff --git a/shaders/slang/shadowmappingcascade/debugshadowmap.slang b/shaders/slang/shadowmappingcascade/debugshadowmap.slang new file mode 100644 index 00000000..925b7d51 --- /dev/null +++ b/shaders/slang/shadowmappingcascade/debugshadowmap.slang @@ -0,0 +1,29 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +[[vk::binding(1,0)]] Sampler2DArray shadowMapSampler; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input, uniform float4 meshPosition, uniform uint cascadeIndex) +{ + float depth = shadowMapSampler.Sample(float3(input.UV, float(cascadeIndex))).r; + return float4(depth.xxx, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/shadowmappingcascade/debugshadowmap.vert.spv b/shaders/slang/shadowmappingcascade/debugshadowmap.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/shadowmappingcascade/debugshadowmap.vert.spv differ diff --git a/shaders/slang/shadowmappingcascade/depthpass.frag.spv b/shaders/slang/shadowmappingcascade/depthpass.frag.spv new file mode 100644 index 00000000..ccca898c Binary files /dev/null and b/shaders/slang/shadowmappingcascade/depthpass.frag.spv differ diff --git a/shaders/slang/shadowmappingcascade/depthpass.slang b/shaders/slang/shadowmappingcascade/depthpass.slang new file mode 100644 index 00000000..287fe6c8 --- /dev/null +++ b/shaders/slang/shadowmappingcascade/depthpass.slang @@ -0,0 +1,46 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +// todo: pass via specialization constant +#define SHADOW_MAP_CASCADE_COUNT 4 + +struct UBO { + float4x4 cascadeViewProjMat[SHADOW_MAP_CASCADE_COUNT]; +}; +[[vk::binding(3, 0)]] ConstantBuffer ubo; + +[[vk::binding(0, 1)]] Sampler2D colorMapSampler : register(s0, space1); + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4 meshPosition, uniform uint cascadeIndex) +{ + VSOutput output; + output.UV = input.UV; + float3 pos = input.Pos + meshPosition.xyz; + output.Pos = mul(ubo.cascadeViewProjMat[cascadeIndex], float4(pos, 1.0)); + return output; +} + +[shader("fragment")] +void fragmentMain(VSOutput input) +{ + float alpha = colorMapSampler.Sample(input.UV).a; + if (alpha < 0.5) { + clip(-1); + } +} \ No newline at end of file diff --git a/shaders/slang/shadowmappingcascade/depthpass.vert.spv b/shaders/slang/shadowmappingcascade/depthpass.vert.spv new file mode 100644 index 00000000..020cdb0d Binary files /dev/null and b/shaders/slang/shadowmappingcascade/depthpass.vert.spv differ diff --git a/shaders/slang/shadowmappingcascade/scene.frag.spv b/shaders/slang/shadowmappingcascade/scene.frag.spv new file mode 100644 index 00000000..3bf83962 Binary files /dev/null and b/shaders/slang/shadowmappingcascade/scene.frag.spv differ diff --git a/shaders/slang/shadowmappingcascade/scene.slang b/shaders/slang/shadowmappingcascade/scene.slang new file mode 100644 index 00000000..8e4bdd56 --- /dev/null +++ b/shaders/slang/shadowmappingcascade/scene.slang @@ -0,0 +1,168 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#define SHADOW_MAP_CASCADE_COUNT 4 + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 ViewPos; + float3 WorldPos; + float2 UV; +}; + +[SpecializationConstant] const int enablePCF = 0; + +#define ambient 0.3 + +struct UBOScene { + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer uboScene; + +[[vk::binding(1, 0)]] Sampler2DArray shadowMapSampler; + +struct UBOCascades { + float4 cascadeSplits; + float4x4 inverseViewMat; + float3 lightDir; + float _pad; + int colorCascades; +}; +[[vk::binding(2, 0)]] ConstantBuffer uboCascades; + +struct CVPM { + float4x4 matrices[SHADOW_MAP_CASCADE_COUNT]; +}; +[[vk::binding(3, 0)]] ConstantBuffer cascadeViewProjMatrices; + +[[vk::binding(0, 1)]] Sampler2D colorMapSampler; + +static const float4x4 biasMat = float4x4( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 +); + +float textureProj(float4 shadowCoord, float2 offset, uint cascadeIndex) +{ + float shadow = 1.0; + float bias = 0.005; + + if (shadowCoord.z > -1.0 && shadowCoord.z < 1.0) { + float dist = shadowMapSampler.Sample(float3(shadowCoord.xy + offset, cascadeIndex)).r; + if (shadowCoord.w > 0 && dist < shadowCoord.z - bias) { + shadow = ambient; + } + } + return shadow; +} + +float filterPCF(float4 sc, uint cascadeIndex) +{ + int3 texDim; + shadowMapSampler.GetDimensions(texDim.x, texDim.y, texDim.z); + float scale = 0.75; + float dx = scale * 1.0 / float(texDim.x); + float dy = scale * 1.0 / float(texDim.y); + + float shadowFactor = 0.0; + int count = 0; + int range = 1; + + for (int x = -range; x <= range; x++) { + for (int y = -range; y <= range; y++) { + shadowFactor += textureProj(sc, float2(dx*x, dy*y), cascadeIndex); + count++; + } + } + return shadowFactor / count; +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4 meshPosition) +{ + VSOutput output; + output.Color = input.Color; + output.Normal = input.Normal; + output.UV = input.UV; + float3 pos = input.Pos + meshPosition.xyz; + output.WorldPos = pos; + output.ViewPos = mul(uboScene.view, float4(pos.xyz, 1.0)).xyz; + output.Pos = mul(uboScene.projection, mul(uboScene.view, mul(uboScene.model, float4(pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 outFragColor; + float4 color = colorMapSampler.Sample(input.UV); + if (color.a < 0.5) { + clip(-1); + } + + // Get cascade index for the current fragment's view position + uint cascadeIndex = 0; + for (uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; ++i) { + if (input.ViewPos.z < uboCascades.cascadeSplits[i]) { + cascadeIndex = i + 1; + } + } + + // Depth compare for shadowing + float4 shadowCoord = mul(biasMat, mul(cascadeViewProjMatrices.matrices[cascadeIndex], float4(input.WorldPos, 1.0))); + + float shadow = 0; + if (enablePCF == 1) { + shadow = filterPCF(shadowCoord / shadowCoord.w, cascadeIndex); + } else { + shadow = textureProj(shadowCoord / shadowCoord.w, float2(0.0, 0.0), cascadeIndex); + } + + // Directional light + float3 N = normalize(input.Normal); + float3 L = normalize(-uboCascades.lightDir); + float3 H = normalize(L + input.ViewPos); + float diffuse = max(dot(N, L), ambient); + float3 lightColor = float3(1.0, 1.0, 1.0); + outFragColor.rgb = max(lightColor * (diffuse * color.rgb), float3(0.0, 0.0, 0.0)); + outFragColor.rgb *= shadow; + outFragColor.a = color.a; + + // Color cascades (if enabled) + if (uboCascades.colorCascades == 1) { + switch(cascadeIndex) { + case 0 : + outFragColor.rgb *= float3(1.0f, 0.25f, 0.25f); + break; + case 1 : + outFragColor.rgb *= float3(0.25f, 1.0f, 0.25f); + break; + case 2 : + outFragColor.rgb *= float3(0.25f, 0.25f, 1.0f); + break; + case 3 : + outFragColor.rgb *= float3(1.0f, 1.0f, 0.25f); + break; + } + } + + return outFragColor; +} diff --git a/shaders/slang/shadowmappingcascade/scene.vert.spv b/shaders/slang/shadowmappingcascade/scene.vert.spv new file mode 100644 index 00000000..385bd66a Binary files /dev/null and b/shaders/slang/shadowmappingcascade/scene.vert.spv differ diff --git a/shaders/slang/shadowmappingomni/cubemapdisplay.frag.spv b/shaders/slang/shadowmappingomni/cubemapdisplay.frag.spv new file mode 100644 index 00000000..7942cd24 Binary files /dev/null and b/shaders/slang/shadowmappingomni/cubemapdisplay.frag.spv differ diff --git a/shaders/slang/shadowmappingomni/cubemapdisplay.slang b/shaders/slang/shadowmappingomni/cubemapdisplay.slang new file mode 100644 index 00000000..bb4b4a49 --- /dev/null +++ b/shaders/slang/shadowmappingomni/cubemapdisplay.slang @@ -0,0 +1,80 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; +}; +ConstantBuffer ubo; + +SamplerCube shadowCubeMapSampler; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV.xy * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 outFragColor = float4(0, 0, 0, 0); + outFragColor.rgb = float3(0.05, 0.05, 0.05); + + float3 samplePos = float3(0, 0, 0); + + // Crude statement to visualize different cube map faces based on UV coordinates + int x = int(floor(input.UV.x / 0.25f)); + int y = int(floor(input.UV.y / (1.0 / 3.0))); + if (y == 1) { + float2 uv = float2(input.UV.x * 4.0f, (input.UV.y - 1.0/3.0) * 3.0); + uv = 2.0 * float2(uv.x - float(x) * 1.0, uv.y) - 1.0; + switch (x) { + case 0: // NEGATIVE_X + samplePos = float3(-1.0f, uv.y, uv.x); + break; + case 1: // POSITIVE_Z + samplePos = float3(uv.x, uv.y, 1.0f); + break; + case 2: // POSITIVE_X + samplePos = float3(1.0, uv.y, -uv.x); + break; + case 3: // NEGATIVE_Z + samplePos = float3(-uv.x, uv.y, -1.0f); + break; + } + } else { + if (x == 1) { + float2 uv = float2((input.UV.x - 0.25) * 4.0, (input.UV.y - float(y) / 3.0) * 3.0); + uv = 2.0 * uv - 1.0; + switch (y) { + case 0: // NEGATIVE_Y + samplePos = float3(uv.x, -1.0f, uv.y); + break; + case 2: // POSITIVE_Y + samplePos = float3(uv.x, 1.0f, -uv.y); + break; + } + } + } + + if ((samplePos.x != 0.0f) && (samplePos.y != 0.0f)) { + float dist = length(shadowCubeMapSampler.Sample(samplePos).xyz) * 0.005; + outFragColor = float4(dist.xxx, 1.0); + } + return outFragColor; +} \ No newline at end of file diff --git a/shaders/slang/shadowmappingomni/cubemapdisplay.vert.spv b/shaders/slang/shadowmappingomni/cubemapdisplay.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/shadowmappingomni/cubemapdisplay.vert.spv differ diff --git a/shaders/slang/shadowmappingomni/offscreen.frag.spv b/shaders/slang/shadowmappingomni/offscreen.frag.spv new file mode 100644 index 00000000..01d9e3fc Binary files /dev/null and b/shaders/slang/shadowmappingomni/offscreen.frag.spv differ diff --git a/shaders/slang/shadowmappingomni/offscreen.slang b/shaders/slang/shadowmappingomni/offscreen.slang new file mode 100644 index 00000000..243447ea --- /dev/null +++ b/shaders/slang/shadowmappingomni/offscreen.slang @@ -0,0 +1,45 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float4 WorldPos; + float3 LightPos; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 view) +{ + VSOutput output; + output.Pos = mul(ubo.projection, mul(view, mul(ubo.model, float4(input.Pos, 1.0)))); + + output.WorldPos = float4(input.Pos, 1.0); + output.LightPos = ubo.lightPos.xyz; + return output; +} + +[shader("fragment")] +float fragmentMain(VSOutput input) +{ + // Store distance to light as 32 bit float value + float3 lightVec = input.WorldPos.xyz - input.LightPos; + return length(lightVec); +} \ No newline at end of file diff --git a/shaders/slang/shadowmappingomni/offscreen.vert.spv b/shaders/slang/shadowmappingomni/offscreen.vert.spv new file mode 100644 index 00000000..509363a2 Binary files /dev/null and b/shaders/slang/shadowmappingomni/offscreen.vert.spv differ diff --git a/shaders/slang/shadowmappingomni/scene.frag.spv b/shaders/slang/shadowmappingomni/scene.frag.spv new file mode 100644 index 00000000..d03d764c Binary files /dev/null and b/shaders/slang/shadowmappingomni/scene.frag.spv differ diff --git a/shaders/slang/shadowmappingomni/scene.slang b/shaders/slang/shadowmappingomni/scene.slang new file mode 100644 index 00000000..b5296ed7 --- /dev/null +++ b/shaders/slang/shadowmappingomni/scene.slang @@ -0,0 +1,80 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 EyePos; + float3 LightVec; + float3 WorldPos; + float3 LightPos; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +SamplerCube shadowCubeMapSampler; + +#define EPSILON 0.15 +#define SHADOW_OPACITY 0.5 + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Normal = input.Normal; + + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + output.EyePos = mul(ubo.model, float4(input.Pos, 1.0f)).xyz; + output.LightVec = normalize(ubo.lightPos.xyz - input.Pos.xyz); + output.WorldPos = input.Pos; + + output.LightPos = ubo.lightPos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Lighting + float3 N = normalize(input.Normal); + float3 L = normalize(float3(1.0, 1.0, 1.0)); + + float3 Eye = normalize(-input.EyePos); + float3 Reflected = normalize(reflect(-input.LightVec, input.Normal)); + + float4 IAmbient = float4(float3(0.05, 0.05, 0.05), 1.0); + float4 IDiffuse = float4(1.0, 1.0, 1.0, 1.0) * max(dot(input.Normal, input.LightVec), 0.0); + + float4 outFragColor = float4(IAmbient + IDiffuse * float4(input.Color, 1.0)); + + // Shadow + float3 lightVec = input.WorldPos - input.LightPos; + float sampledDist = shadowCubeMapSampler.Sample(lightVec).r; + float dist = length(lightVec); + + // Check if fragment is in shadow + float shadow = (dist <= sampledDist + EPSILON) ? 1.0 : SHADOW_OPACITY; + + outFragColor.rgb *= shadow; + return outFragColor; +} \ No newline at end of file diff --git a/shaders/slang/shadowmappingomni/scene.vert.spv b/shaders/slang/shadowmappingomni/scene.vert.spv new file mode 100644 index 00000000..0e8ba849 Binary files /dev/null and b/shaders/slang/shadowmappingomni/scene.vert.spv differ diff --git a/shaders/slang/specializationconstants/uber.frag.spv b/shaders/slang/specializationconstants/uber.frag.spv new file mode 100644 index 00000000..b8c081d7 Binary files /dev/null and b/shaders/slang/specializationconstants/uber.frag.spv differ diff --git a/shaders/slang/specializationconstants/uber.slang b/shaders/slang/specializationconstants/uber.slang new file mode 100644 index 00000000..14fc8af5 --- /dev/null +++ b/shaders/slang/specializationconstants/uber.slang @@ -0,0 +1,110 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerColormap; +Sampler2D samplerDiscard; + +// We use this constant to control the flow of the shader depending on the +// lighting model selected at pipeline creation time +[[SpecializationConstant]] const int LIGHTING_MODEL = 0; +// Parameter for the toon shading part of the shader +[[SpecializationConstant]] const int PARAM_TOON_DESATURATION = 0; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lPos = mul((float3x3)ubo.model, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + switch (LIGHTING_MODEL) { + case 0: // Phong + { + float3 ambient = input.Color * float3(0.25, 0.25, 0.25); + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * input.Color; + float3 specular = pow(max(dot(R, V), 0.0), 32.0) * float3(0.75, 0.75, 0.75); + return float4(ambient + diffuse * 1.75 + specular, 1.0); + } + case 1: // Toon + { + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float intensity = dot(N,L); + float3 color; + if (intensity > 0.98) + color = input.Color * 1.5; + else if (intensity > 0.9) + color = input.Color * 1.0; + else if (intensity > 0.5) + color = input.Color * 0.6; + else if (intensity > 0.25) + color = input.Color * 0.4; + else + color = input.Color * 0.2; + // Desaturate a bit + color = float3(lerp(color, dot(float3(0.2126,0.7152,0.0722), color).xxx, asfloat(PARAM_TOON_DESATURATION))); + return float4(color, 1); + } + case 2: // Textured + { + float4 color = samplerColormap.Sample(input.UV).rrra; + float3 ambient = color.rgb * float3(0.25, 0.25, 0.25) * input.Color; + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * color.rgb; + float specular = pow(max(dot(R, V), 0.0), 32.0) * color.a; + return float4(ambient + diffuse + specular.xxx, 1.0); + } + } + + return float4(0, 0, 0, 0); +} \ No newline at end of file diff --git a/shaders/slang/specializationconstants/uber.vert.spv b/shaders/slang/specializationconstants/uber.vert.spv new file mode 100644 index 00000000..7f77237d Binary files /dev/null and b/shaders/slang/specializationconstants/uber.vert.spv differ diff --git a/shaders/slang/sphericalenvmapping/sem.frag.spv b/shaders/slang/sphericalenvmapping/sem.frag.spv new file mode 100644 index 00000000..a210331f Binary files /dev/null and b/shaders/slang/sphericalenvmapping/sem.frag.spv differ diff --git a/shaders/slang/sphericalenvmapping/sem.slang b/shaders/slang/sphericalenvmapping/sem.slang new file mode 100644 index 00000000..28f75f31 --- /dev/null +++ b/shaders/slang/sphericalenvmapping/sem.slang @@ -0,0 +1,58 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; + float3 EyePos; + float3 Normal; + int TexIndex; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 normal; + float4x4 view; + int texIndex; +}; +ConstantBuffer ubo; + +Sampler2DArray matCapSampler; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + float4x4 modelView = mul(ubo.view, ubo.model); + output.EyePos = normalize(mul(modelView, input.Pos).xyz); + output.TexIndex = ubo.texIndex; + output.Normal = normalize(mul((float3x3)ubo.normal, input.Normal)); + float3 r = reflect(output.EyePos, output.Normal); + float m = 2.0 * sqrt(pow(r.x, 2.0) + pow(r.y, 2.0) + pow(r.z + 1.0, 2.0)); + output.Pos = mul(ubo.projection, mul(modelView, input.Pos)); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 r = reflect( input.EyePos, input.Normal ); + float3 r2 = float3( r.x, r.y, r.z + 1.0 ); + float m = 2.0 * length( r2 ); + float2 vN = r.xy / m + .5; + return float4(matCapSampler.Sample(float3(vN, input.TexIndex)).rgb * (clamp(input.Color.r * 2, 0.0, 1.0)), 1.0); +} diff --git a/shaders/slang/sphericalenvmapping/sem.vert.spv b/shaders/slang/sphericalenvmapping/sem.vert.spv new file mode 100644 index 00000000..e1356455 Binary files /dev/null and b/shaders/slang/sphericalenvmapping/sem.vert.spv differ diff --git a/shaders/slang/ssao/blur.frag.spv b/shaders/slang/ssao/blur.frag.spv new file mode 100644 index 00000000..7a867756 Binary files /dev/null and b/shaders/slang/ssao/blur.frag.spv differ diff --git a/shaders/slang/ssao/blur.slang b/shaders/slang/ssao/blur.slang new file mode 100644 index 00000000..b35d087d --- /dev/null +++ b/shaders/slang/ssao/blur.slang @@ -0,0 +1,30 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import types; + +Sampler2D samplerSSAO; + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + const int blurRange = 2; + int n = 0; + int2 texDim; + samplerSSAO.GetDimensions(texDim.x, texDim.y); + float2 texelSize = 1.0 / (float2)texDim; + float result = 0.0; + for (int x = -blurRange; x <= blurRange; x++) + { + for (int y = -blurRange; y <= blurRange; y++) + { + float2 offset = float2(float(x), float(y)) * texelSize; + result += samplerSSAO.Sample(input.UV + offset).r; + n++; + } + } + return result / (float(n)); +} \ No newline at end of file diff --git a/shaders/slang/ssao/composition.frag.spv b/shaders/slang/ssao/composition.frag.spv new file mode 100644 index 00000000..7f5330c7 Binary files /dev/null and b/shaders/slang/ssao/composition.frag.spv differ diff --git a/shaders/slang/ssao/composition.slang b/shaders/slang/ssao/composition.slang new file mode 100644 index 00000000..04991a03 --- /dev/null +++ b/shaders/slang/ssao/composition.slang @@ -0,0 +1,59 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import types; + +Sampler2D samplerposition; +Sampler2D samplerNormal; +Sampler2D samplerAlbedo; +Sampler2D samplerSSAO; +Sampler2D samplerSSAOBlur; + +struct UBO +{ + float4x4 _dummy; + int ssao; + int ssaoOnly; + int ssaoBlur; +}; +ConstantBuffer uboParams; + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 fragPos = samplerposition.Sample(input.UV).rgb; + float3 normal = normalize(samplerNormal.Sample(input.UV).rgb * 2.0 - 1.0); + float4 albedo = samplerAlbedo.Sample(input.UV); + + float ssao = (uboParams.ssaoBlur == 1) ? samplerSSAOBlur.Sample(input.UV).r : samplerSSAO.Sample(input.UV).r; + + float3 lightPos = float3(0.0, 0.0, 0.0); + float3 L = normalize(lightPos - fragPos); + float NdotL = max(0.5, dot(normal, L)); + + float4 outFragColor; + if (uboParams.ssaoOnly == 1) + { + outFragColor.rgb = ssao.rrr; + } + else + { + float3 baseColor = albedo.rgb * NdotL; + + if (uboParams.ssao == 1) + { + outFragColor.rgb = ssao.rrr; + + if (uboParams.ssaoOnly != 1) + outFragColor.rgb *= baseColor; + } + else + { + outFragColor.rgb = baseColor; + } + } + return outFragColor; +} \ No newline at end of file diff --git a/shaders/slang/ssao/fullscreen.slang b/shaders/slang/ssao/fullscreen.slang new file mode 100644 index 00000000..d734ebc5 --- /dev/null +++ b/shaders/slang/ssao/fullscreen.slang @@ -0,0 +1,16 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import types; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex : SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} diff --git a/shaders/slang/ssao/fullscreen.vert.spv b/shaders/slang/ssao/fullscreen.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/ssao/fullscreen.vert.spv differ diff --git a/shaders/slang/ssao/gbuffer.frag.spv b/shaders/slang/ssao/gbuffer.frag.spv new file mode 100644 index 00000000..8821efa1 Binary files /dev/null and b/shaders/slang/ssao/gbuffer.frag.spv differ diff --git a/shaders/slang/ssao/gbuffer.slang b/shaders/slang/ssao/gbuffer.slang new file mode 100644 index 00000000..73e4d9dd --- /dev/null +++ b/shaders/slang/ssao/gbuffer.slang @@ -0,0 +1,73 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float2 UV; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 Color; + float3 WorldPos; +}; + +struct FSOutput +{ + float4 Position : SV_TARGET0; + float4 Normal : SV_TARGET1; + float4 Albedo : SV_TARGET2; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; + float nearPlane; + float farPlane; +}; +ConstantBuffer ubo; + +[[vk::binding(0, 1)]] Texture2D textureColorMap; +[[vk::binding(0, 1)]] SamplerState samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, input.Pos))); + output.UV = input.UV; + // Vertex position in view space + output.WorldPos = mul(ubo.view, mul(ubo.model, input.Pos)).xyz; + // Normal in view space + float3x3 normalMatrix = (float3x3)mul(ubo.view, ubo.model); + output.Normal = mul(normalMatrix, input.Normal); + output.Color = input.Color; + return output; +} + +float linearDepth(float depth) +{ + float z = depth * 2.0f - 1.0f; + return (2.0f * ubo.nearPlane * ubo.farPlane) / (ubo.farPlane + ubo.nearPlane - z * (ubo.farPlane - ubo.nearPlane)); +} + +[shader("fragment")] +FSOutput fragmentMain(VSOutput input) +{ + FSOutput output; + output.Position = float4(input.WorldPos, linearDepth(input.Pos.z)); + output.Normal = float4(normalize(input.Normal) * 0.5 + 0.5, 1.0); + output.Albedo = textureColorMap.Sample(samplerColorMap, input.UV) * float4(input.Color, 1.0); + return output; +} \ No newline at end of file diff --git a/shaders/slang/ssao/gbuffer.vert.spv b/shaders/slang/ssao/gbuffer.vert.spv new file mode 100644 index 00000000..a4467b5c Binary files /dev/null and b/shaders/slang/ssao/gbuffer.vert.spv differ diff --git a/shaders/slang/ssao/ssao.frag.spv b/shaders/slang/ssao/ssao.frag.spv new file mode 100644 index 00000000..d5881aed Binary files /dev/null and b/shaders/slang/ssao/ssao.frag.spv differ diff --git a/shaders/slang/ssao/ssao.slang b/shaders/slang/ssao/ssao.slang new file mode 100644 index 00000000..624a5f78 --- /dev/null +++ b/shaders/slang/ssao/ssao.slang @@ -0,0 +1,70 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +import types; + +Sampler2D samplerPositionDepth; +Sampler2D samplerNormal; +Sampler2D ssaoNoiseSampler; + +struct UBOSSAOKernel +{ + float4 samples[64]; +}; +ConstantBuffer uboSSAOKernel; + +struct UBO +{ + float4x4 projection; +}; +ConstantBuffer ubo; + +[[SpecializationConstant]] const int SSAO_KERNEL_SIZE = 64; +[[SpecializationConstant]] const float SSAO_RADIUS = 0.5; + +[shader("fragment")] +float fragmentMain(VSOutput input) +{ + // Get G-Buffer values + float3 fragPos = samplerPositionDepth.Sample(input.UV).rgb; + float3 normal = normalize(samplerNormal.Sample(input.UV).rgb * 2.0 - 1.0); + + // Get a random vector using a noise lookup + int2 texDim; + samplerPositionDepth.GetDimensions(texDim.x, texDim.y); + int2 noiseDim; + ssaoNoiseSampler.GetDimensions(noiseDim.x, noiseDim.y); + const float2 noiseUV = float2(float(texDim.x) / float(noiseDim.x), float(texDim.y) / (noiseDim.y)) * input.UV; + float3 randomVec = ssaoNoiseSampler.Sample(noiseUV).xyz * 2.0 - 1.0; + + // Create TBN matrix + float3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); + float3 bitangent = cross(tangent, normal); + float3x3 TBN = transpose(float3x3(tangent, bitangent, normal)); + + // Calculate occlusion value + float occlusion = 0.0f; + for(int i = 0; i < SSAO_KERNEL_SIZE; i++) + { + float3 samplePos = mul(TBN, uboSSAOKernel.samples[i].xyz); + samplePos = fragPos + samplePos * SSAO_RADIUS; + + // project + float4 offset = float4(samplePos, 1.0f); + offset = mul(ubo.projection, offset); + offset.xyz /= offset.w; + offset.xyz = offset.xyz * 0.5f + 0.5f; + + float sampleDepth = -samplerPositionDepth.Sample(offset.xy).w; + + float rangeCheck = smoothstep(0.0f, 1.0f, SSAO_RADIUS / abs(fragPos.z - sampleDepth)); + occlusion += (sampleDepth >= samplePos.z ? 1.0f : 0.0f) * rangeCheck; + } + occlusion = 1.0 - (occlusion / float(SSAO_KERNEL_SIZE)); + + return occlusion; +} + diff --git a/shaders/slang/ssao/types.slang b/shaders/slang/ssao/types.slang new file mode 100644 index 00000000..52f46ab3 --- /dev/null +++ b/shaders/slang/ssao/types.slang @@ -0,0 +1,13 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +module types; + +public struct VSOutput +{ + public float4 Pos : SV_POSITION; + public float2 UV; +}; diff --git a/shaders/slang/stencilbuffer/outline.frag.spv b/shaders/slang/stencilbuffer/outline.frag.spv new file mode 100644 index 00000000..5cbccf9c Binary files /dev/null and b/shaders/slang/stencilbuffer/outline.frag.spv differ diff --git a/shaders/slang/stencilbuffer/outline.slang b/shaders/slang/stencilbuffer/outline.slang new file mode 100644 index 00000000..332f220d --- /dev/null +++ b/shaders/slang/stencilbuffer/outline.slang @@ -0,0 +1,42 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; +} + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; + float outlineWidth; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + // Extrude along normal + VSOutput output; + float4 pos = float4(input.Pos.xyz + input.Normal * ubo.outlineWidth, input.Pos.w); + output.Pos = mul(ubo.projection, mul(ubo.model, pos)); + return output; +} + +[shader("fragment")] +float4 fragmentMain() +{ + return float4(1.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/stencilbuffer/outline.vert.spv b/shaders/slang/stencilbuffer/outline.vert.spv new file mode 100644 index 00000000..a34b5835 Binary files /dev/null and b/shaders/slang/stencilbuffer/outline.vert.spv differ diff --git a/shaders/slang/stencilbuffer/toon.frag.spv b/shaders/slang/stencilbuffer/toon.frag.spv new file mode 100644 index 00000000..355dcc22 Binary files /dev/null and b/shaders/slang/stencilbuffer/toon.frag.spv differ diff --git a/shaders/slang/stencilbuffer/toon.slang b/shaders/slang/stencilbuffer/toon.slang new file mode 100644 index 00000000..b7bf1314 --- /dev/null +++ b/shaders/slang/stencilbuffer/toon.slang @@ -0,0 +1,65 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = float3(1.0, 0.0, 0.0); + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + float3 lPos = mul((float3x3)ubo.model, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 color; + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float intensity = dot(N,L); + if (intensity > 0.98) + color = input.Color * 1.5; + else if (intensity > 0.9) + color = input.Color * 1.0; + else if (intensity > 0.5) + color = input.Color * 0.6; + else if (intensity > 0.25) + color = input.Color * 0.4; + else + color = input.Color * 0.2; + // Desaturate a bit + color = lerp(color, dot(float3(0.2126,0.7152,0.0722), color).xxx, 0.1); + return float4(color, 1); +} \ No newline at end of file diff --git a/shaders/slang/stencilbuffer/toon.vert.spv b/shaders/slang/stencilbuffer/toon.vert.spv new file mode 100644 index 00000000..aa676e9b Binary files /dev/null and b/shaders/slang/stencilbuffer/toon.vert.spv differ diff --git a/shaders/slang/subpasses/composition.frag.spv b/shaders/slang/subpasses/composition.frag.spv new file mode 100644 index 00000000..e5068101 Binary files /dev/null and b/shaders/slang/subpasses/composition.frag.spv differ diff --git a/shaders/slang/subpasses/composition.slang b/shaders/slang/subpasses/composition.slang new file mode 100644 index 00000000..728f1452 --- /dev/null +++ b/shaders/slang/subpasses/composition.slang @@ -0,0 +1,66 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +[[vk::input_attachment_index(0)]] SubpassInput inputPosition; +[[vk::input_attachment_index(1)]] SubpassInput inputNormal; +[[vk::input_attachment_index(2)]] SubpassInput inputAlbedo; + +struct Light { + float4 position; + float3 color; + float radius; +}; +RWStructuredBuffer lights; + +[shader("vertex")] +VSOutput vertexMain(uint VertexIndex: SV_VertexID) +{ + VSOutput output; + output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2); + output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f); + return output; +} + +[shader("fragment")] +float4 fragmentMain() +{ + // Read G-Buffer values from previous sub pass + float3 fragPos = inputPosition.SubpassLoad().rgb; + float3 normal = inputNormal.SubpassLoad().rgb; + float4 albedo = inputAlbedo.SubpassLoad(); + + #define ambient 0.05 + + // Ambient part + float3 fragcolor = albedo.rgb * ambient; + + uint lightsLength; + uint lightsStride; + lights.GetDimensions(lightsLength, lightsStride); + + for(int i = 0; i < lightsLength; ++i) + { + float3 L = lights[i].position.xyz - fragPos; + float dist = length(L); + + L = normalize(L); + + float atten = lights[i].radius / (pow(dist, 3.0) + 1.0); + float3 N = normalize(normal); + float NdotL = max(0.0, dot(N, L)); + float3 diff = lights[i].color * albedo.rgb * NdotL * atten; + + fragcolor += diff; + } + + return float4(fragcolor, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/subpasses/composition.vert.spv b/shaders/slang/subpasses/composition.vert.spv new file mode 100644 index 00000000..ef2e0e8e Binary files /dev/null and b/shaders/slang/subpasses/composition.vert.spv differ diff --git a/shaders/slang/subpasses/gbuffer.frag.spv b/shaders/slang/subpasses/gbuffer.frag.spv new file mode 100644 index 00000000..2afaed32 Binary files /dev/null and b/shaders/slang/subpasses/gbuffer.frag.spv differ diff --git a/shaders/slang/subpasses/gbuffer.slang b/shaders/slang/subpasses/gbuffer.slang new file mode 100644 index 00000000..a648cb31 --- /dev/null +++ b/shaders/slang/subpasses/gbuffer.slang @@ -0,0 +1,78 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Color; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float3 WorldPos; + float3 Tangent; +}; + +struct FSOutput +{ + float4 Color : SV_TARGET0; + float4 Position : SV_TARGET1; + float4 Normal : SV_TARGET2; + float4 Albedo : SV_TARGET3; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; +}; +ConstantBuffer ubo; + +[[SpecializationConstant]] const float NEAR_PLANE = 0.1; +[[SpecializationConstant]] const float FAR_PLANE = 256.0; + +float linearDepth(float depth) +{ + float z = depth * 2.0f - 1.0f; + return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, input.Pos))); + // Vertex position in world space + output.WorldPos = mul(ubo.model, input.Pos).xyz; + // GL to Vulkan coord space + output.WorldPos.y = -output.WorldPos.y; + // Normal in world space + output.Normal = mul((float3x3)ubo.model, normalize(input.Normal)); + // Currently just vertex color + output.Color = input.Color; + return output; +} + +[shader("fragment")] +FSOutput fragmentMain(VSOutput input) +{ + FSOutput output; + output.Position = float4(input.WorldPos, 1.0); + float3 N = normalize(input.Normal); + N.y = -N.y; + output.Normal = float4(N, 1.0); + output.Albedo.rgb = input.Color; + // Store linearized depth in alpha component + output.Position.a = linearDepth(input.Pos.z); + // Write color attachments to avoid undefined behaviour (validation error) + output.Color = float4(0.0); + return output; +} \ No newline at end of file diff --git a/shaders/slang/subpasses/gbuffer.vert.spv b/shaders/slang/subpasses/gbuffer.vert.spv new file mode 100644 index 00000000..e3ba7309 Binary files /dev/null and b/shaders/slang/subpasses/gbuffer.vert.spv differ diff --git a/shaders/slang/subpasses/transparent.frag.spv b/shaders/slang/subpasses/transparent.frag.spv new file mode 100644 index 00000000..d9fbe6c3 Binary files /dev/null and b/shaders/slang/subpasses/transparent.frag.spv differ diff --git a/shaders/slang/subpasses/transparent.slang b/shaders/slang/subpasses/transparent.slang new file mode 100644 index 00000000..846e5c6d --- /dev/null +++ b/shaders/slang/subpasses/transparent.slang @@ -0,0 +1,64 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Color; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Color; + float2 UV; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 view; +}; +ConstantBuffer ubo; + +[[vk::input_attachment_index(0)]] SubpassInput samplerPositionDepth; + +Sampler2D samplerTexture; + +[[SpecializationConstant]] const float NEAR_PLANE = 0.1f; +[[SpecializationConstant]] const float FAR_PLANE = 256.0f; + +float linearDepth(float depth) +{ + float z = depth * 2.0f - 1.0f; + return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain (VSOutput input) +{ + // Sample depth from deferred depth buffer and discard if obscured + float depth = samplerPositionDepth.SubpassLoad().a; + if ((depth != 0.0) && (linearDepth(input.Pos.z) > depth)) + { + clip(-1); + }; + + return samplerTexture.Sample(input.UV); +} diff --git a/shaders/slang/subpasses/transparent.vert.spv b/shaders/slang/subpasses/transparent.vert.spv new file mode 100644 index 00000000..aea9ddf8 Binary files /dev/null and b/shaders/slang/subpasses/transparent.vert.spv differ diff --git a/shaders/slang/terraintessellation/skysphere.frag.spv b/shaders/slang/terraintessellation/skysphere.frag.spv new file mode 100644 index 00000000..2cfde97d Binary files /dev/null and b/shaders/slang/terraintessellation/skysphere.frag.spv differ diff --git a/shaders/slang/terraintessellation/skysphere.slang b/shaders/slang/terraintessellation/skysphere.slang new file mode 100644 index 00000000..1d11ad44 --- /dev/null +++ b/shaders/slang/terraintessellation/skysphere.slang @@ -0,0 +1,42 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +struct UBO +{ + float4x4 mvp; +}; +ConstantBuffer ubo; + +Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(ubo.mvp, float4(input.Pos, 1.0)); + output.UV = input.UV; + return output; +} + + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerColorMap.Sample(input.UV); +} diff --git a/shaders/slang/terraintessellation/skysphere.vert.spv b/shaders/slang/terraintessellation/skysphere.vert.spv new file mode 100644 index 00000000..982ccc80 Binary files /dev/null and b/shaders/slang/terraintessellation/skysphere.vert.spv differ diff --git a/shaders/slang/terraintessellation/terrain.frag.spv b/shaders/slang/terraintessellation/terrain.frag.spv new file mode 100644 index 00000000..31b8a9a6 Binary files /dev/null and b/shaders/slang/terraintessellation/terrain.frag.spv differ diff --git a/shaders/slang/terraintessellation/terrain.slang b/shaders/slang/terraintessellation/terrain.slang new file mode 100644 index 00000000..ad9d475e --- /dev/null +++ b/shaders/slang/terraintessellation/terrain.slang @@ -0,0 +1,252 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct DSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 ViewVec; + float3 LightVec; + float3 EyePos; + float3 WorldPos; +}; + +struct UBO +{ + float4x4 projection; + float4x4 modelview; + float4 lightPos; + float4 frustumPlanes[6]; + float displacementFactor; + float tessellationFactor; + float2 viewportDim; + float tessellatedEdgeSize; +}; +ConstantBuffer ubo; + +Sampler2D samplerHeight; +Sampler2DArray samplerLayers; + +struct HSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal : NORMAL0; + float2 UV : TEXCOORD0; +}; + +struct ConstantsHSOutput +{ + float TessLevelOuter[4] : SV_TessFactor; + float TessLevelInner[2] : SV_InsideTessFactor; +}; + +// Calculate the tessellation factor based on screen space +// dimensions of the edge +float screenSpaceTessFactor(float4 p0, float4 p1) +{ + // Calculate edge mid point + float4 midPoint = 0.5 * (p0 + p1); + // Sphere radius as distance between the control points + float radius = distance(p0, p1) / 2.0; + + // View space + float4 v0 = mul(ubo.modelview, midPoint); + + // Project into clip space + float4 clip0 = mul(ubo.projection, (v0 - float4(radius, float3(0.0, 0.0, 0.0)))); + float4 clip1 = mul(ubo.projection, (v0 + float4(radius, float3(0.0, 0.0, 0.0)))); + + // Get normalized device coordinates + clip0 /= clip0.w; + clip1 /= clip1.w; + + // Convert to viewport coordinates + clip0.xy *= ubo.viewportDim; + clip1.xy *= ubo.viewportDim; + + // Return the tessellation factor based on the screen size + // given by the distance of the two edge control points in screen space + // and a reference (min.) tessellation size for the edge set by the application + return clamp(distance(clip0, clip1) / ubo.tessellatedEdgeSize * ubo.tessellationFactor, 1.0, 64.0); +} + +// Checks the current's patch visibility against the frustum using a sphere check +// Sphere radius is given by the patch size +bool frustumCheck(float4 Pos, float2 inUV) +{ + // Fixed radius (increase if patch size is increased in example) + const float radius = 8.0f; + float4 pos = Pos; + pos.y -= samplerHeight.SampleLevel(inUV, 0.0).r * ubo.displacementFactor; + + // Check sphere against frustum planes + for (int i = 0; i < 6; i++) { + if (dot(pos, ubo.frustumPlanes[i]) + radius < 0.0) + { + return false; + } + } + return true; +} + +ConstantsHSOutput ConstantsHS(InputPatch patch) +{ + ConstantsHSOutput output; + + if (!frustumCheck(patch[0].Pos, patch[0].UV)) + { + output.TessLevelInner[0] = 0.0; + output.TessLevelInner[1] = 0.0; + output.TessLevelOuter[0] = 0.0; + output.TessLevelOuter[1] = 0.0; + output.TessLevelOuter[2] = 0.0; + output.TessLevelOuter[3] = 0.0; + } + else + { + if (ubo.tessellationFactor > 0.0) + { + output.TessLevelOuter[0] = screenSpaceTessFactor(patch[3].Pos, patch[0].Pos); + output.TessLevelOuter[1] = screenSpaceTessFactor(patch[0].Pos, patch[1].Pos); + output.TessLevelOuter[2] = screenSpaceTessFactor(patch[1].Pos, patch[2].Pos); + output.TessLevelOuter[3] = screenSpaceTessFactor(patch[2].Pos, patch[3].Pos); + output.TessLevelInner[0] = lerp(output.TessLevelOuter[0], output.TessLevelOuter[3], 0.5); + output.TessLevelInner[1] = lerp(output.TessLevelOuter[2], output.TessLevelOuter[1], 0.5); + } + else + { + // Tessellation factor can be set to zero by example + // to demonstrate a simple passthrough + output.TessLevelInner[0] = 1.0; + output.TessLevelInner[1] = 1.0; + output.TessLevelOuter[0] = 1.0; + output.TessLevelOuter[1] = 1.0; + output.TessLevelOuter[2] = 1.0; + output.TessLevelOuter[3] = 1.0; + } + } + + return output; +} + +float3 sampleTerrainLayer(float2 inUV) +{ + // Define some layer ranges for sampling depending on terrain height + float2 layers[6]; + layers[0] = float2(-10.0, 10.0); + layers[1] = float2(5.0, 45.0); + layers[2] = float2(45.0, 80.0); + layers[3] = float2(75.0, 100.0); + layers[4] = float2(95.0, 140.0); + layers[5] = float2(140.0, 190.0); + + float3 color = float3(0.0, 0.0, 0.0); + + // Get height from displacement map + float height = samplerHeight.SampleLevel(inUV, 0.0).r * 255.0; + + for (int i = 0; i < 6; i++) + { + float range = layers[i].y - layers[i].x; + float weight = (range - abs(height - layers[i].y)) / range; + weight = max(0.0, weight); + color += weight * samplerLayers.Sample(float3(inUV * 16.0, i)).rgb; + } + + return color; +} + +float fog(float density, float4 FragCoord) +{ + const float LOG2 = -1.442695; + float dist = FragCoord.z / FragCoord.w * 0.1; + float d = density * dist; + return 1.0 - clamp(exp2(d * d * LOG2), 0.0, 1.0); +} + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = float4(input.Pos.xyz, 1.0); + output.UV = input.UV; + output.Normal = input.Normal; + return output; +} + +[shader("hull")] +[domain("quad")] +[partitioning("integer")] +[outputtopology("triangle_cw")] +[outputcontrolpoints(4)] +[patchconstantfunc("ConstantsHS")] +[maxtessfactor(20.0f)] +HSOutput hullMain(InputPatch patch, uint InvocationID: SV_OutputControlPointID) +{ + HSOutput output; + output.Pos = patch[InvocationID].Pos; + output.Normal = patch[InvocationID].Normal; + output.UV = patch[InvocationID].UV; + return output; +} + +[shader("domain")] +[domain("quad")] +DSOutput domainMain(ConstantsHSOutput input, float2 TessCoord: SV_DomainLocation, const OutputPatch patch) +{ + // Interpolate UV coordinates + DSOutput output; + float2 uv1 = lerp(patch[0].UV, patch[1].UV, TessCoord.x); + float2 uv2 = lerp(patch[3].UV, patch[2].UV, TessCoord.x); + output.UV = lerp(uv1, uv2, TessCoord.y); + + float3 n1 = lerp(patch[0].Normal, patch[1].Normal, TessCoord.x); + float3 n2 = lerp(patch[3].Normal, patch[2].Normal, TessCoord.x); + output.Normal = lerp(n1, n2, TessCoord.y); + + // Interpolate positions + float4 pos1 = lerp(patch[0].Pos, patch[1].Pos, TessCoord.x); + float4 pos2 = lerp(patch[3].Pos, patch[2].Pos, TessCoord.x); + float4 pos = lerp(pos1, pos2, TessCoord.y); + // Displace + pos.y -= samplerHeight.SampleLevel(output.UV, 0.0).r * ubo.displacementFactor; + // Perspective projection + output.Pos = mul(ubo.projection, mul(ubo.modelview, pos)); + + // Calculate vectors for lighting based on tessellated position + output.ViewVec = -pos.xyz; + output.LightVec = normalize(ubo.lightPos.xyz + output.ViewVec); + output.WorldPos = pos.xyz; + output.EyePos = mul(ubo.modelview, pos).xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(DSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 ambient = float3(0.5, 0.5, 0.5); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float4 color = float4((ambient + diffuse) * sampleTerrainLayer(input.UV), 1.0); + const float4 fogColor = float4(0.47, 0.5, 0.67, 0.0); + return lerp(color, fogColor, fog(0.25, input.Pos)); +} diff --git a/shaders/slang/terraintessellation/terrain.tesc.spv b/shaders/slang/terraintessellation/terrain.tesc.spv new file mode 100644 index 00000000..e51394a5 Binary files /dev/null and b/shaders/slang/terraintessellation/terrain.tesc.spv differ diff --git a/shaders/slang/terraintessellation/terrain.tese.spv b/shaders/slang/terraintessellation/terrain.tese.spv new file mode 100644 index 00000000..490be63b Binary files /dev/null and b/shaders/slang/terraintessellation/terrain.tese.spv differ diff --git a/shaders/slang/terraintessellation/terrain.vert.spv b/shaders/slang/terraintessellation/terrain.vert.spv new file mode 100644 index 00000000..b50c5a1e Binary files /dev/null and b/shaders/slang/terraintessellation/terrain.vert.spv differ diff --git a/shaders/slang/tessellation/base.frag.spv b/shaders/slang/tessellation/base.frag.spv new file mode 100644 index 00000000..19395722 Binary files /dev/null and b/shaders/slang/tessellation/base.frag.spv differ diff --git a/shaders/slang/tessellation/base.slang b/shaders/slang/tessellation/base.slang new file mode 100644 index 00000000..e671b8df --- /dev/null +++ b/shaders/slang/tessellation/base.slang @@ -0,0 +1,46 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct DSOutput +{ + float3 Normal; + float2 UV; +}; + +[[vk::binding(0, 1)]] Sampler2D samplerColorMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = float4(input.Pos.xyz, 1.0); + output.Normal = input.Normal; + output.UV = input.UV; + return output; +} + +[shader("fragment")] +float4 fragmentMain(DSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(float3(0.0, -4.0, 4.0)); + float4 color = samplerColorMap.Sample(input.UV); + return float4(clamp(max(dot(N,L), 0.0), 0.2, 1.0) * color.rgb * 1.5, 1); +} diff --git a/shaders/slang/tessellation/base.vert.spv b/shaders/slang/tessellation/base.vert.spv new file mode 100644 index 00000000..b50c5a1e Binary files /dev/null and b/shaders/slang/tessellation/base.vert.spv differ diff --git a/shaders/slang/tessellation/passthrough.slang b/shaders/slang/tessellation/passthrough.slang new file mode 100644 index 00000000..b25454e7 --- /dev/null +++ b/shaders/slang/tessellation/passthrough.slang @@ -0,0 +1,83 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct HSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct DSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct ConstantsHSOutput +{ + float TessLevelOuter[3] : SV_TessFactor; + float TessLevelInner[2] : SV_InsideTessFactor; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float tessAlpha; + float tessLevel; +}; +ConstantBuffer ubo; + +ConstantsHSOutput ConstantsHS(InputPatch patch) +{ + ConstantsHSOutput output; + output.TessLevelInner[0] = 1; + output.TessLevelInner[1] = 1; + output.TessLevelOuter[0] = 1; + output.TessLevelOuter[1] = 1; + output.TessLevelOuter[2] = 1; + return output; +} + +[shader("hull")] +[domain("tri")] +[partitioning("integer")] +[outputtopology("triangle_cw")] +[outputcontrolpoints(3)] +[patchconstantfunc("ConstantsHS")] +[maxtessfactor(20.0f)] +HSOutput hullMain(InputPatch patch, uint InvocationID : SV_OutputControlPointID) +{ + HSOutput output; + output.Pos = patch[InvocationID].Pos; + output.Normal = patch[InvocationID].Normal; + output.UV = patch[InvocationID].UV; + return output; +} + +[shader("domain")] +[domain("tri")] +DSOutput domainMain(ConstantsHSOutput input, float3 TessCoord: SV_DomainLocation, const OutputPatch patch) +{ + DSOutput output = (DSOutput)0; + output.Pos = (TessCoord.x * patch[0].Pos) + + (TessCoord.y * patch[1].Pos) + + (TessCoord.z * patch[2].Pos); + output.Pos = mul(ubo.projection, mul(ubo.model, output.Pos)); + + output.Normal = TessCoord.x * patch[0].Normal + TessCoord.y * patch[1].Normal + TessCoord.z * patch[2].Normal; + output.UV = TessCoord.x * patch[0].UV + TessCoord.y * patch[1].UV + TessCoord.z * patch[2].UV; + return output; +} \ No newline at end of file diff --git a/shaders/slang/tessellation/passthrough.tesc.spv b/shaders/slang/tessellation/passthrough.tesc.spv new file mode 100644 index 00000000..8c59ad09 Binary files /dev/null and b/shaders/slang/tessellation/passthrough.tesc.spv differ diff --git a/shaders/slang/tessellation/passthrough.tese.spv b/shaders/slang/tessellation/passthrough.tese.spv new file mode 100644 index 00000000..79673cfa Binary files /dev/null and b/shaders/slang/tessellation/passthrough.tese.spv differ diff --git a/shaders/slang/tessellation/pntriangles.slang b/shaders/slang/tessellation/pntriangles.slang new file mode 100644 index 00000000..f1761ff7 --- /dev/null +++ b/shaders/slang/tessellation/pntriangles.slang @@ -0,0 +1,216 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct HSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float pnPatch[10]; +}; + +struct DSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; +}; + +struct ConstantsHSOutput +{ + float TessLevelOuter[3] : SV_TessFactor; + float TessLevelInner[2] : SV_InsideTessFactor; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float tessAlpha; + float tessLevel; +}; +ConstantBuffer ubo; + +#define uvw TessCoord + +struct PnPatch +{ + float b210; + float b120; + float b021; + float b012; + float b102; + float b201; + float b111; + float n110; + float n011; + float n101; +}; + +PnPatch GetPnPatch(float pnPatch[10]) +{ + PnPatch output; + output.b210 = pnPatch[0]; + output.b120 = pnPatch[1]; + output.b021 = pnPatch[2]; + output.b012 = pnPatch[3]; + output.b102 = pnPatch[4]; + output.b201 = pnPatch[5]; + output.b111 = pnPatch[6]; + output.n110 = pnPatch[7]; + output.n011 = pnPatch[8]; + output.n101 = pnPatch[9]; + return output; +} + +void SetPnPatch(out float output[10], PnPatch patch) +{ + output[0] = patch.b210; + output[1] = patch.b120; + output[2] = patch.b021; + output[3] = patch.b012; + output[4] = patch.b102; + output[5] = patch.b201; + output[6] = patch.b111; + output[7] = patch.n110; + output[8] = patch.n011; + output[9] = patch.n101; +} + +float wij(float4 iPos, float3 iNormal, float4 jPos) +{ + return dot(jPos.xyz - iPos.xyz, iNormal); +} + +float vij(float4 iPos, float3 iNormal, float4 jPos, float3 jNormal) +{ + float3 Pj_minus_Pi = jPos.xyz - iPos.xyz; + float3 Ni_plus_Nj = iNormal + jNormal; + return 2.0 * dot(Pj_minus_Pi, Ni_plus_Nj) / dot(Pj_minus_Pi, Pj_minus_Pi); +} + +ConstantsHSOutput ConstantsHS(InputPatch patch) +{ + ConstantsHSOutput output; + output.TessLevelOuter[0] = ubo.tessLevel; + output.TessLevelOuter[1] = ubo.tessLevel; + output.TessLevelOuter[2] = ubo.tessLevel; + output.TessLevelInner[0] = ubo.tessLevel; + output.TessLevelInner[1] = ubo.tessLevel; + return output; +} + +[shader("domain")] +[domain("tri")] +DSOutput domainMain(ConstantsHSOutput input, float3 TessCoord: SV_DomainLocation, const OutputPatch patch) +{ + PnPatch pnPatch[3]; + pnPatch[0] = GetPnPatch(patch[0].pnPatch); + pnPatch[1] = GetPnPatch(patch[1].pnPatch); + pnPatch[2] = GetPnPatch(patch[2].pnPatch); + + DSOutput output = (DSOutput)0; + float3 uvwSquared = uvw * uvw; + float3 uvwCubed = uvwSquared * uvw; + + // extract control points + float3 b210 = float3(pnPatch[0].b210, pnPatch[1].b210, pnPatch[2].b210); + float3 b120 = float3(pnPatch[0].b120, pnPatch[1].b120, pnPatch[2].b120); + float3 b021 = float3(pnPatch[0].b021, pnPatch[1].b021, pnPatch[2].b021); + float3 b012 = float3(pnPatch[0].b012, pnPatch[1].b012, pnPatch[2].b012); + float3 b102 = float3(pnPatch[0].b102, pnPatch[1].b102, pnPatch[2].b102); + float3 b201 = float3(pnPatch[0].b201, pnPatch[1].b201, pnPatch[2].b201); + float3 b111 = float3(pnPatch[0].b111, pnPatch[1].b111, pnPatch[2].b111); + + // extract control normals + float3 n110 = normalize(float3(pnPatch[0].n110, pnPatch[1].n110, pnPatch[2].n110)); + float3 n011 = normalize(float3(pnPatch[0].n011, pnPatch[1].n011, pnPatch[2].n011)); + float3 n101 = normalize(float3(pnPatch[0].n101, pnPatch[1].n101, pnPatch[2].n101)); + + // compute texcoords + output.UV = TessCoord[2] * patch[0].UV + TessCoord[0] * patch[1].UV + TessCoord[1] * patch[2].UV; + + // normal + // Barycentric normal + float3 barNormal = TessCoord[2] * patch[0].Normal + TessCoord[0] * patch[1].Normal + TessCoord[1] * patch[2].Normal; + float3 pnNormal = patch[0].Normal * uvwSquared[2] + patch[1].Normal * uvwSquared[0] + patch[2].Normal * uvwSquared[1] + + n110 * uvw[2] * uvw[0] + n011 * uvw[0] * uvw[1] + n101 * uvw[2] * uvw[1]; + output.Normal = ubo.tessAlpha * pnNormal + (1.0 - ubo.tessAlpha) * barNormal; + + // compute interpolated pos + float3 barPos = TessCoord[2] * patch[0].Pos.xyz + + TessCoord[0] * patch[1].Pos.xyz + + TessCoord[1] * patch[2].Pos.xyz; + + // save some computations + uvwSquared *= 3.0; + + // compute PN position + float3 pnPos = patch[0].Pos.xyz * uvwCubed[2] + + patch[1].Pos.xyz * uvwCubed[0] + + patch[2].Pos.xyz * uvwCubed[1] + + b210 * uvwSquared[2] * uvw[0] + + b120 * uvwSquared[0] * uvw[2] + + b201 * uvwSquared[2] * uvw[1] + + b021 * uvwSquared[0] * uvw[1] + + b102 * uvwSquared[1] * uvw[2] + + b012 * uvwSquared[1] * uvw[0] + + b111 * 6.0 * uvw[0] * uvw[1] * uvw[2]; + + // final position and normal + float3 finalPos = (1.0 - ubo.tessAlpha) * barPos + ubo.tessAlpha * pnPos; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(finalPos, 1.0))); + return output; +} + +[shader("hull")] +[domain("tri")] +[partitioning("fractional_odd")] +[outputtopology("triangle_cw")] +[outputcontrolpoints(3)] +[patchconstantfunc("ConstantsHS")] +[maxtessfactor(20.0f)] +HSOutput hullMain(InputPatch patch, uint InvocationID : SV_OutputControlPointID) +{ + HSOutput output; + // get data + output.Pos = patch[InvocationID].Pos; + output.Normal = patch[InvocationID].Normal; + output.UV = patch[InvocationID].UV; + + // set base + float P0 = patch[0].Pos[InvocationID]; + float P1 = patch[1].Pos[InvocationID]; + float P2 = patch[2].Pos[InvocationID]; + float N0 = patch[0].Normal[InvocationID]; + float N1 = patch[1].Normal[InvocationID]; + float N2 = patch[2].Normal[InvocationID]; + + // compute control points + PnPatch pnPatch; + pnPatch.b210 = (2.0*P0 + P1 - wij(patch[0].Pos, patch[0].Normal, patch[1].Pos)*N0)/3.0; + pnPatch.b120 = (2.0*P1 + P0 - wij(patch[1].Pos, patch[1].Normal, patch[0].Pos)*N1)/3.0; + pnPatch.b021 = (2.0*P1 + P2 - wij(patch[1].Pos, patch[1].Normal, patch[2].Pos)*N1)/3.0; + pnPatch.b012 = (2.0*P2 + P1 - wij(patch[2].Pos, patch[2].Normal, patch[1].Pos)*N2)/3.0; + pnPatch.b102 = (2.0*P2 + P0 - wij(patch[2].Pos, patch[2].Normal, patch[0].Pos)*N2)/3.0; + pnPatch.b201 = (2.0*P0 + P2 - wij(patch[0].Pos, patch[0].Normal, patch[2].Pos)*N0)/3.0; + float E = ( pnPatch.b210 + pnPatch.b120 + pnPatch.b021 + pnPatch.b012 + pnPatch.b102 + pnPatch.b201 ) / 6.0; + float V = (P0 + P1 + P2)/3.0; + pnPatch.b111 = E + (E - V)*0.5; + pnPatch.n110 = N0+N1-vij(patch[0].Pos, patch[0].Normal, patch[1].Pos, patch[1].Normal)*(P1-P0); + pnPatch.n011 = N1+N2-vij(patch[1].Pos, patch[1].Normal, patch[2].Pos, patch[2].Normal)*(P2-P1); + pnPatch.n101 = N2+N0-vij(patch[2].Pos, patch[2].Normal, patch[0].Pos, patch[0].Normal)*(P0-P2); + SetPnPatch(output.pnPatch, pnPatch); + + return output; +} \ No newline at end of file diff --git a/shaders/slang/tessellation/pntriangles.tesc.spv b/shaders/slang/tessellation/pntriangles.tesc.spv new file mode 100644 index 00000000..21b7dd7e Binary files /dev/null and b/shaders/slang/tessellation/pntriangles.tesc.spv differ diff --git a/shaders/slang/tessellation/pntriangles.tese.spv b/shaders/slang/tessellation/pntriangles.tese.spv new file mode 100644 index 00000000..7322860d Binary files /dev/null and b/shaders/slang/tessellation/pntriangles.tese.spv differ diff --git a/shaders/slang/textoverlay/mesh.frag.spv b/shaders/slang/textoverlay/mesh.frag.spv new file mode 100644 index 00000000..396c7f8e Binary files /dev/null and b/shaders/slang/textoverlay/mesh.frag.spv differ diff --git a/shaders/slang/textoverlay/mesh.slang b/shaders/slang/textoverlay/mesh.slang new file mode 100644 index 00000000..a7c4d07d --- /dev/null +++ b/shaders/slang/textoverlay/mesh.slang @@ -0,0 +1,56 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.UV = input.UV; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, normalize(input.Normal)); + float3 lPos = mul((float3x3)ubo.model, ubo.lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = -pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float diffuse = max(dot(N, L), 0.0); + float specular = pow(max(dot(R, V), 0.0), 1.0); + return float4(((diffuse + specular) * 0.25).xxx, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/textoverlay/mesh.vert.spv b/shaders/slang/textoverlay/mesh.vert.spv new file mode 100644 index 00000000..dce7a391 Binary files /dev/null and b/shaders/slang/textoverlay/mesh.vert.spv differ diff --git a/shaders/slang/textoverlay/text.frag.spv b/shaders/slang/textoverlay/text.frag.spv new file mode 100644 index 00000000..c1aa8731 Binary files /dev/null and b/shaders/slang/textoverlay/text.frag.spv differ diff --git a/shaders/slang/textoverlay/text.slang b/shaders/slang/textoverlay/text.slang new file mode 100644 index 00000000..f4ca9337 --- /dev/null +++ b/shaders/slang/textoverlay/text.slang @@ -0,0 +1,35 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + + struct VSInput +{ + float2 Pos; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; +}; + +Sampler2D samplerFont; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = float4(input.Pos, 0.0, 1.0); + output.UV = input.UV; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float color = samplerFont.Sample(input.UV).r; + return color.xxxx; +} diff --git a/shaders/slang/textoverlay/text.vert.spv b/shaders/slang/textoverlay/text.vert.spv new file mode 100644 index 00000000..79269cc0 Binary files /dev/null and b/shaders/slang/textoverlay/text.vert.spv differ diff --git a/shaders/slang/texture/texture.frag.spv b/shaders/slang/texture/texture.frag.spv new file mode 100644 index 00000000..5a369ab8 Binary files /dev/null and b/shaders/slang/texture/texture.frag.spv differ diff --git a/shaders/slang/texture/texture.slang b/shaders/slang/texture/texture.slang new file mode 100644 index 00000000..8c104e10 --- /dev/null +++ b/shaders/slang/texture/texture.slang @@ -0,0 +1,68 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float LodBias; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 viewPos; + float lodBias; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.LodBias = ubo.lodBias; + + float3 worldPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lightPos = float3(0.0, 0.0, 0.0); + float3 lPos = mul((float3x3)ubo.model, lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = ubo.viewPos.xyz - pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColor.SampleLevel(input.UV, input.LodBias); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float specular = pow(max(dot(R, V), 0.0), 16.0) * color.a; + + return float4(diffuse * color.rgb + specular, 1.0); +} diff --git a/shaders/slang/texture/texture.vert.spv b/shaders/slang/texture/texture.vert.spv new file mode 100644 index 00000000..b58e6baa Binary files /dev/null and b/shaders/slang/texture/texture.vert.spv differ diff --git a/shaders/slang/texture3d/texture3d.frag.spv b/shaders/slang/texture3d/texture3d.frag.spv new file mode 100644 index 00000000..74241b57 Binary files /dev/null and b/shaders/slang/texture3d/texture3d.frag.spv differ diff --git a/shaders/slang/texture3d/texture3d.slang b/shaders/slang/texture3d/texture3d.slang new file mode 100644 index 00000000..c11208b9 --- /dev/null +++ b/shaders/slang/texture3d/texture3d.slang @@ -0,0 +1,66 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UV; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 viewPos; + float depth; +}; +ConstantBuffer ubo; + +Sampler3D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = float3(input.UV, ubo.depth); + + float3 worldPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lightPos = float3(0.0, 0.0, 0.0); + float3 lPos = mul((float3x3)ubo.model, lightPos.xyz); + output.LightVec = lPos - pos.xyz; + output.ViewVec = ubo.viewPos.xyz - pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = samplerColor.Sample(input.UV); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float specular = pow(max(dot(R, V), 0.0), 16.0) * color.r; + + return float4(diffuse * color.r + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/texture3d/texture3d.vert.spv b/shaders/slang/texture3d/texture3d.vert.spv new file mode 100644 index 00000000..65dc9c3c Binary files /dev/null and b/shaders/slang/texture3d/texture3d.vert.spv differ diff --git a/shaders/slang/texturearray/instancing.frag.spv b/shaders/slang/texturearray/instancing.frag.spv new file mode 100644 index 00000000..ed7507bf Binary files /dev/null and b/shaders/slang/texturearray/instancing.frag.spv differ diff --git a/shaders/slang/texturearray/instancing.slang b/shaders/slang/texturearray/instancing.slang new file mode 100644 index 00000000..6db77ac9 --- /dev/null +++ b/shaders/slang/texturearray/instancing.slang @@ -0,0 +1,49 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UV; +}; + +struct Instance +{ + float4x4 model; + float arrayIndex; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + Instance instance[8]; +}; +ConstantBuffer ubo; + +Sampler2DArray samplerArray; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uint InstanceIndex: SV_InstanceID) +{ + VSOutput output; + output.UV = float3(input.UV, ubo.instance[InstanceIndex].arrayIndex); + float4x4 modelView = mul(ubo.view, ubo.instance[InstanceIndex].model); + output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerArray.Sample(input.UV); +} \ No newline at end of file diff --git a/shaders/slang/texturearray/instancing.vert.spv b/shaders/slang/texturearray/instancing.vert.spv new file mode 100644 index 00000000..14880373 Binary files /dev/null and b/shaders/slang/texturearray/instancing.vert.spv differ diff --git a/shaders/slang/texturecubemap/reflect.frag.spv b/shaders/slang/texturecubemap/reflect.frag.spv new file mode 100644 index 00000000..203a318c Binary files /dev/null and b/shaders/slang/texturecubemap/reflect.frag.spv differ diff --git a/shaders/slang/texturecubemap/reflect.slang b/shaders/slang/texturecubemap/reflect.slang new file mode 100644 index 00000000..f938523b --- /dev/null +++ b/shaders/slang/texturecubemap/reflect.slang @@ -0,0 +1,70 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 WorldPos; + float3 Normal; + float LodBias; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 invModel; + float lodBias; +}; +ConstantBuffer ubo; + +SamplerCube samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + output.WorldPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.Normal = mul((float3x3)ubo.model, input.Normal); + output.LodBias = ubo.lodBias; + + float3 lightPos = float3(0.0f, -5.0f, 5.0f); + output.LightVec = lightPos.xyz - output.WorldPos.xyz; + output.ViewVec = -output.WorldPos; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 cI = normalize (input.ViewVec); + float3 cR = reflect (cI, normalize(input.Normal)); + + cR = mul(ubo.invModel, float4(cR, 0.0)).xyz; + // Convert cubemap coordinates into Vulkan coordinate space + cR.z *= -1.0; + + float4 color = samplerColor.SampleLevel(cR, input.LodBias); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 ambient = float3(0.5, 0.5, 0.5) * color.rgb; + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.5, 0.5, 0.5); + return float4(ambient + diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/texturecubemap/reflect.vert.spv b/shaders/slang/texturecubemap/reflect.vert.spv new file mode 100644 index 00000000..e915ac45 Binary files /dev/null and b/shaders/slang/texturecubemap/reflect.vert.spv differ diff --git a/shaders/slang/texturecubemap/skybox.frag.spv b/shaders/slang/texturecubemap/skybox.frag.spv new file mode 100644 index 00000000..71e6a035 Binary files /dev/null and b/shaders/slang/texturecubemap/skybox.frag.spv differ diff --git a/shaders/slang/texturecubemap/skybox.slang b/shaders/slang/texturecubemap/skybox.slang new file mode 100644 index 00000000..618fb2f3 --- /dev/null +++ b/shaders/slang/texturecubemap/skybox.slang @@ -0,0 +1,47 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; +}; +ConstantBuffer ubo; + +SamplerCube samplerCubeMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UVW = input.Pos; + // Convert cubemap coordinates into Vulkan coordinate space + output.UVW.xy *= -1.0; + // Remove translation from view matrix + float4x4 viewMat = ubo.model; + viewMat[0][3] = 0.0; + viewMat[1][3] = 0.0; + viewMat[2][3] = 0.0; + output.Pos = mul(ubo.projection, mul(viewMat, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerCubeMap.Sample(input.UVW); +} \ No newline at end of file diff --git a/shaders/slang/texturecubemap/skybox.vert.spv b/shaders/slang/texturecubemap/skybox.vert.spv new file mode 100644 index 00000000..b7c2316f Binary files /dev/null and b/shaders/slang/texturecubemap/skybox.vert.spv differ diff --git a/shaders/slang/texturecubemaparray/reflect.frag.spv b/shaders/slang/texturecubemaparray/reflect.frag.spv new file mode 100644 index 00000000..84d4c6b8 Binary files /dev/null and b/shaders/slang/texturecubemaparray/reflect.frag.spv differ diff --git a/shaders/slang/texturecubemaparray/reflect.slang b/shaders/slang/texturecubemaparray/reflect.slang new file mode 100644 index 00000000..1edb34b3 --- /dev/null +++ b/shaders/slang/texturecubemaparray/reflect.slang @@ -0,0 +1,68 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 WorldPos; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 invModel; + float lodBias; + int cubeMapIndex; +}; +ConstantBuffer ubo; + +SamplerCubeArray samplerCubeMapArray; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + + output.WorldPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + output.Normal = mul((float3x3)ubo.model, input.Normal); + + float3 lightPos = float3(0.0f, -5.0f, 5.0f); + output.LightVec = lightPos.xyz - output.WorldPos.xyz; + output.ViewVec = -output.WorldPos; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 cI = normalize(input.WorldPos.xyz); + float3 cR = reflect(cI, normalize(input.Normal)); + + cR = mul(ubo.invModel, float4(cR, 0.0)).xyz; + cR.yz *= -1.0; + + float4 color = samplerCubeMapArray.SampleLevel(float4(cR, ubo.cubeMapIndex), ubo.lodBias); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 ambient = float3(0.5, 0.5, 0.5) * color.rgb; + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.5, 0.5, 0.5); + return float4(ambient + diffuse * color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/texturecubemaparray/reflect.vert.spv b/shaders/slang/texturecubemaparray/reflect.vert.spv new file mode 100644 index 00000000..807b6cf9 Binary files /dev/null and b/shaders/slang/texturecubemaparray/reflect.vert.spv differ diff --git a/shaders/slang/texturecubemaparray/skybox.frag.spv b/shaders/slang/texturecubemaparray/skybox.frag.spv new file mode 100644 index 00000000..b03c534b Binary files /dev/null and b/shaders/slang/texturecubemaparray/skybox.frag.spv differ diff --git a/shaders/slang/texturecubemaparray/skybox.slang b/shaders/slang/texturecubemaparray/skybox.slang new file mode 100644 index 00000000..f7686993 --- /dev/null +++ b/shaders/slang/texturecubemaparray/skybox.slang @@ -0,0 +1,49 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 invModel; + float lodBias; + int cubeMapIndex; +}; +ConstantBuffer ubo; + +SamplerCubeArray samplerCubeMapArray; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UVW = input.Pos; + output.UVW.xy *= -1.0; + // Remove translation from view matrix + float4x4 viewMat = ubo.model; + viewMat[0][3] = 0.0; + viewMat[1][3] = 0.0; + viewMat[2][3] = 0.0; + output.Pos = mul(ubo.projection, mul(viewMat, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerCubeMapArray.SampleLevel(float4(input.UVW, ubo.cubeMapIndex), ubo.lodBias); +} \ No newline at end of file diff --git a/shaders/slang/texturecubemaparray/skybox.vert.spv b/shaders/slang/texturecubemaparray/skybox.vert.spv new file mode 100644 index 00000000..e5c3dd2b Binary files /dev/null and b/shaders/slang/texturecubemaparray/skybox.vert.spv differ diff --git a/shaders/slang/texturemipmapgen/texture.frag.spv b/shaders/slang/texturemipmapgen/texture.frag.spv new file mode 100644 index 00000000..14c85ad7 Binary files /dev/null and b/shaders/slang/texturemipmapgen/texture.frag.spv differ diff --git a/shaders/slang/texturemipmapgen/texture.slang b/shaders/slang/texturemipmapgen/texture.slang new file mode 100644 index 00000000..41aaca50 --- /dev/null +++ b/shaders/slang/texturemipmapgen/texture.slang @@ -0,0 +1,68 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float2 UV; + float3 Normal; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float LodBias; + float3 Normal; + float3 ViewVec; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 viewPos; + float lodBias; + int samplerIndex; +}; +ConstantBuffer ubo; + +Texture2D textureColor; +SamplerState samplers[3]; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV * float2(2.0, 1.0); + output.LodBias = ubo.lodBias; + + float3 worldPos = mul(ubo.model, float4(input.Pos, 1.0)).xyz; + + output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + + output.Normal = mul((float3x3)ubo.model, input.Normal); + float3 lightPos = float3(-30.0, 0.0, 0.0); + output.LightVec = worldPos - lightPos; + output.ViewVec = ubo.viewPos.xyz - worldPos; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float4 color = textureColor.Sample(samplers[ubo.samplerIndex], input.UV, int2(0, 0), input.LodBias); + + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(L, N); + float3 diffuse = max(dot(N, L), 0.65) * float3(1.0, 1.0, 1.0); + float specular = pow(max(dot(R, V), 0.0), 16.0) * color.a; + return float4(diffuse * color.r + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/texturemipmapgen/texture.vert.spv b/shaders/slang/texturemipmapgen/texture.vert.spv new file mode 100644 index 00000000..0a076113 Binary files /dev/null and b/shaders/slang/texturemipmapgen/texture.vert.spv differ diff --git a/shaders/slang/texturesparseresidency/sparseresidency.frag.spv b/shaders/slang/texturesparseresidency/sparseresidency.frag.spv new file mode 100644 index 00000000..0ea3e400 Binary files /dev/null and b/shaders/slang/texturesparseresidency/sparseresidency.frag.spv differ diff --git a/shaders/slang/texturesparseresidency/sparseresidency.slang b/shaders/slang/texturesparseresidency/sparseresidency.slang new file mode 100644 index 00000000..6ad26c8a --- /dev/null +++ b/shaders/slang/texturesparseresidency/sparseresidency.slang @@ -0,0 +1,54 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float LodBias; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4 viewPos; + float lodBias; +}; +ConstantBuffer ubo; + +Sampler2D samplerColor; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.UV; + output.LodBias = ubo.lodBias; + output.Pos = mul(ubo.projection, mul(ubo.model, float4(input.Pos.xyz, 1.0))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + // Check if texel is resident + uint status = 0; + float4 sampledColor = samplerColor.Sample(input.UV, int2(0, 0), input.LodBias, status); + bool texelResident = CheckAccessFullyMapped(status); + if (texelResident) { + return sampledColor; + } else { + return float4(0.0, 0.0, 0.0, 1.0); + } +} \ No newline at end of file diff --git a/shaders/slang/texturesparseresidency/sparseresidency.vert.spv b/shaders/slang/texturesparseresidency/sparseresidency.vert.spv new file mode 100644 index 00000000..784b20dd Binary files /dev/null and b/shaders/slang/texturesparseresidency/sparseresidency.vert.spv differ diff --git a/shaders/slang/triangle/triangle.frag.spv b/shaders/slang/triangle/triangle.frag.spv new file mode 100644 index 00000000..81761f27 Binary files /dev/null and b/shaders/slang/triangle/triangle.frag.spv differ diff --git a/shaders/slang/triangle/triangle.slang b/shaders/slang/triangle/triangle.slang new file mode 100644 index 00000000..b403786f --- /dev/null +++ b/shaders/slang/triangle/triangle.slang @@ -0,0 +1,41 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct UBO +{ + float4x4 projectionMatrix; + float4x4 modelMatrix; + float4x4 viewMatrix; +}; +[[vk::binding(0, 0)]] +ConstantBuffer ubo; + +struct VSInput +{ + [[vk::location(0)]] float3 Pos; + [[vk::location(1)]] float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + [[vk::location(0)]] float3 Color; +}; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Pos = mul(ubo.projectionMatrix, mul(ubo.viewMatrix, mul(ubo.modelMatrix, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return float4(input.Color, 1.0); +} diff --git a/shaders/slang/triangle/triangle.vert.spv b/shaders/slang/triangle/triangle.vert.spv new file mode 100644 index 00000000..b3fefe85 Binary files /dev/null and b/shaders/slang/triangle/triangle.vert.spv differ diff --git a/shaders/slang/variablerateshading/scene.frag.spv b/shaders/slang/variablerateshading/scene.frag.spv new file mode 100644 index 00000000..8d3b3031 Binary files /dev/null and b/shaders/slang/variablerateshading/scene.frag.spv differ diff --git a/shaders/slang/variablerateshading/scene.slang b/shaders/slang/variablerateshading/scene.slang new file mode 100644 index 00000000..e5954740 --- /dev/null +++ b/shaders/slang/variablerateshading/scene.slang @@ -0,0 +1,116 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float3 Color; + float4 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; + float2 UV; + float3 ViewVec; + float3 LightVec; + float4 Tangent; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4x4 model; + float4 lightPos; + float4 viewPos; + int colorShadingRates; +}; +ConstantBuffer ubo; + +[[vk::binding(0, 1)]] Sampler2D samplerColorMap; +[[vk::binding(1, 1)]] Sampler2D samplerNormalMap; + +[[SpecializationConstant]] const bool ALPHA_MASK = false; +[[SpecializationConstant]] const float ALPHA_MASK_CUTOFF = 0.0; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Normal = input.Normal; + output.Color = input.Color; + output.UV = input.UV; + output.Tangent = input.Tangent; + + float4x4 modelView = mul(ubo.view, ubo.model); + + output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos.xyz, 1.0))); + + output.Normal = mul((float3x3)ubo.model, input.Normal); + float4 pos = mul(ubo.model, float4(input.Pos, 1.0)); + output.LightVec = ubo.lightPos.xyz - pos.xyz; + output.ViewVec = ubo.viewPos.xyz - pos.xyz; + return output; +} + +static const uint SHADING_RATE_PER_PIXEL = 0; +static const uint SHADING_RATE_PER_2X1_PIXELS = 6; +static const uint SHADING_RATE_PER_1X2_PIXELS = 7; +static const uint SHADING_RATE_PER_2X2_PIXELS = 8; +static const uint SHADING_RATE_PER_4X2_PIXELS = 9; +static const uint SHADING_RATE_PER_2X4_PIXELS = 10; + +[shader("fragment")] +float4 fragmentMain(VSOutput input, uint shadingRate : SV_ShadingRate) +{ + float4 color = samplerColorMap.Sample(input.UV) * float4(input.Color, 1.0); + + if (ALPHA_MASK) { + if (color.a < ALPHA_MASK_CUTOFF) { + discard; + } + } + + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent.xyz); + float3 B = cross(input.Normal, input.Tangent.xyz) * input.Tangent.w; + float3x3 TBN = float3x3(T, B, N); + N = mul(normalize(samplerNormalMap.Sample(input.UV).xyz * 2.0 - float3(1.0, 1.0, 1.0)), TBN); + + const float ambient = 0.1; + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), ambient).rrr; + float3 specular = pow(max(dot(R, V), 0.0), 32.0); + color = float4(diffuse * color.rgb + specular, color.a); + + if (ubo.colorShadingRates == 1) { + switch (shadingRate) { + case SHADING_RATE_PER_PIXEL: + return color * float4(0.0, 0.8, 0.4, 1.0); + case SHADING_RATE_PER_2X1_PIXELS: + return color * float4(0.2, 0.6, 1.0, 1.0); + case SHADING_RATE_PER_1X2_PIXELS: + return color * float4(0.0, 0.4, 0.8, 1.0); + case SHADING_RATE_PER_2X2_PIXELS: + return color * float4(1.0, 1.0, 0.2, 1.0); + case SHADING_RATE_PER_4X2_PIXELS: + return color * float4(0.8, 0.8, 0.0, 1.0); + case SHADING_RATE_PER_2X4_PIXELS: + return color * float4(1.0, 0.4, 0.2, 1.0); + default: + return color * float4(0.8, 0.0, 0.0, 1.0); + } + } + + return color; +} \ No newline at end of file diff --git a/shaders/slang/variablerateshading/scene.vert.spv b/shaders/slang/variablerateshading/scene.vert.spv new file mode 100644 index 00000000..4b157a51 Binary files /dev/null and b/shaders/slang/variablerateshading/scene.vert.spv differ diff --git a/shaders/slang/vertexattributes/scene.frag.spv b/shaders/slang/vertexattributes/scene.frag.spv new file mode 100644 index 00000000..85b56edf Binary files /dev/null and b/shaders/slang/vertexattributes/scene.frag.spv differ diff --git a/shaders/slang/vertexattributes/scene.slang b/shaders/slang/vertexattributes/scene.slang new file mode 100644 index 00000000..39223e4e --- /dev/null +++ b/shaders/slang/vertexattributes/scene.slang @@ -0,0 +1,86 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float2 UV; + float4 Tangent; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float2 UV; + float3 ViewVec; + float3 LightVec; + float4 Tangent; +}; + +struct UBO +{ + float4x4 projection; + float4x4 view; + float4 lightPos; + float4 viewPos; +}; +ConstantBuffer ubo; + +[[vk::binding(0, 1)]] Sampler2D samplerColorMap; +[[vk::binding(1, 1)]] Sampler2D samplerNormalMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input, uniform float4x4 modelMat) +{ + VSOutput output; + output.Normal = input.Normal; + output.UV = input.UV; + output.Tangent = input.Tangent; + + float4x4 modelView = mul(ubo.view, modelMat); + + output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos.xyz, 1.0))); + + output.Normal = mul((float3x3)modelMat, input.Normal); + float4 pos = mul(modelMat, float4(input.Pos, 1.0)); + output.LightVec = ubo.lightPos.xyz - pos.xyz; + output.ViewVec = ubo.viewPos.xyz - pos.xyz; + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input, uniform float4x4 modelMat, uniform uint alphaMask, uniform float alphaMaskCutoff) +{ + float4 color = samplerColorMap.Sample(input.UV); + + if (alphaMask == 1) { + if (color.a < alphaMaskCutoff) { + discard; + } + } + + float3 N = normalize(input.Normal); + float3 T = normalize(input.Tangent.xyz); + float3 B = cross(input.Normal, input.Tangent.xyz) * input.Tangent.w; + float3x3 TBN = float3x3(T, B, N); + N = mul(normalize(samplerNormalMap.Sample(input.UV).xyz * 2.0 - float3(1.0, 1.0, 1.0)), TBN); + + const float ambient = 0.1; + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 diffuse = max(dot(N, L), ambient).rrr; + float3 specular = pow(max(dot(R, V), 0.0), 32.0); + return float4(diffuse * color.rgb + specular, color.a); +} \ No newline at end of file diff --git a/shaders/slang/vertexattributes/scene.vert.spv b/shaders/slang/vertexattributes/scene.vert.spv new file mode 100644 index 00000000..4ed62fbb Binary files /dev/null and b/shaders/slang/vertexattributes/scene.vert.spv differ diff --git a/shaders/slang/viewportarray/multiview.geom.spv b/shaders/slang/viewportarray/multiview.geom.spv new file mode 100644 index 00000000..b980b0f6 Binary files /dev/null and b/shaders/slang/viewportarray/multiview.geom.spv differ diff --git a/shaders/slang/viewportarray/scene.frag.spv b/shaders/slang/viewportarray/scene.frag.spv new file mode 100644 index 00000000..6cbde14f Binary files /dev/null and b/shaders/slang/viewportarray/scene.frag.spv differ diff --git a/shaders/slang/viewportarray/scene.slang b/shaders/slang/viewportarray/scene.slang new file mode 100644 index 00000000..e6b6eb7e --- /dev/null +++ b/shaders/slang/viewportarray/scene.slang @@ -0,0 +1,90 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; + float3 Normal; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 Normal; + float3 Color; +}; + +struct GSOutput +{ + float4 Pos : SV_POSITION; + uint ViewportIndex : SV_ViewportArrayIndex; + uint PrimitiveID : SV_PrimitiveID; + float3 Normal; + float3 Color; + float3 ViewVec; + float3 LightVec; +} + +struct UBO +{ + float4x4 projection[2]; + float4x4 modelview[2]; + float4 lightPos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.Color = input.Color; + output.Normal = input.Normal; + output.Pos = float4(input.Pos.xyz, 1.0); + return output; +} + +[shader("geometry")] +[maxvertexcount(3)] +[instance(2)] +void geometryMain(triangle VSOutput input[3], inout TriangleStream outStream, uint InvocationID: SV_GSInstanceID, uint PrimitiveID: SV_PrimitiveID) +{ + for (int i = 0; i < 3; i++) + { + GSOutput output; + output.Normal = mul((float3x3)ubo.modelview[InvocationID], input[i].Normal); + output.Color = input[i].Color; + + float4 pos = input[i].Pos; + float4 worldPos = mul(ubo.modelview[InvocationID], pos); + + float3 lPos = mul(ubo.modelview[InvocationID], ubo.lightPos).xyz; + output.LightVec = lPos - worldPos.xyz; + output.ViewVec = -worldPos.xyz; + + output.Pos = mul(ubo.projection[InvocationID], worldPos); + + // Set the viewport index that the vertex will be emitted to + output.ViewportIndex = InvocationID; + output.PrimitiveID = PrimitiveID; + outStream.Append(output); + } + + outStream.RestartStrip(); +} + +[shader("fragment")] +float4 fragmentMain(GSOutput input) +{ + float3 N = normalize(input.Normal); + float3 L = normalize(input.LightVec); + float3 V = normalize(input.ViewVec); + float3 R = reflect(-L, N); + float3 ambient = float3(0.1, 0.1, 0.1); + float3 diffuse = max(dot(N, L), 0.0) * float3(1.0, 1.0, 1.0); + float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); + return float4((ambient + diffuse) * input.Color.rgb + specular, 1.0); +} \ No newline at end of file diff --git a/shaders/slang/viewportarray/scene.vert.spv b/shaders/slang/viewportarray/scene.vert.spv new file mode 100644 index 00000000..0b22093f Binary files /dev/null and b/shaders/slang/viewportarray/scene.vert.spv differ diff --git a/shaders/slang/vulkanscene/logo.frag.spv b/shaders/slang/vulkanscene/logo.frag.spv new file mode 100644 index 00000000..ea6e69d8 Binary files /dev/null and b/shaders/slang/vulkanscene/logo.frag.spv differ diff --git a/shaders/slang/vulkanscene/logo.slang b/shaders/slang/vulkanscene/logo.slang new file mode 100644 index 00000000..95974e79 --- /dev/null +++ b/shaders/slang/vulkanscene/logo.slang @@ -0,0 +1,60 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Normal; + float2 TexCoord; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float3 Normal; + float3 Color; + float3 EyePos; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 normal; + float4x4 view; + float3 lightpos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + float4x4 modelView = mul(ubo.view, ubo.model); + float4 pos = mul(modelView, input.Pos); + output.UV = input.TexCoord.xy; + output.Normal = normalize(mul((float3x3)ubo.normal, input.Normal)); + output.Color = input.Color; + output.Pos = mul(ubo.projection, pos); + output.EyePos = mul(modelView, pos).xyz; + float4 lightPos = mul(modelView, float4(1.0, 2.0, 0.0, 1.0)); + output.LightVec = normalize(lightPos.xyz - output.EyePos); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 Eye = normalize(-input.EyePos); + float3 Reflected = normalize(reflect(-input.LightVec, input.Normal)); + float4 diff = float4(input.Color, 1.0) * max(dot(input.Normal, input.LightVec), 0.0); + float shininess = 0.0; + float4 spec = float4(1.0, 1.0, 1.0, 1.0) * pow(max(dot(Reflected, Eye), 0.0), 2.5) * shininess; + return float4((diff + spec).rgb, 1); +} \ No newline at end of file diff --git a/shaders/slang/vulkanscene/logo.vert.spv b/shaders/slang/vulkanscene/logo.vert.spv new file mode 100644 index 00000000..c804643c Binary files /dev/null and b/shaders/slang/vulkanscene/logo.vert.spv differ diff --git a/shaders/slang/vulkanscene/mesh.frag.spv b/shaders/slang/vulkanscene/mesh.frag.spv new file mode 100644 index 00000000..acd220e8 Binary files /dev/null and b/shaders/slang/vulkanscene/mesh.frag.spv differ diff --git a/shaders/slang/vulkanscene/mesh.slang b/shaders/slang/vulkanscene/mesh.slang new file mode 100644 index 00000000..61c80c11 --- /dev/null +++ b/shaders/slang/vulkanscene/mesh.slang @@ -0,0 +1,85 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float4 Pos; + float3 Normal; + float2 TexCoord; + float3 Color; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float2 UV; + float3 Normal; + float3 Color; + float3 EyePos; + float3 LightVec; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 normal; + float4x4 view; + float3 lightpos; +}; +ConstantBuffer ubo; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + output.UV = input.TexCoord.xy; + output.Normal = normalize(mul((float3x3)ubo.normal, input.Normal)); + output.Color = input.Color; + float4x4 modelView = mul(ubo.view, ubo.model); + float4 pos = mul(modelView, input.Pos); + output.Pos = mul(ubo.projection, pos); + output.EyePos = mul(modelView, pos).xyz; + float4 lightPos = mul(modelView, float4(ubo.lightpos, 1.0)); + output.LightVec = normalize(lightPos.xyz - output.EyePos); + return output; +} + +float specpart(float3 L, float3 N, float3 H) +{ + if (dot(N, L) > 0.0) + { + return pow(clamp(dot(H, N), 0.0, 1.0), 64.0); + } + return 0.0; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + float3 Eye = normalize(-input.EyePos); + float3 Reflected = normalize(reflect(-input.LightVec, input.Normal)); + + float3 halfVec = normalize(input.LightVec + input.EyePos); + float diff = clamp(dot(input.LightVec, input.Normal), 0.0, 1.0); + float spec = specpart(input.LightVec, input.Normal, halfVec); + float intensity = 0.1 + diff + spec; + + float4 IAmbient = float4(0.2, 0.2, 0.2, 1.0); + float4 IDiffuse = float4(0.5, 0.5, 0.5, 0.5) * max(dot(input.Normal, input.LightVec), 0.0); + float shininess = 0.75; + float4 ISpecular = float4(0.5, 0.5, 0.5, 1.0) * pow(max(dot(Reflected, Eye), 0.0), 2.0) * shininess; + + float4 outFragColor = float4((IAmbient + IDiffuse) * float4(input.Color, 1.0) + ISpecular); + + // Some manual saturation + if (intensity > 0.95) + outFragColor *= 2.25; + if (intensity < 0.15) + outFragColor = float4(0.1, 0.1, 0.1, 0.1); + + return outFragColor; +} \ No newline at end of file diff --git a/shaders/slang/vulkanscene/mesh.vert.spv b/shaders/slang/vulkanscene/mesh.vert.spv new file mode 100644 index 00000000..962fd3c4 Binary files /dev/null and b/shaders/slang/vulkanscene/mesh.vert.spv differ diff --git a/shaders/slang/vulkanscene/skybox.frag.spv b/shaders/slang/vulkanscene/skybox.frag.spv new file mode 100644 index 00000000..71e6a035 Binary files /dev/null and b/shaders/slang/vulkanscene/skybox.frag.spv differ diff --git a/shaders/slang/vulkanscene/skybox.slang b/shaders/slang/vulkanscene/skybox.slang new file mode 100644 index 00000000..98d38560 --- /dev/null +++ b/shaders/slang/vulkanscene/skybox.slang @@ -0,0 +1,46 @@ +/* Copyright (c) 2025, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct VSInput +{ + float3 Pos; +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; + float3 UVW; +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float4x4 normal; + float4x4 view; +}; +ConstantBuffer ubo; +SamplerCube samplerCubeMap; + +[shader("vertex")] +VSOutput vertexMain(VSInput input) +{ + VSOutput output; + // 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 = input.Pos; + output.Pos = mul(ubo.projection, mul(viewMat, mul(ubo.model, float4(input.Pos.xyz, 1.0)))); + return output; +} + +[shader("fragment")] +float4 fragmentMain(VSOutput input) +{ + return samplerCubeMap.Sample(input.UVW); +} \ No newline at end of file diff --git a/shaders/slang/vulkanscene/skybox.vert.spv b/shaders/slang/vulkanscene/skybox.vert.spv new file mode 100644 index 00000000..427b41e3 Binary files /dev/null and b/shaders/slang/vulkanscene/skybox.vert.spv differ