最近接了一個實現(xiàn)體積云風(fēng)格化染色的需求拔创,策劃大致的意思是想實現(xiàn)如下面幾張圖片示意的效果:
目前實現(xiàn)的體積云顏色效果大多是通過以白色作為基色奸笤,經(jīng)過陰影疊加后呈現(xiàn)出來的克蚂,大致如下圖所示:
在疊加了太陽光之后俱诸,可以做到如下的效果:
可以看到垂攘,其效果距離預(yù)期還有不小的差距维雇,因此需要對方案做修正。
先對參考圖中的效果做一下簡單的分析晒他,Ref 2中表現(xiàn)出來的效果通過一個簡單的疊色就能夠輸出吱型,而不同區(qū)域不同的顏色效果就需要結(jié)合疊色貼圖來給出(或者按照某些規(guī)則,比如與大氣散射相結(jié)合來給出不同位置的不同疊色)陨仅;其余三張貼圖津滞,假如不考慮陽光打在云層底部散射造成的明亮著色,其實也就是一個簡單的疊色就能輸出灼伤,因此總的來說触徐,參考圖中的效果主要包含兩項:
- 疊色,如果想要實現(xiàn)局部疊色狐赡,就需要給出一種規(guī)則撞鹉,在不同的區(qū)域輸出不同的疊色效果。
- inscattering散射颖侄,通過相函數(shù)對太陽光散射效果進(jìn)行模擬
下面我們一起來看一下要想實現(xiàn)給定的著色效果鸟雏,需要做怎么樣的處理。
為了快速確定方案览祖,這里使用shadertoy上現(xiàn)成的案例來進(jìn)行驗證孝鹊,這里是測試方案地址,如不做說明展蒂,下面不同方案嘗試中的效果截圖都來自于此方案又活。
1. 方案預(yù)研
1.1 原始著色
首先來看一下,不做任何處理下的云層效果表現(xiàn)锰悼,如下圖所示:
基本顏色為白色柳骄,通過一些灰色來表示云層對光線的遮擋效果。
1.2 全局疊色
所謂的全局疊色指的是在原始著色的基礎(chǔ)上乘上一個給定的顏色值來實現(xiàn)云層著色的方案箕般,這個方案用如下公式來表示:
return float4(originCol.rgb * tintCol, originCol.a);
效果圖:
從結(jié)果可以看出夹界,雖然相對于原始著色方案而言,確實可以給出非常豐富的著色效果隘世,但是相距預(yù)期方案中豐富的層次變化還有一定距離可柿。
1.3 明暗過渡著色
所謂的明暗過渡著色,指的是根據(jù)原始方案中的著色結(jié)果作為權(quán)重值對兩個給定的顏色進(jìn)行混合的著色方案丙者,用公式來表示:
float3 finalCol = saturate(lerp(darkCol, brightCol, originCol.rgb));
return float4(finalCol, originCol.a);
效果圖:
從效果上來看复斥,明暗過渡顏色在明暗顏色一脈相承(比如都是紅色,只是亮度不相同)時械媒,可以得到比較自然的表現(xiàn)目锭,且對于顏色過渡的力度控制更為細(xì)膩,但是在明暗顏色相去較遠(yuǎn)時痢虹,就會使得效果比較魔幻。
1.4 漸變著色
明暗過渡是通過兩個顏色的混合來實現(xiàn)顏色過渡的主儡,但是從前面的參考效果來看奖唯,云層顏色效果可能會涉及到不少于兩種顏色的參與,比如可以從顏色A過渡到B糜值,再過渡到C的情況丰捷,對于這種情況,可以考慮使用類似photoshop的漸變方案來進(jìn)行著色寂汇,其實現(xiàn)可以用如下公式來表示:
float points[] = float[](0.25, 0.55, 0.9, 0.93, 0.98);
vec3 cols[] = vec3[](
vec3(1.5, 0.0, 0.0),
vec3(1.0, 0.4, 0.6),
vec3(1.0, 0.6, 0.7),
vec3(1.0, 0.8, 0.2),
vec3(1.0, 1.0, 0.0)
);
int colNum = points.length();
float originalCol = OriginalColor.r;
float scale = 1.0;
originalCol = originalCol * scale;
if(originalCol < points[0])
return cols[0];
for(int i = 1; i < colNum; i++)
{
if(originalCol < points[i])
return mix(cols[i - 1], cols[i], (OriginalColor.r - points[i - 1])/(points[i] - points[i - 1]));
}
if(originalCol > points[colNum - 1])
return cols[colNum - 1];
效果圖:
float points[] = float[](0.0, 0.25, 0.55, 0.9, 0.93);
vec3 cols[] = vec3[](
vec3(0.0, 0.0, 0.0),
vec3(0.6, 0.0, 0.0),
vec3(1.0, 0.4, 0.6),
vec3(1.0, 0.65, 0.6),
vec3(1.0, 0.8, 0.6)
);
float points[] = float[](0.0, 0.25, 0.55, 0.9, 0.93, 1.0);
vec3 cols[] = vec3[](
vec3(0.0, 0.0, 0.0),
vec3(1.0, 0.4, 0.6),
vec3(0.5, 0.5, 0.6),
vec3(1.0, 0.6, 0.7),
vec3(1.0, 0.7, 0.7),
vec3(1.0, 1.0, 0.7)
);
float points[] = float[](0.0, 0.25, 0.55, 0.9, 0.93);
vec3 cols[] = vec3[](
vec3(0.0, 0.0, 0.1),
vec3(0.6, 0.0, 0.6),
vec3(1.0, 0.4, 0.6),
vec3(1.0, 0.65, 0.6),
vec3(1.0, 0.7, 0.6)
);
這種方案可以給出多層漸變的著色效果病往,但是存在如下的一些問題:
- 參與漸變的顏色需要保持一致,顏色跳變會使得效果非常難看
- 依然無法模擬參考圖中太陽光打在云層底部經(jīng)由相函數(shù)散射出來的明亮效果(如Ref4的明黃色)骄瓣。
1.5 添加inscattering相函數(shù)散射
在原始方案中添加相函數(shù)控制的inscattering停巷,已經(jīng)能基本模擬底部受太陽光照下的金邊效果:
在此基礎(chǔ)上再添加疊色方案,可以得到如下結(jié)果:
這個方案雖然可以生成邊緣處的金邊榕栏,但是在背光處的黑色太深了畔勤,這是因為只考慮了單次散射的效果,為了消除這種死黑的表現(xiàn)臼膏,還需要添加天光與大地反射導(dǎo)致的散射硼被。
添加了大地散射后,可以做到如下的效果:
在此基礎(chǔ)上再添加天光散射渗磅,可以得到如下效果:
這是未做任何疊色的情況下輸出的結(jié)果嚷硫,其中黃色表示的是太陽光散射的效果,紫色表示的是大地散射的效果仔掸,藍(lán)色表示的是天光散射的效果起暮。
可以看到会烙,在這種算法作用下,不需要做疊色方案系吭,就已經(jīng)能夠得到比前面的疊色方案還要好的結(jié)果肯尺。
2. 方案驗證
shadertoy的實現(xiàn)較為簡單,不過已經(jīng)基本驗證了體積云著色的相關(guān)效果锄蹂,為了能應(yīng)用到項目中寨昙,最終還是需要在引擎中進(jìn)行相關(guān)著色處理舔哪,下面給出在UE4.26中應(yīng)用各種inscattering之后的效果表現(xiàn)捉蚤。
2.1 現(xiàn)有染色方案
2.1.1 太陽光染色
UE4.26提供了一種雙光源體積云著色方案,其中第一盞光源是默認(rèn)的太陽光陕悬,通過對太陽光顏色與亮度進(jìn)行調(diào)整捉超,可以影響到云層的染色效果:
這種方案的一個問題是,這種染色不但會影響到云層染色效果惜纸,還會影響到大氣(天空)散射效果祠够,沒有辦法做到解耦哪审。而要想避開對大氣散射的影響,則需要調(diào)整光源的另一項屬性:
Atmosphere Sun Light開關(guān)用于控制此光源是否用于Cloud/Atmosphere著色,當(dāng)關(guān)閉時痛侍,云層繪制全黑主届。當(dāng)開啟后,就可以通過調(diào)整Cloud Scattered Luminance來調(diào)整散射顏色绘闷。
這種狀態(tài)下的云層著色就不會影響到大氣散射了印蔗。
2.1.2 雙太陽光染色
再嘗試打開第二盞光源华嘹,依然是平行光耙厚,得到的效果并不能令人滿意,除了效果調(diào)整十分費(fèi)勁之外泛豪,且沒有辦法如前面shadertoy demo一樣臀叙,能夠?qū)崿F(xiàn)多層染色劝萤。
2.1.2 Ground染色
VolumetricCloudComponent上有一個變量叫GroundAlbedo,從描述上來看跟我們前面在shadertoy demo中的ground inscattering相似厌处,先來看下這個參數(shù)調(diào)整是否能得到不一樣的表現(xiàn)。
這個開關(guān)的打開需要在材質(zhì)的Volumetric Advanced節(jié)點(diǎn)中打開GroundContribution開關(guān)瑰排,但實際上這個節(jié)點(diǎn)并沒有這個選項:
這是怎么回事呢?實際上這個是在此節(jié)點(diǎn)的選項面板中進(jìn)行打開的:
此外,在源碼追蹤的過程中傻挂,我們發(fā)現(xiàn)Shader在處理Ground相關(guān)的著色時金拒,還存在一個由宏MATERIAL_VOLUMETRIC_ADVANCED_GROUND_CONTRIBUTION進(jìn)行控制的實現(xiàn)邏輯,這個宏
同樣也是由上述開關(guān)進(jìn)行控制幢码。
打開之后症副,調(diào)節(jié)GroundAlbedo闹啦,效果對比如下所示:
可以看到,通過對顏色的調(diào)節(jié),可以實現(xiàn)底部染色的修正挚歧,不過這個修正效果不是很明顯,也就是說,GroundAlbedo對云層的影響力度比較小啄骇,是否可以增強(qiáng)這個影響呢?通過對源碼的分析虽惭,發(fā)現(xiàn)當(dāng)前并無此scale因子,而要想實現(xiàn)就需要添加額外的代碼進(jìn)行處理。
下面給出Cloud Scattered Luminance為綠色谱邪,GroundAlbedo為紫色時的表現(xiàn):
在此基礎(chǔ)上對陽光顏色及亮度進(jìn)行修正,可以得到如下效果:
從上面給出的效果可以看到惦银,GroundAlbedo跟CloudScattered Luminance兩個參數(shù)都是能夠很好的用于調(diào)整云層著色效果的咆课,只是GroundAlbedo影響的強(qiáng)度是固定的(CloudScattered Luminance由于數(shù)據(jù)使用的是浮點(diǎn)數(shù)末誓,可以直接調(diào)整亮度),使用起來不太靈活傀蚌,這里嘗試增加GroundAlbedoScale參數(shù)基显,用于對這個參數(shù)進(jìn)行增強(qiáng)。
2.1.3 Modified CloudScattering/GroundAlbedo
在shader與cpp代碼中添加了GroundAlbedoScale參數(shù)后,可以實現(xiàn)更為明顯的GroundAlbedo調(diào)整效果艺谆,下面給出的是僅僅調(diào)整GroundAlbedoScale的效果對比:
測試的過程中發(fā)現(xiàn)抹估,Shader中存在multiscattering相關(guān)的邏輯贸典,這個邏輯是通過Volumetric Advanced Output節(jié)點(diǎn)的Multi Scattering Approximation Octave Count參數(shù)進(jìn)行控制的班利,此參數(shù)對應(yīng)的是在RayMarching的過程中,射線上的每個Sample在沿著太陽光方向的二次采樣的采樣點(diǎn)數(shù)目丙笋,默認(rèn)為1.0(實際上shader使用的采樣點(diǎn)數(shù)目是在這個基礎(chǔ)上加了1),下面我們改成0換個角度看下效果:
其中綠色是GroundAlbedo的顏色者甲,黃色是太陽光顏色,紫色是Cloud Scattered Luminance的顏色,看起來效果相對于1.0還更柔和一點(diǎn)氧骤。
此外并思,在Volumetric Advanced Output節(jié)點(diǎn)的屬性面板上還有一個Per Sample Phase Evaluation開關(guān)占贫,這個從名字上來判斷,就是用于為射線上的每個sample做單獨(dú)的phase計算凡蜻,這個開關(guān)打開會加重計算消耗,因此在使用的時候需要確認(rèn)是否有必要。
通過對各種參數(shù)的調(diào)整寓免,我們是可以實現(xiàn)如下的一些效果的:
相對于ShaderToy的Demo而言袜香,UE4.26渲染的體積云蜈首,在底部缺少了云層遮蔽導(dǎo)致的黑色效果,從而顯得立體感不足辣卒,我們嘗試來確定這是UE實現(xiàn)機(jī)制的問題疗杉,還是因為參數(shù)調(diào)整不當(dāng)導(dǎo)致狸演。
下面給出了不同Albedo Color下的云層表現(xiàn),可以看到僻他,Albedo Color是可以用于調(diào)整云層底部的亮度的宵距。
雖然如此,云層底部顏色依然無法出現(xiàn)demo中接近黑色的表現(xiàn)吨拗,理論上來說满哪,太陽光從上往下照射時,如果不考慮天光等間接光劝篷,那么云層底部肯定是黑色的哨鸭,而當(dāng)前這種表現(xiàn)只能說明是間接光對于云層渲染具有較大的影響,如果希望削弱這個影響娇妓,就需要降低間接光對云層著色效果的比重像鸡。
反應(yīng)到shader代碼中,我們可以將DistantLightLuminance對SunSkyLuminance的累加作用置為0峡蟋,就可以得到如下這種比較接近的效果(遠(yuǎn)景處存在一些問題坟桅,可能因為相對于近景處,采樣數(shù)目存在差異導(dǎo)致)
作為對比蕊蝗,打開這個邏輯時的效果表現(xiàn)如下圖所示:
不考慮染色效果作用的話仅乓,后者相對而言更接近現(xiàn)實情況中的表現(xiàn)。
此外蓬戚,在調(diào)試效果的過程中夸楣,發(fā)現(xiàn)GroundAlbedo并不是像此前Demo中一樣通過沿著大地散射方向二次采樣得到,而是按照如下的公式計算得到ScatteredLightLuminance之后疊加到最終輸出的Color上面子漩。
float3 ScatteredLightLuminance = AtmosphereTransmittanceToGround * LightIlluminance * GroundToCloudTransfertIsoScatter;
其中AtmosphereTransmittanceToGround從名字上推斷應(yīng)該是從Ground到當(dāng)前被采樣點(diǎn)位置的Atmosphere Transmittance豫喧,即未考慮云層遮擋情況下的Transmittance,但實際我們知道幢泼,對于云層表面的點(diǎn)紧显,這種計算是合理的,而如果采樣點(diǎn)位于云層內(nèi)部缕棵,這種做法就存在較大誤差了孵班;LightIlluminance對應(yīng)的是AtmosphereLightColor,從C++側(cè)賦值邏輯來看招驴,這個值就是太陽光顏色篙程;最后GroundToCloudTransfertIsoScatter對應(yīng)的是GroundAlbedo經(jīng)過一系列加工后的結(jié)果,反映的是光照打在大地上的反射輸出關(guān)系(相當(dāng)于經(jīng)過了BRDF處理)别厘。
從這個公式看出虱饿,ScatteredLightLuminance實際上指的是太陽光顏色經(jīng)過大地反射后加上大氣散射衰減后的結(jié)果。不過正如前面所說,這里的結(jié)果沒有考慮云層內(nèi)部遮擋作用氮发,因此效果上會有些差異渴肉。這里嘗試對散射衰減項進(jìn)行修正,添加上云層內(nèi)部穿透代價折柠,看看效果上是否能有所改善宾娜。
2.1.4 Secondary Raymarching Scattering
UE實現(xiàn)中是增加了沿著太陽光方向的二次散射計算以實現(xiàn)云層背離太陽時的陰影效果的,而這個功能是通過Volumetric Advanced Output節(jié)點(diǎn)屬性面板的RayMarchVolumeShadow開關(guān)進(jìn)行打開的扇售,關(guān)閉這個開關(guān)時前塔,就會使用shadow map來計算云層陰影(包括light shaft),打開時就會啟用沿著光源方向的二次RayMarching進(jìn)行計算承冰,關(guān)閉跟打開開關(guān)時的效果對比如下所示:
看起來對比不是特別明顯华弓,不過在關(guān)閉情況下,底部相對更暗一點(diǎn)困乒。
這里增加沿著向下方向的Ground Inscattering處理邏輯寂屏,查看是否對效果有所增益:
從效果上來看,是有變化的娜搂,但是這個變化方向是有還是劣暫時還不清楚迁霎,調(diào)整下其他效果看下:
看起來關(guān)閉情況下飽和度要高一點(diǎn),不過也不清楚美術(shù)同學(xué)傾向于哪種風(fēng)格百宇,這里將這套邏輯通過Volumetric Advanced Output節(jié)點(diǎn)的選項面板上的RayMarchVolumeGroundScatter開關(guān)進(jìn)行控制考廉,默認(rèn)關(guān)閉。