日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS之深入解析预乘透明度Premultiplied Alpha

發布時間:2024/5/21 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS之深入解析预乘透明度Premultiplied Alpha 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

  • Premultiplied Alpha 的概念,做過游戲開發的應該都知道,Xcode 的工程選項里有一項 Compress PNG Files,會對 PNG 進行 Premultiplied Alpha,Texture Packer 中也有Premultiplied Alpha 的選項。那么,Premultiplied Alpha 到底是什么呢?
  • 在 Alpha Blending: To Pre or Not To Pre 一文中,詳情地闡明了 Premultiplied Alpha 的相關解釋,如果還需要深入理解的可以閱讀《Real Time Rendering》這本書。

二、Alpha Blending

① Alpha Blending 的原理

  • 在圖形學中,Alpha 指的是除了顏色的三個分量(RGB)外的第四個分量:透明度。因此一個真彩色(指利用 RGB 分量合成顏色)的像素就變成由四個分量組成:R、G、B、A。我們這里討論,設 R、G、B、A 均為從 0 到 1 的值,其中 Alpha = 0 為完全透明,Alpha = 1 為完全覆蓋,中間的數值代表半透明,這樣的設定是為了能使本文獨立于顯示硬件,我們把諸如(R,G,B,A)這樣的東西稱為四元組。一個這樣的四元組代表一個由 RA、GA、B*A 組合而成的顏色。
  • 有一點重要的是,要清楚分辨如下兩個關鍵像素的意義:
黑色 = (0,0,0,1)完全透明 = (0,0,0,0
  • 那么,如何根據 Alpha 通道數據進行混合的算法呢?
    • 簡單地,只需要把需要組合的顏色計算出不含 Alpha 分量的原始 RGB 分量然后相加便可,比如現在有兩幅圖象,分別稱為圖象 A 和圖象 B,由這兩幅圖象組合而成的圖象稱為 C,則有如下的四元組:
A: (Ra,Ga,Ba,Alpha_a)B: (Rb, Gb, Bb, Alpha_b)
    • 以及組合后的 RGB 三元組:
C: (Rc, Gc, Bc)
    • 那么:
Rc = Ra * Alpha_a + Rb * Alpha_b Gc = Ga * Alpha_a + Gb * Alpha_b Bc = Ba * Alpha_a + Bb * Alpha_b
    • 便可得出混合后的顏色。如果有多幅圖像需要混合,則按照以上方法兩幅兩幅地進行混合。

② Alpha Blending 的混合公式

  • 最常見的像素表示格式是 RGBA8888 即 (r, g, b, a),每個通道 8 位,0255。例如紅色 60% 透明度就是(255, 0, 0, 153),為了表示方便,Alpha 通道一般記成正規化后的 0~1 的浮點數,也就是(255, 0, 0, 0.6)。而 Premultiplied Alpha 則是把 RGB 通道乘以透明度也就是(r * a, g * a, b * a, a),50% 透明紅色就變成了(153, 0, 0, 0.6)。
  • 透明通道在渲染的時候通過 Alpha Blending 產生作用,如果一個透明度為 as 的顏色 Cs 渲染到顏色 Cd 上,混合后的顏色通過以下公式計算:

  • 以 60% 透明的紅色渲染到白色背景為例:

  • 也就是說,從視覺上(255, 0, 0, 0.6)渲染到白色背景上和(255, 102, 102)是同一個顏色。如果顏色以 Premultiplied Alpha 形式存儲,也就是 Cs 已經乘以透明度了,所以混合公式變成:

三、為什么要 Premultiplied Alpha?

  • Premultiplied Alpha 后的像素格式變得不直觀,因為在畫圖的時候都是先從調色板中選出一個 RGB 顏色,再單獨設置透明度,如果 RGB 乘以透明度就搞不清楚原色是什么。
  • 從前面的 Alpha Blending 公式可以看出,Premultiplied Alpha 之后,混合的時候可以少一次乘法,這可以提高一些效率,但這并不是最主要的原因,最主要的原因是:沒有 Premultiplied Alpha 的紋理無法進行 Texture Filtering(除非使用最近鄰插值)。
  • 以最常見的 filtering 方式線性插值為例,一個寬 2px 高 1px 的圖片,左邊的像素是紅色,右邊是綠色 10% 透明度,如果把這個圖片縮放到 1x1 的大小,那么縮放后 1 像素的顏色就是左右兩個像素線性插值的結果,也就是把兩個像素各個通道加起來除以2,如果使用沒有 Premultiplied Alpha 的顏色進行插值,那么結果就是:
((255, 0, 0, 1) + (0, 255, 0, 0.1)) * 0.5 = (127, 127, 0, 0.55)
  • 如果綠色 Premultiplied Alpha,也就是(0, 255 * 0.1, 0, 0.1),和紅色混合后:
((255, 0, 0, 1) + (0, 25, 0, 0.1)) * 0.5 = (127, 25, 0, 0.55)
  • Premultiplied Alpha 最重要的意義是使得帶透明度圖片紋理可以正常的進行線性插值,這樣旋轉、縮放或者非整數的紋理坐標才能正常顯示,否則就會像上面的例子一樣,在透明像素邊緣附近產生奇怪的顏色。

四、紋理處理

  • 使用的 PNG 圖片紋理,一般是不會 Premultiplied Alpha 的。游戲引擎在載入 PNG 紋理后會手動處理,然后再 glTexImage2D 傳給 GPU,比如 Cocos2D-x 中的 CCImage::premultipliedAlpha:
void Image::premultipliedAlpha() {unsigned int* fourBytes = (unsigned int*)_data;for (int i = 0; i < _width * _height; i++) {unsigned char* p = _data + i * 4;fourBytes[i] = CC_RGB_PREMULTIPLY_ALPHA(p[0], p[1], p[2], p[3]);} _hasPremultipliedAlpha = true; }
  • 而 GPU 專用的紋理格式,比如 PVR、ETC 一般在生成紋理都是默認 Premultiplied Alpha 的,這些格式一般是 GPU 硬解碼,引擎用 CPU 處理會很慢。
  • 總之 glTexImage2D 傳給 GPU 的紋理數據最好都是 Multiplied Alpha 的,要么在生成紋理時由紋理工具 Pre-multiplied,要么載入紋理后由游戲引擎或 UI 框架 Post-multiplied。

五、iOS 中的 Premultiplied Alpha

  • Core Graphics 的 CGImage.h 對圖像透明度信息有如下定義:
typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {// .../* For example, premultiplied RGBA */kCGImageAlphaPremultipliedLast, /* For example, premultiplied ARGB */ kCGImageAlphaPremultipliedFirst, // ... };
  • 預乘透明度(Premultiplied Alpha)圖像簡單地說,即每個顏色分量都乘以 alpha 通道值作為結果值:
color.rgb *= color.alpha
  • 為什么關注預乘透明度圖像?微信團隊因 AR 搶紅包場景的 OpenGL 混色結果出錯引起注意:
Premultiplied alpha is better than conventional blending for several reasons:It works properly when filtering alpha cutouts _(see below)_It works properly when doing image composition _(stay tuned for my next post)_It is a superset of both conventional and additive blending. If you set alpha to zero while RGB is non zero, you get an additive blend. This can be handy for particle systems that want to smoothly transition from additive glowing sparks to dark pieces of soot as the particles age.It plays nice with DXT compression, which only supports transparent pixels with an RGB of zero.

六、理解 Premultiplied Alpha 的 Tips

① 理解 Alpha 混合

  • 最常見的混合是“over”混合,假設已經有一張 RenderTexture,RT 上像素的 RGB 稱其為 RGBdst,Alpha 為 Adst 。現在有一個像素(RGBsrc,Asrc)要和 RT 上的像素混合,那正確的混合會這樣進行:


  • 最終混合出來的顏色由兩部組成:
    • Asrc * RGBsrc 代表 RGBsrc 對最終顏色的貢獻,它受 Alpha 影響,如果 Alpha 為 0 則對最終像素沒有影響,如果 Alpha 為 1 則貢獻 100% 的 RGBsrc
    • Adst * RGBdst 是 RT 中像素原本沒有其它像素覆蓋時的貢獻值,但是現在被一個新來的像素遮擋了,被遮擋了 (1 - Asrc),因此 RT 中像素最終貢獻 (Adst * RGBdst)*(1 - Asrc)。
  • 由此可見,無論對于 src 還是 dst, A * RGB 才是實際的有效顏色,稱其為 premultiplied alpha。令:

  • 則顏色混合可以改寫為:

  • 這么看就更加清楚:
最終的輸出 = 新疊加像素的有效顏色 + RT 中原有像素的有效顏色 * 新像素的遮擋

② SrcAlpha,OneMinusSrcAlpha 顏色混合正確有前提

  • 在 Unity 中的透明混合,默認采用 SrcAlpha,OneMinusSrcAlpha 方式,用符號表示出來:

  • 對比之前給出的計算:

  • 加號右側少乘 Adst ,這是為什么呢?因為 SrcAlpha, OneMinusSrcAlpha 正確的前提是混合目標是不透明的,即 Adst 為 1。
  • 平時渲染時,常見的情況是先渲染不透明物體,再渲染不透明的天空盒,最后再渲染半透明物體做 Alpha 混合,在這種情況下渲染目標是不透明的,不透明物體的有效顏色即其顏色本身。
  • 在滿足這個前提下:

  • 才會成立,其本質為:

  • 依然是符合上文中給出的結論:

  • 只不過此時的 RGBdst 等于 RGBdst??赡苡腥藭a生疑問,不透明背景上混合半透明后,怎么看待混合后的透明度?我們看看上文中的 Alpha 的計算:

  • 發現沒有,其本質是以 Asrc 作為參數的 Adst 到 1.0 線性插值。當 Adst 為 1.0 時,無論 Asrc 是何值,最終輸出都是 1.0?;氐浆F實中,這很好理解,磚墻前放一塊玻璃,當我們將玻璃和墻看作一個整體時,它們是不透明的。

③ SrcAlpha, OneMinusSrcAlpha 混合出來的 Alpha 值是無意義的

  • 這種常見混合方式根據上文 ② 中的,其已默認渲染目標的 Alpha 為 1,因此它不關心 Alpha 結果的正確性。根據其表達式:

  • 可以清晰的看到,這里沒有出現 Adst ,得出正確的 RGB 與 RT 中的 Alpha 存什么沒有任何關聯。
  • 通過 SrcAlpha, OneMinusSrcAlpha 方式計算得到:

  • 這個結果沒有意義。有些情況下,可以利用這種性質,將 RT 中沒有被用到的 Alpha 通道利用起來,例如存儲 bloom 系數。

④ 理解預乘 Alpha 混合公式的顏色部分

  • Premultiplied alpha 混合采用 One, OneMinusSrcAlpha,其實我們在 ① 中就已經看到:

  • 即:

  • One 就是這里的 1.0 而 OneMinusSrcAlpha 就是 (1 - Asrc) 。RGBdst 來自于混合的結果,真正的問題是 RGBrsc 如何獲得,最簡單方式就是紋理中的 RGB 預乘好 Alpha,那么采樣得到的顏色直接就是有效 RGB。

⑤ 紋理預乘 Alpha 實踐上可能有潛在問題

  • 在實踐中,紋理的數據源大多是 RGBA32,即單通道 8 比特,只能表示 0-255 的整數,同時游戲資產還會根據目標平臺做紋理壓縮。
  • 由于精度問題,原本相近的顏色在預乘后會存儲為更相近,甚至相同的顏色,經壓縮后很容易產生大量 artifacts。要使用預乘 Alpha 的紋理,一般會建議采用單通道 16 位的存儲。
  • 由于這種情況,即使預乘有很好的紋理過濾特性,也沒有被廣泛采用,我所了解 WebGL 由于網頁對于 Alpha composition 的天然需求,做了這方面的支持。

⑥ 即使不紋理預乘,采用預乘 Alpha 的混合公式也有好處

  • 采用 One, OneMinusSrcAlpha 混合有個很好的特性,可以統一 Blend 和 Additive,減少 BlendState 切換,還能增加效果,推薦閱讀:A Mind Forever Programming。
  • 簡單理一下思路:
    • 把非預乘紋理的采樣到的 RGBA,在 shader 中輸出 (RGB*A, A) 就是 Blend 模式;
    • 把非預乘紋理的采樣到的 RGBA,在 shader 中輸出 (RGB*A, 0) 就是 Additive 模式。
  • 輸出的 Alpha 可以定義一個 uniform t 控制,輸出 (RGBA, At ),這樣通過 t 就是控制 Blend 和 Additive 模式之間的過渡。
  • 如果再定義一個 uniform s,輸出 (RGBAs, Ats),還可以通過 s 控制其整體透明度,用于淡入淡出,簡直就是特效的救星。
  • 眾所周知,采用 Additive 模式的特效,在亮的場景中幾乎看不到效果,而 Blend 模式的特效在暗的場景中提不亮。采用 One OneMinusSrcAlpha 就可以使用中間態來做出適配比較好的特效,而且不需要 framebuffer fetch。

⑦ Premultiplied Alpha 運算是封閉的

  • 換言之,預乘 alpha 混合得到的顏色也是預乘 alpha 的。細心的你可能會注意到,在 ① 中:

  • 作為運算結果的 RGBresult 是有 prime 符號的,正是想提示這一點。最終輸出的有效顏色來自兩部分:
    • 疊加上去的 src 像素貢獻的有效顏色;
    • 背景 dst 像素貢獻的有效顏色,它被 src 遮擋掉一部分,遮擋的量是 (1 - Asrc)。
  • 觀察 ① 中給出的兩式:

  • (1)(2) 的計算過程是一樣的,這就不禁會產生疑問:(1)式混合兩個未預乘 alpha 的RGB,結果是預乘 Alpha 的RGB?這沒錯,未預乘 Alpha 的顏色經混合得到的是預乘 Alpha 的顏色。
  • 那平時用 SrcAlpha, OneMinusSrcAlpha 為什么能得到未預乘的結果呢?正是 ② 中的原因,由于 SrcAlpha, OneMinusSrcAlpha 混合隱含了一個假設,渲染目標是不透明的,在這個前提下,用正確的混合公式計算,可以得到:
    • 預乘 Alpha 的 RGBresult
    • Aresult = 1.0。
  • 在 ② 中已經講過,與不透明目標混合得到的 Alpha 恒為 1。顯而易見,當 Alpha 為 1 時, RGBresult 等于 RGBresult 。因此(1)式在當渲染目標是不透明時,改成下式是成立的:

⑧ 理解預乘 Alpha 混合公式的 Alpha 部分

  • 預乘 Alpha 混合時,顏色分量和 Alpha 分量的運算是一致的,對比一下:

  • 都是:

  • 因此,不需要額外指定 Alpha 分量的混合公式,就能得到有意義的 Alpha 值,而且無論渲染目標是透明還是不透明,結果都是正確的。

⑨ 僅當必要時 Unmultiply

  • 凡是講 premultiplied alpha 都會告訴你,可以通過以下方式,還原未預乘的顏色值:

  • 常見的、未預乘的顏色值也叫 straight alpha 或 unassociated alpha,而預乘好的叫 premultiplied alpha 或 associated alpha。這種還原操作在渲染自己可控的環境下幾乎用不到。
  • 根據上文中的 Premultiplied Alpha 運算是封閉的,預乘 Alpha 混合時運算封閉,可以多次混合不需要還原 straight alpha。但如果用未預乘 Alpha 混合時,如果渲染目標是半透明的,每次混合完成都要 unmultiply 回 straight alpha 才能繼續混合,而且當一個網格有多層透明疊加時結果是錯誤的。
  • 從實踐上講,預乘 Alpha 混合的結果需要 unmultiply 主要就這種情況:三方組件只接受 straight alpha 表示的紋理。Framebuffer 顯示到屏幕上輸出時,RT 最終總是不透明的,不透明的 Alpha 為 1,預乘和未預乘沒有區別,也不用特殊處理。

⑩ Bleed Alpha 與預乘 Alpha 原理不同,目的相同,結果略有不同

  • 預乘 alpha 和 bleed alpha 目的都是減少半透明紋理過濾產生的瑕疵,但它們有一些比較顯著的區別:
    • Bleed alpha 不需要修改混合公式;
    • Bleed alpha 只能優化完全透明和非完全透明像素邊緣的過濾瑕疵;
    • 預乘 alpha 不僅可以達到 bleed alpha 的結果,半透像素之間的過濾效果也能得到優化;
    • 預乘 alpha 需要修改混合公式,可能產生 tip6 中提到的情況。
  • 當不使用 premultiplied alpha 時,預處理貼圖 bleed alpha 是一個“免費”替代品。雖然效果上會有折扣,但性價比極高。

? 紋理預乘 Alpha 可以減少紋理過濾帶來的 artifacts

  • 紋理預乘 alpha 可以減少 downsampling、upsampling、非 pixel perfect 各種情況下半透紋理過濾產生的 artifacts,推薦閱讀:
    • Alpha Blending: To Pre or Not To Pre;
    • Beware of Transparent Pixels。

總結

以上是生活随笔為你收集整理的iOS之深入解析预乘透明度Premultiplied Alpha的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。