From 53cbbebdc7fa767c2bc11537ca13c63cd0fac6ad Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Wed, 5 Aug 2026 20:54:56 -0500 Subject: [PATCH] render target blending, more sliders --- font.js | 49 ++++++++++++++++++++++++++++++--------- font.wgsl | 51 +++++++++++++++++++++++++++++++++++------ fontSliders.js | 12 ++++++++++ index.html | 6 ++--- index.js | 1 + slider.js | 2 +- src/font_layout/api.cpp | 2 +- 7 files changed, 100 insertions(+), 23 deletions(-) diff --git a/font.js b/font.js index 4696c58..9dc8f3f 100644 --- a/font.js +++ b/font.js @@ -86,7 +86,7 @@ class FontRenderer { const configurationBuffer = device.createBuffer({ label: `configuration buffer ${i}`, - size: 4 * 4, + size: this.sliderArray.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }); @@ -195,7 +195,7 @@ class FontRenderer { buffer: { type: "read-only-storage" } }, { // configuration binding: 1, - visibility: GPUShaderStage.VERTEX, + visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } }] }), @@ -210,6 +210,7 @@ class FontRenderer { ], }); + console.log(canvasFormat); this.renderPipeline = device.createRenderPipeline({ label: `${label} pipeline`, layout: pipelineLayout, @@ -221,6 +222,17 @@ class FontRenderer { module: shaderModule, entryPoint: "fragmentMain", targets: [{ + blend: { + color: { + dstFactor: "one-minus-src-alpha", + srcFactor: "src-alpha", + }, + alpha: { + dstFactor: "one-minus-src-alpha", + srcFactor: "src-alpha", + }, + operation: "add", + }, format: canvasFormat, }] }, @@ -229,11 +241,18 @@ class FontRenderer { }, depthStencil: { depthWriteEnabled: true, - depthCompare: "less", + depthCompare: "always", format: "depth24plus", }, }); + ////////////////////////////////////////////////////////////////////// + // sliders + ////////////////////////////////////////////////////////////////////// + + this.sliders = makeSliders(); + this.sliderArray = new Float32Array(4 * 4); + ////////////////////////////////////////////////////////////////////// // buffers ////////////////////////////////////////////////////////////////////// @@ -252,13 +271,6 @@ class FontRenderer { resource: { buffer: viewUniformBuffer }, }], }); - - ////////////////////////////////////////////////////////////////////// - // sliders - ////////////////////////////////////////////////////////////////////// - - this.sliders = makeSliders(); - this.sliderArray = new Float32Array(4); } update(device, frameNumber) @@ -277,6 +289,19 @@ class FontRenderer { this.sliderArray[0] = configuration.scale; this.sliderArray[1] = configuration.translateX; this.sliderArray[2] = configuration.translateY; + + this.sliderArray[4] = configuration.distanceThreshold; + this.sliderArray[5] = configuration.distanceScale; + this.sliderArray[6] = configuration.distanceOffset; + + this.sliderArray[8] = configuration.outlineThreshold; + this.sliderArray[9] = configuration.outlineScale; + this.sliderArray[10] = configuration.outlineOffset; + + this.sliderArray[12] = configuration.colorMix; + this.sliderArray[13] = configuration.colorScale; + this.sliderArray[14] = configuration.colorOffset; + device.queue.writeBuffer(this.frames[frameNumber].configurationBuffer, 0, this.sliderArray); } @@ -289,7 +314,9 @@ class FontRenderer { renderPass.setBindGroup(1, this.perFontBindGroup); renderPass.setBindGroup(2, this.frames[frameNumber].bindGroup); - renderPass.drawIndexed(6, this.instanceCount); + for (let i = 0; i < this.instanceCount; i++) { + renderPass.drawIndexed(6, 1, 0, 0, i); + } } } diff --git a/font.wgsl b/font.wgsl index 13aff17..ed8098f 100644 --- a/font.wgsl +++ b/font.wgsl @@ -10,6 +10,19 @@ 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 { @@ -70,21 +83,45 @@ fn vertexMain(input: VertexInput) -> VertexOutput 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 { - var color = vec3f(input.texture.xy, 0); - 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 t = f32(base.x > 0.5); + 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); - if (base.x < 0.5) { - discard; - } + var color = vec4f(0, 0, 0, 0); + color = blend(color, insideColor, distance, + config.distanceThreshold, config.distanceScale, config.distanceOffset); - return vec4f(t, t, t, 1); + color = blend(color, outlineColor, distance, + config.distanceThreshold - config.outlineThreshold, + config.outlineScale, config.outlineOffset); + return color; } diff --git a/fontSliders.js b/fontSliders.js index 66a64fb..c0683ee 100644 --- a/fontSliders.js +++ b/fontSliders.js @@ -6,6 +6,18 @@ function makeSliders() scale: new Slider("scale", 0.0, 0.05), translateX: new Slider("translate x", -10, 10), translateY: new Slider("translate y", -10, 10), + + distanceThreshold: new Slider("dist. threshold", 0.0, 1.0), + distanceScale: new Slider("distance scale", 0.0, 200.0), + distanceOffset: new Slider("distance offset", -20.0, 20.0), + + outlineThreshold: new Slider("outline threshold", 0.0, 1.0), + outlineScale: new Slider("outline scale", 0.0, 200.0), + outlineOffset: new Slider("outline offset", -20.0, 20.0), + + colorMix: new Slider("color mix", 0.0, 1.0), + colorScale: new Slider("color scale", 0.0, 10.0), + colorOffset: new Slider("color offset", 0.0, 3.1415), }; } diff --git a/index.html b/index.html index 909d1a6..0c92e17 100644 --- a/index.html +++ b/index.html @@ -41,13 +41,13 @@ background: #eee; } .control-main { - width: 310px; + width: 340px; float: right; margin-right: 15px; background: #444; } .label { - width: calc(30% - 0.3rem); + width: calc(35% - 0.3rem); float: left; clear: left; overflow: hidden; @@ -55,7 +55,7 @@ margin-top: 0.25rem; } .value { - width: calc(70% - 0.2rem); + width: calc(65% - 0.2rem); float: left; margin-top: 0.1rem; margin-right: 0.2rem; diff --git a/index.js b/index.js index 89939b6..289862c 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,7 @@ const canvasFormat = navigator.gpu.getPreferredCanvasFormat(); context.configure({ device: device, format: canvasFormat, + alphaMode: 'premultiplied', }); const encoder = device.createCommandEncoder(); diff --git a/slider.js b/slider.js index 3bba2c8..1d9be89 100644 --- a/slider.js +++ b/slider.js @@ -57,7 +57,7 @@ class Slider { localStorage.setItem(this.label, rawValue); } if (this.high > 10) { - this.span.innerHTML = value.toFixed(0); + this.span.innerHTML = value.toFixed(2); } else { this.span.innerHTML = value.toFixed(4); } diff --git a/src/font_layout/api.cpp b/src/font_layout/api.cpp index db278a0..0c0b240 100644 --- a/src/font_layout/api.cpp +++ b/src/font_layout/api.cpp @@ -51,6 +51,6 @@ extern "C" { void font_layout__draw(FontLayout * font_layout) { - font_layout->draw_string("asdf qwer"); + font_layout->draw_string("hexagon grid"); } };