1. 程式人生 > >WebGPU學習(四):Alpha To Coverage

WebGPU學習(四):Alpha To Coverage

大家好,本文學習與MSAA相關的Alpha To Coverage以及在WebGPU中的實現。

上一篇博文
WebGPU學習(三):MSAA

學習Alpha To Coverage

前置知識

  • WebGPU學習(三):MSAA
  • 一個fragment對應一個畫素

介紹

開啟了MSAA和Alpha To Coverage後,fragment的alpha值(fragment shader輸出的顏色的alpha值)會影響該fragment對應畫素的取樣點是否被覆蓋。

動機

參考亂彈紀錄II:Alpha To Coverage:




原理

覆蓋檢測

通過WebGPU學習(三):MSAA對MSAA原理的介紹,我們知道gpu要經過覆蓋檢測的步驟,來決定哪些取樣點被覆蓋。沒有被覆蓋的取樣點不會進入“解析”步驟。

覆蓋檢測的結果是計算出每個fragment的coverage(覆蓋率)。

根據亂彈紀錄II:Alpha To Coverage 的說法,開啟MSAA後,每個fragment帶了一個新屬性coverage(覆蓋率),它是一個二進位制的bit掩碼mask。

以4X MSAA為例,每個fragment的coverage為xxxx,其中x為0或1。它的每一位對應畫素的一個取樣點sample,0表示該sample沒被覆蓋,1表示被覆蓋。

所以coverage這個掩碼對應了取樣點的覆蓋情況。

如何計算coverage

1.使用者可以設定一個固定的coverage掩碼,這裡命名為FixedSampleMask

2.gpu檢測每個畫素有哪些取樣點被primitive覆蓋,得到該取樣點的coverage掩碼,這裡命名為RasterizerCoverageMask

3.如果開啟了Alpha To Coverage,則會將fragment的alpha值轉換為coverage掩碼,這裡命名為AlphaCoverageMask

轉換的演算法可以參考亂彈紀錄II:Alpha To Coverage :

一個fragment的Alpha值在0~1間,它對應著一個dither mask。還是以4XMSAA為例,這個dither mask也是xxxx的形式,Alpha為0對應了0000,alpha為1對應了1111,至於中間的值的對應關係,OpenGL是交由顯示卡製造商決定的——其實一般就是類似[0~0.249 -> 0000, 0.25~0.499 -> 0001, 0.5~0.749 -> 0011, 0.75~0.99-> 0111]這樣。

4.fragment shader可以輸出該fragment的coverage掩碼,這裡稱為FragShaderSampleMaskOutput

畫素最終的coverage = FixedSampleMask & RasterizerCoverageMask & AlphaCoverageMask & FragShaderSampleMaskOutput
(“&”是邏輯與運算,如0011 & 0010 = 0010)

參考資料

亂彈紀錄II:Alpha To Coverage

WebGPU實現Alpha To Coverage

暫時沒有實現的sample,我們根據WebGPU規範和相關資料,分析下WebGPU如何實現Alpha To Coverage。

  • 在render pipeline descriptor中設定固定的coverage掩碼FixedSampleMask和是否開啟Alpha To Coverage:
dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
...
    unsigned long sampleMask = 0xFFFFFFFF;
    boolean alphaToCoverageEnabled = false;
...    
};

我們注意到sampleMask是unsigned long型別,它是32位的,而coverage應該是二進位制的(如4X MSAA的coverage是4位的二進位制),所以這裡是進行了進位制轉換。

舉例來說:
對於4X MSAA,如果設定sampleMask為0x1(十六進位制),則它轉換為二進位制是0001;
如果設定sampleMask為0x3,則它轉換為二進位制是0010

  • 可以在fragment shader中設定輸出的coverage掩碼FragShaderSampleMaskOutput

根據Investigation: Multisample Coverage,我們知道Vulkan->SPIR-V的fragment shader支援內建的SampleMask變數。

因為Chrome實現的WebGPU也使用SPIR-V作為shader編譯後的位元組碼,所以WebGPU在這點上應該與Vulkan類似。

我沒有搜尋到SPIR-V中關於SampleMask的詳細資料,但是考慮到Chrome實現的WebGPU使用GLSL 4.5,所以我們可以看下它關於gl_SampleMask的說明:

Name
gl_SampleMask — specifies the sample coverage mask for the current fragment
Declaration
out int gl_SampleMask[] ;

我們看到gl_SampleMask的每個元素的型別是32位的,所以也進行了進位制轉換。

又因為它是陣列,所以它支援coverage為超過32位的二進位制(如支援64X MSAA)

用程式碼來說明:

//in fragment shader

gl_SampleMask[0] = 1;   //對於4X MSAA來說,相當於設定該fragment的coverage為0001
//in fragment shader

gl_SampleMask[0] = 2;

gl_SampleMask[1] = 1;   //對於64X MSAA來說,可能相當於設定該fragment的coverage為000...1000...10 (前面的000...1有32位,後面的000...10有32位) (我不能確定這是否正確!)
  • 如果開啟了Alpha To Coverage,則不能在fragment shader中輸出coverage掩碼

  • 如果開啟了Alpha To Coverage,將alpha轉換為掩碼的演算法在不同的瀏覽器中不一樣

參考資料

Investigation: Multisample Coverage
Minutes for GPU Web meeting 2019-04-29
OpenGL->gl_SampleM