Files
SDFMapCreator/SDFMapCreator/SdfKernels.Kernels.cs
2025-04-03 19:28:26 +02:00

137 lines
4.5 KiB
C#

using System.Numerics;
using ILGPU;
using ILGPU.Runtime;
namespace SDFMapCreator;
public partial class SdfKernels {
private const float LUMA_THRESHOLD = 0.0f;
static void SelfMaskKernel(Index2D index, ArrayView2D<Vector3, Stride2D.DenseX> input, ArrayView2D<Vector3, Stride2D.DenseX> mask) {
var x = index.X;
var y = index.Y;
var value = input[x, y];
var lumaA = value.X;
var r = lumaA > LUMA_THRESHOLD ? 1f : 0f;
mask[x, y] = new(r, 0f, 0f);
}
static void ABMaskKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> A,
ArrayView2D<Vector3, Stride2D.DenseX> B,
ArrayView2D<Vector3, Stride2D.DenseX> mask
) {
var x = index.X;
var y = index.Y;
var valueA = A[x, y];
var valueB = B[x, y];
var lumaA = valueA.X;
var lumaB = valueB.X;
var r = lumaA > LUMA_THRESHOLD || lumaB > LUMA_THRESHOLD ? 1f : 0f;
mask[x, y] = new(r, 0f, 0f);
}
static void EdgeKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> mask,
uint width, uint height
) { // early exit if not on mask
if (mask[index].X == 0f) return;
var x = index.X;
var y = index.Y;
//if we are on the edge of the image, return false
if (x == 0 || y == 0 || x == width - 1 || y == height - 1) return;
//check the 3x3 kernel
for (var xi = x - 1; xi <= x + 1; xi++) {
for (var yi = y - 1; yi <= y + 1; yi++) {
if (xi < 0 || xi >= width || yi < 0 || yi >= height)
continue; //skip out of bounds pixels
if (mask[xi, yi].X == 0f)
mask[index].Y = 1f; //if we find a black pixel, return true
}
}
}
static void SdfKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> sdf,
ArrayView1D<Vector2, Stride1D.Dense> edges,
int width, int height
) {
Vector2 pos = new((float)index.X / width, (float)index.Y / height);
var minDist = 2f;
var count = edges.IntExtent.Size;
for (var i = 0; i < count; i++) {
Vector2 edgeNrm = new(edges[i].X / width, edges[i].Y / height);
var dist = Vector2.Distance(pos, edgeNrm);
if (dist < minDist) minDist = dist;
}
if (minDist > 1f) minDist = 1f;
sdf[index] = new(minDist);
}
static void GradientKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> mask,
ArrayView2D<Vector3, Stride2D.DenseX> sdfa,
ArrayView2D<Vector3, Stride2D.DenseX> sdfb,
ArrayView2D<Vector3, Stride2D.DenseX> gradient
) { //early exit if not on mask
if (mask[index].X == 0f || mask[index].Y > 0f) return;
var a = sdfa[index].X;
var b = sdfb[index].X;
gradient[index] = new(a / (a + b));
}
static void DirectionalBlurKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> image,
ArrayView2D<Vector3, Stride2D.DenseX> mask,
ArrayView2D<Vector3, Stride2D.DenseX> output,
float radius, float step, float sigma,
int width, int height
) {
var x = index.X;
var y = index.Y;
var value = image[x, y];
var maskValue = mask[x, y];
if (maskValue.X == 0f) {
output[x, y] = value;
return;
}
var gradient = Vector2.Zero;
for (var dx = -1; dx <= 1; dx++) {
for (var dy = -1; dy <= 1; dy++) {
if (x + dx < 0 || x + dx >= width || y + dy < 0 || y + dy >= height) continue;
gradient += new Vector2(dx, dy) * image[x + dx, y + dy].X;
}
}
if (gradient == Vector2.Zero) {
output[x, y] = value;
return;
}
gradient = Vector2.Normalize(gradient);
float sum = 0;
// now we follow the direction line and sample the image for length;
for (var l = -radius; l <= radius; l += step) {
var xOffset = (int)(gradient.X * l);
var yOffset = (int)(gradient.Y * l);
var xSample = x + xOffset;
var ySample = y + yOffset;
if (xSample < 0 || xSample >= width || ySample < 0 || ySample >= height) continue;
var sampleValue = image[xSample, ySample];
var weight = MathF.Exp(-l * l / (2f * sigma * sigma));
output[x, y] += sampleValue * weight;
sum += weight;
}
output[x, y] /= sum;
}
}