アニソンVJとプロジェクションマッピング

アニソンVJに関する最新ニュースやトピック、技術メモなど、初心者に分かりやすく解説。おすすめアニソンVJソフト: VDMX5, Quartz Composer オススメVJ素材: VJ Beeple 動画編集: Adobe AE, Pr. Apple FCPX, Motion.

VJをするには、VJ機材を以下のようにして接続します。
VJ機材の接続図

以下の機材は不要です。でも絶対必要と思い込んでる人の多いこと。
最新のVJ機材

このように著しく画質が悪化します。なんと7倍も画素数が違います。
最新のVJ機材に国民的2Dアイドルもびっくり

これは酷いですね!国民的2次元アイドルもびっくりです。これは超カッコ悪いですね!
VJ機材の画質比較
どうしても必要なら別の方法があります。詳しくはこちらのページをご覧ください。

ofxKinectの3D画像をSyphon経由でVDMX5とMixする

f:id:ctg:20160522010305j:plain

openFrameworksを使い、Kinectで取得した3D画像をSyphonでVDMX5に送りとMixするというのをやった。

Kinectで取得した3Dをそのままポイントクラウドで表示したが、見た感じあんまりカッコよくない。下図のような感じ。

f:id:ctg:20160707133933j:plain

ポイントクラウド - Wikipedia

そこでポイントクラウドからポリゴンを生成したい、そうすると下図のように表示されるハズ。

f:id:ctg:20160707134023j:plain

任意の点群データからポリゴンを生成するには、ドロネー分割というアルゴリズムを使うらしい。

ドロネー図 - Wikipedia

ポリゴン - Wikipedia

しかしドロネー分割は、自前で処理を書くのは無理。

面倒は嫌いなので探すと、ドロネー分割のアドオンがありました。ofxDelaunayというアドオンです。

github.com

そして以下のページも参考にしつつ、

blog.rettuce.com

www.honeycomb-lab.co.jp

www.honeycomb-lab.co.jp

qiita.com

コーディングして動いた。

f:id:ctg:20160709162439p:plain

書きあがったコードが以下。

ofApp.h

#pragma once

#include "ofMain.h"
#include "ofxDelaunay.h"
#include "ofxGui.h"
#include "ofxKinect.h"
#include "ofxSyphon.h"

class ofApp : public ofBaseApp{

    public:
        void setup();
        void update();
        void draw();

    ofxKinect kinect;
    
    // GUIのパラメーター
    ofxPanel gui;
    ofxFloatSlider tilt;
    ofxFloatSlider depthRangeNear;
    ofxFloatSlider depthRangeFar;
    ofxFloatSlider offsetX;
    ofxFloatSlider offsetY;
    ofxFloatSlider offsetZ;
    ofxFloatSlider originZ;
    ofxToggle toggle;
    
    int angle;
    bool showUI;
    
    ofxDelaunay delaunay;
    ofEasyCam cam;
    ofVboMesh mesh;
    
    ofxSyphonServer mainOutputSyphonServer;

        void keyPressed(int key);
        void keyReleased(int key);
        void mouseMoved(int x, int y );
        void mouseDragged(int x, int y, int button);
        void mousePressed(int x, int y, int button);
        void mouseReleased(int x, int y, int button);
        void mouseEntered(int x, int y);
        void mouseExited(int x, int y);
        void windowResized(int w, int h);
        void dragEvent(ofDragInfo dragInfo);
        void gotMessage(ofMessage msg);
        
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

    ofSetFrameRate(30);
    ofSetWindowShape(640, 480); //ウインドウのサイズ
    ofSetBackgroundColor(128, 128, 128, 0);
    
    mainOutputSyphonServer.setName("Screen Outputh");
    
    kinect.init();
    kinect.open();
    // 0-300の部分だけ深度取る ここ割と細かく設定できると精度上がる気がした
    kinect.setDepthClipping(0,300);
    // Kinect 初期の首角度
    kinect.setCameraTiltAngle(22);
    // これでColorの通常カメラと深度カメラのズレ修正できた
    kinect.setRegistration(true);
    // 0-300の部分だけ深度取る ここ割と細かく設定できると精度上がる気がした
    kinect.setDepthClipping(0,30000);
    // これでColorの通常カメラと深度カメラのズレ修正できた
    kinect.setRegistration(true);
    // Kinect 初期の首角度
    kinect.setCameraTiltAngle(22);
    
    //GUIのセットアップ
    gui.setup();
    gui.add(tilt.setup("tilt", 5, -29, 29));
    gui.add(depthRangeFar.setup("depthRangeFar", 2000, 0, 3000));
    gui.add(depthRangeNear.setup("depthRangeNear", 100, 0, 1000));
    gui.add(offsetX.setup("offset x", 10, -3000, 3000));
    gui.add(offsetY.setup("offset y", 10, -3000, 3000));
    gui.add(offsetZ.setup("offset z", 10, -10000, 10000));
    gui.add(originZ.setup("origin z", 10, -10000, 10000));
    gui.add(toggle.setup("toggle", true));
}

//--------------------------------------------------------------
void ofApp::update(){

    kinect.update();
    // Kinect首角度
    kinect.setCameraTiltAngle(tilt);

    //ドロネーをリセット
    delaunay.reset();

    //ドロネー三角形分割
    for (int i=0; i<5000; i++) {
        ofPoint point = ofPoint(ofRandom(0, ofGetWidth()),ofRandom(0, ofGetHeight()));

        if( kinect.getDistanceAt(point.x, point.y) < 500 or kinect.getDistanceAt(point.x, point.y) > 3000 ){
            continue;
        }

        delaunay.addPoint(ofPoint(point.x,point.y));
    }
    delaunay.triangulate();

    //メッシュの中身をクリア
    mesh.clear();

    for (int i=0; i<delaunay.triangleMesh.getNumIndices()/3; i++) {

        int idx1 = delaunay.triangleMesh.getIndex(i*3);
        int idx2 = delaunay.triangleMesh.getIndex(i*3+1);
        int idx3 = delaunay.triangleMesh.getIndex(i*3+2);

        ofVec2f v1 = delaunay.triangleMesh.getVertex(idx1);
        ofVec2f v2 = delaunay.triangleMesh.getVertex(idx2);
        ofVec2f v3 = delaunay.triangleMesh.getVertex(idx3);

        if(kinect.getDistanceAt(v1.x, v1.y) > depthRangeFar  or
           kinect.getDistanceAt(v1.x, v1.y) < depthRangeNear or
           kinect.getDistanceAt(v2.x, v2.y) > depthRangeFar  or
           kinect.getDistanceAt(v2.x, v2.y) < depthRangeNear or
           kinect.getDistanceAt(v3.x, v3.y) > depthRangeFar  or
           kinect.getDistanceAt(v3.x, v3.y) < depthRangeNear ){
            continue;
        }

        //Depthを付加
        ofVec3f aVec1 = ofVec3f(v1.x, v1.y, kinect.getDistanceAt(v1.x, v1.y));
        ofVec3f aVec2 = ofVec3f(v2.x, v2.y, kinect.getDistanceAt(v2.x, v2.y));
        ofVec3f aVec3 = ofVec3f(v3.x, v3.y, kinect.getDistanceAt(v3.x, v3.y));

        //重心の色を使う
        ofVec2f gp = (v1+v2+v3)/3.0;
        ofColor color = kinect.getColorAt((int)gp.x, (int)gp.y);

        mesh.addVertex(aVec1);
        mesh.addVertex(aVec2);
        mesh.addVertex(aVec3);

        mesh.addColor(color);
        mesh.addColor(color);
        mesh.addColor(color);
    }

    //toggleボタンがoffならY軸回転しない
    if (toggle == false) {
        return;
    }

    //Y軸回転パラメーターのアップデート
    if (angle<359) {
        angle++;
    }else{
        angle = 0;
    }
}

//--------------------------------------------------------------
void ofApp::draw(){

    glEnable(GL_DEPTH_TEST);

    ofSetColor(0);
    
    ofPushMatrix();
    
    ofTranslate(ofGetWidth()/2, ofGetHeight()/2, 0);
    ofTranslate(0, 0, originZ);
    ofRotateY(angle);

    ofPushMatrix();

    ofScale(2, -2, 2); // flip the y axis and zoom in a bit
    ofRotateZ(180);
    ofTranslate(offsetX, offsetY, offsetZ);
    
    mesh.draw();

    ofPopMatrix();

    mainOutputSyphonServer.publishScreen();

    //中心軸マーカーを三角形で描画
    ofSetColor(255, 200, 30);
    ofDrawTriangle(0, 0, 40, 0, 40, 0, 0, 0, 0);
    ofSetColor(30, 200, 255);
    ofDrawTriangle(0, 30, 0, 20, 10, 0, -20,10,0);

    ofPopMatrix();

    glDisable(GL_DEPTH_TEST);
    //GUIを描画
    gui.draw();
}