LoginSignup
24
19

More than 5 years have passed since last update.

three.jsとタブレットでホログラム

Last updated at Posted at 2016-12-11

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


vlcsnap1.png

ホログラムを知ったきっかけ

three.jsで色々と検索をしていたら、ICS MEDIAさんのこの記事を見つけました。
私は小さい頃から光るものが大好きで、記事を見てすぐにプラスチック板の方を作りました。
映像の方も作ろうと思い立ち、controlsやdat.guiで映すものの変化を操作できたら面白いだろう、ということで付けてみました。
demo

canvasでホログラムを行う上で必要なもの

透明な板 比率に合わせて切った台形 4枚を貼ったもの
タブレットかスマホ
three.js等で作った映像

まずは透明な板の準備

比率に合わせて切った台形を4枚用意して、テープ等でくっつけます。
100円ショップで売っているクリア下敷きが便利です。
ホログラム板.png

次に映す端末の選択

スマホで出来ないこともないのですが、タブレットの方がオススメです。
ブラウザで表示することが出来るので、サイズの合った端末なら友達のところでも映すことができます。

three.js等で作った映像

index.html
<!DOCTYPE html>

<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>holo</title>
    <style>
        body,
        canvas {
            background-color: #000000;
            overflow: hidden;
        }

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

<body>
    <div id="holo" width="300" height="300"></div>
    <canvas id="holo1"></canvas>
</body>

</html>
main.js
var initScene, render,
    renderer,
    scene, camera, camera2, light, light2, controls, loader, geometry, material, mesh,
    r, urls, textureCube, gui, canvasMoto, canvasUp, ctx;

var rad = -90 * Math.PI / 180; // -90度
var viewW = window.innerWidth; // ウィンドウサイズの横幅
var viewH = window.innerHeight; // ウィンドウサイズの高さ
var size = Math.min(viewW, viewH); // 幅と高さの小さい方を返す
var dw = size / 3;
var dh = size / 3;
var px = (viewW - size) / 2;
var py = (viewH - size) / 2;
var setting = {        // dat.guiの設定
    color: '#80ff80',  // 色
    wireframe: true,   // ワイヤーフレーム
    reflectivity: 0.1  // 反射率
}

function initScene() {

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(75, 1 / 1, 0.1, 1000);
    camera.position.set(0, 0, 2);
    controls = new THREE.OrbitControls(camera, holo1);
    scene.add(camera);

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

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

    loader = new THREE.TextureLoader();
    cubeLoader = new THREE.CubeTextureLoader();  // meshに反射させるテクスチャ
    mycolor = loader.load('tex/white.png');  // 真っ白いテクスチャを貼っています
    r = "tex/";
    // skyboxのテクスチャ 右、左、上、下、前、後 の順です
    urls = [r + "right.jpg", r + "left.jpg", r + "top.jpg", r + "bottom.jpg", r + "front.jpg", r + "back.jpg"];
    textureCube = cubeLoader.load(urls);

    geometry = new THREE.IcosahedronGeometry(1, 1);  // 値は半径、分割回数
    material = new THREE.MeshPhongMaterial({
        wireframe: setting.wireframe,       // ワイヤーフレーム
        color: setting.color,               // 色
        map: mycolor,                       // テクスチャ
        envMap: textureCube,                // 反射させるテクスチャ
        reflectivity: setting.reflectivity  // 反射率
    });
    mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);

    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    renderer.setSize(330, 330);
    renderer.setClearColor(0x000000);

    canvasMoto = document.getElementById("holo").appendChild(renderer.domElement);  // 元のキャンバス
    canvasUp = document.getElementById("holo1");  // 映るキャンバス
    ctx = canvasUp.getContext("2d");  // 2Dのコンテキストを取得
    canvasUp.width = viewW;
    canvasUp.height = viewH;

    gui = new dat.GUI();  // dat.gui
    gui.addColor(setting, "color").onChange(function(value) {
        value = value.replace('#', '0x');
        material.color.setHex(value);
    });
    gui.add(setting, "wireframe").onChange(function(value) {
        material.wireframe = value;
    });
    gui.add(setting, 'reflectivity', 0.0, 1.0).onChange(function(value) {
        material.reflectivity = Number(value);
    });
    gui.open();

    render();
}

function render() {
    requestAnimationFrame(render);

    // 四角形の形にクリア
    ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); // clearRect(左上のx座標, 左上のy座標, 四角形の幅, 四角形の高さ);

    ctx.strokeStyle = "rgb(255, 215, 0)"; // 中心の枠
    ctx.strokeRect((viewW - size / 6) * 0.5, (viewH - size / 6) * 0.5, size / 6, size / 6);

    ctx.save(); // 現在の描画状態を保存
    ctx.translate(px + dw * 2, py);
    ctx.scale(-1, 1);
    ctx.drawImage(canvasMoto, 0, 0, dw, dh);
    ctx.restore(); // 描画状態を保存した時点のものに戻す

    ctx.save();
    ctx.translate(px, py + dh);
    ctx.scale(1, -1);
    ctx.rotate(rad);
    ctx.drawImage(canvasMoto, 0, 0, dw, dh);
    ctx.restore();

    ctx.save();
    ctx.translate(px + dw, py + dh * 3);
    ctx.scale(-1, 1);
    ctx.rotate(rad * 2);
    ctx.drawImage(canvasMoto, 0, 0, dw, dh);
    ctx.restore();

    ctx.save();
    ctx.translate(px + dw * 3, py + dh * 2);
    ctx.scale(1, -1);
    ctx.rotate(rad * 3);
    ctx.drawImage(canvasMoto, 0, 0, dw, dh);
    ctx.restore();

    controls.update();
    mesh.rotation.y += 0.01;
    renderer.render(scene, camera);
}

window.onload = initScene;

コードをのせて、コメントものせてありますので、よかったら作ってみてください。

最後に

私は板を3枚で作ったりして楽しんでいます。
暗い部屋で見ると、とても綺麗です。
ICS MEDIAさんの記事のおかげです。ありがとうございます。

動画

three.jsとiPad miniでホログラム。20面体の撮り方改良版。 #threejs pic.twitter.com/vc5nZCJLIh

— 雪解 (@snowdoke) 2016年9月25日
24
19
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
24
19