のぐそんブログ

暗いおじさんがシコシコ書くブログです。

Three.jsでグリッチの練習メモ

グリッチの練習メモです。

1.ノイズを作る

頂点シェーダ

頂点シェーダは特別なことはしていません。

varying vec2 vUv;

void main() {
  vUv = uv; 
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

フラグメントシェーダー

シンプレックスノイズはこちらをお借りします。

//シンプレックスノイズ
vec3 mod289(vec3 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec2 mod289(vec2 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 permute(vec3 x) {
    return mod289(((x * 34.0) + 1.0) * x);
}

float snoise(vec2 v) {
    const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0,
        0.366025403784439, // 0.5*(sqrt(3.0)-1.0),
        -0.577350269189626, // -1.0 + 2.0 * C.x,
        0.024390243902439); // 1.0 / 41.0,
    vec2 i = floor(v + dot(v, C.yy));
    vec2 x0 = v - i + dot(i, C.xx);

    vec2 i1;
    i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
    vec4 x12 = x0.xyxy + C.xxzz;
    x12.xy -= i1;

    i = mod289(i);
    // Avoid truncation effects in permutation,
    vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));

    vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
    m = m * m;
    m = m * m;

    vec3 x = 2.0 * fract(p * C.www) - 1.0;
    vec3 h = abs(x) - 0.5;
    vec3 ox = floor(x + 0.5);
    vec3 a0 = x - ox;

    m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);

    vec3 g;
    g.x = a0.x * x0.x + h.x * x0.y;
    g.yz = a0.yz * x12.xz + h.yz * x12.yw;
    return 130.0 * dot(m, g);
}

void main() {
  float offset = snoise(vUv);
  gl_FragColor = vec4(vec3(offset), 1.);

}

UV座標をそのままノイズ関数に渡すとこんな模様になります。

2. UV座標を動かしてみる

ノイズ関数と、頂点シェーダーは手順1と同じなので割愛したいと思います。 UV座標を動かしています。

float offset = snoise(vUv + time);
gl_FragColor = vec4(vec3(offset), 1.);

3. 模様の輪郭をはっきりさせる

void main(){
    float offset = snoise(vUv + time);
    //0.0以外の値が大きくなるため、ボケていた輪郭がくっきりする
    //distortionには10.0がuniform変数として設定されている
     offset = offset * distortion;
    gl_FragColor = vec4(vec3(offset), 1.);
}

4. テクスチャのUV座標を歪ませてみる

↓のテクスチャに、手順3のoffsetの値を入れてみます。

void main(){
    vec2 p = vUv;
    float offset = snoise(p + time);
    offset = offset * distortion;
    gl_FragColor = texture2D(tDiffuse,  vec2(offset));
}

こんな感じになります。

5. テクスチャをゆらゆらさせる

テクスチャを横にゆらゆらさせます。 その為、y座標の変化を、UV座標のxの値に足します。

void main(){
    vec2 p = vUv;
    //y座標に経過時間を足す
    float y = p.y + time;
    float offset = snoise(vec2(y,0.0)) * 0.2;
    offset = offset * distortion;
    //fract関数で小数以下の値だけ使う。
    gl_FragColor = texture2D(tDiffuse,  vec2(fract(p.x + offset),p.y));
}

横だけにゆらゆらする。

12月-23-2017 13-48-52.gif

6. 動画テクスチャを強くゆらゆらしてみる

テクスチャを動画にするのはjs側で行います。

window.onload = function () {

    var renderer;
    var camera, scene;
    var theta = 0;
    var clock = new THREE.Clock();
    var time = 0.0;
    var composer;
    var customPass;

    var windowWidth = window.innerWidth;
    var windowHeight = window.innerHeight;
    var aspect = windowWidth / windowHeight;
    var videoTexture;
    var video;

    init();

    function init() {

        // rendererの作成
        renderer = new THREE.WebGLRenderer();
        renderer.setClearColor(new THREE.Color(0xffffff), 1.0);

        // canvasをbodyに追加
        document.body.appendChild(renderer.domElement);

        // canvasをリサイズ
        renderer.setSize(windowWidth, windowHeight);

        // ベースの描画処理(renderTarget への描画用)
        scene = new THREE.Scene();

        //LIGHTS
        var light = new THREE.AmbientLight(0xffffff, 0.5);
        scene.add(light);

        //ベースの描画処理用カメラ                      
        camera = new THREE.PerspectiveCamera(60, windowWidth / windowHeight, 0.1, 1000);
        camera.position.z = 1;

        //Load Video
        video = document.createElement('video');
        video.loop = true;
        video.src = 'movie/mv.mp4';
        video.play();


        videoTexture = new THREE.Texture(video);
        videoTexture.minFilter = THREE.LinearFilter;
        videoTexture.magFilter = THREE.LinearFilter;
        var material = new THREE.MeshBasicMaterial({
            map: videoTexture
        });

        //var material = new THREE.MeshLambertMaterial();
        var geometry = new THREE.PlaneGeometry(2, 3, 1, 1);
        var mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);

        composer = new THREE.EffectComposer(renderer);

        //現在のシーンを設定
        var renderPass = new THREE.RenderPass(scene, camera);
        composer.addPass(renderPass);
        //カスタムシェーダー
        var myEffect = {
            uniforms: {
                "tDiffuse": {
                    value: null
                },
                "time": {
                    type: "f",
                    value: time
                },
                "distortion": {
                    type: "f",
                    value: 3.0
                },
                "distortion2": {
                    type: "f",
                    value: 1.0
                },
                "rollSpeed": {
                    type: "f",
                    value: 0.0
                },
                "speed": {
                    type: "f",
                    value: 1.0
                },
                "resolution": {
                    type: 'v2',
                    value: new THREE.Vector2(windowWidth, windowHeight)
                }
            },
            vertexShader: baseVert,
            fragmentShader: baseFrag
        }

        //エフェクト結果をスクリーンに描画する
        customPass = new THREE.ShaderPass(myEffect);
        customPass.renderToScreen = true;
        composer.addPass(customPass);


        render();
    }

    function render() {

        if (video.readyState === video.HAVE_ENOUGH_DATA) {
            if (videoTexture) videoTexture.needsUpdate = true;
        }

        time = clock.getElapsedTime();
        customPass.uniforms.time.value = time;
        composer.render();

        requestAnimationFrame(render);
    }
};

こんな感じになります。

12月-23-2017 23-58-11.gif

すこしゆらゆらが強いので、係数を掛けます。

void main(){
    vec2 p = vUv;
    float y = p.y + time;
    float offset = snoise(vec2(y,0.0)) * 0.2;
    //0.1を掛ける
    offset = offset * distortion * 0.1;
    gl_FragColor = texture2D(tDiffuse,  vec2(fract(p.x + offset),p.y));
}

12月-23-2017 23-54-02.gif

7. 弱くゆらゆらする

↓のようなノイズを加えて細かいゆらゆらを作ります。

void main(){
    vec2 p = vUv;
    float y = p.y + time;
    offset = snoise(vec2(y * 50.0,0.0)) ;
    gl_FragColor = vec4(vec3(offset), 1.);
}

少しゆらゆらが強いのでこちらも係数をかけます。

void main{
    vec2 p = vUv;
    float y = p.y + time;
    //係数0.005を掛ける
    float offset = snoise(vec2(y * 50.0,0.0)) *0.005;
    gl_FragColor = texture2D(tDiffuse,  vec2(fract(p.x + offset),fract(p.y)));

}

8. 強いゆらゆらと、弱いゆらゆらを混ぜる

強いゆらゆらと、弱いゆらゆらを混ぜて、より精度の高いゆらゆらを作ります。

係数を掛けて、UV座標に適応します。

void main(){
    vec2 p = vUv;
    float y = p.y + time;
    float offset = snoise(vec2(y,0.0));
    offset = offset * distortion * 0.01;
    offset += snoise(vec2(y * 50.0,0.0)) * 0.005;
    gl_FragColor = texture2D(tDiffuse,  vec2(fract(p.x + offset),p.y));
}

まとめ

ノイズをうまく使うと色々な表現ができそうです。

参考

こちらを参考にさせていただきました。 と、いうよりもほとんど参考サイトのままです。 https://github.com/felixturner/bad-tv-shader