最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - noisy gradient in p5.js - Stack Overflow

programmeradmin1浏览0评论

The Goal

An often seen effect in illustrations and other graphic works is a gradient between two colors, which is provided with a grain/noise and thus gets a very special effect. (especially example 3.)

My research has shown many solutions how to achieve this effect in software like Illustrator, but I would like to recreate it with p5js or the vanilla js canvas.

(source: /@stefanhrlemann/how-to-create-noisy-risograph-style-gradients-and-textures-in-photoshop-in-3-ways-394d6012a93a)

My attempt

I have already tried to create a gradient and set a random noise with a specific color using the pixel array on top of it. Which only partially leads to the desired effect:

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

what do you think? is this the right approach or is there a better / simpler solution?

The Goal

An often seen effect in illustrations and other graphic works is a gradient between two colors, which is provided with a grain/noise and thus gets a very special effect. (especially example 3.)

My research has shown many solutions how to achieve this effect in software like Illustrator, but I would like to recreate it with p5js or the vanilla js canvas.

(source: https://medium./@stefanhrlemann/how-to-create-noisy-risograph-style-gradients-and-textures-in-photoshop-in-3-ways-394d6012a93a)

My attempt

I have already tried to create a gradient and set a random noise with a specific color using the pixel array on top of it. Which only partially leads to the desired effect:

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

what do you think? is this the right approach or is there a better / simpler solution?

Share Improve this question edited May 2, 2020 at 19:40 iamrobin. asked May 2, 2020 at 19:33 iamrobin.iamrobin. 1,6343 gold badges20 silver badges31 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

You need to blend your noise with the gradient. You can't blend directly an ImageData, you need to first transform it to a bitmap (putting it on a canvas can do). So instead of the overlay blending your tutorial talked about, you may prefer the hard-light one, which will invert the order of our layers.

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>

But blending requires you have an opaque scene. If you want to have transparency, then you'd have to use positing too:

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>

发布评论

评论列表(0)

  1. 暂无评论