随着最近几年Web技术的快速发展,Web3D和WebVR在网页端效果越来越好。
本文讲述如何在云耀云服务器L实例上部署WebVR服务器,服务器代码里添加一个3D模型,然后再使用本地浏览器作为客户端来查看模型加载和交互情况,并体验WebVR。
文章目录
- 一 创建云耀云服务器L实例
- 二 生成3D模型
- 三 开启Win10混合现实门户
- 四 基于BabylonJS构建WebVR服务
- 1. 搭建基础工程
- 2. 导入3D模型
- 3. 体验WebVR
- 4. 测试大的3D模型
- 五 总结
一 创建云耀云服务器L实例
拿到代金券之后,直接去华为云官网购买云耀云服务器L并创建实例,本人创建的实例如下,带宽是3Mbit/s,
因为本人对Ubuntu比较熟悉,所以选择创建了Ubuntu系统实例。然后就是在华为云官网修改登录密码,修改好之后就才可以使用ssh进行远程登录。登录后状态如下,默认是root账户,
二 生成3D模型
斯坦福大学提供了几个开源免费的3D模型,本文将从中选择一个用来做测试。
首先打开http://graphics.stanford.edu/data/3Dscanrep/,然后往下拉找到Bunny模型,如下,最后点击红框下载该模型,
下载好后使用7-zip进行解压,得到2个文件夹,
然后进入reconstruction目录,
可以看到该模型格式是ply,而本文是测试WebVR,这就需要把模型转换为适合WebVR的glTF2.0格式。解压好之后使用Blender打开该模型,步骤是File->Import->Stanford(.ply),如下图,
在新界面里选中龙模型文件—dragon_vrip.ply,然后点击Import PLY,
等待一会就可以在Blender里看到模型了,
模型是躺着的,也没有任何材质,首先在Blender界面右侧的属性栏里把模型翻转90度,
然后把模型放大5倍,
最后再点击材质,给模型上个色,
此时发现兔子模型已经变成蓝色了,
此时再点击File->Export->glTF2.0(.glb/.gltf),
在弹出的界面右上角点击一下,然后选择glTF Embedded (.gltf),最后点击Export导出模型,
可以看出导出的模型是7M多,
这样模型就生成好了,暂时留着,等到后面再用。
三 开启Win10混合现实门户
体验WebVR要需要VR眼镜或者偷窥,如果没有,则可以使用Win10自带的混合现实门户来模拟,开启混合现实门户可以参考本人写的这篇文章。
四 基于BabylonJS构建WebVR服务
本文使用BabylonJS+Vite在云耀云服务器L实例上构建WebVR。BabylonJS是微软出品的Web3D库,性能强悍且功能丰富。
1. 搭建基础工程
首先创建VR目录,然后cd进入,接着创建webvr目录再cd进入,然后执行以下命令安装vite,BabylonJS和plugin-basic-ssl,
- npm i vite
- npm i -D @babylonjs/core
- npm i -D @babylonjs/inspector
- npm i -D @vitejs/plugin-basic-ssl
安装完毕后,在webvr目录下执行”npm init vite”,会提示是否安装create-vite,直接回车安装,
然后会提示输入工程名,可以自行输入或者直接回车使用vite-project作为工程名,
然后提示选择前端框架,直接回车选Vanilla就可以了,
最后选择编程语言,回车选择TypeScript,因为BabylonJS是使用TypeScript,
最后会提示后续操作,
根据提示,在当前目录下可以看到有个vite-project目录,cd进入,然后执行
- npm install
- npm run dev
最后出现以下信息,
本人的云服务器公网IP是60.204.136.232,在浏览器里输入http://60.204.136.232:5173/,发现无法打开。因为这个服务器用了5173端口,默认在云服务器L实例上没有打开,需要在入方向规则添加5173的规则,首先按Ctrl+C关闭服务器,然后添加以下规则,
接着在vite-project目录下创建vite.config.js,然后输入以下内容,
import basicSsl from `@vitejs/plugin-basic-ssl`
export default {
server: {
https: true,
host: '0.0.0.0'
},
plugins: [
basicSsl()
]
}
PS:“server”对应的值是开启https,并允许外网访问该服务器;plugins对应的值是使能自签名证书,为https提供支撑。因为WebVR要求https才可以访问,所以这里先弄好。
接着重新执行”npm run dev”,此时输出如下信息,可以看到https也开启了,
此时我们在浏览器里输入https://60.204.136.232:5173/,然后回车,会提示不安全,此时点击高级,然后点击继续,就可以出现如下界面,
点击黑色框“count is 0”可以实现count加1。说明工程运行OK,这样基础工程就搭建好了。
2. 导入3D模型
首先按Ctrl+C关闭服务器,然后把vite-project目录下的public目录和src目录里的文件全部删除,接着在src目录下创建app.ts
打开vite-project目录下的index.html,内容替换成如下,
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title of Your Project</title>
<style>
html, body {
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script type="module" src="./src/app.ts"></script>
</body>
</html>
打开vite-project目录下的tsconfig.json,内容替换成如下,
{
"compilerOptions": {
"target": "es6", // choose our ECMA/JavaScript version (all modern browsers support ES6 so it's your best bet)
"lib": [ // choose our default ECMA/libraries to import
"dom", // mandatory for all browser-based apps
"es6" // mandatory for targeting ES6
],
"useDefineForClassFields": true, // enable latest ECMA runtime behavior with older ECMA/JavaScript versions (delete this line if target: "ESNext" or "ES2022"+)
"module": "ESNext", // use the latest ECMA/JavaScript syntax for our import statements and such
"moduleResolution": "node", // ensures we are using CommonJS for our npm packages
"noResolve": false, // disable TypeScript from automatically detecting/adding files based on import statements and etc (it's less helpful than you think)
"isolatedModules": true, // allows our code to be processed by other transpilers, such as preventing non-module TS files (you could delete this since we're only using base TypeScript)
"removeComments": true, // remove comments from our outputted code to save on space (look into terser if you want to protect the outputted JS even more)
"esModuleInterop": true, // treats non-ES6 modules separately from ES6 modules (helpful if module: "ESNext")
"noImplicitAny": false, // usually prevents code from using "any" type fallbacks to prevent untraceable JS errors, but we'll need this disabled for our example code
"noUnusedLocals": false, // usually raises an error for any unused local variables, but we'll need this disabled for our example code
"noUnusedParameters": true, // raises an error for unused parameters
"noImplicitReturns": true, // raises an error for functions that return nothing
"skipLibCheck": true // skip type-checking of .d.ts files (it speeds up transpiling)
},
"include": ["src"] // specify location(s) of .ts files
}
打开vite-project/src/app.ts,内容如下,
import * as BABYLON from "@babylonjs/core"
import "@babylonjs/loaders"
import * as GUI from "@babylonjs/gui"
// create the canvas html element and attach it to the webpage
var canvas = document.createElement("canvas");
canvas.style.width = "100%";
canvas.style.height = "100%";
canvas.id = "gameCanvas";
document.body.appendChild(canvas);
const engine = new BABYLON.Engine(canvas, true);
const createScene = function() {
const scene = new BABYLON.Scene(engine);
const alpha = Math.PI/4;
const beta = Math.PI/3;
const radius = 8;
const target = new BABYLON.Vector3(0, 0, 0);
const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene);
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 4, height: 4});
BABYLON.SceneLoader.ImportMesh(null, "./", "bunny.gltf", scene, function (meshes, particleSystems, skeletons) {
console.log(meshes);
meshes[1].actionManager = new BABYLON.ActionManager(scene);
meshes[1].actionManager.registerAction(
new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger,
function (evt) {
const sourceBox = evt.meshUnderPointer;
if (sourceBox)
{
const boxMaterial = new BABYLON.StandardMaterial("material", scene);
boxMaterial.diffuseColor = BABYLON.Color3.Random();
meshes[1].material = boxMaterial;
sourceBox.position.x += 0.1;
sourceBox.position.y += 0.1;
}
}));
});
const xrPromise = scene.createDefaultXRExperienceAsync({
floorMeshes: [ground]
});
return xrPromise.then((xrExperience) => {
console.log("Done, WebXR is enabled.");
return scene;
});
};
createScene().then(sceneToRender => {
engine.runRenderLoop(() => sceneToRender.render());
});
PS:代码逻辑是先加载兔子模型,然后当兔子被点击后会随机变成一种颜色,并把兔子的y和z位置值加0.1
接着把之前生成的3D兔子模型通过WinSCP传输到public目录下,最后重新运行“npm run dev”,此时网页会自动刷新,等待一会就可以看到如下图像,
可以看到兔子模型已经加载出来了,颜色也是和之前在Blender里设置的一样。页面右下角有个眼镜图标,是用来进入WebVR的。
3. 体验WebVR
点击页面右下角那个眼镜图标,此时会启动本地电脑里的混合现实门户,等待一会会看到如下界面,
表示已经进入WebVR中了,手柄是通过鼠标来模拟,移动鼠标就可以移动手柄,然后把手柄的聚焦点移动到兔子上,如下图中的兔子身上的白色斑点,
此时按一下空格(表示对目标进行选中一次),可以看到兔子的颜色和位置都发生了变化,如下图,
和代码逻辑是一致的,而且很流畅。
如果想退出VR,可以点击页面右下角的EXIT,
然后又变成了眼镜图标,
4. 测试大的3D模型
由于兔子模型比较小,不能完全体现云服务器性能,于是从斯坦福的模型网站上找到龙模型并下载,
按照“二 生成3D模型”里的步骤把龙模型导入Blender里,
同样翻转90度,放大5倍并添加材质,最后变成如下这样,
然后导出为glTF2.0格式,模型大小是93M,是之前兔子模型的13倍多点,
最后使用WinSCP把该模型传到云耀云服务器L实例上,同时修改app.ts把模型名字改成dragon.gltf,然后保存,此时页面会刷新,变成如下,
点击右下角的眼镜图标进入混合现实门户,
同样把手柄的聚焦点指向这条龙,然后按空格选中它,可以看到颜色和位置都发生了变化,
同样非常流畅。
五 总结
本文讲述了如何在华为云云耀云服务器L实例上运行WebVR,并通过Win10自带的混合现实门户来体验WebVR。
总体感觉不错,但是运行时发现3D模型加载很慢,兔子模型(7M多)要等十几秒才加载出来,而龙模型(93M)则要接近1分钟才能加载出来,应该是和云服务器带宽有关,本人创建的云耀云服务器L实例的带宽是3Mbit/s,如果服务器带宽比较大就会加载比较快。
模型加载出来之后就非常流畅了,再进入混合现实门户后也依然丝滑,与本地运行基本感觉不到差别。
最后,希望越来越多的WebVR应用可以部署在云耀云服务器L上,这也是未来的发展趋势。