p5.js 中的嘈杂梯度
noisy gradient in p5.js
目标
插图和其他图形作品中经常看到的效果是两种颜色之间的渐变,它带有grain/noise,从而获得非常特殊的效果。 (尤其是示例 3。)
我的研究已经展示了如何在 Illustrator 等软件中实现这种效果的许多解决方案,但我想用 p5js 或 vanilla js 重新创建它 canvas。
我的尝试
我已经尝试创建渐变并使用其顶部的像素阵列设置具有特定颜色的随机噪声。
这仅部分导致了预期的效果:
function draw() {
setGradient(0, 0, width, height, color(0, 0, 0), color(255, 255 ,255));
setNoise();
}
function setNoise() {
loadPixels();
for (let x = 0; x < width; x++ ) {
for (let y = 0; y < height; y++ ) {
if (random(1) > 0.9) {
const index = (x + y * width) * 4;
pixels[index] = 255;
pixels[index + 1] = 255;
pixels[index + 2] = 255;
pixels[index + 3] = 255;
}
}
}
updatePixels();
}
function setGradient(x, y, w, h, c1, c2) {
noFill();
for (let i = y; i <= y + h; i++) {
let inter = map(i, y, y + h, 0, 1);
let c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x + w, i);
}
}
你怎么看?这是正确的方法还是有更好/更简单的解决方案?
您需要将噪声与渐变混合。您不能直接混合 ImageData,您需要先将其转换为位图(将其放在 canvas 上即可)。因此,您可能更喜欢 hard-light
,而不是 overlay 混合你的教程,它会颠倒我们层的顺序。
const w = 300;
const h = 300;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
// some colored noise
const data = Uint32Array.from( {length: w*h }, () => Math.random() * 0xFFFFFFFF );
const img = new ImageData( new Uint8ClampedArray( data.buffer ), w, h );
ctx.putImageData( img, 0, 0 );
// first pass to convert our noise to black and transparent
ctx.globalCompositeOperation = "color";
ctx.fillRect( 0, 0, w, h );
ctx.globalCompositeOperation = "hard-light";
ctx.fillStyle = ctx.createLinearGradient( 0, 0, 0, h );
ctx.fillStyle.addColorStop( 0.1, 'white' );
ctx.fillStyle.addColorStop( 0.9, 'black' );
ctx.fillRect( 0, 0, w, h );
canvas { background: lime; }
<canvas id="canvas" height="300"></canvas>
但是混合需要你有一个不透明的场景。如果你想有透明度,那么你也必须使用合成:
const w = 300;
const h = 300;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
// some black and transparent noise
const data = Uint32Array.from( {length: w*h }, () => Math.random() > 0.5 ? 0xFF000000 : 0 );
const img = new ImageData( new Uint8ClampedArray( data.buffer ), w, h );
ctx.putImageData( img, 0, 0 );
ctx.fillStyle = ctx.createLinearGradient( 0, 0, 0, h );
ctx.fillStyle.addColorStop( 0.1, 'transparent' );
ctx.fillStyle.addColorStop( 0.9, 'black' );
// apply transparency gradient on noise (dim top)
ctx.globalCompositeOperation = "destination-in";
ctx.fillRect( 0, 0, w, h );
// apply black of the gradient on noise (darken bottom)
ctx.globalCompositeOperation = "multiply";
ctx.fillRect( 0, 0, w, h );
// optionally change the color of the noise
ctx.globalCompositeOperation = "source-atop";
ctx.fillStyle = "red";
ctx.fillRect( 0, 0, w, h );
canvas { background: lime; }
<canvas id="canvas" height="300"></canvas>
目标
插图和其他图形作品中经常看到的效果是两种颜色之间的渐变,它带有grain/noise,从而获得非常特殊的效果。 (尤其是示例 3。)
我的研究已经展示了如何在 Illustrator 等软件中实现这种效果的许多解决方案,但我想用 p5js 或 vanilla js 重新创建它 canvas。
我的尝试
我已经尝试创建渐变并使用其顶部的像素阵列设置具有特定颜色的随机噪声。 这仅部分导致了预期的效果:
function draw() {
setGradient(0, 0, width, height, color(0, 0, 0), color(255, 255 ,255));
setNoise();
}
function setNoise() {
loadPixels();
for (let x = 0; x < width; x++ ) {
for (let y = 0; y < height; y++ ) {
if (random(1) > 0.9) {
const index = (x + y * width) * 4;
pixels[index] = 255;
pixels[index + 1] = 255;
pixels[index + 2] = 255;
pixels[index + 3] = 255;
}
}
}
updatePixels();
}
function setGradient(x, y, w, h, c1, c2) {
noFill();
for (let i = y; i <= y + h; i++) {
let inter = map(i, y, y + h, 0, 1);
let c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x + w, i);
}
}
你怎么看?这是正确的方法还是有更好/更简单的解决方案?
您需要将噪声与渐变混合。您不能直接混合 ImageData,您需要先将其转换为位图(将其放在 canvas 上即可)。因此,您可能更喜欢 hard-light
,而不是 overlay 混合你的教程,它会颠倒我们层的顺序。
const w = 300;
const h = 300;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
// some colored noise
const data = Uint32Array.from( {length: w*h }, () => Math.random() * 0xFFFFFFFF );
const img = new ImageData( new Uint8ClampedArray( data.buffer ), w, h );
ctx.putImageData( img, 0, 0 );
// first pass to convert our noise to black and transparent
ctx.globalCompositeOperation = "color";
ctx.fillRect( 0, 0, w, h );
ctx.globalCompositeOperation = "hard-light";
ctx.fillStyle = ctx.createLinearGradient( 0, 0, 0, h );
ctx.fillStyle.addColorStop( 0.1, 'white' );
ctx.fillStyle.addColorStop( 0.9, 'black' );
ctx.fillRect( 0, 0, w, h );
canvas { background: lime; }
<canvas id="canvas" height="300"></canvas>
但是混合需要你有一个不透明的场景。如果你想有透明度,那么你也必须使用合成:
const w = 300;
const h = 300;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
// some black and transparent noise
const data = Uint32Array.from( {length: w*h }, () => Math.random() > 0.5 ? 0xFF000000 : 0 );
const img = new ImageData( new Uint8ClampedArray( data.buffer ), w, h );
ctx.putImageData( img, 0, 0 );
ctx.fillStyle = ctx.createLinearGradient( 0, 0, 0, h );
ctx.fillStyle.addColorStop( 0.1, 'transparent' );
ctx.fillStyle.addColorStop( 0.9, 'black' );
// apply transparency gradient on noise (dim top)
ctx.globalCompositeOperation = "destination-in";
ctx.fillRect( 0, 0, w, h );
// apply black of the gradient on noise (darken bottom)
ctx.globalCompositeOperation = "multiply";
ctx.fillRect( 0, 0, w, h );
// optionally change the color of the noise
ctx.globalCompositeOperation = "source-atop";
ctx.fillStyle = "red";
ctx.fillRect( 0, 0, w, h );
canvas { background: lime; }
<canvas id="canvas" height="300"></canvas>