Created
December 21, 2015 14:45
-
-
Save kaz/b5fc603078f588e72316 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ja"> | |
<head> | |
<meta charset="UTF-8"> | |
<style> | |
* { | |
margin: 0; | |
padding: 0; | |
} | |
#container { | |
display: none; | |
position: relative; | |
width: 480px; | |
height: 360px; | |
} | |
#video, #overlay { | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
#live2d { | |
display: none; | |
} | |
#stat { | |
font-size: 2em; | |
} | |
</style> | |
</head> | |
<body> | |
<button id="start">START</button><br> | |
<div id="container"> | |
<video id="video" width="480" height="360"></video> | |
<canvas id="overlay" width="480" height="360"></canvas> | |
</div> | |
<canvas id="live2d" width="480" height="480"></canvas> | |
<pre id="stat"></pre> | |
<!-- Live2D SDK --> | |
<script src="./js/live2d.min.js"></script> | |
<script src="./js/Live2DFramework.js"></script> | |
<script src="./js/PlatformManager.js"></script> | |
<!-- clmtrackr --> | |
<script src="./js/clmtrackr.js"></script> | |
<script src="./js/model_pca_20_svm.js"></script> | |
<script> | |
/** | |
* @license [traP Advent Calendar 2015 - Day22] | |
* by traP TokyoTech (kaz) | |
*/ | |
document.querySelector("#start").addEventListener("click", (function(window, document, navigator, alert, clm, pModel, Live2D, Live2DFramework, Live2DModelWebGL, PlatformManager){ | |
var live2dParams; | |
//clmtrackrを初期化 | |
var initTracker = function(stream){ | |
var video = document.querySelector("#video"); | |
var tracker = new clm.tracker({useWebGL: true}); | |
//カメラから画像を取得しトラッキング開始 | |
video.src = window.URL.createObjectURL(stream); | |
video.play(); | |
tracker.init(pModel); | |
tracker.start(video); | |
return tracker; | |
}; | |
//clmtrackr描画開始 | |
var drawTracker = function(tracker){ | |
var stat = document.querySelector("#stat"); | |
var overlay = document.querySelector("#overlay"); | |
var ctx = overlay.getContext("2d"); | |
(function(){ | |
window.requestAnimationFrame(arguments.callee); | |
//トラッキングした顔パーツ位置を描画 | |
ctx.clearRect(0, 0, overlay.width, overlay.height); | |
tracker.draw(overlay); | |
//Live2D用パラメータを計算して表示 | |
live2dParams = calculateParams(tracker); | |
stat.textContent = JSON.stringify(live2dParams, null, 4); | |
})(); | |
}; | |
//顔パーツの位置からLive2Dのパラメータを計算 | |
var calculateParams = function(tracker){ | |
var params = {SCORE: tracker.getScore()}; | |
var pos = tracker.getCurrentPosition(); | |
if(pos){ | |
var faceL = pos[62][0] - pos[2][0]; | |
var faceR = pos[12][0] - pos[62][0]; | |
var vecL = [pos[2][0] - pos[7][0], pos[2][1] - pos[7][1]]; | |
var vecR = [pos[12][0] - pos[7][0], pos[12][1] - pos[7][1]]; | |
var lipH = pos[53][1] - pos[57][1]; | |
var eyeHL = pos[26][1] - pos[24][1]; | |
var eyeHR = pos[31][1] - pos[29][1]; | |
//顔の向き | |
params["PARAM_ANGLE_X"] = 90 * (faceL - faceR) / (faceL + faceR); | |
params["PARAM_ANGLE_Y"] = -90 * (vecL[0] * vecR[0] + vecL[1] * vecR[1]) / Math.sqrt(vecL[0] * vecL[0] + vecL[1] * vecL[1]) / Math.sqrt(vecR[0] * vecR[0] + vecR[1] * vecR[1]); | |
params["PARAM_ANGLE_Z"] = -90 * (pos[33][0] - pos[62][0]) / (pos[33][1] - pos[62][1]); | |
//口の開閉・形 | |
params["PARAM_MOUTH_OPEN_Y"] = (pos[57][1] - pos[60][1]) / lipH - 0.5; | |
params["PARAM_MOUTH_FORM"] = 2 * (pos[50][0] - pos[44][0]) / (pos[30][0] - pos[25][0]) - 1; | |
//眼球の動き | |
params["PARAM_EYE_BALL_X"] = (pos[27][0] - pos[23][0]) / (pos[25][0] - pos[23][0]) - 0.5; | |
params["PARAM_EYE_BALL_Y"] = (pos[27][1] - pos[24][1]) / eyeHL - 0.5; | |
//目の開閉 | |
params["PARAM_EYE_L_OPEN"] = 0.7 * eyeHL / lipH; | |
params["PARAM_EYE_R_OPEN"] = 0.7 * eyeHR / lipH; | |
//眉の上下 | |
params["PARAM_BROW_L_Y"] = 2 * (pos[24][1] - pos[21][1]) / lipH - 4; | |
params["PARAM_BROW_R_Y"] = 2 * (pos[29][1] - pos[17][1]) / lipH - 4; | |
} | |
return params; | |
}; | |
//Live2Dを初期化 | |
var initLive2D = function(callback){ | |
var _assetsDir = "assets"; | |
var _modelFile = _assetsDir + "/shizuku.moc"; | |
var _physicsFile = _assetsDir + "/shizuku.physics.json"; | |
var _textureFiles = [ | |
_assetsDir + "/shizuku.1024/texture_00.png", | |
_assetsDir + "/shizuku.1024/texture_01.png", | |
_assetsDir + "/shizuku.1024/texture_02.png", | |
_assetsDir + "/shizuku.1024/texture_03.png", | |
_assetsDir + "/shizuku.1024/texture_04.png" | |
]; | |
var model; | |
var phys; | |
var loaded = 0; | |
var gl = getWebGLContext(); | |
var pm = new PlatformManager(); | |
//初期化 | |
Live2D.init(); | |
Live2DFramework.setPlatformManager(pm); | |
gl.clearColor(0.0, 0.0, 0.0, 0.0); | |
//モデル・物理演算データを読み込み | |
pm.loadBytes(_modelFile, function(data){ | |
model = Live2DModelWebGL.loadModel(data); | |
for(var i in _textureFiles){ | |
pm.loadTexture(model, i, _textureFiles[i], function(){ loaded++; }) | |
} | |
}); | |
pm.loadBytes(_physicsFile, function(data){ | |
phys = L2DPhysics.load(data); | |
}); | |
//読み込み終了後の処理 | |
(function(){ | |
if(model && phys && loaded == _textureFiles.length){ | |
//描画領域指定 | |
var s = 2.0 / model.getCanvasWidth(); | |
model.setMatrix([s,0,0,0 , 0,-s,0,0 , 0,0,1,0 , -1,1,0,1]); | |
model.setGL(gl); | |
//不要なパーツを非表示に | |
model.setPartsOpacity("PARTS_01_ARM_L_02", 0); | |
model.setPartsOpacity("PARTS_01_ARM_R_02", 0); | |
callback(gl, model, phys); | |
}else{ | |
setTimeout(arguments.callee, 100); | |
} | |
})(); | |
}; | |
//Live2D描画開始 | |
var drawLive2D = function(gl, model, phys){ | |
(function(){ | |
window.requestAnimationFrame(arguments.callee); | |
//パラメータを適用・頂点移動・描画 | |
gl.clear(gl.COLOR_BUFFER_BIT); | |
for(var paramName in live2dParams){ | |
model.setParamFloat(paramName, live2dParams[paramName]); | |
} | |
phys.updateParam(model); | |
model.update(); | |
model.draw(); | |
})(); | |
}; | |
//CanvasのWebGLコンテキストを取得 | |
window.getWebGLContext = function(){ | |
var canvas = document.querySelector("#live2d"); | |
var ctxNames = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; | |
for(var i in ctxNames){ | |
var ctx = canvas.getContext(ctxNames[i], { | |
alpha: true, | |
premultipliedAlpha: true | |
}); | |
if(ctx){ | |
return ctx; | |
} | |
} | |
return null; | |
}; | |
//開始関数 | |
return function(){ | |
//描画領域を表示 | |
document.querySelector("#container").style.display = document.querySelector("#live2d").style.display = "inline-block"; | |
//ベンダープレフィックスを外す | |
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; | |
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame; | |
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; | |
//カメラにアクセス | |
navigator.getUserMedia({video: true}, function(stream){ | |
initLive2D(drawLive2D); | |
drawTracker(initTracker(stream)); | |
}, function(){ | |
alert("Failed to access camera"); | |
}); | |
}; | |
})(window, document, navigator, alert, clm, pModel, Live2D, Live2DFramework, Live2DModelWebGL, PlatformManager)); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment