上节课已经大致讲解了webvr。所以我们这里不再赘述。
那么我们直接进入正题,首先了解一下 VREffect.js 它是做什么的。
VR 分屏器,这是 three.js 的一个场景分屏的渲染器,提供戴上 VR 头显的显示方式,VREffect.js 重新创建了左右两个相机,
对场景做二次渲染,产生双屏效果。
VREffect库的使用有一下两点
一、把渲染器传入VREffect然后设置VREffect的大小与渲染器一致。
// 将VR立体渲染应用于渲染器。
effect = new THREE.VREffect(renderer);
effect.setSize(renderer.domElement.clientWidth, renderer.domElement.clientHeight, false);
二、在循环中使用VREffect渲染场景
// 渲染场景。
effect.render(scene, camera);
下面我们来看看 VRControls.js
VR 控制器,是 three.js 的一个相机控制器对象,引入 VRcontrols.js 可以根据用户在空间的朝向渲染场景,它通过调用
WebVR API的 orientation 值控制 camera 的 rotation 属性。
VRControls.js的使用很简单:
// 将虚拟现实耳机位置数据应用于相机。
controls = new THREE.VRControls(camera);
然后在循环中更新它
// 更新VR耳机位置并应用于相机。
controls.update();
以上就是这两个库的使用,如果你想在你的项目中使用它们,还需要和下面的WebVR API结合使用:
// 接口的Navigator.getVRDisplays()方法返回一个promise,该promise解析为VRDisplay表示连接到计算机的任何可用VR显示的对象数组。
navigator.getVRDisplays().then(function(vrDisplays) {
// 如果我们有本地显示器,或者我们有一个cardboardvrdisplay
// 可用VR设备大于0,获取第一个设备应用于camera
if (vrDisplays.length) {
vrDisplay = vrDisplays[0];
// 将虚拟现实耳机位置数据应用于相机。
controls = new THREE.VRControls(camera);
// 开启渲染循环
vrDisplay.requestAnimationFrame(animate);
console.log('开启VR模式');
}// 否则,我们提供桌面视图的控件
else {
controls = new THREE.OrbitControls(camera);
controls.target.set(0, 0, -1);
// 禁用“进入虚拟现实”按钮
var enterVRButton = document.querySelector('#vr');
enterVRButton.disabled = true;
// 开启渲染循环
requestAnimationFrame(animate);
console.log('开启正常模式');
}
});
我们来分析上面的代码:
接口Navigator.getVRDisplays()方法返回一个promise,
该promise解析为VRDisplay表示连接到计算机的任何可用VR显示的对象数组。
简单的说就是获取可用的VR设备,以数组形式返回。详情请移步JavaScript MDN
细心的小伙伴可能发现了。这个if中有两个 requestAnimationFrame
咱们先来说第一个
vrDisplay.requestAnimationFrame(animate);
官方的解释是:
接口的requestAnimationFrame()方法VRDisplay是一个Window.requestAnimationFrame包含回调函数的特殊实现,
每次VRDisplay呈现一个新的表示帧时都会调用该函数:
当VRDisplay没有呈现场景时,这在功能上等同于Window.requestAnimationFrame。
当VRDisplay呈现时,回调以其本机刷新率被调用。
第二个嘛就是 window.requestAnimationFrame
该window.requestAnimationFrame()方法告诉浏览器您希望执行动画并请求浏览器调用指定的函数以在下次重绘之前更新动画。
该方法将回调作为在重绘之前调用的参数。
好了。重点知识就这么多。下面是一个完整的示例:
index.html
WEBVR
Main.js
var scene,camera,renderer,controls,cube;
var WIDTH,HEIGHT;
var effect,vrDisplay,canvas;
// 使浏览器支持webvr API
function initPolyfill(){
// 从URL获取配置
var config = (function() {
var config = {};
var q = window.location.search.substring(1);
if (q === '') {
return config;
}
var params = q.split('&');
var param, name, value;
for (var i = 0; i < params.length; i++) {
param = params[i].split('=');
name = param[0];
value = param[1];
// 所有配置值都是布尔值或浮点值
config[name] = value === 'true' ? true : value === 'false' ? false : parseFloat(value);
}
return config;
}
)();
var polyfill = new WebVRPolyfill(config);
console.log("使用WebVR PolyFill版本 " + WebVRPolyfill.version + " 配置: " + JSON.stringify(config));
}
// 创建渲染器 并将 VR立体渲染应用于渲染器
function initRender(){
renderer = new THREE.WebGLRenderer({
antialias:true,
});
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
canvas = renderer.domElement;
document.body.appendChild(canvas);
// 将VR立体渲染应用于渲染器。
effect = new THREE.VREffect(renderer);
effect.setSize(canvas.clientWidth, canvas.clientHeight, false);
}
// 创建场景
function initScene(){
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
}
// 创建相机
function initCamera(){
camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,10000);
scene.add(camera);
}
// 创建灯光
function initLight(){
// 环境光
var light = new THREE.AmbientLight();
scene.add(light);
// 方向光
light = new THREE.DirectionalLight();
light.position.set(0,0,1);
scene.add(light);
}
// 创建对象
function initObject(){
// 正方体
var geometry = new THREE.BoxGeometry( 0.5, 0.5, 0.5 );
var material = new THREE.MeshNormalMaterial();
cube = new THREE.Mesh( geometry, material );
cube.position.z = -1;
scene.add( cube );
// 创建十字线 相机的视线中心
var reticle = new THREE.Mesh(new THREE.RingBufferGeometry(0.005,0.01,15),new THREE.MeshBasicMaterial({
color: 0xffffff
}));
reticle.position.z = -0.5;
camera.add(reticle);
}
// 获取VR 设备
function initVrdisplay(){
// 接口的Navigator.getVRDisplays()方法返回一个promise,该promise解析为VRDisplay表示连接到计算机的任何可用VR显示的对象数组。
navigator.getVRDisplays().then(function(vrDisplays) {
// 如果我们有本地显示器,或者我们有一个cardboardvrdisplay
// 可用VR设备大于0,获取第一个设备应用于camera
if (vrDisplays.length) {
vrDisplay = vrDisplays[0];
// 将虚拟现实耳机位置数据应用于相机。
controls = new THREE.VRControls(camera);
// 开启渲染循环
vrDisplay.requestAnimationFrame(animate);
console.log('开启VR模式');
}// 否则,我们提供桌面视图的控件
else {
controls = new THREE.OrbitControls(camera);
controls.target.set(0, 0, -1);
// 禁用“进入虚拟现实”按钮
var enterVRButton = document.querySelector('#vr');
enterVRButton.disabled = true;
// 开启渲染循环
requestAnimationFrame(animate);
console.log('开启正常模式');
}
});
// 当我们调整大小和更改模式时,调整WebGL画布的大小。
window.addEventListener('resize', onResize);
// 点击VR (WebVR/Mobile only)按钮进入VR模式
document.querySelector('button#vr').addEventListener('click', function() {
//requestPresent()方法 VRDisplay开始VRDisplay呈现场景
vrDisplay.requestPresent([{
source: renderer.domElement
}]).then(function(){
console.log('使用VRDisplay呈现场景');
});
});
}
// 入口函数
function initThree(){
initPolyfill();
initRender();
initScene();
initCamera();
initLight();
initObject();
initVrdisplay();
}
// 循环
function animate(){
cube.rotation.y += 0.01;
// 更新VR耳机位置并应用于相机。
controls.update();
// 渲染场景。
effect.render(scene, camera);
// 保持循环;如果使用VRdisplay,则调用其requestAnimationFrame,否则调用window.requestAnimationFrame。
if (vrDisplay) {
vrDisplay.requestAnimationFrame(animate);
} else {
requestAnimationFrame(animate);
}
}
function onResize() {
// 延迟确保浏览器有机会布局
// 更新页面的clientwidth / clientheight。
// 这个问题在iOS下尤其突出。
if (!onResize.resizeDelay) {
onResize.resizeDelay = setTimeout(function() {
onResize.resizeDelay = null;
console.log('Resizing to %s x %s.', canvas.clientWidth, canvas.clientHeight);
effect.setSize(canvas.clientWidth, canvas.clientHeight, false);
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}, 250);
}
}