裸眼 3D 是什么效果?
作者:沙因,騰訊 IEG 前端開發工程師
介紹一種裸眼 3D 的實現方式,代碼以 web 端為例。
平常我們都是戴著 3D 眼鏡才能感受 3D 效果,那裸眼能直接看 3D 么?可以看看下面這個視頻:
感興趣可以掃描這個二維碼實際體驗下:
以上效果是基于 threejs 封裝了個相機組件:
使用 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及其算法應用
總結
以上是生活随笔為你收集整理的裸眼 3D 是什么效果?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何提高自身监控系统的能力?
- 下一篇: 一款 0 门槛轻松易上手的数据可视化工具