【p5.js】シェーダーで手書き風エフェクト(ノイズエフェクト)
p5.jsとシェーダーで手書き風エフェクトを行います。
作例
まず、作例を見ていきます。
この様な形で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)で取り扱う方法の詳細は下の記事で解説しています。
まとめ
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); } } } }