|
#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); |
|
} |