Skip to content

@blooper.gg/toodle


@blooper.gg/toodle / text/text.wgsl / default

Variable: default

default: "\n// Adapted from: https://webgpu.github.io/webgpu-samples/?sample=textRenderingMsdf\n\n// Quad vertex positions for a character\nconst pos = array(\n vec2f(0, -1),\n vec2f(1, -1),\n vec2f(0, 0),\n vec2f(1, 0),\n);\n\n// Debug colors for visualization\nconst debugColors = array(\n vec4f(1, 0, 0, 1),\n vec4f(0, 1, 0, 1),\n vec4f(0, 0, 1, 1),\n vec4f(1, 1, 1, 1),\n);\n\n// Vertex input from GPU\nstruct VertexInput {\n @builtin(vertex_index) vertex: u32,\n @builtin(instance_index) instance: u32,\n};\n\n// Output from vertex shader to fragment shader\nstruct VertexOutput {\n @builtin(position) position: vec4f,\n @location(0) texcoord: vec2f,\n @location(1) debugColor: vec4f,\n @location(2) @interpolate(flat) instanceIndex: u32,\n};\n\n// Metadata for a single character glyph\nstruct Char {\n texOffset: vec2f, // Offset to top-left in MSDF texture (pixels)\n texExtent: vec2f, // Size in texture (pixels)\n size: vec2f, // Glyph size in ems\n offset: vec2f, // Position offset in ems\n};\n\n// Metadata for a text block\nstruct TextBlockDescriptor {\n transform: mat3x3f, // Text transform matrix (model matrix)\n color: vec4f, // Text color\n fontSize: f32, // Font size\n blockWidth: f32, // Total width of text block\n blockHeight: f32, // Total height of text block\n bufferPosition: f32 // Index and length in textBuffer\n};\n\n// Font bindings\n@group(0) @binding(0) var fontTexture: texture_2d<f32>;\n@group(0) @binding(1) var fontSampler: sampler;\n@group(0) @binding(2) var<storage> chars: array<Char>;\n@group(0) @binding(3) var<uniform> fontData: vec4f; // Contains line height (x)\n\n// Text bindings\n@group(1) @binding(0) var<storage> texts: array<TextBlockDescriptor>;\n@group(1) @binding(1) var<storage> textBuffer: array<vec4f>; // Each vec4: xy = glyph pos, z = char index\n\n// Global uniforms\n@group(2) @binding(0) var<uniform> viewProjectionMatrix: mat3x3f;\n\n// Vertex shader\n@vertex\nfn vertexMain(input: VertexInput) -> VertexOutput {\n // Because the instance index is used for character indexing, we are\n // overloading the vertex index to store the instance of the text metadata.\n //\n // I.e...\n // Vertex 0-4 = Instance 0, Vertex 0-4\n // Vertex 4-8 = Instance 1, Vertex 0-4\n // Vertex 8-12 = Instance 2, Vertex 0-4\n let vertexIndex = input.vertex % 4;\n let textIndex = input.vertex / 4;\n\n let text = texts[textIndex];\n let textElement = textBuffer[u32(text.bufferPosition) + input.instance];\n let char = chars[u32(textElement.z)];\n\n let lineHeight = fontData.x;\n let textWidth = text.blockWidth;\n let textHeight = text.blockHeight;\n\n // Center text vertically; origin is mid-height\n let offset = vec2f(0, -textHeight / 2);\n\n // Glyph position in ems (quad pos * size + per-char offset)\n let emPos = pos[vertexIndex] * char.size + char.offset + textElement.xy - offset;\n let charPos = emPos * (text.fontSize / lineHeight);\n\n var output: VertexOutput;\n let transformedPosition = viewProjectionMatrix * text.transform * vec3f(charPos, 1);\n\n output.position = vec4f(transformedPosition, 1);\n output.texcoord = pos[vertexIndex] * vec2f(1, -1);\n output.texcoord *= char.texExtent;\n output.texcoord += char.texOffset;\n output.debugColor = debugColors[vertexIndex];\n output.instanceIndex = textIndex;\n return output;\n\n // To debug - hardcode quad in bottom right quarter of the screen:\n // output.position = vec4f(pos[input.vertex], 0, 1);\n}\n\n// Signed distance function sampling for MSDF font rendering\nfn sampleMsdf(texcoord: vec2f) -> f32 {\n let c = textureSample(fontTexture, fontSampler, texcoord);\n return max(min(c.r, c.g), min(max(c.r, c.g), c.b));\n}\n\n// Fragment shader\n// Anti-aliasing technique by Paul Houx\n// more details here:\n// https://github.com/Chlumsky/msdfgen/issues/22#issuecomment-234958005\n@fragment\nfn fragmentMain(input: VertexOutput) -> @location(0) vec4f {\n let text = texts[input.instanceIndex];\n\n // pxRange (AKA distanceRange) comes from the msdfgen tool.\n let pxRange = 4.0;\n let texSize = vec2f(textureDimensions(fontTexture, 0));\n\n let dx = texSize.x * length(vec2f(dpdxFine(input.texcoord.x), dpdyFine(input.texcoord.x)));\n let dy = texSize.y * length(vec2f(dpdxFine(input.texcoord.y), dpdyFine(input.texcoord.y)));\n\n let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);\n let sigDist = sampleMsdf(input.texcoord) - 0.5;\n let pxDist = sigDist * toPixels;\n\n let edgeWidth = 0.5;\n let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);\n\n if (alpha < 0.001) {\n discard;\n }\n\n let msdfColor = vec4f(text.color.rgb, text.color.a * alpha);\n return msdfColor;\n\n // Debug options:\n // return text.color;\n // return input.debugColor;\n // return vec4f(1, 0, 1, 1); // hardcoded magenta\n // return textureSample(fontTexture, fontSampler, input.texcoord);\n}\n"

Defined in: text/text.wgsl.ts:1