struct View { viewProj: mat4x4f, lightViewProj: mat4x4f, lightPosition: vec4f, eyePosition: vec4f, aspect: f32, }; struct Config { scale: f32, translateX: f32, translateY: f32, padding0: f32, distanceThreshold: f32, distanceScale: f32, distanceOffset: f32, padding1: f32, outlineThreshold: f32, outlineScale: f32, outlineOffset: f32, padding2: f32, colorMix: f32, colorScale: f32, colorOffset: f32, padding3: f32, }; struct GlyphBuffer { texPosition: vec2f, texSize: vec2f, size: vec2f, unused: vec2f, }; struct LayoutBuffer { position: vec4f, // glyph_index w }; @group(0) @binding(0) var view: View; @group(1) @binding(0) var linearSampler: sampler; @group(1) @binding(1) var fontTexture: texture_2d; @group(1) @binding(2) var glyphBuffer: array; @group(2) @binding(0) var layoutBuffer: array; @group(2) @binding(1) var config: Config; struct VertexInput { @builtin(vertex_index) vertex_index: u32, @builtin(instance_index) instance_index: u32, }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) texture: vec2f, @location(1) @interpolate(flat) glyphIndex: u32, @location(2) foo: vec4f, }; const vertices = array( vec2f(1.0, 0.0), vec2f(0.0, 1.0), vec2f(0.0, 0.0), vec2f(1.0, 1.0), ); @vertex fn vertexMain(input: VertexInput) -> VertexOutput { let texture = vertices[input.vertex_index]; let position = vec2f(texture.x, 1.0 - texture.y); let glyphIndex = u32(layoutBuffer[input.instance_index].position.w); let viewPosition = position * glyphBuffer[glyphIndex].size + layoutBuffer[input.instance_index].position.xy; let screenPosition = viewPosition * vec2f(1 / view.aspect, 1) * config.scale + vec2f(config.translateX, config.translateY); var output: VertexOutput; output.position = vec4f(screenPosition, 0.0, 1.0); output.texture = texture; output.glyphIndex = glyphIndex; output.foo = vec4f(layoutBuffer[glyphIndex].position.xy, 0, 0); return output; } fn blend(dst: vec4f, src: vec4f, distance: f32, threshold: f32, scale: f32, offset: f32) -> vec4f { let scaledDistance = (distance - threshold) * scale + offset; let alpha = clamp(scaledDistance, 0.0, 1.0); let opacity = clamp(src.a * alpha - dst.a, 0.0, 1.0); return opacity * src + (1.0 - opacity) * dst; } fn palette(t: f32) -> vec4f { let a = vec3f(0.5, 0.5, 0.5); let b = vec3f(0.5, 0.5, 0.5); let c = vec3f(1.0, 1.0, 1.0); let d = vec3f(0.00, 0.33, 0.67); //0.263,0.416,0.557 let color = a + b * cos(6.28318 * (c * t + d)); return vec4f(color, 1); } @fragment fn fragmentMain(input: VertexOutput) -> @location(0) vec4f { let p = glyphBuffer[input.glyphIndex].texPosition; let s = glyphBuffer[input.glyphIndex].texSize; let base = textureSample(fontTexture, linearSampler, input.texture * s + p); let distance = base.x; let insideColorW = vec4f(1, 1, 1, 1); let insideColorP = palette(distance * config.colorScale + config.colorOffset); let insideColor = mix(insideColorW, insideColorP, config.colorMix); let outlineColor = vec4f(0, 0, 0, 1); var color = vec4f(0, 0, 0, 0); color = blend(color, insideColor, distance, config.distanceThreshold, config.distanceScale, config.distanceOffset); color = blend(color, outlineColor, distance, config.distanceThreshold - config.outlineThreshold, config.outlineScale, config.outlineOffset); return color; }