TOD的云層渲染和Flare遮擋

關(guān)于TOD

前項目在模仿塞爾達的晝夜變換效果時用過 Time Of Day 這個Unity插件歉铝。

本文記錄一下 TOD 的云層渲染方式,以及如何讓 TOD 的云層遮擋太陽光的 Flare扑媚。

TOD渲染順序

image

以上圖的白天為例,TOD的主要渲染順序如下:

  1. 繪制太陽 (Background+30)
  2. 繪制大氣層 (Background+50)
  3. 繪制云層 (Geometry+530)

其中轻姿,太陽和大氣層都是實體渲染乳乌,按照實體渲染從近往遠的優(yōu)化策略,這里 RenderQueue 放在 Background 似乎不太合理叙淌,我們游戲中會把他們的渲染順序調(diào)整到 Geometry 的后面秤掌。

關(guān)于太陽和大氣層的渲染,特別是大氣層的渲染鹰霍,內(nèi)容還不少闻鉴,日后慢慢寫,本文主要是關(guān)于云層渲染的茂洒。

TOD的云層渲染

大氣層的模型是一個球面孟岛,而云層的模型是一個半球面,因為云層不會出現(xiàn)在地平線之下:

image

云層渲染的基本原理比較簡單督勺,就是根據(jù)一張密度圖計算云層的形狀渠羞,貼在這個半球面上,然后和大氣層做Alpha混合智哀。

作者在這個插件的主頁說這個云層是 Semi-volumetric 的次询,從代碼上看,這里的半體積其實就是在 yz 兩個方向計算云的密度瓷叫,這樣計算很快屯吊,雖然不是真正的 體積云送巡,但是效果不錯,移動設(shè)備也能跑得動雌芽。

云層的密度圖

云層的形狀主要根據(jù)作者提供的一張密度圖來計算授艰,密度圖的4個通道分別是不同頻率的噪聲圖,如下:

image

在介紹密度計算之前世落,我們首先需要處理好半球面上的點到密度圖的UV映射問題淮腾。

云層的UV計算

根據(jù) TOD 的實現(xiàn)機制,攝像機剛好位于球面的中心屉佳,我們可以用模型空間下 歸一化viewDir 來映射密度圖的紋理坐標谷朝。

這里 viewDir 計算方式如下:

o.viewDir  = normalize(v.vertex.xyz);

考慮下面這種最簡單的映射方式:用 viewDir 的水平面 XZ坐標 直接映射紋理的 UV

image

可以看到,云層越接近水平線武花,拉伸的就越厲害圆凰,因為均勻變化的水平坐標XZ對應的球面跨度變化是不均勻的铣减。

要得到正確的結(jié)果佣赖,我們可以參考 這篇文章 來做球面點到紋理二維坐標的轉(zhuǎn)換闹蒜,不過作者用了一個計算量更小的方式:

inline float3 CloudPosition(float3 viewDir, float3 offset)
{
    float mult = 1.0 / lerp(0.1, 1.0, viewDir.y);
    return (float3(viewDir.x * mult + offset.x, 0, viewDir.z * mult + offset.z)) / TOD_CloudSize;
}

這里就是把 viewDir.y 考慮進去观挎,這個值越小越接近水平線,XZ 平面上的跨度就越大割择,效果如下:

image

云層的密度計算

正確計算UV后蚤吹,我們就可以在像素著色器計算密度了球榆。

這里先不考慮N層噪聲的疊加娃兽,我們看一下一層噪聲的代碼:

inline half3 CloudLayerDensity(sampler2D densityTex, float4 uv, float3 viewDir)
{
    half3 density = 0;
    half4 n = tex2D(densityTex, uv.xy);

    // Density when marching in up direction
    density.y += n.r;

    // Density when marching in view direction
    density.z += n.r;

    // Coverage
    density.yz = (density.yz - TOD_CloudCoverage) * half2(TOD_CloudAttenuation, TOD_CloudDensity);

    // Opacity
    density.x = saturate(density.z);

    return density;
}

這里 yz 兩個方向的密度都取密度圖的R通道菇民,TOD_CloudCoverage 用于裁剪云朵的范圍,TOD_CloudDensity 可以控制云朵邊緣的透明程度投储,TOD_CloudAttenuation 可以控制云朵的顏色衰減第练。

云層的顏色計算

得到密度后,云層的顏色計算代碼如下:

inline half4 CloudLayerColor(sampler2D densityTex, float4 uv, float4 color, float3 viewDir, float3 lightDir, float3 lightCol)
{
    half3 density = CloudLayerDensity(densityTex, uv, viewDir);

    half4 res = 0;
    res.a = density.x;
    res.rgb = 1.0 - density.y;

    res *= color;

    return res;
}

代碼很簡單玛荞,不啰嗦娇掏。

云層的渲染分級

上面的代碼只是云層最基礎(chǔ)的計算,TOD 針對云層的渲染質(zhì)量分了三級:

  • TOD_CloudQualityType.Low
    • 一層噪聲
  • TOD_CloudQualityType.Medium
    • 四層噪聲疊加
  • TOD_CloudQualityType.High
    • 四層噪聲疊加
    • 米氏散射疊加

這里就不貼代碼了勋眯,有興趣的話可以去支持一下作者驹碍。

Flare遮擋

下面回到項目中實際遇到的問題,如下圖所示凡恍,太陽其實已經(jīng)被云層遮擋了,但是 Flare 還是顯示了出來:

image

要解決這個問題其實也簡單怔球,首先我們需要定義自己的 LensFlare 的渲染嚼酝,如下圖:

image

然后,在像素著色器部分竟坛,我們需要計算出太陽所在位置的云密度闽巩,進而計算出 影衰減 并應用到 Flare 上钧舌,主要代碼如下:

half4 frag (v2f i) : SV_Target
{
    half4 texColor = tex2D(_FlareTexture, i.uv);

    float3 skyPos = normalize(mul((float3x3)TOD_World2Sky, TOD_SunWorldPos));
    float4 cloudUV = CloudUV(skyPos);
    float cloudShadowAtten = TOD_CloudOpacity * CloudShadow(TOD_CloudTexture, cloudUV);
    cloudShadowAtten = 1 - cloudShadowAtten;

    texColor.rgb = texColor.rgb * cloudShadowAtten;
    return texColor * i.color;
}

最后放一張動圖:

image

個人主頁

本文的個人主頁鏈接:https://baddogzz.github.io/2020/04/16/Tod-Cloud/

好了涎跨,拜拜洼冻!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市隅很,隨后出現(xiàn)的幾起案子撞牢,更是在濱河造成了極大的恐慌,老刑警劉巖叔营,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屋彪,死亡現(xiàn)場離奇詭異,居然都是意外死亡绒尊,警方通過查閱死者的電腦和手機畜挥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來婴谱,“玉大人蟹但,你說我怎么就攤上這事√犯幔” “怎么了华糖?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長口糕。 經(jīng)常有香客問我缅阳,道長,這世上最難降的妖魔是什么景描? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任十办,我火速辦了婚禮,結(jié)果婚禮上超棺,老公的妹妹穿的比我還像新娘向族。我一直安慰自己,他們只是感情好棠绘,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布件相。 她就那樣靜靜地躺著,像睡著了一般氧苍。 火紅的嫁衣襯著肌膚如雪夜矗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天让虐,我揣著相機與錄音紊撕,去河邊找鬼。 笑死赡突,一個胖子當著我的面吹牛对扶,可吹牛的內(nèi)容都是我干的区赵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼浪南,長吁一口氣:“原來是場噩夢啊……” “哼笼才!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起络凿,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤骡送,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后喷众,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體各谚,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年到千,在試婚紗的時候發(fā)現(xiàn)自己被綠了昌渤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡憔四,死狀恐怖膀息,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情了赵,我是刑警寧澤潜支,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站柿汛,受9級特大地震影響冗酿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜络断,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一裁替、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧貌笨,春花似錦弱判、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膀跌,卻和暖如春遭商,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捅伤。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工株婴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓困介,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蘸际。 傳聞我的和親對象是個殘疾皇子座哩,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容