關(guān)于ComputeScreenPos和ComputeGrabScreenPos的差別

一個(gè)Bug

今天QA報(bào)了一個(gè)渲染相關(guān)的bug:一個(gè)用了 扭曲 效果的翅膀特效在場(chǎng)景相機(jī)下顯示正常辕坝,但是在UI相機(jī)上卻有問題,截圖如下:

screenshot1.png

扭曲背景 上下顛倒 了崭别。


Bug的修正

這里用的 扭曲shader 是我們的美術(shù)同學(xué)從他們前項(xiàng)目搬過來的垦细,代碼很簡(jiǎn)單:

  • GrabPass 抓取當(dāng)前屏幕做為扭曲背景浅碾。
  • 添加 UV擾動(dòng) 后再采樣屏幕背景,即可達(dá)到扭曲效果从祝。

問題是襟己,這里采樣 GrabTexture 的時(shí)候用的是 screenUV 而非 grabUV,代碼如下:

頂點(diǎn)著色器:

    o.screenPos = ComputeScreenPos (o.pos);

像素著色器:

    float2 sceneUVs = (i.screenPos.xy / i.screenPos.w) + (_Value * diffuseTex.a * float2(diffuseTex.r, diffuseTex.g) * i.vertexColor.a);
    half4 sceneColor = tex2D(_GrabTexture, sceneUVs);

修正這個(gè)問題很簡(jiǎn)單牍陌,把 ComputeScreenPos 換成 ComputeGrabScreenPos 即可擎浴,修正后的代碼如下:

頂點(diǎn)著色器:

o.screenPos = ComputeGrabScreenPos (o.pos);

調(diào)整完之后就正常了,如下圖:

screenshot2.png

關(guān)于ComputeScreenPos和ComputeGrabScreenPos的差別

修正容易毒涧,但是搞清楚 ComputeScreenPosComputeGrabScreenPos 的差別卻要費(fèi)一些功夫贮预。

我們看一下相關(guān)代碼:

inline float4 ComputeNonStereoScreenPos(float4 pos) {
    float4 o = pos * 0.5f;    
    o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
    o.zw = pos.zw;
    return o;
}

inline float4 ComputeScreenPos(float4 pos) {
    float4 o = ComputeNonStereoScreenPos(pos);
#if defined(UNITY_SINGLE_PASS_STEREO)
    o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
#endif
    return o;
}

inline float4 ComputeGrabScreenPos (float4 pos) {
    #if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
    #else
    float scale = 1.0;
    #endif
    float4 o = pos * 0.5f;    
    o.xy = float2(o.x, o.y*scale) + o.w;
#ifdef UNITY_SINGLE_PASS_STEREO
    o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
#endif
    o.zw = pos.zw;
    return o;
}

通過分析可以發(fā)現(xiàn),這兩個(gè)函數(shù)的主要差別就是 UNITY_UV_STARTS_AT_TOP_ProjectionParams.x 的差別链嘀。

我們知道 RenderTexture 的紋理坐標(biāo)在 Direct3D-like 平臺(tái)和 OpenGL-like 平臺(tái)存在差異:

  • Direct3D-like平臺(tái)萌狂,UNITY_UV_STARTS_AT_TOP = 1,紋理坐標(biāo)0在頂部怀泊,并往下增長(zhǎng)茫藏。
  • OpenGL-like平臺(tái),UNITY_UV_STARTS_AT_TOP = 0霹琼,紋理坐標(biāo)0在底部务傲,并往上增長(zhǎng)凉当。

當(dāng)渲染到紋理的時(shí),Unity遵從 OpenGL-like 平臺(tái)的標(biāo)準(zhǔn)售葡。

當(dāng)工作在 Direct3D-like 平臺(tái)時(shí)看杭,為了兼容這個(gè)平臺(tái)差異,Unity會(huì) 翻轉(zhuǎn)投影矩陣 從而翻轉(zhuǎn) RenderTexture挟伙,這樣既遵從了 OpenGL-like 平臺(tái)的約定楼雹,又可以獲取正確的采樣結(jié)果。

_ProjectionParams.x 標(biāo)識(shí)了投影矩陣是否經(jīng)過翻轉(zhuǎn)尖阔。

  • _ProjectionParams.x = 1表示沒有翻轉(zhuǎn)贮缅。
  • _ProjectionParams.x = -1表示翻轉(zhuǎn)。

那么介却,是不是 Direct3D-like 平臺(tái)下 RenderTexture 一定會(huì)進(jìn)行翻轉(zhuǎn)操作呢谴供?如果沒有翻轉(zhuǎn),而Unity又采用了 OpenGL-like 平臺(tái)的約定齿坷,這種情況要怎么處理呢桂肌?

事實(shí)上,Unity在一些情況下確實(shí)不會(huì)翻轉(zhuǎn) RenderTexture永淌,它的幫助文檔 Platform-specific rendering differences 這一章節(jié)列舉了 Direct3D-like 平臺(tái)下不翻轉(zhuǎn) RenderTexture 的幾種情況:

  • Image Effects + 抗鋸齒
  • GrabPass

對(duì)于 GrabPass崎场,Unity文檔做了特別說明:在 Direct3D-like 平臺(tái)下,GrabPass 不會(huì)進(jìn)行 RenderTexture 的翻轉(zhuǎn)操作仰禀,因此我們需要在shader中手工翻轉(zhuǎn)uv以獲取正確的采樣結(jié)果照雁。

ComputeGrabScreenPos 這里只需要判斷 UNITY_UV_STARTS_AT_TOP 的取值:

  • 如果是 Direct3D-like 平臺(tái)(UNITY_UV_STARTS_AT_TOP = 1),我們就需要手工翻轉(zhuǎn)uv答恶。
  • 如果是 OpenGL-like 平臺(tái)(UNITY_UV_STARTS_AT_TOP = 0)饺蚊,則無需翻轉(zhuǎn)uv。
inline float4 ComputeGrabScreenPos (float4 pos) {
    #if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
    #else
    float scale = 1.0;
    #endif
    float4 o = pos * 0.5f;    
    o.xy = float2(o.x, o.y*scale) + o.w;
#ifdef UNITY_SINGLE_PASS_STEREO
    o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
#endif
    o.zw = pos.zw;
    return o;
}

Direct3D-like 平臺(tái)下悬嗓,如果我們用 _ProjectionParams.x 來判斷是否需要手工翻轉(zhuǎn)uv就錯(cuò)了污呼,因?yàn)?RenderTexture 并未發(fā)生翻轉(zhuǎn),此時(shí) _ProjectionParams.x = 1包竹。


關(guān)于投影矩陣的翻轉(zhuǎn)

bug是修正了燕酷,也知道了原因:對(duì)于GrabPass,我們應(yīng)該用 UNITY_UV_STARTS_AT_TOP 而非 _ProjectionParams.x 去判斷是否要手工進(jìn)行uv翻轉(zhuǎn)周瞎。

但是還有一個(gè)疑問沒有解開:前文說了苗缩,這個(gè) 扭曲 特效在場(chǎng)景相機(jī)下工作正常,在UI相機(jī)下才有問題声诸,這又是為什么呢酱讶?

說到相機(jī),我們游戲內(nèi)一共3個(gè)相機(jī)彼乌,渲染順序如下:

場(chǎng)景相機(jī)(開后處理) --> UI相機(jī)1(關(guān)后處理) --> UI相機(jī)2(關(guān)后處理)

出現(xiàn)問題的相機(jī)是 UI相機(jī)2泻肯,此時(shí)我們并沒有開 抗鋸齒渊迁,并且 場(chǎng)景相機(jī) 處于 關(guān)閉 狀態(tài)。

如果我們打開 場(chǎng)景相機(jī)灶挟,或者把 UI相機(jī)1 的后處理打開琉朽,又或者把 UI相機(jī)2 的后處理打開,這些情況下這個(gè)bug都不會(huì)出現(xiàn)稚铣。

似乎 多相機(jī) 以及 Image Effect的開關(guān) 也會(huì)影響 _ProjectionParams.x 的設(shè)值箱叁。

可惜的是,Unity文檔對(duì) 投影矩陣的翻轉(zhuǎn) 語(yǔ)焉不詳榛泛,只是告訴你 _ProjectionParams.x = -1 即代表了翻轉(zhuǎn):

x is 1.0 (or –1.0 if currently rendering with a flipped projection matrix)

沒有源碼的情況下蝌蹂,何時(shí)翻轉(zhuǎn)投影矩陣就比較難說清楚了噩斟。

不過我們只需要記得:

  • _ProjectionParams.x = -1 代表翻轉(zhuǎn)了投影矩陣曹锨,在計(jì)算 屏幕坐標(biāo) 的時(shí)候,如果發(fā)生了 投影矩陣翻轉(zhuǎn)剃允,那么我們也需要在shader中手工翻轉(zhuǎn)uv沛简,這樣才能獲得正確的 屏幕坐標(biāo)
  • Unity的 ComputeScreenPos 幫我們處理好了這個(gè)過程斥废。

早前在寫 Fantastic SSR Water 這個(gè)插件的時(shí)候椒楣,我也遇到過類似的問題。

Fantastic SSR Water 是一款關(guān)于水的插件牡肉,用 屏幕空間反射 實(shí)現(xiàn)水的反射捧灰。

  • 因?yàn)樾枰谄聊豢臻g計(jì)算 光線步進(jìn),因此我需要計(jì)算屏幕坐標(biāo) screenUV统锤。
  • 因?yàn)橛昧?GrabPass 去抓取屏幕顏色以計(jì)算反射顏色毛俏,因此我還需要計(jì)算 grabUV

當(dāng)時(shí)饲窿,我錯(cuò)誤的把 screenUVgrabUV 等同了煌寇,然后發(fā)現(xiàn)只有在特定的設(shè)置選項(xiàng)下渲染才正確,設(shè)置選項(xiàng)包括:

  • 平臺(tái)的選擇
  • 前向渲染/延遲渲染的選擇
  • 抗鋸齒開關(guān)的選擇

后面逾雄,我用 ComputeScreenPos 去計(jì)算 screenUV阀溶,用 ComputeGrabScreenPos 去計(jì)算 grabUV,問題就解決了鸦泳,在各種設(shè)置組合下渲染都正確银锻。

最后,附 Fantastic SSR Water 截圖一張:

screenshot3.jpg

個(gè)人主頁(yè)

本文的個(gè)人主頁(yè)鏈接:https://baddogzz.github.io/2020/01/02/GrabUV-Bug/

好了做鹰,拜拜击纬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市誊垢,隨后出現(xiàn)的幾起案子掉弛,更是在濱河造成了極大的恐慌症见,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殃饿,死亡現(xiàn)場(chǎng)離奇詭異谋作,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乎芳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門遵蚜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奈惑,你說我怎么就攤上這事吭净。” “怎么了肴甸?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵寂殉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我原在,道長(zhǎng)友扰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任庶柿,我火速辦了婚禮村怪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浮庐。我一直安慰自己甚负,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布审残。 她就那樣靜靜地躺著梭域,像睡著了一般。 火紅的嫁衣襯著肌膚如雪维苔。 梳的紋絲不亂的頭發(fā)上碰辅,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音介时,去河邊找鬼没宾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛沸柔,可吹牛的內(nèi)容都是我干的循衰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼褐澎,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼会钝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤迁酸,失蹤者是張志新(化名)和其女友劉穎先鱼,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奸鬓,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焙畔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了串远。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宏多。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖澡罚,靈堂內(nèi)的尸體忽然破棺而出伸但,到底是詐尸還是另有隱情,我是刑警寧澤留搔,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布更胖,位于F島的核電站,受9級(jí)特大地震影響催式,放射性物質(zhì)發(fā)生泄漏函喉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一荣月、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梳毙,春花似錦哺窄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至奸柬,卻和暖如春生年,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背廓奕。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工抱婉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桌粉。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓蒸绩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親铃肯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子患亿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348