天天看點

支付寶那些以假亂真的渲染效果,是怎麼來的?

小叽導讀:AR技術在增強現實方向上的不斷努力,目的都是為了讓虛拟模型能夠更好地與現實環境融為一體,真假分不清楚,達到真實感渲染的體驗,進而創造出更好的場景,本文就圍繞這種虛拟與真實環境結合的環境紋理渲染技術實作探索來開展。

背景

先來看兩個AR效果,真實地面打開效果:

支付寶那些以假亂真的渲染效果,是怎麼來的?

地面被挖開效果:

支付寶那些以假亂真的渲染效果,是怎麼來的?

可以看到真實世界的"地面"作用了虛拟模型的動畫效果慢慢打開或鏟開,進而再做出虛拟模型的一系列動畫渲染效果,給使用者一種真實的地面被打開或挖開的效果。那麼如何實作這種酷炫的渲染效果呢?

實戰初探分步走

第一步:制作簡單模型

為了實作這種與地面融合的渲染效果,我們制作一個簡單的模型及動畫:一個立方體從地面鑽出,然後頂部螢幕從中間慢慢左右打開。

使用Maya簡單制作一個模型,這裡用最普通的立方體模型,讓它的最頂端的平面節點位于水準面之下(模型坐标系),同時緊貼在其上增加兩個半平面節點用于左右打開效果的動畫實作,為了效果看起來不是那麼挫(實際最終效果還是很挫,我不是一個合格的UED),在兩個半平面節點與立方體頂端平面之間增加一個視訊節點,這樣當"地面"打開時,從地面鑽出個箱子,有個視訊在箱子上面播放。

支付寶那些以假亂真的渲染效果,是怎麼來的?
支付寶那些以假亂真的渲染效果,是怎麼來的?

第二步:節點紋理與實景紋理綁定

模型資源已經準備好了,是以為了能與實景融合,我們将相機實時采集的圖像每一幀不斷的渲染至"模型的平面"(模型節點)所在的紋理上,也就是之前模型制作時的用于左右分開的模型節點上,OpenGL部分核心代碼如下:

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _dstTexture, 0);

_dstTexture為擷取到的用于左右分開動畫的節點目标紋理,我們将相機幀渲染至此紋理,至此,我們可以看到這樣的效果:

支付寶那些以假亂真的渲染效果,是怎麼來的?

整個場景都被渲染上來了,我們最終想要的,僅僅是此時該平面真正占據的實景部分,即立方體頂部平面所占據的一小塊實景部分,那麼意味着,我們需要對紋理進行“裁剪處理”。

第三步:對實景紋理進行裁剪

如何進行準确的裁剪?并且随着模型的旋轉、平移、縮放都能夠不斷更新且準确地裁剪到對應的紋理區域?

那麼我們首先想到的是模型的頂部平面在未結合環境紋理是怎麼被渲染到螢幕的?

首先模型通過mesh檔案提取出模型頂點資訊,渲染管線會在所有頂點着色器運作後,将我們所有可見的頂點都變為标準化裝置坐标。也就是說,每個頂點的x,y,z坐标都應該在-1.0到1.0之間,超出這個坐标範圍的頂點都将不可見,頂點坐标經過不同的坐标系變換類似流水線的方式拿到标準化裝置坐标,即經過如下不同的坐标系空間:

  • 局部空間(Local Space,或者稱為物體空間(Object Space))
  • 世界空間(World Space)
  • 觀察空間(View Space,或者稱為視覺空間(Eye Space))
  • 裁剪空間(Clip Space)
  • 螢幕空間(Screen Space)

這些就是我們将所有頂點轉換為片段之前,頂點需要處于的不同的狀态,即大家熟知的MVP。

如下圖的變換過程:

支付寶那些以假亂真的渲染效果,是怎麼來的?

局部坐标是對象相對于局部原點的坐标,也是對象開始的坐标。即該例中立方體的上平面在局部坐标中的位置。

将局部坐标轉換為世界坐标,世界坐标是作為一個更大空間範圍的坐标系統。這些坐标是相對于世界的原點的。

接下來我們将世界坐标轉換為觀察坐标,觀察坐标是指以錄影機或觀察者的角度觀察的坐标

在将坐标處理到觀察空間之後,我們需要将其投影到裁剪坐标。裁剪坐标是處理-1.0到1.0範圍内并判斷哪些頂點将會出現在螢幕上。

最後,我們需要将裁剪坐标轉換為螢幕坐标,我們将這一過程稱為視口變換(Viewport Transform)。視口變換将位于-1.0到1.0範圍的坐标轉換到由glViewport函數所定義的坐标範圍内。最後轉換的坐标将會送到光栅器,由光栅器将其轉化為片段。

矩陣公式為:

支付寶那些以假亂真的渲染效果,是怎麼來的?

頂點着色器的輸出需要所有的頂點都在裁剪空間内,然後OpenGL在裁剪空間中執行透視劃分進而将它們轉換到标準化裝置坐标,在這個過程中将位置向量的x,y,z分量分别除以向量的齊次w分量;透視劃分是将4維裁剪空間坐标轉換為3維标準化裝置坐标。這一步會在每一個頂點着色器運作的最後被自動執行。OpenGL會使用glViewPort内部的參數來将标準化裝置坐标映射到螢幕坐标,每個坐标都關聯了一個螢幕上的點。

支付寶那些以假亂真的渲染效果,是怎麼來的?

頂點坐标資訊通過以上的MVP變換最終被賦予頂點着色器中的gl_Position且OpenGL将會自動進行透視劃分和裁剪。

通過以上過程,我們已經知道了箱子的頂部平面是經過 MVP矩陣變換+透視劃分的頂點着色器處理送給光栅化處理的了,那麼我們可以将相同的處理作用給環境紋理,進而确定了我們紋理應該裁剪得到哪一部分環境紋理。但這裡有一個問題,我們通過MVP矩陣變換+透視劃分拿到的坐标區域是OpenGL坐标系下的,而紋理映射是UV坐标下的。

20180919172334.png

OpenGL坐标系是從(-1,-1)到(1,1)而UV坐标是(0,0)到(1,1)

支付寶那些以假亂真的渲染效果,是怎麼來的?

是以我們需要對結果坐标position從OpenGL變換至UV坐标系下:

x = position.x/2.0 + 0.5, 
y = position.y/2.0 + 0.5           

至此,我們通過最初的頂點資訊經過

  • MVP矩陣變換
  • 透視劃分
  • OpenGL至UV坐标系變換

拿到了需要裁剪的紋理坐标。

通過以上處理最終作用到紋理坐标v_texCoord上,看下效果:

支付寶那些以假亂真的渲染效果,是怎麼來的?

目前環境紋理渲染效果已經在支付寶10.1.35版本的星寶中使用(星寶入口:支付寶首頁搜尋“AR星寶”生活号→進入“AR星寶”),效果如下:

支付寶那些以假亂真的渲染效果,是怎麼來的?

遇到的問題

在實際的應用中,我們綁定了環境紋理後,便不希望随着模型節點的RT(旋轉、移動)再去更新節點的環境紋理,即不希望環境紋理有更新,進而才能産生真實的“地面被挖開”等效果。我們通過記下需要停止更新時刻的MVP,後續都使用該MVP去計算紋理裁剪坐标即可。

但是在設計節點動畫時,設計師為了酷炫的動畫效果,會針對要綁定環境紋理的節點做一系列骨骼動畫的處理,當我們對有骨骼動畫的節點做環境紋理處理的時候,遇到環境紋理還是會不斷更新的問題,細究原因,骨骼動畫即是對頂點坐标在MVP處理之前有一系列M的動畫矩陣運算達到動畫效果,那麼,我們要麼去緩存下每個頂點在骨骼動畫前的資訊,面對上萬的頂點資訊,這不太現實,于是,我們做一個取舍,不管有沒有骨骼動畫,在停止更新時都使用未經過處理(骨骼動畫)的position,這樣就避免了骨骼動畫對于環境紋理的影響。

擴充

這種将現實世界環境紋理與虛拟物體結合起來渲染的技術目前應用的場景也較多,比如虛拟球體的環境鏡面反射效果,通過将環境紋理映射到球體表面:

支付寶那些以假亂真的渲染效果,是怎麼來的?
支付寶那些以假亂真的渲染效果,是怎麼來的?

包括WWDC2018蘋果結合ARKit2.0做的環境紋理(Environment Texturing),現實世界環境被渲染在立方體貼圖中,可被用于作為渲染引擎中的反射探針(Reflection Probe)。此反射探針可應用環境紋理在虛拟物體上,這将大大提升了會反射的物體的視覺效果。

支付寶那些以假亂真的渲染效果,是怎麼來的?
支付寶那些以假亂真的渲染效果,是怎麼來的?
支付寶那些以假亂真的渲染效果,是怎麼來的?

繼續閱讀