From 487fd21d44a2b52782840f7ead5b2dc449894e9c Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 31 May 2025 13:43:45 +0200 Subject: [PATCH] Added final missing slang shaders Requires a very recent version of slang --- .../deferredmultisampling/deferred.slang | 140 ++++++++++++++++++ shaders/slang/deferredmultisampling/mrt.slang | 83 +++++++++++ shaders/slang/subpasses/composition.slang | 66 +++++++++ shaders/slang/subpasses/gbuffer.slang | 78 ++++++++++ shaders/slang/subpasses/transparent.slang | 64 ++++++++ 5 files changed, 431 insertions(+) create mode 100644 shaders/slang/deferredmultisampling/deferred.slang create mode 100644 shaders/slang/deferredmultisampling/mrt.slang create mode 100644 shaders/slang/subpasses/composition.slang create mode 100644 shaders/slang/subpasses/gbuffer.slang create mode 100644 shaders/slang/subpasses/transparent.slang 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/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/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/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/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); +}