前言:
如今朋友们对“camerajs”大约比较重视,朋友们都想要剖析一些“camerajs”的相关内容。那么小编同时在网络上网罗了一些关于“camerajs””的相关资讯,希望兄弟们能喜欢,朋友们快快来了解一下吧!介绍一种裸眼3D的实现方式,代码以web端为例。
先看下裸眼3d的效果
视频加载中...
如果感兴趣可以扫描这个二维码实际体验下:
以上效果是基于threejs封装了个相机组件:
<scriptsrc=";></script>new THREE.GlassFree3dCamera(x, y, z, width, height);
使用GlassFree3dCamera代替正常的相机,其中xyz为裸眼3d相机的坐标,width,height为投影平面的宽高。
实现原理
这种裸眼3d实际上是基于一种视觉误差产生的,与传统的双眼产生的不同的图像差形成距离感不同,这种裸眼是依赖3d的“离轴投影”,离轴投影将产生“非对称相机”视锥体。
不过离轴投影与非对称相机并不是已有的专业名词,这是TheParallaxView作者提出的一个概念。
视频中,作者利用ARkit的faceid功能,定位到眼睛的位置,然后将裸眼3d的相机位置替代到人眼球位置。
除去ARkit的功能,这个效果的核心技术在其实现的“非对称的镜头”上。当时觉得这个看起来不难,就尝试的实现了一下,将传统相机的轴锁定(lookAt)在“盒子”的正中心。
传统相机效果:
虽然也有“立体感”,但那是平常我们常见的“全景”专题的3d。
实际上的裸眼3d效果应该是下面这种:
“盒子”的四个角始终“粘”在画面的四个角上。
为什么会有这种区别?
首先,我们要了解3d相机的工作机制。
3d相机机制
3d相机的算法核心是投影矩阵:
在一个视锥体内的3d对象,通过投影矩阵渲染到平面上。
three.js的投影矩阵:
var te = [];var x = 2 \* near / ( right - left );var y = 2 \* near / ( top - bottom );var a = ( right + left ) / ( right - left );var b = ( top + bottom ) / ( top - bottom );var c = - ( far + near ) / ( far - near );var d = - 2 \* far \* near / ( far - near );te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
具体投影矩阵的算法,可以查看文章最后的参考资料。
其中,相机与视锥截面中心的连线垂直于视锥截面。
投影矩阵可以渲染出红色方块的纵深,但是当相机旋转后将会出现方块的边“溢出”屏幕的情况:
而正常情况下,我们看一个现实中的3d盒子是这样的视角:
这时候的投影是一个不规则的四边形,直接计算这个四边形的范围是很困难的。
而这个看似非常难以实现的效果,实际上转换一下思维就变得很简单了,我们会卡在这一步的原因就是先入为主的认为,视锥体一定需要是一个正锥体。
但是,只要我们直接平移投影矩阵,就可以创造出偏离轴心的投影矩阵。
该方案的裸眼3d技术核心就是这个“离轴投影”的算法。
实际做法只要在投影矩阵计算的时候,输入指定区域的视锥参数即可。了解投影矩阵的前提下,视锥移轴并没有新的算法,仅仅参数不同,但是这个思维转换在这过程中却是很重要的一环。关于投影矩阵的详细介绍可以参考文末的参考资料。
完成裸眼3d相机后,只要对相机的位置进行操作绑定即可。
如TheParallaxView作者,就是将相机的位置与人的眼睛位置通过ARkit进行绑定:
glassfree3dcamera组件中,添加了touch事件进行绑定,通过下面语句开启点击事件。
camera.initMove();
当然,这种裸眼3d效果,一般情况下肯定是需要搭配陀螺仪进行使用,于是一开始我也简单的绑定了陀螺仪效果,因为感觉那似乎不是很困难。
camera.initDeviceOrientation();
按照平时的陀螺仪绑定的方式,一直无法实现“真实”的裸眼3d效果。
这个效果是合成的,文末有链接。后来发现,这是裸眼3d效果的第二个难点。
手机的朝向与陀螺仪
手机陀螺仪关于参数解释的几张图:
这几张图很容易让人产生误解,当然不是说这几张图是错的。
首先,alpha,beta,gamma的值是以地球坐标为基准的,其中,alpha甚至与手机的朝向没有关系。也就是说,alpha图也可以画成这样:
这几张手机转动的都是alpha角,而不是只有当手机绕着垂直于屏幕的z轴旋转才触发alpha角。beta角是手机的y轴与地面的角度值:
gamma角是手机的x轴与地面的角度值:
alpha与手机的轴无关,beta,gamma值与手机当前yx轴的位置相关,而另外一个很容易让人误解的就是谷歌开发者工具里的sensors:
当手机横置的时候,出现alpha:0,beta:90,gamma:-90。
实际上,手机里的陀螺仪是不可能同时出现这3个数值的,因为beta:90时,意味着手机垂直于地面,此时gamma必然平行与地面,所以gamma值为0。
而sensors里出现这个值,并不是陀螺仪返回的值,实际上反而是欧拉角输入的值。
上面的值表示,(比如按yxz方向)beta转过90度,此时手机竖屏直立,然后alpha角不动,接着gamma转-90度,手机从竖屏直立横躺下,到达了现在这种状态。
当前这个状态的陀螺仪返回值应该是alpha:0,beta:0,gamma:-90。
了解了陀螺仪角度真正含义后,我们就可以把陀螺仪返回的角度值,先转为欧拉角,再计算四元数(避免万向节锁):
var quaternion = new THREE.Quaternion();var euler= new THREE.Euler();euler.set( beta, alpha, - gamma, 'YXZ' );quaternion.setFromEuler( euler );
四元数可以通过四维投影到三维空间的球体来理解,具体四元数的对应关系可以查看Visualizing quaternions
通过四元数记录手机选择角度,然后将裸眼3d相机位置按照对应转动角度反向转动,即可实现陀螺仪操控的裸眼:
假设相机的初始位置是p1,当手机旋转q1值时,此时相机位置在p2处,但是相对手机来说相机依然是在手机的正前方,所以,相机需要逆向转动q1,从p2移到p1,其中p2即为一开始的p1值。
用户视角:
裸眼3d只计算转动值,所以还需要初始化转动前的角度值,即提前记录p1位置。
结语:
裸眼3d的效果很大程度需要一个专门定制的模型以及交互引导(手机拿在手上慢慢转)才能最大限度发挥其效果,目前这项技术还没有具体的活动落地,但是“视频版本”的裸眼3d效果却频频的在人们的信息流中脱颖而出,也许一个可以“玩”的裸眼3d意外的具有潜力。
参考资料:
作者:signhuang,腾讯 IEG 前端开发工程师
更多干货,尽在 腾讯技术
标签: #camerajs