炫舞引擎日常:截圖

前言:退乎的mq聚聚一語道破天機:拷貝RenderTarget就行了。然而從2008到2018年,QQ炫舞馬上要進行十年周年慶了,然而我寫的一個功能,emmmm。

本文並沒有做一定的「名詞」解釋,其使用的名詞也不一定精緻,歡迎大家斧正

我不知道還有沒有別的公司有"截圖"這樣的需求,按照老大sssa2000的話說,截圖這個功能我們已經反覆寫了十年了,而就在前陣子不久,炫舞時代也重新弄了"截圖"。

為了不涉及屁眼保密條款,我打算用歡樂麻將這個遊戲來舉例子,並且不會給出圖示,你需要自己腦補。

需求背景

你的遊戲有個商城,在商城中會出售服裝,手套。出於商品展示的需求,產品並不希望商城裡面購買東西只出來一堆光禿禿的文字列表,他們表示,應該做的像電商網站那樣:

某二手網站

他們希望展示後頁面的圖片就是穿戴後的效果,由於遊戲不是現實。產品並沒有要求需要指定的POSE,配合指定的表情。但是也不希望是全身效果圖,他們只希望上衣就顯示上一部分,連頭沒有的。

(不同的遊戲對於具體截取那些內容大多數是由策劃決議的)

注意:這個地方如果想要獲得最佳的效果,最好是美術手繪。但是手繪我個人覺得的一個問題:效果太好了,玩家會有心裡落差。

說到這裡,你大概已經明白「截圖」是什麼:

把一個或多個模型畫到一張圖片上,並保存下來,然後給UI使用。

技術實現

1.創建一個RenderTarget

2.RenderToRenderTarget

3.D3DXSavePng()

問題

廢話:儘管png沒有alpha的概念,但是UI需要透明,但是UI圖片資源使用PNG格式難道不是放之四海而皆準嗎?(QQ炫舞不是)。

問題一:PhotoShop的數值顯示問題

你寫了一個截圖,你發現顏色值不對(使用PhotoShop中的取色功能,查看前景色的顏色值)。無論你是代入gamma=2.2還是等於gamma=1.0,別人打開你保存出來的BMP顯示出來的又是另外一個值

答案:因為我太菜,這個問題我沒有找到答案。但是PhotoShop中的顏色設置確實會影響值得顯示,最終使用了「屏幕」達到了預期想要的值。(有那位聚聚能解釋一下這個嘛?)


問題二:Alpha不對

為了避免討論頭髮到底應該怎麼畫alpha。我們退化到沒有那麼複雜的毛髮:睫毛。

我們假設毛髮只有一層

首先明確一下:睫毛不是鏤空,而是透明。(那麼他應該是雙面透明還是單面了,笑)

產品表現:你們繪製的商品展示圖裡面應該有睫毛,我機智的加了一個drawcall。

由於我們遊戲是手繪風格。所以我寫的睫毛 pixel shader 只有一句話:

return _MainTex.Sample(i.uv);

然後我設置了混合行為為:

D3D11_BLEND_SRC_ALPHA , D3D11_BLEND_INV_SRC_ALPHA

出於偷懶的我們,Alpha的混合方式應該是和顏色值是一樣的:

DstAlpha = SrcAlpha*SrcAlpha + DstAlpha*(1-SrcAlpha)

顯然,這張睫毛用的貼圖應該是部分區域的alpha為0(透出臉的部分),部分的值位於0-1之間,根據上面的渲染狀態。我們渲染之後的RT的alpha值會變成什麼了:

  1. 當alpha為0時,其alpha會和初始RT中的alpha值是一致的
  2. 當alpha為1時,其alpha會是1
  3. 當alpha為0-1中的x時,其alpha為有兩種情況(初始RT的alpha初始值只可能是0或者1)
    1. 當初始RT的alpha初始值為1時:x^2+1-x
    2. 當初始RT的alpha初始值為0時:x^2

你很快就會明白為什麼alpha錯了,然後很快想出解決方案。

讓我們回到需求,產品要求我們截取一張帶alpha通道的png,為什麼需要alpha,是因為他要用於UI繪製。也許你會說這個展示圖難道還需要透明什麼東西嗎?是的,在展示圖下,其背後有一個2D特效(這個特效是會動的),而你的圖是提前產生的。

詳細的技術實現:

1.創建一張alpha為0的初始RT!(因為如果沒有繪製任何東西,我們就剛好看到了一個2D特效)

2.繪製臉,臉是不透明的,被繪製上的像素區域alpha變為1。(模型擋住了2D特效,很好)

3.繪製睫毛,睫毛由於在臉上,發生overdraw(overdraw區域的alpha根據前面的推論,變成了1)

讓我們換個情景:假設臉的alpha的值錯了,是0,那麼睫毛的alpha也會發生錯誤,那麼我們臉上的alpha就會有一部分是0!

這個時候會發生什麼,繪製這張UI圖的時候,臉上睫毛那快區域把2D特效透明透過來了!

為什麼臉上貼圖的alpha值不對,我想這應該是個bug,我打算周一把他修復了。


問題三:正常疊加取決於非透明資源的正確性,那麼預乘(premultiplication) 的疊加了。

其實我並太不懂預乘,主要是我不太明白什麼場景下需要用它。有人說他就相當於cpp語言中的c_str(GPU 時代的 C-style 字元串),我頓時笑了,解決了我一個多年的困惑。如果圖片被預乘了,那麼我們的alpha混合方法就會受到RGB混合方法改變的影響,變成這樣:

DstAlpha = SrcAlpha + DstAlpha*(1-SrcAlpha)

讓我們再推一邊alpha值的情況:

當Alpha為0時,其alpha會和初始RT中的alpha值是一致的

當alpha為1時,其alpha會是1:

當alpha為0-1中的x時,其alpha為有兩種情況(初始RT的alpha初始值只可能是0或者1)

當初始RT的alpha初始值為1時:x+1-x=》1

當初始RT的alpha初始值為0時:x

講道理,我們一個端游不太可能需要「c_str」,但是上面這張疊加方式在QQ炫舞應該是被稱之為:火焰透明。在炫舞時代中美術可能言之為:越疊越亮。

其實這種alpha混合方法也沒有什麼影響,仔細想想,如果一個特效使用這種方式,那麼這個特效的商品預覽圖確實是應該有部分是透明的,透出來背後的2D特效,這樣就能表現我是個透明特效的效果。


問題四:現實世界永遠比文章裡面寫的要麻煩,我還在想要不要繼續寫下去。當我們有兩個特效疊在一起,得到了一個顏色,那麼你覺得這個alpha應該是多少才能正確的在商品預覽圖上面表現這個特效的透明效果了?

RT中的顏色值你總是能通過一系列後處理把他弄對,但是你的alpha呢?

拷貝rt前,你真的能確認:alpha值最後導致的透明效果和美術想要的效果是一樣的嗎?美術也給不出一個答案。

完!


推薦閱讀:

海龜誤吞 毒死珊瑚:亂扔塑料袋 瞧人類乾的"好事"
這個200戶住家的小城,幾千年來沒有剩飯
我為何終日與垃圾為伍|C講壇
珠穆朗瑪,世界上最高的垃圾場,左邊是屍體,右邊是屎!

TAG:垃圾處理 | 遊戲引擎 | 計算機圖形學 |