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