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