render target blending, more sliders

This commit is contained in:
Zack Buhman 2026-08-05 20:54:56 -05:00
parent 997bfac193
commit 53cbbebdc7
7 changed files with 100 additions and 23 deletions

49
font.js
View File

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

View File

@ -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;
}
return vec4f(t, t, t, 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;
}

View File

@ -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),
};
}

View File

@ -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;

View File

@ -25,6 +25,7 @@ const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device: device,
format: canvasFormat,
alphaMode: 'premultiplied',
});
const encoder = device.createCommandEncoder();

View File

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

View File

@ -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");
}
};