【W(wǎng)ater Drop系列】是雨效渲染實(shí)現(xiàn)的整套方案的翻譯總結(jié)掷贾,本文是第二篇,主要介紹降雨的基本效果的實(shí)現(xiàn)方法廊蜒。這里是原文鏈接
本篇雨效渲染文章的主要參考是ATI的“Toy Shop”Demo趴拧,這個(gè)Demo曾經(jīng)被Natalya Tatarchuck在很多會(huì)議上所提及。不過(guò)雖然這個(gè)demo是2005年的技術(shù)了山叮,但是依然無(wú)法在當(dāng)代的PS3/XBOX360等設(shè)備上很好的重現(xiàn)著榴。這篇文章主要介紹作者在“Remember Me”上面嘗試復(fù)現(xiàn)上述Demo技術(shù)的一些努力,實(shí)現(xiàn)引擎為UE3.
Rain Effects
Rain splashes / Falling drops splashes
先來(lái)討論下雨滴墜落的實(shí)現(xiàn)屁倔,而雨滴濺起的水花相對(duì)而言實(shí)現(xiàn)會(huì)容易一點(diǎn)局雄,只需要檢測(cè)雨滴粒子與場(chǎng)景的碰撞之后驴娃,生成一個(gè)雨花特效粒子即可唧领。不過(guò)碰撞檢測(cè)比較費(fèi)丹锹,且如此之多的雨滴在地面上碰撞,也很難分清到底哪個(gè)雨花是哪個(gè)雨滴導(dǎo)致的骑篙,基于這一點(diǎn),雨花跟雨滴的實(shí)現(xiàn)可以拆分成兩個(gè)獨(dú)立的系統(tǒng)來(lái)實(shí)現(xiàn),這樣可以兼顧表現(xiàn)跟性能席舍。
大多數(shù)游戲?qū)崿F(xiàn)雨花的方式是從世界的頂部生成一系列隨機(jī)的垂線,對(duì)這些直線(出于優(yōu)化的考慮哮笆,直線最好盡可能的分布在近景處)與一個(gè)能夠代表場(chǎng)景大致形狀的簡(jiǎn)單幾何體進(jìn)行碰撞檢測(cè)来颤,并在碰撞點(diǎn)處生成對(duì)應(yīng)的雨花特效,對(duì)于那些無(wú)法找到一個(gè)能夠代表場(chǎng)景地形的簡(jiǎn)單幾何體的情況稠肘,還可以直接在場(chǎng)景幾何體的邊緣位置手動(dòng)放置對(duì)應(yīng)的粒子發(fā)射器福铅,生成雨花特效,雖然沒有真實(shí)雨效那么隨機(jī)项阴,但至少效果是有了滑黔。
本文給出的雨花實(shí)現(xiàn)方式有所不同,直接采用類似陰影貼圖的方式生成一張雨景深度貼圖(以垂直向下的方式得到的正交投影貼圖)环揽,有了這張貼圖之后就不需要模擬場(chǎng)景地形的簡(jiǎn)單幾何體了略荡,可以直接在任意想要的位置生成隨機(jī)的雨花,基本步驟給出如下:
- Render a depth map —— 雨景深度貼圖(需要為透明物體寫入深度)
- Transfer depth map from GPU to CPU memory —— CPU讀取深度貼圖
- Use the depth map to generate random positions following the world geometry —— 在CPU根據(jù)深度數(shù)據(jù)生成隨機(jī)的雨花生成點(diǎn)
- Emit the water splash at the generated positions —— 將生成點(diǎn)轉(zhuǎn)換成對(duì)應(yīng)的世界坐標(biāo)歉胶,并生成雨花特效
這種方式得到的雨花撞芍,還可以有效的避免室內(nèi)雨花穿幫問(wèn)題。
陰影貼圖渲染的一切優(yōu)化手段這里都可以用上(剔除優(yōu)化跨扮,Z雙倍加速序无,只渲染頂點(diǎn)位置,放棄細(xì)小物體渲染衡创,使用LOD帝嗡,shadow cache等等)
出于一些特殊考慮,有些物體不需要進(jìn)行雨花渲染璃氢,有些物體只渲染雨花而不需要在正式場(chǎng)景中渲染哟玷,因此這里為物體增加了一些可以交由美術(shù)同學(xué)控制的選項(xiàng)。
深度圖的精度取決于分辨率與覆蓋范圍一也,對(duì)于一張256x256的貼圖巢寡,如果用于覆蓋20m x 20m的范圍的話,那么每個(gè)像素將對(duì)應(yīng)于7.8平方厘米椰苟,在這種數(shù)據(jù)下抑月,如果存在某個(gè)物件在7.8平方厘米內(nèi)存在較大的高度差的話,那么采樣得到的結(jié)果可能就會(huì)出現(xiàn)偏差舆蝴,導(dǎo)致生成的雨花特效的高度與實(shí)際情況不匹配谦絮,作為性能跟效果的平衡题诵,這一點(diǎn)也是沒有辦法的。
陰影貼圖通常使用正交投影矩陣层皱,這是因?yàn)橥ǔ?lái)說(shuō)陰影貼圖都是對(duì)應(yīng)于方向光的性锭,但是這里理論上來(lái)說(shuō)也是應(yīng)該使用正交矩陣,不過(guò)由于雨花本來(lái)就是近似的叫胖,所以使用透視矩陣也是可以的草冈,而如果使用透視矩陣的話,最好使用reversed z來(lái)提升深度貼圖的精度(浮點(diǎn)數(shù)在靠近0的位置瓮增,精度最高)疲陕,此外,這里還給出了各個(gè)平臺(tái)兼容性的一些考慮钉赁。
// ScreenPosition is the projected position
#if ORTHOGONAL
// We encode the Z device value for orthogonal matrix
// ScreenPosition.w is supposed to be 1.0 in orthogonal projection matrix
OutColor = float4(0, 0, 0, ScreenPosition.z / ScreenPosition.w);
#else // PERSPECTIVE
// Define A = ProjectionMatrix[2][2] and B = ProjectionMatrix[3][2] (row major)
// Standard projection do Z_NDC = A + B / Z => Reversed (1 - Z_NDC) = 1 - A - B / Z
OutColor = float4(0, 0, 0, 1 - A - B / ScreenPosition.w);
#endif
渲染完成之后,下一步就是將數(shù)據(jù)從GPU傳入CPU携茂,在DX9上有兩種做法你踩,一種是阻塞式的,CPU一直等待讳苦,直到GPU渲染完成带膜,這種比較浪費(fèi),另一種則是直接拿取上一幀渲染的結(jié)果來(lái)使用鸳谜,并將本幀渲染的結(jié)果交給下一幀使用膝藕,即double buffer的實(shí)現(xiàn)方式。
主機(jī)上這個(gè)問(wèn)題要相對(duì)簡(jiǎn)單一點(diǎn)咐扭,因?yàn)椴恍枰鋈魏蔚耐剑词箤戝e(cuò)了一個(gè)數(shù)據(jù)對(duì)于成千上萬(wàn)的數(shù)據(jù)而言芭挽,也不過(guò)是滄海一粟),且傳輸速度極快(在PS3上的數(shù)據(jù)為蝗肪,傳輸256x256的深度貼圖只需要36us)
下一步是根據(jù)深度貼圖生成雨花粒子的位置袜爪,雨花粒子發(fā)生器一般會(huì)選擇放在比較中心的位置,比如角色位置薛闪,或者在TPS游戲中辛馆,放在相機(jī)位置,之后每發(fā)射一個(gè)雨花粒子豁延,都需要進(jìn)行如下處理:將這個(gè)雨花粒子按照雨景深度貼圖的ViewProjection矩陣進(jìn)行變化昙篙,得到uv坐標(biāo)讀取深度值,并進(jìn)行反向投影诱咏,得到最終碰撞點(diǎn)的世界坐標(biāo)苔可。
// Project current position in our depth map and set world space z location.
Vector4 ShadowPosition = ViewProjMatrix.TransformVector4(Particle->Location);
ShadowPosition = ShadowPosition / ShadowPosition.W;
// Save depth map X Y position for later world space reconstruction
Vector2D PosNDC(Clamp(ShadowPosition.X, -1.0f, 1.0f), Clamp(ShadowPosition.Y, -1.0f, 1.0f));
// If we are out of shadowmap, just kill the pixel (We do this by testing if value change by clamp)
if (PosNDC.X - ShadowPosition.X + PosNDC.Y - ShadowPosition.Y)
{
return ;
}
// Convert to shadow map texel space - apply a clamp mode address mode
ShadowPosition.X = Clamp(ShadowPosition.X * 0.5f + 0.5f, 0.0f, 1.0f);
ShadowPosition.Y = Clamp(ShadowPosition.Y * -0.5f + 0.5f, 0.0f, 1.0f);
int PosX = (int)(ShadowPosition.X * (float)(SizeX - 1));
int PosY = (int)(ShadowPosition.Y * (float)(SizeY - 1));
#if CONSOLE
Data = &Data[(PosY * DepthBuffer->DepthSurfacePitch) + (PosX * 4)]; // Data is the CPU memory containing the depth map
// Big endian on console - D24S8 depth/stencil buffer
unsigned int Val = (Data[0] << 16) + (Data[1] << 8) + Data[2]; // Remove stencil value
float DepthNDC = (float)Val / 16777215.0f; // 2^24 - 1 == 16777215.0f (for 24bit depth )
#else
Data = &Data[(PosY * SizeX + PosX) * sizeof(Float16Color)];
float DepthDeviceFloat = ((Float16Color*)Data)->A; // Convert to float
// As inversion is not handled inside the projection matrix but in
// the shader we must invert here
if (UsesInvertedZ && ProjMatrix[3][3] < 1.0f) // Orthogonal projection is not inversed
{
DepthDeviceFloat = 1.0f - DepthDeviceFloat;
}
#endif
Vector4 ReconstructedPositionWS = InverseViewProjection.TransformVector4(Vector4(PosNDC.X, PosNDC.Y, DepthNDC, 1.0f));
Particle->Location = ReconstructedPositionWS/ ReconstructedPositionWS.W;
上面的偽代碼使用的深度貼圖格式為D24S8的,這種格式可以借用硬件的PCF功能袋狞,使得結(jié)果更為平滑硕蛹,如果深度貼圖只用于被CPU所讀取使用的話醇疼,也可以將之換成純粹的深度數(shù)據(jù)格式。除此之外法焰,考慮到設(shè)備兼容性秧荆,還需要對(duì)不同的格式有一個(gè)清晰的了解。
之后就可以根據(jù)位置生成雨花粒子了埃仪,不過(guò)想要模擬完全真實(shí)的雨花粒子是比較困難的乙濒,參考文獻(xiàn)7給出了一種實(shí)現(xiàn)方法,總結(jié)如下:
雨花的產(chǎn)生是由于雨滴與水面的碰撞卵蛉,導(dǎo)致水面產(chǎn)生一圈圓形的漣漪颁股,漣漪中心的水面下降的幅度足夠大的話,就會(huì)出現(xiàn)較強(qiáng)的反彈從而產(chǎn)生水花傻丝,而水花大致可以分成兩種:一種是垂直于水面的王冠狀水花甘有,王冠狀水花在于水花與水面還依然銜接在一起;另一種則是迅速與水面脫離的水滴狀水花葡缰。
這里給出了YouTube上的兩個(gè)使用高速相機(jī)捕捉到的水花生成視頻亏掀。
簡(jiǎn)單來(lái)說(shuō),王冠水花需要有較厚的水面泛释,且水花存在時(shí)長(zhǎng)要介于10~20ms之間滤愕,否則就會(huì)成為水滴狀水花。
水花的動(dòng)態(tài)效果取決于很多因素怜校,這些因素可以分成兩大類:水面的材質(zhì)屬性(粗糙度间影,堅(jiān)硬度Rigidity,濕度茄茁,傾斜度以及排水度)魂贬,以及墜落水滴的屬性(尺寸,速度等)裙顽。粗糙材質(zhì)對(duì)于結(jié)果的影響幅度較大随橘,王冠狀水花的半徑跟高度可以跟雨滴的屬性關(guān)聯(lián)在一起,而水滴狀水花的水滴數(shù)量也跟雨滴下落的速度有關(guān)锦庸,水滴狀水花的水滴分布可以用一個(gè)隨機(jī)模型來(lái)描述机蔗,更詳細(xì)的信息可以參考文獻(xiàn)7.
游戲中通常不會(huì)將細(xì)節(jié)做得這么深入,比如前面說(shuō)過(guò)的ATI的Demo就是直接使用一個(gè)公告板進(jìn)行不同的縮放來(lái)模擬不同的水花甘萧。
一種較好的保存兩種水花主要特征的實(shí)現(xiàn)方式為:使用一種通用的王冠形狀萝嘁,這個(gè)形狀允許沿著高度跟半徑進(jìn)行縮放,在這個(gè)基礎(chǔ)上增加一些水滴狀水花扬卷。不過(guò)由于水花的效果通常是由美術(shù)同學(xué)控制牙言,他們可能對(duì)物理特性不太了解也不太關(guān)注,考慮到這一點(diǎn)怪得,John David Thornthon為《冰河世紀(jì)》提出了一種模塊化的裝配方式咱枉,通過(guò)這種方式可以在保留物理性的基礎(chǔ)上讓美術(shù)同學(xué)更好的進(jìn)行效果的控制
最后卑硫,關(guān)于水花的分布函數(shù),現(xiàn)實(shí)中水花的數(shù)目跟下墜的水滴數(shù)目應(yīng)該是保持一致的蚕断,因此欢伏,這里簡(jiǎn)單的將雨效的強(qiáng)度跟水花的數(shù)目關(guān)聯(lián)起來(lái)。
其他的水滴效果亿乳,比如雨停之后屋檐滑落的水滴以及樹梢殘留的水滴等導(dǎo)致的水花硝拧,可以另外用一套水花系統(tǒng)來(lái)控制,可以復(fù)用之前的雨景深度圖葛假,其他的實(shí)現(xiàn)方式都是一致的障陶。
PS3上面,256x256分辨率的深度貼圖繪制需要0.32ms聊训,而強(qiáng)降雨情景下的水花渲染需要0.33ms抱究,XBox360的數(shù)據(jù)分別為0.2ms跟0.25ms。
如果角色身上的雨花難以渲染带斑,可以直接使用特效完成鼓寺,問(wèn)題在于可能無(wú)法將人物身上的雨花分布跟場(chǎng)景中的雨花分布保持一致。
在上述的實(shí)現(xiàn)中遏暴,沒有考慮光照因素對(duì)水花效果的影響,導(dǎo)致在晚上等背光環(huán)境中指黎,水花的效果看起來(lái)反常一般的亮眼朋凉,之前提到的Toy Shop Demo則是通過(guò)在vs中進(jìn)行一次光照平均來(lái)減輕這個(gè)問(wèn)題。
Rain/Raindrops
現(xiàn)實(shí)中的雨效比較復(fù)雜醋安,現(xiàn)存有許多的研究成果杂彭。想要實(shí)現(xiàn)跟現(xiàn)實(shí)世界完全一樣的雨效是比較困難的。雨幕在明亮的場(chǎng)景中不如在陰暗的環(huán)境中顯眼吓揪。
雨滴是0.5~10mm左右大小的透明物體亲怠,小尺寸的雨滴接近球狀,而大尺寸的則接近扁球狀柠辞。
雨滴的折射(165度固體角团秽,6%的衰減)跟反射特性(亮度增強(qiáng))
雨滴的FOV遠(yuǎn)寬于被其遮擋的背景的FOV,導(dǎo)致其亮度與其所遮擋的背景的亮度的關(guān)聯(lián)其實(shí)并不是很強(qiáng)烈叭首。
雨滴亮度不怎么受其他雨滴的影響习勤,因?yàn)橄鄬?duì)于雨滴本身張角覆蓋的范圍而言,此范圍內(nèi)的其他雨滴所占的面積基本上可以忽略焙格。
由于雨滴的折射效果图毕,想要正確的渲染雨滴,就需要渲染一個(gè)帶有折射反射以及內(nèi)部反射的球形眷唉,這就非常的不劃算了予颤。
雨滴下降過(guò)程中囤官,會(huì)經(jīng)歷一個(gè)快速的形變扭曲。一顆雨滴的降落速度基本上是恒定的蛤虐,小尺寸雨滴大概是3m/s党饮,而大尺寸雨滴可以達(dá)到9M/S。由于視覺暫留效應(yīng)笆焰,人眼或者相機(jī)捕捉到的雨滴降落的效果通常會(huì)帶有運(yùn)動(dòng)模糊從而形成一條垂直的透明帶劫谅,而這種帶狀細(xì)絲的形狀也不是恒定不變的,通常會(huì)隨著空間跟時(shí)間而變化嚷掠,具體可以參考文獻(xiàn)[8]
雨滴的效果跟很多參數(shù)有關(guān)捏检。遠(yuǎn)處的降雨模型可以使用Simple Photometric Model,近處的要考慮的東西就老鼻子多了
想要在游戲中完全復(fù)刻現(xiàn)實(shí)中的雨效不皆,在當(dāng)前的硬件水平上是不太可能的贯城。
因此游戲中實(shí)現(xiàn)雨效通常會(huì)考慮如下的兩種方式:粒子系統(tǒng)或者大尺寸貼圖。
粒子系統(tǒng)通常會(huì)使用方片模型來(lái)表示雨束霹娄。粒子系統(tǒng)模擬出來(lái)的雨效可以得到較好的動(dòng)態(tài)效果能犯,能夠被風(fēng)力所驅(qū)動(dòng)且在GPU上的性能表現(xiàn)也很不錯(cuò)。通常為了提升性能犬耻,這個(gè)特效系統(tǒng)會(huì)直接掛在相機(jī)上踩晶,以減少需要管理的粒子數(shù)目,比如《太空潛艇》使用了一個(gè)view frustum的粒子生成器來(lái)實(shí)現(xiàn)枕磁。粒子系統(tǒng)的缺點(diǎn)在于可擴(kuò)展性不太好渡蜻,強(qiáng)降雨需要通過(guò)增加粒子數(shù)目來(lái)實(shí)現(xiàn),這會(huì)導(dǎo)致性能受到較大影響计济。
大尺寸貼圖方案則是通過(guò)對(duì)貼圖進(jìn)行uv動(dòng)畫來(lái)完成茸苇。這種方案沒有粒子系統(tǒng)那種強(qiáng)降雨?duì)顟B(tài)性能受損的不足,代價(jià)則是其效果表現(xiàn)不夠真實(shí)(深度數(shù)據(jù)跟運(yùn)動(dòng)數(shù)據(jù)都不夠)沦寂。Toy Shop Demo的是通過(guò)屏幕空間后處理完成的学密,通過(guò)對(duì)不同的uv變換速度以及深度的多層貼圖采樣來(lái)增強(qiáng)真實(shí)感(采樣的時(shí)候會(huì)根據(jù)屏幕空間的背景像素的位置來(lái)決定使用那一層貼圖)[3]。這種方案的缺陷在于當(dāng)相機(jī)移動(dòng)的時(shí)候传藏,鏡頭的效果就會(huì)變得很蹩腳腻暮,比如當(dāng)鏡頭朝下觀看的時(shí)候,就會(huì)看到雨束是水平流動(dòng)的了毯侦。
《Flight simulator 2004》這個(gè)游戲別出心裁的使用了兩個(gè)椎體加上四層貼圖來(lái)模擬雨束效果西壮,很好的避免了前面提到的鏡頭移動(dòng)導(dǎo)致的穿幫。這種做法在鏡頭向上或者向下觀看的時(shí)候叫惊,可以看到雨束來(lái)自于一個(gè)起點(diǎn)或者回歸到一個(gè)終點(diǎn)款青,跟平時(shí)透視投影的效果非常接近,增強(qiáng)了真實(shí)感霍狰。這里用到的四層貼圖每一層的雨束都比上一層的雨束要小抡草,且移動(dòng)的速度也比上一層的更慢(畢竟距離更遠(yuǎn)饰及,下降的高度也就更高了,不能同時(shí)落地)康震,通過(guò)這種方式可以很好的模擬視差效果燎含。
本文要介紹的方法跟《Flight simulator 2004》的方法比較接近。
將鏡頭前方的雨景區(qū)域分割成四層腿短。
每一層都使用一張相同的提前進(jìn)行過(guò)運(yùn)動(dòng)模糊處理的雨束貼圖屏箍。
這里貼圖所附著的模型不再是前面的兩個(gè)椎體組成的形狀了,而是下圖所示的半椎體半柱體的形狀橘忱。這個(gè)形狀的中心與相機(jī)重合赴魁,渲染的時(shí)候使用的是內(nèi)面。
參考文獻(xiàn)12中會(huì)將雨效的透明度存儲(chǔ)在頂點(diǎn)色中钝诚,從而實(shí)現(xiàn)對(duì)雨效透明度的調(diào)整颖御,不過(guò)本文給出的方案并沒有采取這種做法。
為了模擬雨滴墜落時(shí)的動(dòng)態(tài)效果以及近大遠(yuǎn)小近疏遠(yuǎn)密的真實(shí)感凝颇,對(duì)于每一層貼圖的采樣都會(huì)根據(jù)速度與尺寸的不同使用不同的uv縮放潘拱,另外,為了模擬風(fēng)力作用效果拧略,還會(huì)對(duì)uv的采樣方向進(jìn)行旋轉(zhuǎn)芦岂。
float2 SinT = sin(Time.xx * 2.0f * Pi / speed.xy) * scale.xy;
// rotate and scale UV
float4 Cosines = float4(cos(SinT), sin(SinT));
float2 CenteredUV = UV - float2(0.5f, 0.5f);
float4 RotatedUV = float4(dot(Cosines.xz*float2(1,-1), CenteredUV)
, dot(Cosines.zx, CenteredUV)
, dot(Cosines.yw*float2(1,-1), CenteredUV)
, dot(Cosines.wy, CenteredUV) ) + 0.5f);
float4 UVLayer12 = ScalesLayer12 * RotatedUV.xyzw;
為了能讓雨效能表現(xiàn)出正確的遮擋效果(比如室內(nèi)無(wú)雨),這里采用的方法是根據(jù)前面劃分的區(qū)域垫蛆,根據(jù)高度圖選定對(duì)應(yīng)的深度數(shù)值(如何選定禽最?這里的高度圖是為camera前的每一個(gè)區(qū)域所單獨(dú)生成的,對(duì)高度圖采樣的目的是為了實(shí)現(xiàn)雨點(diǎn)前后錯(cuò)落的效果月褥,制造更強(qiáng)的立體感)作為雨滴的深度弛随,并根據(jù)射線方向重建雨滴的虛擬世界坐標(biāo)瓢喉,高度圖最好通過(guò)程序的方式漸進(jìn)生成宁赤。(如果只是選取個(gè)別點(diǎn)進(jìn)行采樣,感覺精度會(huì)有較大損失栓票,而且在相機(jī)移動(dòng)的時(shí)候决左,可能會(huì)出現(xiàn)雨效的頻繁切換與抖動(dòng))
為了避免前面提到的生硬過(guò)渡問(wèn)題,這里給出了軟切的實(shí)現(xiàn)方法走贪,即通過(guò)漸進(jìn)式的調(diào)整雨滴的透明度來(lái)實(shí)現(xiàn)雨幕從無(wú)到有之間的切換佛猛,從而避免pop in/out的問(wèn)題。這種方案的缺陷在于如果相機(jī)朝向地面坠狡,就會(huì)導(dǎo)致雨幕消失(因?yàn)橛昴凰幍纳疃缺坏乇硭鶕踝〖陶遥鋵?shí)也是采樣點(diǎn)數(shù)目過(guò)少導(dǎo)致的弊端)
對(duì)于室內(nèi)雨景遮擋的實(shí)現(xiàn),可以通過(guò)雨景深度圖與采樣點(diǎn)的位置進(jìn)行比對(duì)來(lái)實(shí)現(xiàn)逃沿,通過(guò)硬件PCF的方式還能夠提升效果跟效率婴渡。
出于對(duì)性能的考慮幻锁,這里只對(duì)前兩層進(jìn)行遮擋剔除計(jì)算,因此雨景深度貼圖只需要覆蓋前兩層即可边臼,沒看懂偽代碼中將occlusionDistance跟深度測(cè)試結(jié)果相乘的目的在哪里
// Background Pixel depth - in view space
float Depth = CalcSceneDepth(ScreenPosition);
// Layers Depth tests :
float2 VirtualDepth = 0;
// Constant are based on layers distance
VirtualDepth.x = tex2D(Heightmap, UVLayer12.xy).r * RainDepthRange.x + RainDepthStart.x;
VirtualDepth.y = tex2D(Heightmap, UVLayer12.zw).r * RainDepthRange.y + RainDepthStart.y;
// Mask using virtual position and the scene depth
float2 OcclusionDistance = saturate((Depth - VirtualDepth) * 10000.0f);
// Calc virtual position
float3 Dir = normalize(PixelPosition); // Cylinder is link to camera
float3 VirtualPosition1WS = CameraPositionWS.xyz + Dir * DepthLayers.x;
float3 VirtualPosition2WS = CameraPositionWS.xyz + Dir * DepthLayers.y;
// Mask using virtual layer depth and the depth map
// RainDepthMapTest use the same projection matrix than
// the one use for render depth map
float2 Occlusion= 0;
Occlusion.x = RainDepthMapTest(VirtualWPos1);
Occlusion.y = RainDepthMapTest(VirtualWPos2);
Occlusion*= OcclusionDistance;
根據(jù)layer的距離與全分辨率深度buffer的關(guān)系哄尔,還會(huì)進(jìn)行一次平滑的遮擋處理,以減輕前面使用低分辨率做遮擋測(cè)試帶來(lái)的瑕疵柠并。
// Depth is in view space
// RainDepthStart contain the start distance of each layer
// RainDepthRange contain the area size of each layer
float4 Mask = saturate((Depth - RainDepthStart) / RainDepthRange);
為了避免后面兩層雨效出現(xiàn)重復(fù)紋理岭接,以及出于效果優(yōu)化的考慮,還會(huì)使用兩張動(dòng)態(tài)生成的低分辨率動(dòng)畫貼圖來(lái)對(duì)效果進(jìn)行疊加臼予。
void RainLowPixelShader(...){ // Mask with magic values (detail are not provide as this is "artistic" feature)// Layer 3float2 NoiseUV = tex2D(DistortionTexture, DistoUV.xy).xy+ tex2D(DistortionTexture, DistoUV.zw).xy; NoiseUV = NoiseUV * UV.y * 2.0f + float2(1.5f, 0.7f)*UV.xy+ float2(0.1f, -0.2f) * Time; float LayerMask3 = tex2D(NoiseTexture, NoiseUV) + 0.32f; LayerMask3 = saturate(pow(2.0f * Layer1, 2.95f) * 0.6f); // Layer 4 float LayerMask4 = tex2D(NoiseTexture, BlendUV.xy)+ tex2D(NoiseTexture, BlendUV.zw) + 0.37f;// Background Pixel depth - in view spacefloat Depth = CalcSceneDepth(ScreenPosition);// Layers Depth tests :float2 VirtualDepth = 0;// Constant are based on layers distanceVirtualDepth.x = tex2D(Heightmap, UVLayer12.xy).r * RainDepthRange.x + RainDepthStart.x;VirtualDepth.y = tex2D(Heightmap, UVLayer12.zw).r * RainDepthRange.y + RainDepthStart.y;// Mask using virtual position and the scene depthfloat2 OcclusionDistance = saturate((Depth - VirtualDepth) * 10000.0f);// Calc virtual positionfloat3 Dir = normalize(PixelPosition); // Cylinder is link to camerafloat3 VirtualPosition1WS = CameraPositionWS.xyz + Dir * DepthLayers.x;float3 VirtualPosition2WS = CameraPositionWS.xyz + Dir * DepthLayers.y;// Mask using virtual layer depth and the depth map// RainDepthMapTest use the same projection matrix than// the one use for render depth mapfloat2 Occlusion= 0;Occlusion.x = RainDepthMapTest(VirtualWPos1);Occlusion.y = RainDepthMapTest(VirtualWPos2);Occlusion*= OcclusionDistance;OutColor = float4(Occlusion.xy, LayerMask3, LayerMask4);
}
// Depth is in view space
float Depth = CalcSceneDepth(ScreenPosition);
// RainDepthMin contain the start distance of each layer
// RainDepthRange contain the area size of each layer
// RainOpacities allow to control opacity of each layer (useful with lightning
// or to mask layer)
float4 Mask = RainOpacities * saturate((Depth - RainDepthStart) / RainDepthRange);
float2 MaskLowUV = ScreenPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f);
float4 MaskLow = tex2D(RainLowTexture, MaskLowUV);
float4 Values;
Values.x = tex2D(RainTexture, CylUVLayer1.xy);
Values.y = tex2D(RainTexture, CylUVLayer1.zw);
Values.z = tex2D(RainTexture, CylUVLayer2.xy);
Values.w = tex2D(RainTexture, CylUVLayer2.zw);
// The merge of all mask: occlusion, pattern, distance is perform here
float RainColor = dot(Values, Mask * MaskLow);
float3 FinalColor = RainColor.xxx * 0.09f * RainIntensity;
給美術(shù)同學(xué)提供了一個(gè)不透明度參數(shù)鸣戴,可以允許自由的參數(shù)設(shè)定,此外瘟栖,當(dāng)閃電來(lái)臨時(shí)葵擎,也可以調(diào)整這個(gè)參數(shù)生成更接近真實(shí)的效果。提供的雨效強(qiáng)度參數(shù)也可以用來(lái)制造不同程度的降雨效果半哟,實(shí)現(xiàn)方式為對(duì)雨幕貼圖上的雨點(diǎn)進(jìn)行移除酬滤,因此雨幕貼圖給出的應(yīng)該是最大強(qiáng)度的降雨效果。
出于優(yōu)化的考慮寓涨,整個(gè)過(guò)程分成兩個(gè)pass完成盯串,第一個(gè)是低分辨率pass,分辨率為原分辨率的1/4戒良,第二個(gè)是全分辨率pass体捏。全分辨率pass中會(huì)處理其他所有所有相關(guān)的后處理操作,比如motion blur糯崎,DOF几缭,color grading,tonemapping等沃呢,從而避免重復(fù)勞動(dòng)年栓。
兩種設(shè)備上的消耗大概為2ms左右。
這種實(shí)現(xiàn)的問(wèn)題在于透過(guò)半透物體看到的雨效與透過(guò)完全透明物體看到的雨效是一樣的薄霜。
可以考慮添加一張與雨幕貼圖相匹配的強(qiáng)度貼圖某抓,用于實(shí)現(xiàn)對(duì)不同雨滴強(qiáng)度的精確控制
float3 SiTransmissionDirection (float fromIR, float toIR,
float3 incoming, float3 normal)
{
float eta = fromIR/toIR; // relative index of refraction
float c1 = -dot (incoming, normal); // cos(theta1)
float cs2 = 1.-eta*eta*(1.-c1*c1); // cos^2(theta2)
float3 v = (eta*incoming + (eta*c1-sqrt(cs2))*normal);
if (cs2 < 0.) v = 0; // total internal reflection
return v;
}
// Reimplemented - Not in the shaderX 5 article
float3 SiReflect (float3 view, float3 normal)
{
return 2*dot (view, normal)*normal - view;
}
// In the main shader:
// Retrieve the normal map normal for rain drops:
float3 vNormalTS = tex2Dproj( tBump, texCoord ).xyz;
vNormalTS = SiComputeNormalATI2N( vNormalTS );
// Compute normal in world space:
float3x3 mTangentToWorld = float3x3( normalize( i.vTangent ),
normalize( i.vBinormal ), normalize( i.vNormal ));
float3 vNormalWS = normalize( mul( mTangentToWorld, vNormalTS ));
// Compute the reflection vector:
float3 vReflectionWS = SiReflect( vViewWS, vNormalWS );
(...)
// Environment contribution:
float3 cReflection = texCUBE( tEnvironment, vReflectionWS );
(...)
// Approximate fresnel term
float fFresnel = SiComputeFresnelApprox( vNormalWS, vViewWS );
// Compute refraction vector: 0.754 = 1.003 (air) / 1.33 (water)
float3 vRefractWS = SiTransmissionDirection( 1.003, 1.33, vViewWS, vNormalWS );
// Refraction contribution:
float3 cRefraction = texCUBE( tEnvironment, vRefractWS );
(...)
cResult = saturate( (cReflection * fFresnel * 0.25f) +
cRefraction * (1.0f - (fFresnel * 0.75 )));
這里給出了雨滴光照實(shí)現(xiàn)的一些優(yōu)秀方法。
Droplets / wall glides / additional rain
除了雨效之外惰瓜,雨水與場(chǎng)景的交互效果也很重要否副,這些效果通常都是由美術(shù)同學(xué)預(yù)先設(shè)定好的,不太適合根據(jù)雨水的強(qiáng)度進(jìn)行動(dòng)態(tài)設(shè)置(頂多使用一個(gè)blend進(jìn)行fade in/out)崎坊,Toy Shop demo給出了一些程序式的實(shí)現(xiàn)方法备禀。
玻璃上的水流通過(guò)貼圖采樣+uv動(dòng)畫實(shí)現(xiàn),貼圖采樣通常會(huì)使用不同的參數(shù)進(jìn)行兩次采樣,之后在此基礎(chǔ)上還會(huì)根據(jù)需要進(jìn)行一次你去處理曲尸,考慮到折射效果呻待,可能還需要跟環(huán)境貼圖相結(jié)合。
玻璃上的水流通過(guò)貼圖采樣+uv動(dòng)畫實(shí)現(xiàn)队腐,貼圖采樣通常會(huì)使用不同的參數(shù)進(jìn)行兩次采樣蚕捉,之后在此基礎(chǔ)上還會(huì)根據(jù)需要進(jìn)行一次你去處理,考慮到折射效果柴淘,可能還需要跟環(huán)境貼圖相結(jié)合迫淹。
屋檐的水滴通常是由特效來(lái)實(shí)現(xiàn),目標(biāo)是最好能做到通用为严。
這個(gè)效果的實(shí)現(xiàn)方式跟玻璃上的水流一致敛熬,區(qū)別在于需要考慮水流侵入墻體的視覺感。
根據(jù)情況是使用特效面片還是使用網(wǎng)格第股,網(wǎng)格效果更好且特效面片在有些情況中可能會(huì)造成較多的overdraw应民?(是因?yàn)閯?dòng)態(tài)效果需要多層疊加的原因吧)
根據(jù)情況采用不同的雨效實(shí)現(xiàn)方案
Camera droplets
鏡頭雨效是增強(qiáng)真實(shí)感的重要元素,當(dāng)鏡頭往上抬起來(lái)的時(shí)候夕吻,就需要考慮鏡頭受雨點(diǎn)影響帶來(lái)的效果變化诲锹,一種方法是通過(guò)后處理來(lái)跟一個(gè)扭曲貼圖進(jìn)行blend,blend需要考慮鏡頭方向的變化涉馅,這種方法的弊端在于:
1.效果不夠動(dòng)態(tài)
2.消耗較高(全屏)
這里給出的第二種方案是屏幕粒子系統(tǒng)归园,粒子系統(tǒng)具有可控性好渲染性能高的優(yōu)點(diǎn)。
屏幕空間粒子系統(tǒng)的實(shí)現(xiàn)不是很好做稚矿,一種可行的方法是將粒子系統(tǒng)貼在相機(jī)空間的近平面上庸诱, 并將之轉(zhuǎn)換函數(shù)跟相機(jī)相綁定,這種方法的缺陷在于更改FOV之后會(huì)導(dǎo)致效果的變化(就像屏幕被拉伸一樣)晤揣,不過(guò)這種影響基本上是可以忽略的桥爽,偽代碼給出如下:
if (ParticleSystem->UseAsCameraLensEffect())
{
// View matrix transform from world to view.
// Here we want that's the view transform
// has no effect as if we were spanwing in view space.
// So use inverse of the view matrix.
ParticleSystem->LocalToWorld = View->InvViewMatrix;
}
出于對(duì)性能的考慮,上面還進(jìn)行了特效的裁剪昧识。
整個(gè)效果大概需要花費(fèi)0.4ms左右
Rain effects control panel
這里給出供美術(shù)同學(xué)使用的一些參數(shù)的定義钠四,詳情請(qǐng)參見原文。
這里給出了雨滴相關(guān)參數(shù)與降水量的一個(gè)關(guān)聯(lián)公式滞诺,其中重要的是不同參數(shù)選擇對(duì)于曲線的影響:
現(xiàn)實(shí)世界中的雨滴尺寸實(shí)際上是不一致的形导,存在一定的分布規(guī)律环疼,且雨的強(qiáng)度越大习霹,分布的范圍也就越廣。
游戲中炫隶,前面給出的一些規(guī)律沒有太大的使用價(jià)值淋叶,這里給出的實(shí)現(xiàn)方式為根據(jù)雨效強(qiáng)度線性提升雨滴數(shù)目,主要分成三類雨:
這里給出的參數(shù)可以根據(jù)雨效強(qiáng)度進(jìn)行插值伪阶。
Reference
[1] Barrero, “Relic’s FX system: Making big battles comes Alives”, http://www.slideshare.net/proyZ/relics-fx-system
[2] Tatarchuck, “Artist directable real-time rain rendering in city environments ” http://www.ati.com/developer/gdc/2006/GDC06-Advanced_D3D_Tutorial_Day-Tatarchuk-Rain.pdf
[3] Tatarchuck, “Rendering Multiple Layers of Rain with a Post-Processing Composite Effect”, ShaderX 5
[4] Tatarchuck, “Artist-Directable Real-Time Rain Rendering in City Environments”, http://developer.amd.com/wordpress/media/2012/10/Tatarchuk-Isidoro-Rain%28EGWNph06%29.pdf
[5] Alexander, Johnson, “The Art and Technology Behind Bioshock’s Special Effects”, http://gdcvault.com/play/289/The-Art-and-Technology-Behind
[6] Garg, K. Nayar, “Photometric Model of a Rain Drop”, http://www1.cs.columbia.edu/CAVE/publications/pdfs/Garg_TR04.pdf
[7] Garg, Krishnan, K. Nayar , “Material Based Splashing of Water Drops”, http://www1.cs.columbia.edu/CAVE/projects/mat_splash/
[8] Garg, K. Nayar , “Photorealistic Rendering of Rain Streaks”, http://www1.cs.columbia.edu/CAVE/projects/rain_ren/rain_ren.php
[9] Thornton, “Directable Simulation of Stylized Water Splash Effects in 3D Space”, http://nguyendangbinh.org/Proceedings/Siggraph/2006/cd2/content/sketches/0106-thornton.pdf
[10] Persson, “Graphics Gems for Games – Findings from Avalanche Studios”, http://www.humus.name/index.php?page=Articles
[11] Garg, Krishnan, K. Nayar , “Vision and Rain”, http://www1.cs.columbia.edu/CAVE/publications/pdfs/Garg_IJCV07.pdf
[12] Wang, Wade, “Rendering falling rain and sown”, http://www.ofb.net/~niniane/rainsnow/rainsnow-sketch.pdf
[13] Tariq, “Rain”, http://developer.download.nvidia.com/SDK/10/direct3d/Source/rain/doc/RainSDKWhitePaper.pdf
[14] Feng, Tang, Dong, Chou, “Real-Time Rain Simulation”
[15] Glossary of Meteorology, http://amsglossary.allenpress.com/glossary/search?id=rain1
[16] Rain – Wikipedia, http://en.wikipedia.org/wiki/Rain
[17] Marshall, Palmer, “The distribution of raindrops with size”, http://journals.ametsoc.org/doi/pdf/10.1175/1520-0469%281948%29005%3C0165%3ATDORWS%3E2.0.CO%3B2
[18] Kircher, “Lighting & Simplifying Saints Row: The Third”, http://twvideo01.ubm-us.net/o1/vault/gdc2012/slides/Programming%20Track/Kircher_Lighting_and_Simplifying_Saints_Row_The_Third.pdf
[19] NASA, “Precipitation, Fog And Icing”, https://standards.nasa.gov/released/1001/1001_7.pdf
[20] Rousseau, Jolivet, Ghazanfarpour, “Realistic real-time rain rendering”, http://www.pierrerousseau.fr/rain.html