VR 時代的主流編程語言是什麼?


跑個題。。。

大家可記得上世紀90年代有個語言叫VRML.....


GitHub - aframevr/awesome-aframe: A collection of awesome things regarding A-Frame ecosystem.

相信Aframe 和 React 將會大放異彩


C/C++


什麼VR?說的那麼高上大,就是那幫做遊戲的換了一種新玩法。主要的3D軟體框架還是U3D和Unreal。對應的編程語言是C#和C++。


雖然用C++好像是主流,但是用JavaScirpt也是可行的——使用Node.js或者類似的語言來讀取感測器的數據搭建服務,再用Web 3d遊戲引擎來渲染。

在目前看來,使用JavaScript是最好的構建原型的方法——誰讓C++是個坑呢!

在目前看來,使用JavaScript這是最好的構建原型的方法——誰讓C++是個坑呢!

在目前看來,使用JavaScript這是最好的構建原型的方法——誰讓C++是個坑呢!

這篇文章分成三部分:

  1. 基礎知識:3D世界與四元數

  2. 一個Hello, World

  3. 應用篇——高級示例

因為我只玩過公司的Oculus DK2,所以這裡是以DK2為內容而展開的。

實際上,要用JavaScript來用VR程序是很簡單的一件事:

  1. 使用Node.js來讀取Oculus上的感測器的數據,將這些數據用WebSocket協議來提供一個服務。

  2. 尋找一個3D遊戲引擎,如Three.js來創建一個3D世界。

  3. 讀取感測器的值將其表示在3D世界中。

這點也可以用在混合應用上,你只需要有一個CardBoard即可。使用Cordova讀取手機感測器的數據,再通過這些數據來改變WebView的狀態——除了發熱會比較嚴重,應該沒有別的影響。

一、基礎知識:3D世界與四元數

在我們所熟知的3D遊戲里,點的位置由三個坐標決定的(x,y,z),如下圖所示:

這三個坐標只能表示我們在這個世界的位置,而不能上下的看這個世界。

Oculus DK2用的是MPU(Motion Processing Unit)晶元是MPU6500,是第二個整合性6軸運動處理組件(第一個是MPU6050)。它可以數字輸出6軸或9軸的旋轉矩陣、四元數(quaternion)、歐拉角格式(Euler Angle forma)的融合演算數據。

這時候,我們就需要歐拉角以及四元數來表示物體在虛擬世界的狀態。(PS:原諒我只能簡單地提一下)

歐拉角是一組用於描述剛體姿態的角度,歐拉提出,剛體在三維歐氏空間中的任意朝向可以由繞三個軸的轉動複合生成。通常情況下,三個軸是相互正交的。

其對應的三個角度又分別成為roll(橫滾角),pitch(俯仰角)和yaw(偏航角)。

而四元數則是:

四元數可以用於表示三維空間里的旋轉。它常用的另外兩種表示方式(三維正交矩陣和歐拉角)是等價的。人們用四元數來表示旋轉要解決兩個問題,一是如何用四元數表示三維空間里的點,二是如何用四元數表示三維空間的旋轉。

之前玩過的6050出來大概就是這樣子的,如果你玩四軸飛行器的話,你也應該這樣玩過:

Copy/Paste完上面的內容後,你可能沒有啥概念,還是舉個hello,world的例子。

二、例子:一個hello,world

讓我們在回到一開始說的那三步,我們將需要做三件事:

  1. 尋找一個Node的Oculus拓展——不過,這件事現在可以交給WebVR。

  2. 尋找一個Web的3D庫,及其對應的Oculus展示插件。

  3. 讀取感測器數據,顯示到虛擬世界中。

如下圖所示:

於是找至了對應的Node庫有:Node-HMD,它可以讀取感測器的數據。

還有Three.js和 Oculus Effect插件,可以顯示出下面的視圖:

這樣,我們DK2 Control讀取感測器的數據,就可以到這個虛擬世界玩了~~。

更詳細的介紹可以見: GitHub - phodal/oculus-nodejs-threejs-example: Oculus + Node.js + Three.js 打造VR世界

三、高級應用:火星漫遊者

上面的應用示例還是太簡單了,讓我們來看一個高級應用——這是我們在兩個月前做的另外一個Hackday Idea,這是另外一個「火星漫遊者」:

想像一下你想去看看火星,但是你又沒有錢去。而你可以租用這樣的一個機器人,然後你就可以在火星漫遊了。

因此,首先我們需要一個實時視頻通訊,這裡我們就用到了WebRTC:

通過WebRTC我們就可以在計算機瀏覽器上實現實時通訊,再通過Three.js就可以將這個視頻轉為一個近似3D的視角。而捕獲這個視頻即可以通過手機上的瀏覽器,也可以在手機上編寫相應的Web應用。

這裡有一個在線的Demo:three.js - Oculus Rift

架構大致如下圖所示:

這樣我們就解決了實時視頻這個問題,然後我們還需要去控制硬體:

  1. 用WebSocket協議來提供Oculus的上、下、左、右運動的數據

  2. 在手機上讀取這個感測器數據,並將這個數據通過BLE傳送到小車上。

  3. 小車以通過指令來做相應的運動。

關於這部分內容的可以看我之前的那篇文章《我是如何Hack一個機器人的?》,鏈接: 我是如何Hack一個機器人的?

四、總結:All in JavaScript。

與C坑坑(C++)相比,JavaScript更適合搭建原型——快速、直接、有效,畢竟C++編譯需要時間的。運行起來的效果也如預期的一樣,電腦風扇各種轉,不知道是不是Mac專有的。不過,我想這個性能問題是一直都有的。

如我在《RePractise前端篇: 前端演進史》一文中所說的,這似乎就是新的"前端"。


前幾天google開源 vr view允許開發者在web或者app中嵌入vr相關多媒體素材

https://github.com/google/vrview


主要還是依賴 Game Engine 的傾向。

比如現在最火的 Unity 支持 C# 與 JavaScript (實際上是 UnityScript,個人覺得不如直接拿 TypeScript 過來),還有 Unreal 的 C++ 等;

雖然各個 Runtime 上會多一些選擇,比如:

  • Cardboard SDK for Android 自然是 Java
  • WebVR 當然少不了 JavaScript

但是遊戲開發界(目前 VR 開發的主力還是遊戲)的主流一直都是使用跨平台的遊戲引擎,畢竟主要還是和圖形打交道,用不到多少 platform-specific SDK,大概也是 write once, run eveywhere 實現得最靠譜的場景了


還記得當年屠龍之技裡面最牛逼的就是Lisp了,然後是perl。


可以參考這篇文章:詳細地址:Oculus + Node.js + Three.js 打造VR世界 |VR第一資訊 VR013

周五Hackday Showcase的時候,突然有了點小靈感,便將閑置在公司的Oculus DK2借回家了——已經都是灰塵了~~。

在嘗試一個晚上的開發環境搭建後,我放棄了開發原生應用的想法。一是沒有屬於自己的電腦(如果Raspberry Pi II不算的話)——沒有Windows、沒有GNU/Linux,二是公司配的電腦是Mac OS。對於嵌入式開發和遊戲開發來說,Mac OS簡直是手機中的Windows Phone——坑爹的LLVM、GCC(Mac OS )、OpenGL、OGLPlus、C++11。並且官方對Mac OS和Linux的SDK的支持已經落後了好幾個世紀。

說到底,還是Web的開發環境到底還是比較容易搭建的。這個repo的最後效果圖如下所示:

效果:

  1. WASD控制前進、後退等等。
  2. 旋轉頭部 = 真實的世界。
  3. 附加效果: 看久了頭暈。

現在,讓我們開始構建吧。

Node Oculus Services

這裡,我們所要做的事情便是將感測器返回來的四元數(Quaternions)與歐拉角(Euler angles)以API的形式返回到前端。

安裝Node NMD

Node.js上有一個Oculus的插件名為node-hmd,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)
node-hmd@0.2.1 node_modules/node-hmd

不過,有最後一行就夠了。

GitHub - phodal/oculus-nodejs-threejs-example: Oculus + Node.js + Three.js 打造VR世界Node.js Oculus Hello,World

現在,我們就可以寫一個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

在網上看到three.js - Oculus Rift這個虛擬現實的電影,並且發現了它有一個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
});

上面有一行注釋是我之前一直遇到的一個坑,總之需要callback就是了。

Three.js + Oculus Effect + DK2 Control

在最後我們需要如下的畫面:

當然,如果你已經安裝了Web VR這一類的東西,你就不需要這樣的效果了。如標題所說,你已經知道要用Oculus Effect,它是一個Three.js的插件。

在之前的版本中,Three.js都提供了Oculus的Demo,當然只能用來看。並且交互的介面是HTTP,感覺很難玩~~。

GitHub - phodal/oculus-nodejs-threejs-example: Oculus + Node.js + Three.js 打造VR世界Three.js DK2Controls

這時,我們就需要根據上面傳過來的四元數(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好,麻煩提出正確的說法,原諒我這個掛過高數的人。我只在高中的時候,看到這些資料。)

歐拉角是一組用於描述剛體姿態的角度,歐拉提出,剛體在三維歐氏空間中的任意朝向可以由繞三個軸的轉動複合生成。通常情況下,三個軸是相互正交的。

對應的三個角度又分別成為roll(橫滾角),pitch(俯仰角)和yaw(偏航角),就是上面的postion裡面的三個值。。

roll = (rotation about Z);

pitch = (rotation about (Roll ? Y));

yaw = (rotation about (Pitch ? Raw ? Z));」

-- 引自《Oculus Rift In Action》

轉換成代碼。。

this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);

四元數是由愛爾蘭數學家威廉·盧雲·哈密頓在1843年發現的數學概念。

從明確地角度而言,四元數是複數的不可交換延伸。如把四元數的集合考慮成多維實數空間的話,四元數就代表著一個四維空間,相對於複數為二維空間。

反正就是用於描述三維空間的旋轉變換。

結合下代碼:

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和controller的旋轉。

這使我有足夠的理由相信Oculus就是一個手機 + 一個6軸運動處理組件的升級板——因為,我玩過MPU6050這樣的感測器,如圖。。。

GitHub - phodal/oculus-nodejs-threejs-example: Oculus + Node.js + Three.js 打造VR世界Three.js DK2Controls

雖然下面的代碼不是我寫的,但是還是簡單地說一下。

/*
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的時候,不斷地獲取最新的感測器狀態,然後update。誰在調用update方法?Three.js

我們需要在我們的初始化代碼里初始化我們的control:

var oculusControl;

function init() {
...
oculusControl = new THREE.DK2Controls( camera );
...
}

並且不斷地調用update方法。

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就好了~~。

GitHub - phodal/oculus-nodejs-threejs-example: Oculus + Node.js + Three.js 打造VR世界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試試吧~~

準備在我的網站上更新教程了: VR教程 |VR第一資訊 VR013

---------------------分割符--------------------------

QQ群號:544405366

http://qm.qq.com/cgi-bin/qm/qr?k=i70vfJ44qMkTqj423-1Jakyrdw1w9qw7 (二維碼自動識別)


反正不是PHP


VR技術的編程語言是C#為主,其他的語言相輔助。VR開發需要掌握的技術有:

C#:C#基礎語法與演算法、面向對象編程、C#數據結構與高級語法;

Unity3D:引擎結構與物理結構、UGUI與動畫系統、導航系統,數據存儲,資料庫,協程,WWW類、網路,資源管理,特效系統,性能優化;

計算機圖形學與GPU編程:計算機圖形學與Shader編程;

項目實戰:AR項目,VR項目;

Unreal虛幻4引擎部分:Unreal虛幻4;


Unity C# and Javascript

Unreal C++ and Blueprints?


不要想了。離不開C/C++/Assembly造輪子。


JavaScript


其實答案就一個 C++


我覺得這個取決於你是造輪子還是造車子。

學術圈大部分都是以unity為基礎,用C#來寫代碼。至於怎樣map到不同設備,相關的輪子早就有unity給你寫好了。自己浪費時間寫輪子是不太划算的事兒,calibration都能累死人,且不說亂七八糟的演算法。

造車子目前以game engine為主,具體哪門語言就看是unity還是unreal了,還有Cryengine系列。

造輪子就得看你去哪家了…不過總的來說…C++/C++++為主


unity 3D 、C#


其實換湯不換藥。,虛擬現實,或稱靈境技術,實際上是一種可創建和體驗虛擬世界(Virtual World)的計算機系統。VR技術只在輸出技術上創造了巨大的革新。但是實質以及基礎仍然是主流的C語言。編程語言是很難發生變革的。


我聽說有一門語言叫做:自然交互語言 簡稱為NUL 。我不確定微軟的holo這個設備是不是和kinect一樣是相同的開發人員的開發的。如果是的,那就是有至少c++,c#的。當然了有一點可以肯定的是:c或許是c++肯定是作為和硬體交互的底層實現的編程語言。


Unity是由Unity Technologies開發的一個全面整合的專業遊戲引擎。Unity通用性較強,可以開發出基於各種平台的遊戲,包括手機遊戲(iPhone、Android、Windows Phone、黑莓)、PC(Windows、Mac、Linux)、網頁遊戲(基於各種主流瀏覽器)、遊戲機專用遊戲(Wii、Xbox360、XboxOne、PS3、PS4)。


推薦閱讀:

C++/C/JAVA/Python之間的區別?
什麼樣的編程語言會不支持遞歸呢?
模板類未實例化時,為什麼編譯器會漏檢一些錯誤?
學編程是否應該堅持看英文版著作?

TAG:互聯網 | 程序員 | 編程語言 | 虛擬現實VR |