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/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