ギンの備忘録

デジタルアートやプログラミングやテクノロジー関連のこと。

【p5.js】シェーダーで手書き風エフェクト(ノイズエフェクト)

p5.jsとシェーダーで手書き風エフェクトを行います。

作例

まず、作例を見ていきます。

f:id:gin_graphic:20210224205944p:plain:w600

この様な形でp5.jsで描いた図形にノイズエフェクトをシェーダー(GLSL)で適用します。 その効果で、線が手で書いた様な少し乱れた漢字を再現しています。

見えにく場合はクリックして拡大して、確認してみて下さい。

ノイズエフェクト

シェーダー(GLSL)でエフェクトを実現している部分を下に抜き出します。

      float radius = 0.0075;
      uv.x = uv.x + rand(uv)*radius ;
      uv.y = uv.y + rand(uv)*radius;
      vec4 tex = texture2D(u_tex, uv);

p5.jsの描画をテクスチャとしてGLSLで扱います。 そのテクスチャに対して、ランダムなノイズを加算します。 x方向、y方向に別々にノイズを乗せて、pixel単位で色情報をずらしています。

これにより、p5.jsの描画をランダムに拡散させている様なイメージで、描いた図形が手書き風に見える様になります。

p5.jsで描画したものを、シェーダー(GLSL)で取り扱う方法の詳細は下の記事で解説しています。

gin-graphic.hatenablog.com

まとめ

p5.jsで作成した描画に対して、シェーダー(GLSL)でノイズエフェクトを適用することで、手書き風エフェクトを実現しました。 クリエイティブコーディングで、こんな表現もできるのは面白いです。 工夫すれば、手書き風の作品が作れる気がします。 これから活用していきたいテクニックです。

ソースコード

let vs = `
   precision highp float;
   precision highp int;

   attribute vec3 aPosition;
   attribute vec2 aTexCoord;

   varying vec2 vTexCoord;

   uniform mat4 uProjectionMatrix;
   uniform mat4 uModelViewMatrix;

    void main() {
      vec4 positionVec4 = vec4(aPosition, 1.0);
      gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
      vTexCoord = aTexCoord;
   }
`;

let fs = `
   precision highp float;
   precision highp int;

   varying vec2 vTexCoord;

   uniform sampler2D u_tex;

   float pi = 3.14159265358979;

   float rand(vec2 co) {
     float a = fract(dot(co, vec2(2.067390879775102, 12.451168662908249))) - 0.5;
     float s = a * (6.182785114200511 + a * a * (-38.026512460676566 + a * a * 53.392573080032137));
     float t = fract(s * 43758.5453);
     return t;
   }

   void main() {
      vec2 uv = vTexCoord;

      float radius = 0.0075;
      uv.x = uv.x + rand(uv)*radius ;
      uv.y = uv.y + rand(uv)*radius;
      vec4 tex = texture2D(u_tex, uv);

      gl_FragColor = tex;
    }
`;

const w = 720;
let theShader ;
let pg ;

function setup() {
   createCanvas(w, w, WEBGL);

   rectMode(CENTER);
   colorMode(HSB);
   noStroke();

   theShader = createShader(vs, fs);
   pg = createGraphics(w*1.25, w*1.25);
   noLoop();
}

function draw(){
   background(random(360), 10, 90);

   set_graphics(pg);
   shader(theShader);
   theShader.setUniform(`u_tex`, pg);

   fill(0, 0, 0, 0);
   rect(0, 0, w);
}

function set_graphics(in_pg){
   in_pg.colorMode(HSB);
   in_pg.rectMode(CENTER);

   const stw = 10;
   in_pg.strokeWeight(stw);

   const num = 5;
   const pgr = in_pg.width/num;

   for(let i=0;i<num;i++){
      for(let j=0;j<num;j++){
         let col = color(random(360), 90, 60);
         in_pg.stroke(col);
         let px = pgr * (i+0.5);
         let py = pgr * (j+0.5);

         if(random(1)<0.5){
            in_pg.fill(0,0,0,0);
         } else{
            in_pg.fill(col);
         }

         if(random(1)<0.5){
           in_pg.rect(px, py, pgr*0.85);
         } else{
           in_pg.ellipse(px, py, pgr*0.85);
         }
      }
   }
}

参考文献

ics.media