LoginSignup
14
8

More than 5 years have passed since last update.

three.jsで動画(video)にdisplacementMap

Last updated at Posted at 2016-12-12

この記事は、Three.js Advent Calendar 2016 13日目の記事です。


SnapCrab1.png

displacementに触れたきっかけ

絵に凹凸がついたり、白と黒を判別するところが不思議な感じがして、触れてみたくなりました。
静止画にできるなら動画にも適用できるかも、と試してみたところ、うまくいきました。

demo

video displacementについて

コード自体短く、シンプルなので、要点を書いていきます。
demoでは、凹凸が見やすいようにニ画面にしてあります。
ニ画面のやり方はこの記事が参考になります。

videoのソース

まず、videoタグを書いて
オートプレイ、ループ、インライン、ミュートを追加します。
ios10からvideoをインラインで再生可能になったため、このようにしています。
ios9より以前の端末ではうまく映らない可能性があります。

<video id="video" src="01.mp4" autoplay loop playsinline muted></video>

そして、javascript側で

video = document.getElementById("video");  // 指定IDの要素を取得

要素を取得します。

#video { display: none; }

videoが重なって映ってしまうため、videoタグの方をnoneにしていますが、もっと良い方法があるかもしれません。

カラーとして映るvideoTextureと凹凸を判別するvideoTextureを別々に設定することも可能です。

texture = new THREE.VideoTexture(video);

material = new THREE.MeshPhongMaterial({
    side: THREE.DoubleSide,    // 両面に映す
    map: texture,              // テクスチャ
    displacementMap: texture,  // displacementのテクスチャ
    displacementScale: -12     // displacementの大きさ
});

全体のコードものせておきます。

index.html
<!DOCTYPE html>

<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>displacement</title>
    <style>
        * {
            overflow: hidden;
        }

        #displ {
            margin: 0;
            padding: 0;
            border: 0;
        }

        #button {
            position: absolute;
            top: 10px;
            left: 10px;
        }

        #video {
            display: none;
        }
    </style>
    <script src="js/three.min.js"></script>
    <script src="js/OrbitControls.js"></script>
    <script src="js/main.js"></script>
</head>

<body>
    <div id="displ"></div>
    <div id="button">
        <input type="button" value="はじめから" onclick="video.load();" />
        <input type="button" value="再生" onclick="video.play();" />
        <input type="button" value="一時停止" onclick="video.pause();" />
    </div>
    <video id="video" src="001.mp4" autoplay loop playsinline muted></video>
</body>

</html>
main.js
    var initScene, render,
        renderer, scene, cameraL, cameraR, light, light2, controls,
        geometry, material, texture, plane;
    var SCREEN_WIDTH = window.innerWidth,
        SCREEN_HEIGHT = window.innerHeight;

    function initScene() {

        scene = new THREE.Scene();

        // 左側のカメラ
        cameraL = new THREE.PerspectiveCamera(75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 1000);
        cameraL.position.set(0, 0, 70);
        controls = new THREE.OrbitControls(cameraL);
        scene.add(cameraL);

        // 右側のカメラ
        cameraR = new THREE.PerspectiveCamera(75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 1000);
        cameraR.position.set(45, 5, 0);
        cameraR.lookAt({x: 0, y: 0, z: 0});
        scene.add(cameraR);

        light = new THREE.AmbientLight(0xffffff);
        scene.add(light);

        light2 = new THREE.DirectionalLight(0xffffff, 1);
        light2.position.set(20, 20, 20);
        scene.add(light2);

        video = document.getElementById("video");  // 指定IDの要素を取得

        geometry = new THREE.PlaneGeometry(96, 54, 256, 256);
        texture = new THREE.VideoTexture(video);
        texture.minFilter = THREE.LinearFilter;
        texture.magFilter = THREE.LinearFilter;
        texture.format = THREE.RGBFormat;

        material = new THREE.MeshPhongMaterial({
            side: THREE.DoubleSide,    // 両面に映す
            map: texture,              // テクスチャ
            displacementMap: texture,  // displacementのテクスチャ
            displacementScale: -12     // displacementの大きさ
        });
        plane = new THREE.Mesh(geometry, material);
        plane.geometry.verticesNeedUpdate = true;
        scene.add(plane);

        renderer = new THREE.WebGLRenderer({antialias: true});
        renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
        renderer.setClearColor(0xaaaaaa);
        renderer.autoClear = false;
        document.getElementById("displ").appendChild(renderer.domElement);

        document.addEventListener('keydown', onKeyDown, false);

        keydown = {};

        function onKeyDown(event) {
            switch (event.keyCode) {
                case 87:
                    /*W*/ video.load();
                    break;
                case 83:
                    /*S*/ video.play();
                    break;
                case 88:
                    /*X*/ video.pause();
                    break;
            }
        }
        render();
    }

    function render() {
        requestAnimationFrame(render);
        controls.update();
        plane.geometry.verticesNeedUpdate = true;

        cameraL.aspect = 0.5 * SCREEN_WIDTH / SCREEN_HEIGHT;
        cameraR.aspect = 0.5 * SCREEN_WIDTH / SCREEN_HEIGHT;
        cameraL.updateProjectionMatrix();
        cameraR.updateProjectionMatrix();

        renderer.setViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
        renderer.clear();

        renderer.setViewport(1, 1, 0.5 * SCREEN_WIDTH - 2, SCREEN_HEIGHT - 2);
        renderer.render(scene, cameraL);

        renderer.setViewport(0.5 * SCREEN_WIDTH + 1, 1, 0.5 * SCREEN_WIDTH - 2, SCREEN_HEIGHT - 2);
        renderer.render(scene, cameraR);
    }
    window.onload = initScene;

最後に

どう使うかが悩み所ですが、音楽と合わせて、凹凸を出したりしたら面白いのではないかなど考えています。
displacement、とても面白いのでまた何か作りたいものです。
では、最後までありがとうございました。

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8