Skip to content

Instantly share code, notes, and snippets.

@TheNorthEestern
Forked from destefanis/DitheredFBM.metal
Created May 4, 2025 13:57
Show Gist options
  • Save TheNorthEestern/d3149a2fff4aa10ed93e2a2a0dd9e4ed to your computer and use it in GitHub Desktop.
Save TheNorthEestern/d3149a2fff4aa10ed93e2a2a0dd9e4ed to your computer and use it in GitHub Desktop.
DitheredFBM.metal
#ifndef COMMON_METAL
#define COMMON_METAL
#include <metal_stdlib>
using namespace metal;
// Base structures
struct VertexIn {
float3 position [[attribute(0)]];
float2 uv [[attribute(1)]];
};
struct VertexOut {
float4 position [[position]];
float2 uv;
};
// Common utilities namespace
namespace utils {
static inline float rand(float2 n) {
return fract(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453);
}
static inline float4 C(float2 u, float3 baseColor, float3 resolution) {
float len = dot(u, u);
float c = smoothstep(0.08/resolution.y, 0.0, len - 0.08);
return float4(baseColor, c);
}
}
#endif /* COMMON_METAL */
// Generic non-namespaced methods ^
namespace dithered_fbm {
struct Uniforms {
float iTime;
float3 padding;
float3 iResolution;
float padding2;
float colorNum;
float pixelSize;
float2 padding3;
};
// Color palette
constant float3 COLOR_DARK = float3(0.1, 0.1, 0.1);
constant float3 COLOR_MEDIUM = float3(0.3, 0.3, 0.3);
constant float3 COLOR_LIGHT = float3(0.7, 0.7, 0.7);
// 8x8 Bayer matrix
constant float bayerMatrix8x8[64] = {
0.0/64.0, 48.0/64.0, 12.0/64.0, 60.0/64.0, 3.0/64.0, 51.0/64.0, 15.0/64.0, 63.0/64.0,
32.0/64.0, 16.0/64.0, 44.0/64.0, 28.0/64.0, 35.0/64.0, 19.0/64.0, 47.0/64.0, 31.0/64.0,
8.0/64.0, 56.0/64.0, 4.0/64.0, 52.0/64.0, 11.0/64.0, 59.0/64.0, 7.0/64.0, 55.0/64.0,
40.0/64.0, 24.0/64.0, 36.0/64.0, 20.0/64.0, 43.0/64.0, 27.0/64.0, 39.0/64.0, 23.0/64.0,
2.0/64.0, 50.0/64.0, 14.0/64.0, 62.0/64.0, 1.0/64.0, 49.0/64.0, 13.0/64.0, 61.0/64.0,
34.0/64.0, 18.0/64.0, 46.0/64.0, 30.0/64.0, 33.0/64.0, 17.0/64.0, 45.0/64.0, 29.0/64.0,
10.0/64.0, 58.0/64.0, 6.0/64.0, 54.0/64.0, 9.0/64.0, 57.0/64.0, 5.0/64.0, 53.0/64.0,
42.0/64.0, 26.0/64.0, 38.0/64.0, 22.0/64.0, 41.0/64.0, 25.0/64.0, 37.0/64.0, 21.0/64.0
};
float random(float2 st) {
return fract(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123);
}
float vnoise(float2 st) {
float2 i = floor(st);
float2 f = fract(st);
float a = random(i);
float b = random(i + float2(1.0, 0.0));
float c = random(i + float2(0.0, 1.0));
float d = random(i + float2(1.0, 1.0));
float2 u = smoothstep(0.0, 1.0, f);
return mix(
mix(a, b, u.x),
mix(c, d, u.x),
u.y
);
}
float fbm(float2 p) {
float value = -0.2;
float amplitude = 1.0;
float frequency = 2.5;
for (int i = 0; i < 8; i++) {
value += amplitude * abs(vnoise(p));
p *= frequency;
amplitude *= 0.35;
}
return value;
}
float3 dither(float2 coord, float3 color, float colorNum) {
int x = int(coord.x) % 8;
int y = int(coord.y) % 8;
float threshold = bayerMatrix8x8[y * 8 + x] - 0.25;
color += threshold / colorNum;
color = floor(color * colorNum) / (colorNum - 1.0);
return color;
}
}
vertex VertexOut dithered_fbm_vertex_main(VertexIn in [[stage_in]]) {
VertexOut out;
out.position = float4(in.position, 1.0);
out.uv = in.uv;
return out;
}
fragment float4 dithered_fbm_fragment(VertexOut in [[stage_in]],
constant dithered_fbm::Uniforms &uniforms [[buffer(1)]]) {
float2 uv = in.uv - 0.5;
uv.x *= uniforms.iResolution.x/uniforms.iResolution.y;
float2 p2 = uv - uniforms.iTime * 0.05;
float f = dithered_fbm::fbm(uv - dithered_fbm::fbm(uv + dithered_fbm::fbm(p2)));
float3 col = mix(dithered_fbm::COLOR_DARK, dithered_fbm::COLOR_MEDIUM, f);
col = mix(col, dithered_fbm::COLOR_LIGHT, pow(f, 3.0));
float2 coord = in.position.xy;
col = dithered_fbm::dither(coord, col, uniforms.colorNum);
return float4(col, 1.0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment