裸眼 3D 是什么效果?

作者:沙因,腾讯 IEG 前端开发工程师

介绍一种裸眼 3D 的实现方式,代码以 web 端为例。

平常我们都是戴着 3D 眼镜才能感受 3D 效果,那裸眼能直接看 3D 么?可以看看下面这个视频:

感兴趣可以扫描这个二维码实际体验下:

以上效果是基于 threejs 封装了个相机组件:

<script
src="https://game.gtimg.cn/images/js/sign/glassfree3d/js/GlassFree3dCamera.js"
></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 意外的具有潜力。

参考资料:

https://www.anxious-bored.com/blog/2018/2/25/theparallaxview-illusion-of-depth-by-3d-head-tracking-on-iphone-x

http://www.songho.ca/opengl/gl_projectionmatrix.html

https://www.bilibili.com/video/av90631060

https://eater.net/quaternions

推荐阅读:

深入理解 MySQL 索引底层原理

腾讯高性能图计算框架Plato及其算法应用

本文分享自微信公众号 - 腾讯技术工程(Tencent_TEG),作者:沙因

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • WEB加速,协议先行 ( 上)

    本文分享了 STGW 及腾讯云 CLB 在 WEB 协议优化过程中的实践经验,并对WEB协议的未来进行了探讨分析。

    腾讯技术工程官方号
  • 从零开始的C++网络编程

    ? 导语:本文主要介绍如何从零开始搭建简单的C++客户端/服务器,并进行简单的讲解和基础的压力测试演示。该文章相对比较入门,主要面向了解计算机网络但未接触...

    腾讯技术工程官方号
  • 基于语义关联的中文查询纠错框架

    本文作者:内部搜索平台部推荐中心组长,2009年加入腾讯,从事搜索相关工作,包括社区问答,网页搜索,音乐、视频、应用宝等项目,方向涉及query理解,文本相关性...

    腾讯技术工程官方号
  • swift Dictionary

    字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。字典中的数据项并没有具体顺序,我们在需要通...

    赵哥窟
  • 软件工程期末考试复习(四) 设计原理高内聚,低耦合的系统有什么好处呢? 面向数据流的设计方法

    总体设计的基本且的就是回答“概括地说,系统应该如何实现”这个问题。因此,总体设计又称为概要设计或初步设计。通过这个阶段的工作将划分出组成系统的物理元素程序、文件...

    用户2417870
  • 话题 | 百度 PaddlePaddle 1.0 正式发布啦,感觉百度最近宣传势头挺猛的呀,有小伙伴用吗~

    蹬蹬蹬蹬!对于“百度 PaddlePaddle 1.0 正式发布”这个话题,你有什么想法呢?

    AI研习社
  • 问答 | 百度 PaddlePaddle 1.0 正式发布啦,感觉百度最近宣传势头挺猛的呀,有小伙伴用吗~

    社长为你推荐来自 AI 研习社问答社区的精华问答。如有你也有问题,欢迎进社区提问。

    AI科技评论
  • 不开玩笑!皮皮虾为人类自动驾驶的危险检测做出了巨大贡献

    通过模仿皮皮虾的视野,即使在最具挑战性的道路条件下,新型生物启发式相机也能发现危险。

    镁客网
  • 自己设计一个 JAVA + MyBatis 解析实体类多表通用查询

    工具人
  • PHP留言板

    我学习php过程中自己制作的一个超级简单的留言板(没有使用数据库) 文件结构:index.html(留言页面)、post.php(将留言内容写入文件保存)、d...

    苦咖啡

扫码关注云+社区

领取腾讯云代金券