Oculus+Node.js+Three.jsはVRの世界を作ります.


Oculus+Node.js+Three.jsはVRの世界を作ります.
Oculus Riftは電子ゲームのために設計されたヘッドセットディスプレイです.これは仮想現実装置である.この設備は未来の人々のゲームの方式を変える可能性があります.
金曜日のHackday Showcaseの時に、ちょっとヒントができました.会社に休んでいたOculus DK 2を家に借りました.もうほこりだらけです.
一晩の開発環境の構築を試みた後、原生応用の開発を断念しました.一つは自分のコンピュータ(Raspberry Pi IIは計算しないと)ではないです.Windowsがなく、GNU/Linuxがなく、もう一つは会社のコンピューターはMac OSです.組み込み開発とゲーム開発にとって、Mac OSはまるで携帯電話の中のWindows Phone――ピット父のLLVM、GCC(Mac OS)、OpenGL、OLPlus、C+11です.また、Mac OSやLinuxに対する公式のSDKの支持は何世紀も遅れています.
結局、やはりWebの開発環境は構築しやすいです.このrepoの最後の効果図は以下の通りです.
効果:
  • WASD制御前進、後退など.
  • 回転ヘッド=リアルな世界.
  • 付加効果:長く見たらめまいがします.
  • これから、構築を始めましょう.
    Node Oculus Services
    ここで、私たちがやるべきことは、センサーを戻した四元の数(Quarternions)とオーラ角(Euler angles)をAPIの形で先端に戻すことです.
    Node NMDのインストール
    Node.jsにOculusというプラグインがあります.hmdは顔を頭に向けてディスプレイします.Oculus SDKのNodeインターフェースです.年代はもう古いですが、Mac OSとLinuxに対する公式のSDKはもう長い間更新されていません.
    GNU/Linuxシステムでは、下のこれらのものをインストールする必要があります.
    freeglut3-dev
    mesa-common-dev
    libudev-dev
    libxext-dev
    libxinerama-dev
    libxrandr-dev
    libxxf86vm-dev
    Mac OSのインストールに失敗したら、Clangを使ってください.GCCのC標準ライブラリ(PS:Clang+GCCの混合体です.それらの間は複雑な関係です.):
    export CXXFLAGS=-stdlib=libstdc++
    
    export CC=/usr/bin/clang
    export CXX=/usr/bin/clang++
    (PS:Mac OS El Captian+Xcode 7.0.2)clangバージョンは以下の通りです.
    Apple LLVM version 7.0.2 (clang-700.1.81)
    Target: x86_64-apple-darwin15.0.0
    Thread model: posix
    どうせ誤報があります.
    ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Service/Service_NetClient.o) was built for newer OSX version (10.7) than being linked (10.5)
    ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Tracking/Tracking_SensorStateReader.o) was built for newer OSX version (10.7) than being linked (10.5)
    ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_ImageWindow.o) was built for newer OSX version (10.7) than being linked (10.5)
    ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_Interface.o) was built for newer OSX version (10.7) than being linked (10.5)
    ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_LatencyTest2Reader.o) was built for newer OSX version (10.7) than being linked (10.5)
    ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_Render_Stereo.o) was built for newer OSX version (10.7) than being linked (10.5)
    [email protected] node_modules/node-hmd
    でも、最後の行があれば十分です.
    Node.js Oculus Hello,World
    今からハロー、ワールドを書くことができます.直接に公式の例に来ます.
    var hmd = require('node-hmd');
    
    var manager = hmd.createManager("oculusrift");
    
    manager.getDeviceInfo(function(err, deviceInfo) {
        if(!err) {
            console.log(deviceInfo);
        }
        else {
            console.error("Unable to retrieve device information.");
        }
    });
    
    manager.getDeviceOrientation(function(err, deviceOrientation) {
        if(!err) {
            console.log(deviceOrientation);
        }
        else {
            console.error("Unable to retrieve device orientation.");
        }
    });
    運転する前に、まずあなたのOculusを連結してください.次のような結果があります.
    { CameraFrustumFarZInMeters: 2.5,
      CameraFrustumHFovInRadians: 1.29154372215271,
      CameraFrustumNearZInMeters: 0.4000000059604645,
      CameraFrustumVFovInRadians: 0.942477822303772,
      DefaultEyeFov:
       [ { RightTan: 1.0923680067062378,
           LeftTan: 1.0586576461791992,
           DownTan: 1.3292863368988037,
           UpTan: 1.3292863368988037 },
         { RightTan: 1.0586576461791992,
           LeftTan: 1.0923680067062378,
           DownTan: 1.3292863368988037,
           UpTan: 1.3292863368988037 } ],
      DisplayDeviceName: '',
      DisplayId: 880804035,
      DistortionCaps: 66027,
      EyeRenderOrder: [ 1, 0 ],
      ...
    そして、私たちはリアルタイムでこれらのデータを返すことができます.
    Node Oculus Websocket
    ネットで見ましたhttp://laht.info/WebGL/DK2Demo.htmlこの仮想現実の映画は、WebSocketがあることを発見しましたが、Javaが書いたもので、参照コードとしてしか使えません.
    今はこのようなWeb Servicesを書くことができます.まだExpress+Node.js+WSを使っています.
    var hmd = require("node-hmd"),
        express = require("express"),
        http = require("http").createServer(),
        WebSocketServer = require('ws').Server,
        path = require('path');
    
    // Create HMD manager object
    console.info("Attempting to load node-hmd driver: oculusrift");
    var manager = hmd.createManager("oculusrift");
    if (typeof(manager) === "undefined") {
        console.error("Unable to load driver: oculusrift");
        process.exit(1);
    }
    // Instantiate express server
    var app = express();
    app.set('port', process.env.PORT || 3000);
    
    app.use(express.static(path.join(__dirname + '/', 'public')));
    app.set('views', path.join(__dirname + '/public/', 'views'));
    app.set('view engine', 'jade');
    
    app.get('/demo', function (req, res) {
        'use strict';
        res.render('demo', {
            title: 'Home'
        });
    });
    
    // Attach socket.io listener to the server
    var wss = new WebSocketServer({server: http});
    var id = 1;
    
    wss.on('open', function open() {
        console.log('connected');
    });
    
    // On socket connection set up event emitters to automatically push the HMD orientation data
    wss.on("connection", function (ws) {
        function emitOrientation() {
            id = id + 1;
            var deviceQuat = manager.getDeviceQuatSync();
            var devicePosition = manager.getDevicePositionSync();
    
            var data = JSON.stringify({
                id: id,
                quat: deviceQuat,
                position: devicePosition
            });
    
            ws.send(data, function (error) {
                //it's a bug of websocket, see in https://github.com/websockets/ws/issues/337
            });
        }
    
        var orientation = setInterval(emitOrientation, 1000);
    
        ws.on("message", function (data) {
            clearInterval(orientation);
            orientation = setInterval(emitOrientation, data);
        });
    
        ws.on("close", function () {
            setTimeout(null, 500);
            clearInterval(orientation);
            console.log("disconnect");
        });
    });
    
    // Launch express server
    http.on('request', app);
    http.listen(3000, function () {
        console.log("Express server listening on port 3000");
    });
    つまり、連続して設備を発見するデータです.
    var data = JSON.stringify({
        id: id,
        quat: deviceQuat,
        position: devicePosition
    });
    
    ws.send(data, function (error) {
        //it's a bug of websocket, see in https://github.com/websockets/ws/issues/337
    });
    上の行のコメントは前に会った穴です.とにかくcalbackが必要です.
    Thre.js+Oculus Effect+DK 2 Control
    最後に次のような画面が必要です.
    もちろん、Web VRというようなものをインストールしていたら、このような効果は必要ありません.タイトルの通り、Oculus Effectを使うと知っています.Three.jsのプラグインです.
    以前のバージョンでは、Three.jsはすべてOculusのDemoを提供しています.もちろん見るだけです.そして相互作用のインターフェースはHTTPで、とても遊びにくいです.
    Three.js DK 2 ontrolls
    この時、私達は上から伝わってきた (Quaternions)とEuler anglesによって対応しなければなりません.
    {
        "position": {
            "x": 0.020077044144272804,
            "y": -0.0040545957162976265,
            "z": 0.16216422617435455
        },
        "quat": {
            "w": 0.10187230259180069,
            "x": -0.02359195239841938,
            "y": -0.99427556991577148,
            "z": -0.021934293210506439
        }
    }
    オーラ角と四元の数
    (ps:copyがよくないなら、正しい言い方をしてください.高数の人を許してください.高校生の時だけ、これらの資料を見ました.)
    Euler角は剛体の姿勢を記述するための一組の角度であり,Eulaは3次元の欧氏空間における剛体の任意の向きは3軸を回る回転再結合によって生成できると提案した.通常、3つの軸は互いに直交している.
    対応する3つの角度はそれぞれroll(横転角)、pitch(仰向け角)とyaw(ヨー角)となり、上のpostionの中の3つの値です.
    roll = (rotation about Z);
    
    pitch = (rotation about (Roll • Y));
    
    yaw = (rotation about (Pitch • Raw • Z));”
    --「Oculus Rift Inアクション」から引く
    コードに変換します.
    this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);
    四元数はアイルランドの数学者ウィリアム・ルージュ・ハミルトンが1843年に発見した数学概念です.
    明確な観点から言えば、4元の数は複数の交換可能な拡張である.四元数の集合を多次元実数空間と考えると、四元数は複数に対して二次元空間である四次元空間を表している.
    とにかく に使われる.
    コードを結合:
    this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);
    this.headQuat.set(sensorData.quat.x, sensorData.quat.y, sensorData.quat.z, sensorData.quat.w);
    
    this.camera.setRotationFromQuaternion(this.headQuat);
    this.controller.setRotationFromMatrix(this.camera.matrix);
    つまり、cameraとcontrolerの回転をセットする必要があります.
    これは私に十分な理由があると信じています.Oculusは携帯電話+6軸スポーツ処理コンポーネントのアップグレードボードです.MPU 6050のようなセンサーを使ったことがあります.
    Three.js DK 2 ontrolls
    下のコードは私が書いたのではないですが、簡単に話してください.
    /*
     Copyright 2014 Lars Ivar Hatledal
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
     http://www.apache.org/licenses/LICENSE-2.0
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
     */
    
    THREE.DK2Controls = function (camera) {
    
        this.camera = camera;
        this.ws;
        this.sensorData;
        this.lastId = -1;
    
        this.controller = new THREE.Object3D();
    
        this.headPos = new THREE.Vector3();
        this.headQuat = new THREE.Quaternion();
    
        var that = this;
        var ws = new WebSocket("ws://localhost:3000/");
        ws.onopen = function () {
            console.log("### Connected ####");
        };
    
        ws.onmessage = function (evt) {
            var message = evt.data;
            try {
                that.sensorData = JSON.parse(message);
            } catch (err) {
                console.log(message);
            }
        };
    
        ws.onclose = function () {
            console.log("### Closed ####");
        };
    
        this.update = function () {
    
            var sensorData = this.sensorData;
            if (sensorData) {
                var id = sensorData.id;
                if (id > this.lastId) {
                    this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);
                    this.headQuat.set(sensorData.quat.x, sensorData.quat.y, sensorData.quat.z, sensorData.quat.w);
    
                    this.camera.setRotationFromQuaternion(this.headQuat);
                    this.controller.setRotationFromMatrix(this.camera.matrix);
                }
                this.lastId = id;
            }
    
    
            this.camera.position.addVectors(this.controller.position, this.headPos);
            if (this.camera.position.y < -10) {
                this.camera.position.y = -10;
            }
    
            if (ws) {
                if (ws.readyState === 1) {
                    ws.send("get
    "); } } }; };
    WebSocketを開くと、常に最新のセンサー状態を取得し、udateを実行します.誰がudateメソッドを呼び出していますか?Thre.js
    私達は私達の初期化コードの中で私達のcontrolを初期化する必要があります.
    var oculusControl;
    
    function init() {
            ...
        oculusControl = new THREE.DK2Controls( camera );
        ...
    }
    そしてどんどんudateメソッドを呼び出します.
    function animate() {
        requestAnimationFrame( animate );
        render();
        stats.update();
    }
    function render() {
        oculusControl.update( clock.getDelta() );
        THREE.AnimationHandler.update( clock.getDelta() * 100 );
    
        camera.useQuaternion = true;
        camera.matrixWorldNeedsUpdate = true;
    
        effect.render(scene, camera);
    }
    最後に、該当するKeyHandlerを追加すればいいです.
    Three.js KeyHandler
    KeyHandlerはWeb開発に慣れた人にとっては簡単です.
    this.onKeyDown = function (event) {
        switch (event.keyCode) {
            case 87: //W
                this.wasd.up = true;
                break;
            case 83: //S
                this.wasd.down = true;
                break;
            case 68: //D
                this.wasd.right = true;
                break;
            case 65: //A
                this.wasd.left = true;
                break;
        }
    };
    
    this.onKeyUp = function (event) {
        switch (event.keyCode) {
            case 87: //W
                this.wasd.up = false;
                break;
            case 83: //S
                this.wasd.down = false;
                break;
            case 68: //D
                this.wasd.right = false;
                break;
            case 65: //A
                this.wasd.left = false;
                break;
        }
    };
    そして百悪のif文です.
    if (this.wasd.up) {
        this.controller.translateZ(-this.translationSpeed * delta);
    }
    
    if (this.wasd.down) {
        this.controller.translateZ(this.translationSpeed * delta);
    }
    
    if (this.wasd.right) {
        this.controller.translateX(this.translationSpeed * delta);
    }
    
    if (this.wasd.left) {
        this.controller.translateX(-this.translationSpeed * delta);
    }
    
    this.camera.position.addVectors(this.controller.position, this.headPos);
    
    if (this.camera.position.y < -10) {
        this.camera.position.y = -10;
    }
    早くあなたのHMDを取ってみてください.
    おわりに
    私が『RePractise前端編:前端演進史』で言ったように、これは新しい「先端」のようです.