blur layers
This commit is contained in:
@@ -200,57 +200,67 @@ function drawShock(c, shock, tx, ty, scale, u, P, params, sprite) {
|
||||
const px = tx(shock.x), py = ty(shock.y), R = shock.r * scale;
|
||||
const useBubbles = params.diskBubbles !== false;
|
||||
|
||||
// when softening, draw the whole disk to its own offscreen and composite it
|
||||
// back through a single Gaussian blur — softens just this layer's edges.
|
||||
const soften = (params.diskSoften || 0) * u;
|
||||
let T = c, tmp = null;
|
||||
if (soften > 0) {
|
||||
tmp = document.createElement('canvas');
|
||||
tmp.width = c.canvas.width; tmp.height = c.canvas.height;
|
||||
T = tmp.getContext('2d');
|
||||
}
|
||||
|
||||
// disk body: dark annulus that keeps a lighter, detailed centre.
|
||||
// softer when the line work is bubbles (the bubbles carry the density).
|
||||
const bodyK = useBubbles ? 0.6 : 1.0;
|
||||
const core = c.createRadialGradient(px, py, 0, px, py, R);
|
||||
const core = T.createRadialGradient(px, py, 0, px, py, R);
|
||||
core.addColorStop(0.0, `rgba(${r},${g},${b},${0.06 * shock.intensity * bodyK})`);
|
||||
core.addColorStop(0.35, `rgba(${r},${g},${b},${0.18 * shock.intensity * bodyK})`);
|
||||
core.addColorStop(0.72, `rgba(${r},${g},${b},${0.38 * shock.intensity * bodyK})`);
|
||||
core.addColorStop(0.94, `rgba(${r},${g},${b},${0.34 * shock.intensity * bodyK})`);
|
||||
core.addColorStop(1, `rgba(${r},${g},${b},0)`);
|
||||
c.fillStyle = core;
|
||||
c.beginPath(); c.arc(px, py, R, 0, Math.PI * 2); c.fill();
|
||||
T.fillStyle = core;
|
||||
T.beginPath(); T.arc(px, py, R, 0, Math.PI * 2); T.fill();
|
||||
|
||||
if (useBubbles && shock.bubbleStrokes) {
|
||||
// describe striations / rings / rim / core with the particle method
|
||||
const dRng = makeRng(params.seed, 'diskbubbles');
|
||||
for (const stroke of shock.bubbleStrokes) {
|
||||
const bubs = sampleBubbles(stroke, params, dRng);
|
||||
c.globalAlpha = Math.min(1, 0.45 + stroke.weight * 0.5);
|
||||
T.globalAlpha = Math.min(1, 0.45 + stroke.weight * 0.5);
|
||||
for (const bb of bubs) {
|
||||
const rr = Math.max(bb.r * scale, 0.45);
|
||||
const d = rr * 2.4;
|
||||
c.drawImage(sprite, tx(bb.x) - d / 2, ty(bb.y) - d / 2, d, d);
|
||||
T.drawImage(sprite, tx(bb.x) - d / 2, ty(bb.y) - d / 2, d, d);
|
||||
}
|
||||
}
|
||||
c.globalAlpha = 1;
|
||||
T.globalAlpha = 1;
|
||||
} else {
|
||||
// legacy: clean vector strokes
|
||||
c.lineCap = 'round';
|
||||
// clean vector strokes
|
||||
T.lineCap = 'round';
|
||||
for (const s of shock.striations) {
|
||||
const ix = px + Math.cos(s.a) * s.inner * scale, iy = py + Math.sin(s.a) * s.inner * scale;
|
||||
const ox = px + Math.cos(s.a) * s.outer * scale, oy = py + Math.sin(s.a) * s.outer * scale;
|
||||
const mx = (ix + ox) / 2 + Math.cos(s.a + Math.PI / 2) * s.wobble * scale;
|
||||
const my = (iy + oy) / 2 + Math.sin(s.a + Math.PI / 2) * s.wobble * scale;
|
||||
c.strokeStyle = `rgba(${r},${g},${b},${s.opacity})`;
|
||||
c.lineWidth = s.width * u;
|
||||
c.beginPath(); c.moveTo(ix, iy); c.quadraticCurveTo(mx, my, ox, oy); c.stroke();
|
||||
T.strokeStyle = `rgba(${r},${g},${b},${s.opacity})`;
|
||||
T.lineWidth = s.width * u;
|
||||
T.beginPath(); T.moveTo(ix, iy); T.quadraticCurveTo(mx, my, ox, oy); T.stroke();
|
||||
}
|
||||
for (const ring of shock.rings) {
|
||||
c.strokeStyle = `rgba(${r},${g},${b},${ring.opacity})`;
|
||||
c.lineWidth = ring.width * u;
|
||||
c.beginPath(); c.arc(px, py, ring.rr * scale, 0, Math.PI * 2); c.stroke();
|
||||
T.strokeStyle = `rgba(${r},${g},${b},${ring.opacity})`;
|
||||
T.lineWidth = ring.width * u;
|
||||
T.beginPath(); T.arc(px, py, ring.rr * scale, 0, Math.PI * 2); T.stroke();
|
||||
}
|
||||
for (const seg of (shock.rimSegs || [])) {
|
||||
c.strokeStyle = `rgba(${r},${g},${b},${seg.opacity})`;
|
||||
c.lineWidth = seg.width * u;
|
||||
c.beginPath(); c.arc(px, py, shock.r * scale, seg.a0, seg.a1); c.stroke();
|
||||
T.strokeStyle = `rgba(${r},${g},${b},${seg.opacity})`;
|
||||
T.lineWidth = seg.width * u;
|
||||
T.beginPath(); T.arc(px, py, shock.r * scale, seg.a0, seg.a1); T.stroke();
|
||||
}
|
||||
for (const k of shock.core) {
|
||||
c.strokeStyle = `rgba(${r},${g},${b},${k.opacity})`;
|
||||
c.lineWidth = k.width * u;
|
||||
c.beginPath(); c.moveTo(tx(k.x1), ty(k.y1)); c.lineTo(tx(k.x2), ty(k.y2)); c.stroke();
|
||||
T.strokeStyle = `rgba(${r},${g},${b},${k.opacity})`;
|
||||
T.lineWidth = k.width * u;
|
||||
T.beginPath(); T.moveTo(tx(k.x1), ty(k.y1)); T.lineTo(tx(k.x2), ty(k.y2)); T.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,32 +268,41 @@ function drawShock(c, shock, tx, ty, scale, u, P, params, sprite) {
|
||||
if (shock.stains) {
|
||||
for (const st of shock.stains) {
|
||||
const sr = st.r * scale;
|
||||
const grad = c.createRadialGradient(tx(st.x), ty(st.y), 0, tx(st.x), ty(st.y), sr);
|
||||
const grad = T.createRadialGradient(tx(st.x), ty(st.y), 0, tx(st.x), ty(st.y), sr);
|
||||
if (st.dark) {
|
||||
c.globalCompositeOperation = 'source-over';
|
||||
T.globalCompositeOperation = 'source-over';
|
||||
grad.addColorStop(0, `rgba(${r},${g},${b},${st.opacity})`);
|
||||
grad.addColorStop(1, `rgba(${r},${g},${b},0)`);
|
||||
c.fillStyle = grad;
|
||||
T.fillStyle = grad;
|
||||
} else {
|
||||
c.globalCompositeOperation = 'destination-out';
|
||||
T.globalCompositeOperation = 'destination-out';
|
||||
grad.addColorStop(0, `rgba(0,0,0,${st.opacity * 1.4})`);
|
||||
grad.addColorStop(1, 'rgba(0,0,0,0)');
|
||||
c.fillStyle = grad;
|
||||
T.fillStyle = grad;
|
||||
}
|
||||
c.beginPath(); c.arc(tx(st.x), ty(st.y), sr, 0, Math.PI * 2); c.fill();
|
||||
T.beginPath(); T.arc(tx(st.x), ty(st.y), sr, 0, Math.PI * 2); T.fill();
|
||||
}
|
||||
c.globalCompositeOperation = 'source-over';
|
||||
T.globalCompositeOperation = 'source-over';
|
||||
}
|
||||
|
||||
// keep a bright, detailed centre by clearing a soft hole in the ink
|
||||
// keep a bright, detailed centre by clearing a soft hole
|
||||
if (shock.bright) {
|
||||
c.save();
|
||||
c.globalCompositeOperation = 'destination-out';
|
||||
const hole = c.createRadialGradient(px, py, 0, px, py, shock.bright * scale);
|
||||
T.save();
|
||||
T.globalCompositeOperation = 'destination-out';
|
||||
const hole = T.createRadialGradient(px, py, 0, px, py, shock.bright * scale);
|
||||
hole.addColorStop(0, 'rgba(0,0,0,0.85)');
|
||||
hole.addColorStop(1, 'rgba(0,0,0,0)');
|
||||
c.fillStyle = hole;
|
||||
c.beginPath(); c.arc(px, py, shock.bright * scale, 0, Math.PI * 2); c.fill();
|
||||
T.fillStyle = hole;
|
||||
T.beginPath(); T.arc(px, py, shock.bright * scale, 0, Math.PI * 2); T.fill();
|
||||
T.restore();
|
||||
}
|
||||
|
||||
// composite the (optionally blurred) disk back onto the ink layer
|
||||
if (tmp) {
|
||||
c.save();
|
||||
c.filter = `blur(${soften.toFixed(2)}px)`;
|
||||
c.drawImage(tmp, 0, 0);
|
||||
c.filter = 'none';
|
||||
c.restore();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user