像玻璃球這種透明且帶有折射現象的物體如何進行實時渲染?

我現在在BRDF的基礎上直接將計算反射光的函數改成計算折射光的函數,然後根據所得向量在立方體貼圖上進行採樣得到了一個還算看得過去的折射效果,後來想通過btdf來計算折射但是總感覺這個方法似乎是配合光線追蹤法來渲染透明物體的。請問,對於實時渲染,透明物體和不透明物體之間以及透明物體之間應該如何渲染呢?


實時渲染的話,比較經典的做法是參考05年Approximate ray-tracing on the gpu with distance impostors這篇文章,用割線法把折射向量和深度環境貼圖求交,十二年前就可以幾百FPS了。

像這樣:

左一是只用普通環境貼圖的結果,左二是用割線法做一次求交的,中間是做兩次求交的。最右邊傳統ray-trace的結果。

對於一張環境貼圖,因為是從固定點渲染的(下圖的 vec{o} ),所以只有固定點視角的信息,所以取方向 vec{R} 的時候,只能得到 vec{r} 的信息。而計算折射的時候因為像素空間位置(下圖的 vec{x} )可能和那個固定點有一定距離,所以要得到從 vec{x} 發射的方向 vec{R} 的射線與環境的交點(下圖的 vec{q} ),就需要迭代求交。這個演算法就是通過迭代求交得到近似位置 vec{q}vec{l}

Shader的代碼文章里有,摘抄如下,x是當前像素位置,R是射線方向,如果需要算折射的話就是折射方向。函數返回根據深度環境貼圖得到的近似交點(上圖的 vec{l} )。

float3 Hit(float3 x, float3 R, sampler mp) {
float rl = texCUBE(mp, R).a; // |r|
float dp = rl - dot(x, R);
float3 p = x + R * dp;
float ppp = length(p)/texCUBE(mp,p).a;
float dun =0, dov =0, pun = ppp, pov = ppp;
if (ppp &< 1) dun = dp; else dov = dp; float dl = max(dp + rl * (1 - ppp), 0); float3 l = x + R * dl; // iteration for(int i = 0; i &< NITER; i++) { float ddl; float llp = length(l)/texCUBE(mp,l).a; if (llp &< 1) { // undershooting dun = dl; pun = llp; ddl = (dov == 0) ? rl * (1 - llp) : (dl-dov) * (1-llp)/(llp-pov); } else { // overshooting dov = dl; pov = llp; ddl = (dun == 0) ? rl * (1 - llp) : (dl-dun) * (1-llp)/(llp-pun); } dl = max(dl + ddl, 0); // avoid flip l = x + R * dl; } return l; }

有了近似求交,想做透明、半透明應該都不難的樣子。一層深度環境貼圖不夠的話可以做多層。比如二次折射就是像下圖這樣:

  1. 從攝像機光柵化頂點坐標,得到 vec{x}
  2. 根據斯涅爾定律計算方向 vec{R} ,用上面的割線法對從折射物體refractor中心渲染的深度環境貼圖求交,得到近似交點 vec{l}
  3. vec{l} 根據斯涅爾定律計算方向 vec{R}_1 ,對去除了折射物體的深度環境貼圖求交,得到交點 vec{l}_1 ,取該點顏色,用兩次菲涅耳定律或者Schlick的簡化版本算兩個折射率就知道回來多少了。

一般做玻璃材質的話,還要把直接反射部分加上( vec{x} 點求反射方向,用上面割線法做追蹤)才能看。做半透明的話,如果只考慮最簡單的同質散射,把立方體貼圖拿來的顏色做個按距離(下圖中的 |vec{x}-vec{l}| )指數衰減就行了。

這個還有個高級一點的用法就是可以做實時焦散,像下面這樣:

以及實時全局光照,像下面這樣,因為有折射,所以物體陰影的形狀和深淺也會根據折射面而改變:

(摘自Multi-Image Based Photon Tracing for Interactive Global Illumination of Dynamic Scenes)


現在除了光線追蹤(半實時)或有限元方法(不能實時)以外,沒有基於多邊形光柵化流程的準確渲染方法。這主要是因為光柵化流程只能在入射表面計算一次折射,而無法高效地判定出射位置(射線折射之後位置發生了偏移,而偏移量取決於第1次折射角)。

如果只計算入射面的折射現象,或者將忽略偏移量並假定射線出射位置不變,那麼這種近似渲染方案是可以實時的,可以很大程度上騙過人的視覺。


推薦閱讀:

學計算機圖學時,自己寫的軟渲染器代碼結構是否應模仿OpenGL的狀態機模式?
為什麼CryENGINE3引擎號稱最強大、最真實的引擎,但在孤島危機2/3當中的畫面仍然有一種光澤度過強的塑料質感?
Unity中DrawCall和openGL、光柵化等有何內在聯繫,為什麼說DC降低有助於渲染性能優化?
unity走圖形學好,還是轉行做架構?

TAG:渲染 | 計算機圖形學 |