混合和透明物體渲染

以前一直沒有很仔細的地思考過Blend這個概念,惟一的映像就是\(C_o = C_s*Alpha+C_d*(1-Alpha)\)。因此在軟件渲染器中也只是理論上支持這個東西,但實際上卻只作了深度測試。如今來仔細看一看這個玩意兒,也記錄一下透明物體渲染的東西。算法

Blending

這部分主要參考DX11龍書Blending一章。
簡單的說,Blending就是使當前顏色做爲混合源\(C_s\)不要直接覆蓋前面一個顏色\(C_d\),而是和前面的一個顏色混合起來,因此叫作混合。這個有助於渲染透明物體(固然這只是其中一種,只是咱們目前最關注這一種而已)。固然對物要求也是有順序的,因此在投入片元的時候要求渲染保序。
在D3D中混合方程以下:
\[C=C_s*F_s @ C_d*F_d\]
其中\(C_s、C_d\)是參與混合的源顏色,和混合目標顏色,\(F_x\)則是混合函數。\(*\)是逐項乘符號,\(@\)則是任意運算符號。
在D3D11中,他可使最大最小加減反減等等。
D3D11中對顏色的RGBA分開設置混合符號和混合函數,RGB爲一個,A爲一個。能夠參考下列代碼,這應該是配置Blending選項,而後再OM階段進行混合。windows

D3D11_BLEND_DESC blendStateDescription;
    ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
    // Create an alpha enabled blend state description.
    blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
    //blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
    blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
    blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
    blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
    blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
    blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
    blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f;

    g_env->d3d_device->CreateBlendState(&blendStateDescription, &m_alphaEnableBlendingState);

具體的用法不是重點就不說了,查文檔就知道了。ide

通常來講,混合只管RGB值而無論A值,除非在back buffer中A有特別的用途。函數

透明物體渲染

Blending渲染

混合能夠作不少事情,可是這裏只關注透明物體渲染。公式就是我最開始寫的公式。只是在使用公式混合以前要對他們所有按照由遠到近排序,而後從後往前繪製,並且在繪製全部透明物體以前必須先繪製全部不透明物體。這種從後往前的方法叫作OVER Blending。
這種混合操做有各類稀奇古怪的優化,好比在D3D11Shader中使用clip函數等,這個函數能夠致使指定的像素在結束shader以後直接跳過OM階段省事很多,因此在alpha接近0的時候就所有discard掉。post

Using a DISCARD instruction in most circumstances will break EarlyZ (more on this later). However, if you’re not writing to depth (which is usually the case when alpha blending is enabled) it’s okay to use it and still have EarlyZ depth testing!
還有一種就是使用一個低的分辨率(通常一半)來渲染目標作混合的。
另外還有一種叫作UNDER Blending的從前日後渲染的方法。這種方法把公式拆爲
\[C_d = A_d(A_s*C_s)+C_d\]
\[A_d = 0 + (1-A_s)A_d\]
\(A_d\)初始化爲1.0。
最後,須要把算出了的顏色和背景顏色\(C_{bg}\)混合:
\[C_d = A_d*C_{bg}+C_d\]性能

OIT

傳統的ODT透明物體渲染方法通常是先渲染不透明對象,而後對透明的對象按照深度排序,而後進行渲染。這種方法對於交叉物體的渲染是有問題的。爲了處理更通常的透明物體渲染,就要使用OIT方法。
下面有幾個實時方法:測試

depth peeling[1]

渲染正常視圖,將這一層深度剝離,渲染下一層深度的視圖,以此類推。渲染每個深度的圖像,而後把全部的深度渲染結果混合起來。
這是一個多pass算法,具體的pass數和場景最大的深度複雜度有關。
還有一種拓展是Dual Depth Peeling[2]。這種算法在一個pass內剝離最近和最遠的兩個深度,若是有中間深度就按照規定來處理。最後對於這兩種剝離,分別使用OVER和UNDER的blending方法來進行blending。優化

Per-Pixel Linked Lists[3]

這種方法利用SM5.0(這算是一個限制吧)的structured buffers和atomic operations[4]來創建每一個像素的鏈表,而後對鏈表進行深度排序,最後按照排序結果使用Ray Marching方法來進行渲染。
下圖大概說明了這種算法:
this

Start Offset Buffer對應於每一個像素,存放鏈表頭。
Fragment and Link Buffer是屏幕的n倍大,每一個fragment的輻射度,體積衰減係數,物體的反照度,朝向信息,攝像機距離等數據都會打包成一個Structed Buffer而後扔到Fragment and Link Buffer中,這些單獨的structed buffer用一個成員儲存Fragment and Link Buffer中的索引連接下一個structed buffer。具體的能夠參考這個slider[5]。
排序原論文經過權衡考慮使用的插排。
這個算法的理論性能比Depth peeling高一些,由於後者要N個pass全部pass都要處理全部的fragment,而這個算法只要一個pass就可。可是,Fragment and Link Buffer的大小沒法事先比較精確的控制,這可能會致使空間浪費或者溢出。
值得注意的是:這種相似的方法產生的數據(這裏的鏈表)涵蓋整個場景的信息,咱們可使用這些數據來作不少事,好比:體素化甚至光線跟蹤。atom

Ref

【1】Interactive Order-Independent Transparency
【2】Order Independent Transparency with Dual Depth Peeling
【3】Order Independent Transparency with Per-Pixel Linked Lists
【4】https://msdn.microsoft.com/en-us/library/windows/desktop/ff476335(v=vs.85).aspx#Structured_Buffer
【5】Oit And Indirect Illumination Using Dx11 Linked Lists

相關文章
相關標籤/搜索