この記事は、Three.js Advent Calendar 2016 12日目の記事です。
#ホログラムを知ったきっかけ
three.jsで色々と検索をしていたら、ICS MEDIAさんのこの記事を見つけました。
私は小さい頃から光るものが大好きで、記事を見てすぐにプラスチック板の方を作りました。
映像の方も作ろうと思い立ち、controlsやdat.guiで映すものの変化を操作できたら面白いだろう、ということで付けてみました。
demo
#canvasでホログラムを行う上で必要なもの
透明な板 比率に合わせて切った台形 4枚を貼ったもの
タブレットかスマホ
three.js等で作った映像
#まずは透明な板の準備
比率に合わせて切った台形を4枚用意して、テープ等でくっつけます。
100円ショップで売っているクリア下敷きが便利です。
#次に映す端末の選択
スマホで出来ないこともないのですが、タブレットの方がオススメです。
ブラウザで表示することが出来るので、サイズの合った端末なら友達のところでも映すことができます。
#three.js等で作った映像
<!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>
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日