寫給美術看的Unity全域性光照技術(理論篇)
很久沒有混跡知乎了,今天突然起了興致,決定放一篇遠古時期寫的文章,分享給大家。
文章知識點是我當時對Unity GI的認識,技術大佬們看到有紕漏的地方就湊合著看吧哈哈,不過這些內容對美術來說,還是是一定幫助的。
寫在前面
什麼是全域性光照? 全域性照明(GI)是一個系統,用於模擬光從表面反射到其他表面(間接光)的方式,而不是僅限於直接從光源(直接光)照射到表面的光。 全域性光照 = 直接光照 + 間接光照那什麼是直接光照?什麼是間接光照?
在Unity4中,我們可以試試打一盞燈照亮物體,會發現物體在光源沒有覆蓋的地方,是一片死黑的。這就是僅考慮直接光照的結果。現實世界中,光線到達物體表面後被彈射開去,在空氣中傳播,在各個表面來回彈射,直到能量消耗殆盡。可以想象一束光投進窗戶,從而整個房間的陰暗角落也被照亮的畫面。這就是間接光照效果。然而,實現間接光照的演算法非常複雜,計算速度太慢,用過3D Max渲染的同學應該知道渲染一張圖片需要多長時間。所以,面對遊戲等實時渲染領域,一秒鐘需要渲染數十張圖片以達到流暢的畫面感的要求,要實現全域性光照(GI)效果,顯然需要另尋蹊徑。
在遊戲領域,解決這一侷限性的一種方式就是:只為預先知道不會移動的物體(即被標記為靜態的物體)計算間接光照。這樣慢的計算可以提前完成,同時由於物件不移動,所以以這種方式預先計算的全域性光照在執行時仍然是正確的。 Unity支援這種稱為Baked GI(也稱為Baked Lightmaps)的技術,也就是我們常說的“烘焙”。除了間接光照外,Baked GI還利用了更多的計算時間,可以從area light和間接光中生成更逼真的柔和陰影。至此,Unity4烘焙出來的lightmap資訊包括了直接光+間接光+烘焙陰影。
ps:全域性照明效果對於動態物體也是有辦法的。Unity提供了一套Light Probe的技術,可以將光照資訊儲存在這些預先佈置好的燈光探頭中,動態物體從這些探頭中獲得光照資訊。當然,效果不會很精細。這種技術後面再做論述。
然而隨著對遊戲畫面要求的日益提升,這種方法暴露了諸多侷限性與不足。為此,Unity5提供了一套更為完善的光照技術解決方案。有如下特點: - 支援預計算的實時全域性光照效果(Precomputed Realtime Global Illumination)。 - 更完善的混合光照模式。 - 加入環境反射探針(refletion probe),以適配PBR效果。
EnLighten全域性光照系統
Enlighten為我們提供了預計算的實時全域性光照(Precomputed Realtime Global Illumination)和混合光照(Mixed Lighting),這些技術不一定都用得上。根據專案要求和畫面特點,選擇效能和效果最平衡的技術方案,才是我們學習這套系統的關鍵。
組成部分
一般來說,Unity中實現照明的方法可以被分為“實時”和“預計算(烘焙)”兩種,都可以結合使用來創造逼真的場景照明。因此,我們需要了解全域性照明的各個組成部分,以便根據相對優勢和效能特徵來選擇適當的方法。
全域性照明(global illumination) = 直接光照(direct lighting) + 間接光照(indirect lighting) + 環境光(ambient light)
直接光照(direct lighting):
- directional/point/spot等光源的直接光照
- 環境光(ambient light):對於動態物體,提供實時的環境光計算。實際上是light probe。
間接光照(indirect lighting):
- directional/point/spot等光源的間接光照(indirect bounce)
- 環境光(ambient light):對於靜態物體,作為簡介光照烘焙到Lightmap中。
- 自發光材質(emission Material)
Unity對於indirect lighting的定義為:自光源發射,至少擊中場景表面兩次,最終到達攝像機的光線。
常用光源型別
常用的光源型別包括平行光(directional light)、點光源(point light)、聚光燈(spot light)、區域光(area light),這些光源可以為場景物體提供直接光和間接光效果,需要注意的是由於area light計算過於複雜,目前只支援烘焙的實現方法。 下面以spot light為例,通過構建一個純淨的實驗環境,分別展示直接光照、間接光照的效果。ps:暫時不用管間接照明是用哪種方法實現的。
spot light的直接光照效果(Indirect Multiplier為0,表示間接光強度為0)
spot light的間接光照效果(燈光元件是非啟用狀態,因為間接光已經烘焙到lightmap中,而這裡要遮蔽直接光效果)
spot light的直接+間接光照效果(可以留意到光源範圍內強烈的光照,以及範圍外的光線從各表面彈射產生的結果)
環境光
環境光表示場景周圍的光線,並不是來自任何特定的源物體。它對整個場景的色彩基調起到重要作用。
可以在Lighting皮膚的Environment Lighting選項中對環境光進行設定,它的色彩來源Source可以設定成 - Skybox與指定顏色的混合(Skybox) - 指定固態顏色(Color) - 漸變色(Gradient)
Ambient Mode通常為自動設定,這取決於你當前使用的是Baked GI還是Precomputed Realtime GI。除非你兩者都開了或者不開。
PS:在Unity中,ambient light對於動態物體或未經烘焙的物體而言,是作為直接光照並實時參與計算的。對於靜態物體而言,ambient light所有成分都被視為間接光照並參與到烘焙中。
下面展示ambient light作為直接光照與間接光照的區別。
圖一:所有物體為動態物體,環境光只為物體提供實時的直接光照
圖二:靜態物體經過烘焙後,環境光作為間接光照被烘焙到lightmap中
圖一中環境光作為直接光參與實時計算,導致所有物體都是同一種顏色。這是因為環境照明本身就作為從四面八方照亮物體的照明,沒有考慮到物體與物體之間的遮擋關係。我們把這個問題叫做環境光遮蔽(Ambient Occlusion)問題,也就是我們常說的AO。解決這一問題常用的方法就是採用SSAO技術,但不在本文討論範圍內。
圖二中,環境光作為間接光照,在烘焙過程考慮到光線從物體各表面彈射(bounce),從而能夠烘焙出柔和的陰影。如果要更明顯的環境遮擋效果,Unity還提供了烘焙AO到lightmap的功能,可在Lightmapping Settings選項中勾選Ambient Occlusion開啟。這會增加烘焙時間,而且效果也不如SSAO理想,但對於靜態物體來說,也是一個可選的方案。
自發光材質
與area light一樣,自發光材質(emissive materials) 在其表面發射光線,可以在場景中作為光源照亮其他物體,但這毫無疑問,計算過程複雜,需要烘焙(baked)或預計算(precomputed)才能發揮效果,而這就要求自發光物體和被照亮的物體都必須是靜態物體,而且需要Shader本身支援Emission效果。
Emitted Light自發光線也包含直接光和間接光部分。其直接光部分可視為對發光體自身進行照明,間接光部分對其他物體進行照明。
以下摘自官方文件,對於全域性光線的圖解。
選擇一種合適的光照技術方案
當你看到這裡,相必你已經對全域性照明效果的組成部分、光源的型別有了大致的認識,這有助於我們去選擇一個合適的光照技術方案。 所以接下來,我們要學習的是對光照效果的控制,以及選擇其合適的實現方式。 在本節中,我們將簡要介紹不同方案的適用場合,相對優勢和效能特徵。
光照模式(Lighting Modes)
選中燈光,在Inspector皮膚中,你可以指定光源的光照模式,這個光照模式定義了該光源的預期用途。
實時直接光照(Realtime)
實時直接光照的環境設定:把Realtime GlobalIllumination和Baked Global Illumination勾選去掉,同時將Auto Generate關閉並清空烘焙資料Clear Baked Data。
此時,realtime燈光為場景提供實時直接光照,並能選擇是否開啟實時陰影。實時光照是在場景內對物體進行照明的最基本的方式,對於照亮人物或其他可移動的物體非常有用。
但值得注意的是,在沒有被光源範圍覆蓋的地方,這些區域是完全黑色的,因為沒有反彈的光線(bounced light)。該問題見 上文闡述。因此,為了營造更逼真的場景,我們需要啟用全域性照明技術,Unity5為我們提供了兩套解決方案:混合光照(Mixed lighting)和預計算全域性實時光照(Precomputed realtime global illumination)
混合光照(Mixed lighting)
Unity在5.5版本中加入了混合光照技術,是可以將Realtime光照與Baked GI混合的一種技術方案,讓我們可以按照實際需求和主觀意願,去組合搭配。
典型應用一:燈光的間接光部分被烘焙到lightmap中,烘焙完成後,該光源還能繼續參與實時光照計算,以提供材質的法線高光效果。 典型應用二:將燈光對靜態物體產生的陰影烘焙到單獨的lightmap中,這種lightmap叫做ShadowMask,在執行時,根據ShadowMask,對動態物體產生的實時陰影能夠與烘焙陰影更好地混合。
烘焙GI(Baked GI)
開啟方法:只要勾選Baked Global Illumination,就會啟用Mixed Lighting的技術方案,因為Baked GI是它的一部分。我們只需要將所有光源設定成Baked模式,確保只使用了Baked GI技術。 Unity在執行時之前預計算這些燈光,並且不會在任何執行時內計算它們。這意味著Baked燈沒有執行時間的開銷。 Unity將 Baked燈光的直接和間接照明對映到光照貼圖(lightmap)照亮靜態遊戲物件)和光源探測器(light probe)(照亮動態光照遊戲物件)。 Baked模式也是唯一的不能在其他動態GameObjects上投射陰影的light模式。
Baked GI的優點
- 來自靜態GameObjects的高品質陰影,而無需額外成本。
- 提供間接照明。
- 靜態GameObjects的所有光照來自Shader中的一張Texture,即Lightmap。
Baked GI的缺點
- 沒有實時直接照明(導致也沒有鏡面高光(Specular Highlight)效果)。
- 動態GameObjects要獲得來自靜態GameObjects投射的陰影,只能使用Light Probes,但是其效果是低解析度的。
- 與實時照明相比,Lightmap圖集會導致記憶體需求增加,因為光照貼圖包含直接照明資訊而需要更加精細。
混合光照模式(Mixed Lighting Mode)
Mixed燈光是指將Mode屬性設定為Mixed的Light元件。 Mixed燈光可以在在執行時可以改變其Transfrom屬性和燈光屬性(如顏色或強度),但只能在有限的範圍內。 它們照亮靜態和動態GameObjects,總是提供直接照明,並可以選擇提供間接照明。 混合光照下的動態GameObjects可以在其他動態GameObjects上投射實時陰影。
場景中的所有混合燈光使用相同的混合光照模式(Mixed Lighting Mode),需要在Lighting皮膚Mixed Lighting選項的Lighting Mode中進行選擇,有Subtractive、Baked Indirect、ShadowMask、DistanceShadowMask四種模式。
Subtractive模式
在Subtractive模式下:
- 會將Mixed燈光的直接照明、間接照明、陰影資訊全部烘焙到Lightmap中去,這一點跟Baked GI是一樣。
- Subtractive模式也是四種混合光照模式中,唯一會將直接照明烘焙到lightmap的模式。
- 在烘焙完成後,Mixed仍然可以給動態GameObjects提供實時光照,並支援高光效果。但是,他們只能通過Light Probes從靜態GameObjects接收陰影。
- 在減法模式中,主方向光是唯一能夠將動態GameObject中的實時陰影投射到靜態GameObjects上的光源。但該模式不能保證烘焙陰影和實時陰影的正確組合。因此,Subtractive模式提供Realtime Shadow Color設定。
Unity在Shader中使用此顏色將實時陰影與烘焙陰影進行合成。要做到這一點,它可以減少動態GameObjects覆蓋區域的光照效果。因為引擎無法提供一個預定的正確值,所以選擇一個適用於任何給定場景的值是自己的藝術選擇。
注意:Mixed Light的Shadow Type設定若為No Shadow,該光源只有間接光部分參與烘焙,並可以為所有物體提供實時光照。這與文件描述的不符,疑似Unity的一個Bug,是一個要注意的問題。所以,要麼所有Mixed Light都開啟陰影選項,要麼就都設定成Baked模式,只留一個平行光作為Mixed模式,並注意開啟陰影選項。
解決了靜態陰影和動態陰影的融合問題,全面提供實時光照效果,可對比上圖Subtractive模式
仍然存在的不足: - 它不能為靜態物體提供實時直接照明,因此不提供高光效果。 - 它不提供將動態GameObject中的實時陰影投射到靜態GameObjects上,除了一個方向燈(主燈)。 - 它無法很好地解決動態和靜態陰影組合。
Baked Indirect模式
對於設定為Baked Indirect模式的燈光,生成的Lightmap和light probe只包含間接光部分資訊,並且不執行陰影的烘焙。烘焙後,Mixed燈光會繼續為所有物體提供實時直接照明和實時陰影。 Baked Indirect模式的效能要求使其成為中檔PC和高階移動裝置的理想選擇。
優點:
- 它提供了與實時照明相同的視覺效果。
- 它為靜態和動態GameObjects的所有組合提供實時陰影。
- 它提供間接照明。
缺點:
- 與其他混合光模式相比,它具有更高的效能要求。
- 由於Unity實時陰影會有距離限制,遠處的陰影將不會被渲染。
ShadowMask模式
ShadowMask是一張遮罩圖,它與相應的光照貼圖共享相同的UV佈局和解析度。它的每個畫素儲存最多4個燈光的遮擋資訊,因為紋理最多隻有RGBA四個通道。
Unity會烘焙靜態GameObjects間的陰影資訊,並將它們儲存在一個單獨的Shadowmask Texture中,同一個畫素最多可儲存4個燈光的遮擋資訊,如果超過4個燈重疊,任何額外的燈產生的陰影將會被烘焙到lightmap中去。光探頭也可以接收最多4個燈的相同資訊。
在ShadowMask模式下:
- 靜態GameObjects通過ShadowMask接收來自其他靜態GameObjects的陰影。他們也會從動態GameObjects中獲得實時陰影,但是隻有陰影距離(Shadow Distance)內的陰影。可在QualitySettings皮膚中設定。
- 動態GameObjects通過ShadowMap從陰影距離內的其他動態GameObjects接收陰影。他們還通過Light Probes從靜態GameObjects獲得陰影。陰影保真度取決於場景中Light Probe的密度,以及在Mesh Renderer上選擇的Light Probes模式。
PS:ShadowMap叫做陰影貼圖,Unity是實現實時陰影過程中生成的一張貼圖。注意與ShadowMask做區分。
Unity會根據ShadowMask(包含靜態GameObject陰影資訊)和ShadowMap(包含動態GameObject陰影資訊),在Shader中做相應的處理,將靜態和動態GameObjects的重疊陰影組合在一起,
解決了靜態陰影和動態陰影的融合問題,全面提供實時光照效果,可對比上圖Subtractive模式
優點:
- 為所有物體提供與實時照明相同的視覺效果,因此具有法線高光效果。
- 所有Mixed光源都能提供從動態GameObjects到靜態GameObjects的實時陰影。
- 很好的融合了動態和靜態陰影。
- 適用於中低以上的效能要求,因為靜態GameObjects的陰影不是實時計算的。
- 提供間接照明。
缺點:
- 它只能通過Light Probes將靜態GameObjects中的低解析度陰影提供給動態GameObjects。
- ShadowMask允許最多4個重疊的光線量。
- ShadowMask貼圖增加了較多的紋理記憶體。
DistanceShadowMask模式
DistanceShadowMask模式是ShadowMask模式的升級版。
在陰影距離(Shadow Distance)內的GameObjecs(Edit > Project Settings > Quality > Shadows): Unity會將動態和靜態的GameObjects渲染到ShadowMap中,換而言之,就是都進行實時陰影計算。由於這個原因,DistanceShadowMask模式比ShadowMask模式具有更高的效能要求。
在Shadow Distance之外的GameObjecs:
- 靜態GameObjects通過ShadowMask從其他靜態GameObjects接收高解析度陰影。
- 動態GameObjects通過Light Probes接收來自靜態GameObjects的低解析度陰影
實時全域性光照(Realtime GI)
儘管Mixed Lighting已經很完善,但還是無法對場景的間接照明做動態變化。為此,Unity 5.0新增了號稱為“預計算實時GI(Precomputed realtime GI)”的新技術,通過這種方法,可以建立具有豐富全域性照明的光照環境,並實時響應照明變化。 一個很好的例子就是晝夜變化系統:光源的位置和顏色隨時間而變化。 用傳統的Baked GI,這是不可能的實現的。
需要注意:Realtime GI仍然需要一個類似於上面提到的烘焙(baked)的預計算(precomputed)階段,仍然限於靜態物件。但是,它不僅在構建時預計算光線的反彈,而且還會預先計算所有靜態物體間光線反射可能經過的路徑,並對這些資訊進行編碼以供在執行時使用。所以對於所有的靜態物件來說,預計算(precomputed)階段解決的問題是“如果任何光照射到這個表面,它會在哪裡彈跳?”然後Unity會儲存這個資訊,指出光線可以傳播哪些路徑以備後用。最後的照明是在執行時通過將現有的實際燈光輸入到先前計算的光傳播路徑中完成的。更多內容見技術細節。
這意味著燈的數量和型別,其位置,方向和其他屬性都可以改變,間接照明也會相應更新。類似的,也可以改變物體的材質屬性,例如它們的顏色,它們吸收多少光線或者它們自己發射多少光線。
雖然預先計算的實時GI也會產生柔和陰影,但是除非場景非常小,否則它們通常必須比Baked GI所能達到效果的更粗糙。還要注意的是,雖然預計算實時GI在執行時計算最終的照明,但它在幾個幀內迭代地計算的,所以如果在照明中做了大幅度的改變,則需要更多的幀才能完全生效。儘管這對於實時應用來說足夠快,但是如果目標平臺資源非常有限,那麼使用Baked GI可能會更好地實現更好的執行時效能。
使用Realtime GI
實時照明與Realtime GI的組合是Unity中最靈活和最現實的照明選項。要啟用Realtime GI,開啟Lighting視窗並勾選實時Realtime Global Illumination。
啟用實時GI後,實時燈會向場景中提供間接照明以及直接照明。但這種組合適用於光源變化緩慢,對場景有高度的視覺衝擊的情況,如太陽在天空中移動,或者在封閉的走廊中緩慢的脈動光。不要使用Realtime GI來快速改變燈光,因為迭代的計算造成的延遲效果得不償失。
請注意,與較不復雜的Baked GI相比,實時GI使用大量的系統資源。全域性照明在Unity中由一個名為Enlighten的中介軟體管理,該中介軟體具有自己的開銷(系統記憶體和CPU週期)。
因此Realtime GI適用於針對中高階PC系統的遊戲,以及針對PS4和Xbox One等平臺的遊戲。一些高階移動裝置也可能足夠強大,以利用此功能,但您應該保持場景小,實時燈光解析度低以節省系統資源。
要禁用某一光源的實時GI的效果,可以選中該光源Gamobject,然後在Light元件中將Indirect Multiplier設定為0。這意味著Light不會提供任何間接光源。要完全禁用實時GI,請開啟Lighting視窗,並取消Realtime Global Illumination。
技術細節
預先計算的實時GI模式:Unity僅預先計算表面到表面的資訊:物體間光線可能會經過的路徑。
在執行時,Enlighten利用這些預計算的資訊,通過CPU計算光線的彈射,因為過程計算量很大,所以它被分散到幾個幀中計算,這就是上文一直說的迭代計算。並將最終的結果儲存在動態光照貼圖(Dynamic lightmap)和光照探針(light probe)。換句話說,Realtime GI最終的實現方式還是lightmap,只不過這個Lightmap是在執行時動態更新的,而且它需要幾幀時間,直到光線完全反射到場景中的靜態元素上,動態光照貼圖和光探針才能獲得到最終結果。
對於屬性緩慢變化的燈(例如發光的太陽在天空中移動),這不會造成問題。但是,對於屬性變化很快的燈(如閃爍的燈泡),實時GI的迭代計算的特性可能不適用。而且快速的變化屬性的照明不會顯著地影響光系統,所以在計算中包含它們沒有意義,可以考慮將這部分的光的realtime GI效果關掉,通過調節light元件上的Indirect Multiplier為0。
有幾種方法可以解決迭代計算造成的延遲問題。一種方法是減少實時光照解析度(在Lighting皮膚的Lightmapping Settings選項中的Indirect Resolution屬性)。因為這會減少的計算量,照明迭代的速度更快。另一個選項是增加實時GI執行時的CPU使用率設定。通過投入更多的CPU時間,執行時更快地迭代。權衡當然是其他系統接受較少的CPU時間來完成他們的工作。這取決於專案的具體情況。
缺點
- 由於動態光照貼圖(dynamic lightmap)和光照探針(light probe)取樣,用於儲存由Enlighten照明系統計算的實時間接反彈,增加了Shader計算複雜度和效能消耗。
- 間接照明是迭代計算的,更新需要一定的時間,所以光源的屬性變化不能太快。 雖然一個適當的HDR ToneMapping可能會幫您掩蓋這一缺點。
GI與材質
物體的材質屬性也是影響光線的一個重要因素,材質表面的顏色,直接決定了從該表面彈射出去的光線的顏色。還有更多的影響因素如:PBR(基於物理的渲染)材質的金屬度(Metallic)和粗糙度(Roughness)會影響光線的吸收和反射程度。
粗糙的非金屬材質
光滑的金屬材質
GI的侷限性
無論是Baked GI和還是PreComputed Realtime GI都有一個侷限性,即烘焙/預計算中只能包含靜態物體,所以移動的物體不能將光線反射到其他物體上,反之亦然。 不過,他們仍然可以使用Light Probes拾取靜態物體的反射光。將 Light Probe事先佈置在場景中的,在烘焙/預計算階段, Light Probe會儲存其位置上的照明資訊,然後在執行時階段,動態物體會獲取附近的light probe裡面的光照資訊,從而近似地獲得靜態物體反射的光線。
在PBR材質中有一個很重要的照明部分:IBL(基於影像的光照)。它基於環境貼圖(Environment Map)的每個畫素作為入射光,計算來自環境貼圖各個方向的入射光對物體的光照資訊,是PBR中模擬物體周圍環境光照的一個重要方法。這也是GI演算法無法達到的效果。對應相關的技術是反射探針(Refletion Probe)以及Lighting皮膚的相關設定。
後記
以上便是“理論篇”的全部內容,一部分描述翻譯自Unity官方文件。 接下來會在“實踐篇”重點介紹如下內容: - 項引數詳解 - 烘焙流程 - 常見問題及技巧 - PBR效果等內容
作者:Kerry
專欄地址:https://zhuanlan.zhihu.com/p/126362480
相關文章
- 寫給美術看的Unity全域性光照詳解(引數篇)Unity
- Unite 2019|Unity的光照烘焙技術(上)Unity
- 【小白寫論文】技術性論文結構剖析
- 全域性視角看技術-Java多執行緒演進史Java執行緒
- 美團技術leader:寫給工程師的十條精進原則工程師
- 【GPT-4理論篇-1】GPT-4核心技術探秘GPT
- 規避技術:全域性作業系統物件作業系統物件
- 影片美顏SDK動態處理技術與靜態處理技術
- 天美F1技術美術專家:技術美術的未來前景如何?
- 2021年美團技術團隊最受歡迎的22篇技術文章
- 遊戲技術美術之<技術&美術>知識構成遊戲
- 寫給開發們的色彩理論
- 寫給大家看的量子力學——量子通訊、量子隱形傳輸技術簡介
- 全域性性謀劃、戰略性佈局、整體性推進智慧技術
- 併發技術1:CSP併發理論
- 如何寫好一篇技術型文件?
- 寫一篇好的技術文章有多難?
- PDM系統的產生及相關技術理論
- 技術美術師是做什麼的?
- 給技術人員一些技術以外的建議
- 來自Riot 的一份遊戲美術教程(五):技術美術遊戲
- 寫給前端工程師看的Docker教程-基礎篇前端工程師Docker
- 寫給前端工程師看的Docker教程-實戰篇前端工程師Docker
- 寫給前端工程師看的Docker教程-中級篇前端工程師Docker
- NeurIPS 2024|SparseLLM:突破性全域性剪枝技術,大語言模型稀疏化革命模型
- 人臉識別活體檢測技術理論
- GDC 2019《戰神4》開發解讀:全域性光照
- 從ARM”斷供“華為看底層技術的重要性
- ?如何寫一篇技術部落格,談談我的看法
- 資料庫大牛李海翔詳解全域性讀一致性技術資料庫
- 各種技術論壇
- 關於遊戲技術美術工程師的思考遊戲工程師
- 訊號處理技術:現代通訊技術的基石
- CIKM 2024 | 美團技術團隊精選論文解讀
- CVPR 2022 | 美團技術團隊精選論文解讀
- 我的2020回顧——技術篇
- 程式設計師如何寫好一篇技術文章?程式設計師
- 從技術雷達看DevOps的十年——容器技術和微服務dev微服務