Menu

戰慄時空2 Source 遊戲引擎導覽

關於什麼是Engine(引擎)?

在Half-Life 完成之後不久,Valve開始籌備他們的下一個主打遊戲--Half-Life 2。在商討遊戲引擎的採用問題上,開發小組明白到原版遊戲改採用的改良Quake 1引擎對於Half-Life 2來說顯得過於破舊,而且在很多的方面都不符合這一代遊戲發展的要求。於是,vlave的開發小組決定從美工設計以及遊戲代碼開始,設計出一個全新的遊戲框架,並在其中加入了一套來自havok的定製物理引擎。 
對於一個遊戲來說,“引擎”就是一個系統,就像是一輛汽車的發動機一樣,他能輸入來自加速器的動能,並輸出到輪子的轉動。軟件(在這裡為遊戲)的引擎實現的東西和汽車的發動機一樣,輸入來自框架(某些程序或者文件的片段),然後輸出程序員們想要的結果。再次拿汽車的發動機為例,一個軟件的引擎不能自行運作,他必須依靠另外一個系統,就好像,你只能開著“一台汽車”而非“一台發動機”去上班。在Source中,一個純粹的引擎只能是一堆代碼,完全不具有可玩性。所以不難對引擎下這樣的定義:引擎就是一套讓遊戲元素發生互動關係並將其編譯成可玩程序的代碼或程式,依靠框架進行操作。

band

 

 

Source引擎總覽

Source並不是一套簡單的3D引擎,也可以是說,他並非只是一套渲染器。Source引擎包含了很多不同的模組,程序員可以在引擎的程序包中方便地取出以及添加進其他的元素。 
在這篇文章裡面,我將會為大家展示這些模組是什麼回事並且對遊戲產生怎樣的影響。在下面將要陳述的問題主要講解在Source引擎中一些令人驚奇的模組是怎樣對整個遊戲的畫面以及遊戲效果產生影響,而不是去解釋Source引擎的代碼怎樣去運作。對於此,可能大家會覺得比較枯燥,畢竟,這些在程序實現上的問題針對的是對遊戲有一定研究的玩家。我們並沒有打算深入到Source的程序代碼進行研究,因為這些已經不屬於我們一般老百姓可以研究的範疇了。在這裡必須要給讀者澄清一下,由於目前Source引擎的非公開性,我們並不能準確地將Source引擎中每一個模組的特性都準確地表示出來,如果你一定要深入研究的話,請參考Half-Life 2發佈之後的 SDK 參考文檔以及Valve以後的白皮書。本文的章節細分以筆者對Source引擎的瞭解為依據。請根據實際情況印證並參考其他專著以及文獻。

  • 3D 引擎
    • 渲染器
      • Pixel 、vertex shaders
      • 光影效果
      • HDR (High Dynamic Range)
    • 動畫以及角色面部表情
    • 幾何構成
    • VGUI遊戲界面
  • 物理引擎,基於Havok 定製的物理引擎
    • 剛體的動力學模型以及關節約束機制
    • 彈性機構、繩索機構、布紋處理、車輛系統
    • 水以及火光
    • 粒子系統
    • 怪物/NPC/程序 上的物理學系統
  • 材質系統
  • AI 系統

在這裡,我尚且用3D engine來描述造Source引擎中,生成引擎輸出圖像及其幾何體的模組。

渲染器

這部分最能體現顯卡的功力,也是玩家最為注重的一個重點。渲染器的作用主要主要功能就是採集畫面幾何體和材質的數據,通過一系列繁雜的過程,生成一個三維的圖像。Valve並沒有重新創造Source引擎的渲染器而採用了Microsoft DirectX 9.0 的 API,並借助Half-Life SL高階編程語言編寫引擎,在很大程度上節省了寶貴的時間,這歸功於 DirectX9良好的硬件兼容性以及先進的代碼設計流程。原有的Half-Life 1引擎被設計成支持 OpenGL and Direct3D的雙模式,但正如各位所見,這個引擎在Direct3D模式下的渲染十分糟糕,特別是在目前主流的顯卡上工作時,其效率以及畫質遠不及OpenGL模式下的表現。在設計Source引擎的時候,Valve放棄了ogl的渲染模式進而採用DirectX架構,以增強其硬件兼容性以及對未來特效的支持,比如是Shader2.0b甚至是Shader3.0 Model的支持。 
Pixel and Vertex shaders

Shader就是一些能夠在GPU/VPU上執行的程序句段。目前市面上流行的遊戲中,採用實時渲染的畫面均大量運用了Shader,你的顯卡無時無刻在處理著這些Shader信息。現代顯卡主要的改進就是用Shader代碼替代沿用自GeForce GTS時代固定的硬件T&L (Transform and Lighting)。Shader允許程序員創建他們喜歡的Shader特效程序片段以取代有限個數的固化在顯卡核心中的預設特效。經過Shader編程之後,如果有一個關於bump mapping的技術推出,程序員只需改寫或者加入該部分的Shader代碼便能使這些特效運行在現今的顯卡上,而不需等待下一代的顯卡出現。於是,顯卡的處理特性便得以最大程度地延伸。在程序接口方面,由Microsoft推出的HLSL、nVIDIA推出的Cg、以及OpenGL小組推出的GLSL都是編寫Shader的工具。能和目前個人PC、工業用電腦上的DirectX以及OpenGL很好地對應。

Source對directx中的pixel/vertex shaders version 2.0提供了完整的支持,Valve最近的聲明指出Source引擎將在未來硬件成熟的時候提供對Shader3.0的支持。目前的Source引擎中,所有的directX 9 Shader均在 (high level shader language)下完成,對代碼的運用上只需簡單地插入片段,省卻了在底層使用彙編語言重編譯的麻煩,增加了Source引擎的彈性。在引擎代碼方面,所有的代碼均用C/C++ using Visual Studio 6.0進行開發,有很大的開放度以及可擴展性,目前已經有數家公司得到Valve授權採用Source引擎進行下一代遊戲的開發,除傳統的FPS遊戲之外,還有aRPG,SPD等模式,相信在明年開初將會見到很多採用Source引擎製作的佳作。

對於玩家,Shader就能實現依靠驅動程序實現更加多以及更富彈性的特效,在Half-Life 2中最為凸出的Shader2.0特效就是那些吸引眼球的充滿反射和折射的水紋效果。在目前推出的基於Source引擎的CS:Source選項設置中單獨對水紋效果提供了一個現實效果設置選項。

光影效果:

在 Source 引擎中使用了兩類的方法處理光影:輻射度光影貼圖和環境光立方體貼圖。

  • 輻射度光影貼圖主要用於靜態的大型的物體,比如地形模型。
  • 環境光立方體貼圖用於動態 的模型或模型庫中可重複調用的模型,比如各種遊戲中的人物角色、家具陳設、各種在遊戲中可拿起的具有物理特性的物體。

輻射度光影貼圖

整個引擎的光影效果實現佔據了Source引擎的絕大比重,這裡只略略講述Half-Life 2畫面的秘訣--輻射度光影貼圖。在 Source 引擎的渲染器中,輻射度光影貼圖扮演著一個最為重要的角色。先給大家一個輻射度光影貼圖所能實現的大概效果:

map_2_tn 
map_1_tn

方向性光照

輻射度光照

在Source以及整個Half-Life 2架構中,輻射度法線化光影貼圖起到的作用就是將真實精確的光影效果(輻射度光照)以及更加細膩的模型細節(法線貼圖)通過算法整合到最後的效果圖中。

bumpBasis

輻射度法線貼圖的三個不同的偏移法線基線

在傳統的法線貼圖中,每次工作只會累積一個光源的效果,多重漫射光將會由法線和光線方向的點積 N*L 多次進行合成。而另一方面處理輻射度光影貼圖的時候只能處理一個顏色值。在以輻射度法線貼圖預處理每個燈光時,並不直接是和表面的法線做運算,而是和變換到切線空間中的三個不同的偏移法線基線 bumpBasis 做運算,並把運算結果保存到三張光影貼圖中。在DirectX中,再由法線從這三張光影貼圖中合成,以擴展出相應的光線與顏色……。 這樣輻射度法線貼圖能在一遍渲染中實現任意多光線的處理,並把法線也加入渲染計算中。在輻射度貼圖中,使用下面的方程將法線從多張輻射度法線貼圖轉換為最終的輻射度法線貼圖 :

float3 dp;

 dp.x = saturate( dot( normal, bumpBasis[0] ) ); 
dp.y = saturate( dot( normal, bumpBasis[1] ) );
dp.z = saturate( dot( normal, bumpBasis[2] ) );
dp *= dp;
diffuseLighting = dp.x * lightmapColor1 +
dp.y * lightmapColor2 +
dp.z * lightmapColor3;

這三個不同的偏移法線基線並不是隨便選擇的,而是三個長度為 1 的互相垂直的矢量並能均勻的分佈在表面上。

basic_1

在3D物體創建上,一般都會使用cube map製作鏡面反射光線,在Source引擎中,cube map會在引擎的渲染器中預先處理,根據嗅探器的作用,在編輯器中創建的環境會選用最近、最貼切的cube map創造反射光線,為解決材質的邊界問題,可以手動為某個表面手動添加cube map

在Hammar這個Half-Life 2地圖編輯器中,設計者在材質表面安設了環境探測用的實體以作為鏡面反射的參照物。

hammar_tn

接下來將會為大家講解在Half-Life 2中整個靜態環境的生成。首先將會為大家介紹一下我們想得到的效果,並以一個小範圍為例說明整個Shader序列的創建過程:

map_1_02_tn

實現的效果可以用下面的流程圖進行說明:

map_1_01_bg

左下腳的Normal法線貼圖和三張Lightmap混合後的輻射度貼圖再加上漫反射係數的控制就可以創建出以上所說的輻射度法線貼圖;再以加法加上傳統的表面光滑度以及反射度的貼圖結果,就可以生成最終的貼圖效果。在Source中創建一個靜態的環境時還需要用到三張Lightmap,主要是考慮到為了適應任意光源的作用。在下面的分組圖中,大家會看得更加明白:

map_1_05_tn map_1_06_tn map_1_07_tn

LightMap #1                                  LightMap #2                                 LightMap #3

map_1_09_tn

三張直接輻射能貼圖整合之後的效果,由於缺乏了法線貼圖的支撐,整體沒有細節可言。

由於這個過程處於貼圖的預處理程序,需要考慮到整體執行的效率,因此只採用了分辨率較低的lightmaps貼圖(因為diffuse材質的irradiance十分平滑的,只需很小的分辨率,渲染結果是通過filter還原),在這一步驟中,渲染的貼圖可以說丟失了所有的細節。如果盲目增大lightmaps的分辨率,處理時間會成倍增加,顯存帶寬也會被大量佔據。

因此,Source引擎將lightmaps分成三個成分,每個貼圖都有其對應的基本方程,最後根據法線貼圖儲存的法線進行混合得出第一步的處理結果,在很大程度上利用了低分辨率的好處,並且能將法線貼圖所最擅長的細節表達發揮出來。

另一方面,程序設計員可以採用不同的法線貼圖與之結合,在debug模式下實時檢查渲染結果變得比以前方便得多。

接下來,Source會將上面由Lightmaps生成的輻射貼圖附上法線貼圖,

map_1_09_2_tn map_1_04_tn

輻射度貼圖                                                                法線貼圖

map_1_08_tn

被細節化的輻射度貼圖

物體的表面有凹凸的細節,形狀方面需要依靠法線貼圖實現,至於光線的表現方面,則需要依靠漫反射係數來確定。下面的流程就是繼續細化並加上法線貼圖的輻射度貼圖。

map_1_08_2_tn map_1_10_tn

輻射度法線貼圖                                                      漫反射係數

map_1_11_tn

被加上漫反射係數的輻射度法線貼圖

同時,考慮物體表面反光程度的鏡面反射貼圖也在進行之中,第一步也是採用立方體反射貼圖和法線貼圖的混合:

map_2_1_tn map_1_04_tn[1]

立方體反射貼圖                                                      法線貼圖

map_2_2_tn

法線貼圖加載到鏡面反射貼圖上

map_2_2_2_tn map_2_3_tn

上一步生成的鏡面反射法線貼圖                        鏡面反射係數

map_2_4_tn

鏡面反射法線貼圖和鏡面反射係數的混合

最後的步驟,由加上了漫反射係數的輻射度法線貼圖和加上了鏡面反射係數的鏡面反射貼圖進行加法運算,得出最後結果

final_1 final_2

加上了漫反射係數的輻射度法線貼圖                加上了鏡面反射係數的鏡面反射貼圖

final_3

最後結果

在整個渲染流程中,法線貼圖起著引導性作用,ATI目前的頂級產品Radeon X800系列已經完整支持新一代的法線貼圖處理方式,並對大材質的貼圖採用了全新的3Dc紋理壓縮技術。雖然我們目前未收到任何關於Source引擎採用3Dc紋理壓縮的消息,但從ATI發佈的白皮書來看,X800系列顯卡在處理法線貼圖的算法技術上會和Valve有更好的溝通,實操效果會佔據一定的優勢。

環境光立方體貼圖

    《半條命2》動態物件和人物模型的光影效果

    前面介紹了靜態場景的光影效果,下面我們來看看在《半條命2》中的動態模型是如何繪製的。

    核心技術-基於環境光立方體 (Ambient cube)的光能傳遞凹凸貼圖技術

    由於動態模型的位置和形狀都有可能發生變化,所以就不能像靜態場景那樣處理了。動態模型所受到的光照也是由直接光照和間接光照組成的。在Source引擎中,只能從照射一個模型的所有直接光源中選出有兩個最主要的來使用比較複雜的光照公式進行實時計算,而其他比較次要的直接光源以及場景中的間接光源都會被預先保存到一個稱為“環境光立方體”的東西中。

model_ambientcube

環境光立方體

    首先Valve的技術人員在關卡場景中放置一個虛擬的立方體,然後地圖編輯器就分別對立方體的每個面計算出垂直通過這個面的環境光的顏色和強度。場景裡那些比較次要(遠或暗)的直接光源,也被計算在內。

    對於每個面都計算一個光照顏色,可以得到六個顏色,基本上代表了從四面八方射來的穿過這個體積中的所有比較次要的光線的信息。這六個顏色是可以預先計算出來保存在關卡場景數據裡的。

    通過模型表面的法線來決定取這六個顏色的比重並將它們混合,就可以得到一種類似於光能傳遞的效果。在環境光立方體的六個面中,必然有至少三個面會在法線的相反方向,這些面的顏色忽略不計。所以一般只有另外三個面的顏色會用到。法線方向越正對哪個面,這個面對應顏色所佔的比重就越大。

model_radiosity

    用環境光立方體技術渲染的蟻獅模型,雖然沒有加入凹凸貼圖,但是已經有一種類似光能傳遞的效果。

從立方體環境貼圖中獲取光照的著色器代碼:

float3 AmbientLight( const float3 worldNormal) 
{
float3 nSquared = worldNormal * worldNormal;
int3 isNegative = ( worldNormal< 0.0 );
float3 linearColor;
linearColor = nSquared.x * cAmbientCube[isNegative.x] +
nSquared.y * cAmbientCube[isNegative.y+2] +
nSquared.z * cAmbientCube[isNegative.z+4];
return linearColor;
}

    在前面說道為了結合使用法線貼圖,對於靜態場景,一個面對應著三張光照貼圖。而在這裡也是類似的,一個動態模型對應著三個環境光立方體。這三個環境光立方體面上的顏色是分別根據各面局部坐標系的三個基向量來計算出來的。

model_radiosity_d1

model_radiosity_d2

model_radiosity_d3

a.b.c分別使用相對於三個基向量的環境光照圖和環境光立方體的效果

    我們把法線貼圖貼到場景中看一看:

model_normalmap

法線貼圖

    按照和靜態場景類似的方法用法線貼圖來尋址環境光立方體的顏色,就可以得到下面的效果了:

model_normalmap_radiosity

    和前面沒有凹凸細節的圖對比一下吧:

model_radiosity[1]

    不要忘了,模型本身也是有材質貼圖的。我們把它貼上去看一看:

model_albedo

模型自身的材質貼圖,沒有光照

    如果把光照效果和它混合,就成了這個樣子:

model_albedo_normalmap_radiosity

混合操作依然是顏色各通道相乘。角色表面具有凹凸細節。

    和沒有使用法線貼圖的效果對比一下:

model_albedo_radiosity

沒有凹凸貼圖

    漫反射的部分完成後,就該加入高光了。對於角色的高光,Source引擎仍然使用高光環境反射貼圖。

model_specular

完全的高光環境反射,也沒有加入凹凸貼圖,好一隻光滑的金屬蟻獅

model_normalmap_specular

加了凹凸貼圖

model_specular_factor

高光強度貼圖,也是越白的地方高光越強, 
我們可以看出蟻獅表面的高光並不是很強的

model_normalmap_specular_factor

用高光強度貼圖去過濾高光

model_final

把高光疊加到漫反射部分上去,得到最後的效果

  點光源處理代碼:

 

void AddBumpedPointLight( ...) 
{
float3 lightDir = cLightInfo[lightNum].pos-worldPos; // Light direction
float lightDistSquared = dot( lightDir, lightDir); // Light distance^2
float ooLightDist = rsqrt( lightDistSquared); // 1/lightDistance
lightDir *= ooLightDist; // Normalize
float4 attenuationFactors; // Dist attenuation
attenuationFactors.x = 1.0f;
attenuationFactors.y = lightDistSquared* ooLightDist;
attenuationFactors.z = lightDistSquared;
attenuationFactors.w = ooLightDist;
float4 distanceAtten = 1.0f / dot( cLightInfo[lightNum].atten, attenuationFactors);
// Compute N dot L
float3 lambertAtten= float3( dot( worldBumpBasis1, lightDir),
dot( worldBumpBasis2, lightDir),
dot( worldBumpBasis3, lightDir) );
lambertAtten= max( lambertAtten, 0.0 );
float3 color = cLightInfo[lightNum].color* distanceAtten;
color1 += color * lambertAtten.x;
color2 += color * lambertAtten.y;
color3 += color * lambertAtten.z;
}

 

Half-Life2 Episode 1 中的材質更新

halfLambert

調整過的光照曲線

   Half-Life2 Epsisode 1 發佈的時候,支持 SM 3.0 的顯卡已經大量的上市了。所以可以編寫更複雜的效果,對於模型物體來講,反射的效果又加強了。兩個動態光源的高光反射也加入了計算,另外對於支持 SM3.0 的顯卡還加入了高光反射指數和菲涅耳反射係數的計算。

shadetree

前面介紹 Source 引擎會根據物體的位置選擇環境立方體反射貼圖和環境立方體光線,在 Half-Life2 中物體移動時因為選擇了不同的環境立方體,光線的突然變化會顯得非常生硬。在 Half-Life2 Episode 1 可以在兩個相鄰的環境立方體之間做採樣過渡來解決這個問題。

withoutBoosted

withBoosted

折射效果:

折射效果用於場景中的玻璃或者水面之類的特效。如果我們把場景中的一個多邊形面所擋住的東西畫到貼圖上,再貼到這個面上去,那麼這個面看起來就是透明的了。Source 引擎中使用 StretchRect() 從場景渲染後暫存的幀緩存中複製出一個紋理,就是我們常說的實時渲染到紋理。如果我們在貼這個貼圖時做一個擾動,比如加一些水波或者凹凸的擾動,那麼這個面看起來就像是水面或者凹凸不平的玻璃的折射的效果了。這個紋理將參照 dudv 凹凸貼圖或法線貼圖在屏幕空間中做適當的偏移,然後再投射到幾何體上。借助法線貼圖和最新的DirectX 9.0 Pixel Shader技術,加入這個擾動的過程十分容易實現。

refract_1[1]

部分著色器代碼:

 sampler RefractSampler:          register( s2 ); 
sampler NormalSampler: register( s3 );
sampler RefractTintSampler: register( s5 );
const float3 g_EnvmapTint: register( c0 );
const float3 g_RefractTint: register( c1 );
const float3 g_EnvmapContrast: register( c2 );
const float3 g_EnvmapSaturation: register( c3 );
const float2 g_RefractScale: register( c5 );
struct PS_INPUT
{
float2 vBumpTexCoord: TEXCOORD0;
float3 vWorldVertToEyeVector: TEXCOORD1;
float3 x3tangentSpaceTranspose: TEXCOORD2;
float3 vRefractXYW: TEXCOORD5;
float3 projNormal: TEXCOORD6;
};

float4 main( PS_INPUT i ) : COLOR
{
// Load normal and expand range
float4 vNormalSample = tex2D( NormalSampler, i.vBumpTexCoord);
float3 tangentSpaceNormal= vNormalSample * 2.0 -1.0;
float3 refractTintColor= 2.0 * g_RefractTint * tex2D( RefractTintSampler, i.vBumpTexCoord);
// Perform division by W only once
float ooW= 1.0f / i.vRefractXYW.z;
// Compute coordinates for sampling refraction
float2 vRefractTexCoordNoWarp= i.vRefractXYW.xy * ooW;
float2 vRefractTexCoord= tangentSpaceNormal.xy;
float scale = vNormalSample.a * g_RefractScale;
vRefractTexCoord = vRefractTexCoord * scale;
vRefractTexCoord += vRefractTexCoordNoWarp;
float3 result = refractTintColor * tex2D( RefractSampler, vRefractTexCoord.xy);
return float4( result, vNormalSample.a);
}

水面的視覺效果:

    首先我們來看反射。Source 引擎首先將水面上要反射的景物上下顛倒地渲染到一張貼圖上。然後是折射,將水下的景物渲染到另一張貼圖上。然後將這兩張貼圖貼到水面的多邊形上,同時根據凹凸貼圖,使用 Pixel Shader 對紋理坐標進行調整,以加入擾動,得到水的最終效果!簡單吧。 然後將這兩張貼圖貼到水面的多邊形上,同時根據凹凸貼圖,使用 Pixel Shader 分別加入擾動,得到水的最終效果!簡單吧。如果沒有DirectX 9.0中的Pixel Shader技術,想對反射和折射貼圖進行像素級的擾動來實現這樣的水面效果是十分困難的。在Source引擎中對水面的視覺模擬範例 只採用了30行左右的代碼:

water_1_tn

最終的效果

water_2_tn

water_3_tn

反射貼圖

折射貼圖

float4 main( PS_INPUT i ) : COLOR 
{
// Load normal and expand range
float4 vNormalSample= tex2D( NormalSampler, i.vBumpTexCoord);
float3 vNormal= vNormalSample * 2.0 -1.0;
float ooW= 1.0f / i.W; // Perform division by W only once
float2 vReflectTexCoord, vRefractTexCoord;
float4 vN; // vectorize the dependent UV calculations (reflect = .xy, refract = .wz)
vN.xy = vNormal.xy;
vN.w = vNormal.x;
vN.z = vNormal.y;
float4 vDependentTexCoords = vN* vNormalSample.a * g_ReflectRefractScale;
vDependentTexCoords += ( i.vReflectXY_vRefractYX * ooW);
vReflectTexCoord = vDependentTexCoords.xy;
vRefractTexCoord = vDependentTexCoords.wz;
float4 vReflectColor = tex2D( ReflectSampler, vReflectTexCoord) * vReflectTint; // Sample reflection
float4 vRefractColor = tex2D( RefractSampler, vRefractTexCoord) * vRefractTint; // and refraction
float3 vEyeVect= texCUBE( NormalizeSampler, i.vTangentEyeVect) * 2.0 -1.0;
float fNdotV= saturate( dot( vEyeVect, vNormal) ); // Fresnel term
float fFresnel= pow( 1.0 - fNdotV, 5 );
if( g_bReflect && g_bRefract) {
return lerp( vRefractColor, vReflectColor, fFresnel);
}
else if( g_bReflect) {
return vReflectColor;
} else if( g_bRefract) {
return vRefractColor;
} else{
return float4( 0.0f, 0.0f, 0.0f, 0.0f );
}
}

色澤貼圖(tonemap)

如果你有一間只有一角被照亮的,灰暗的房子,那麼在這個角落裡的物體旁邊的光線會比房間其他地方要亮。這就是一個”泛光”的現象。如果你將整個房間照亮的話,在這個角落上的物體也會在視覺上出現高於正常的亮度。在這個黑暗的房子的頂棚開一個洞,比如是天窗或者是破舊的穿洞什麼的,讓外面的陽光直射進屋,這時在天窗或者孔洞的周圍就會出現泛光的現象,這一圈的範圍會出現很多鮮明生動的色彩。在 Half Life 2 中,這些泛光或光暈是由光澤貼圖進行處理的。

hdr_03_tnhdr_04_tn

由於一般 HDR 也在後期處理的時候使用光澤貼圖,所以許多遊戲中應用的光澤貼圖並且往往被誤認為是 HDR。

hdr_01_tnhdr_02_tn

hdr_05_tnhdr_06_tn

HDR (High Dynamic Range)

在 Half Life 2 新放出的 Lost Coast 地圖中採用的 HDR 的渲染方式。當開發小組開始考慮通過什麼場景來展現 HDR 的超強性能時,海灘成為了我們的首選。 天空、海水以及岩石的視覺關係只有通過 HDR 才能完美重現。 為了獲得高動態範圍,以便準確模擬光線與周圍表面(如潮濕的岩石)之間的相互影響,開發小組需要掌握過去從未瞭解過的、涉及物體表面的精確信息。每種材質貼圖都需要按一個真實的比例進行反射值的調節,比如 一般的材質都不能完全反射或完全吸收光線,所以白色物體的反射值將被降低到 80% 甚至更低而不是 100% 的白色。而黑色物體的反射值被提高到 20% 或更高。因此,開發小組借助 3D 程序對紋理進行了模擬,從而確保紋理中的物理信息讓 HDR 能夠準確地形成表面的反射光。我們還設計了每種表面的顏色和值,確保它們能夠正確涵蓋所有曝光量級。

另外由於傳統的 24 位色的 RGB 值的範圍被限制在 0 到 1 之間,而且單色的精度只有 8 位即 256 個亮度級,很多的反射值在經過大量的運算後就會出現誤差。而且按照常規的渲染方法,如果屏幕物體具有 20% 的反光度,例如濕潤的沙地,其紋理最亮的地方是赫色的,則最大反射亮度將只有顯示器最高亮度的 20%,也就是說不會比這個赫色還要亮。 HDR 更加精確的光線模擬能力將確保陽光照射濕潤沙地時產生的反射更加逼真,反射光線可達顯示器的最高亮度,也就是說反光可以是白色的。此外,HDR 通過模糊效果來模擬超過顯示器最大亮度的光線。

目前ATI的8500以上顯卡均已經支持浮點著色以及緩衝區的多對象著色,nVIDIA的顯卡也已經從NV40開始支持該功能,能通過硬件實現 HDR 效果 。最簡單的來說,只要把光源的格式幀和緩存格式設置為 HDR 的格式,很多近似於現實的物理效果能被模擬出來。

HDR Light Source(HDR光源): 
未經壓縮的光線數據值令設計人員能夠從任何給定的場景中使用更大範圍的光線數據值。 這些光源將影響到輻射度光影貼圖、輻射度環境立方體貼圖的生成。

HDR 光影貼圖: 
通過輻射度渲染過程產生,將光輻射/全局照明也考慮在內。所有的靜態的物體都是使用光影貼圖的,所以我們可以在牆上看到這種特效。 

20050624145838_11084
 

20050624145917_16982
 
HDR Cube Map(HDR立方體貼圖): 
由引擎生成,使用 HDR 天空盒貼圖和 HDR 光源及 HDR 光影貼圖合力產生。HDR 立方體貼圖讓一個物體的反射光線更符合光源的亮度。 這影響到場景中所有的動態的物體。 

20050624144730_46726
 
20050624144845_65026

以上是對應前面介紹的靜態物體和動態物體的光影渲染方式對光源進行的 HDR 的改造。其它物體上的貼圖,如紋理、法線、反射度等貼圖只要進行略微的調整或幾乎不需要進行調整就可以在 HDR 渲染中繼續使用。

HDR 天空盒: 
因為天空盒是渲染完場景物體後在渲染合成的,那麼只需要把天空盒的紋理格式換為 HDR 的格式即可。實時曝光調整 將在後期由動態光澤貼圖進行處理。 

20050624144313_44881
 

20050624144424_55084

HDR折射效果:

Source 引擎支持的著色範圍廣泛。窗口中的折射陰影需要我們將場景複製到紋理中,加上折射,然後再重新應用到窗口的表面上。折射實際上是一種用到了幀緩存的紋理特效,為了完全支持 HDR,引擎中的每種著色代碼都需要進行更新,從而改進這種折射陰影以支持完整範圍的對比 ,其中最重要的就是把幀緩存的格式提升到 HDR 所需要的精度。HDR 光照穿過可折射材料傳輸時產生的符合這些材料性質的效果(比如陽光照射在LC中修道院的彩色玻璃時產生絢麗的五彩光芒) ,如果不使用 HDR 由於渲染精度問題將造成效果的錯誤。 

20050624145322_57779
 

20050624145618_34669

HDR 水反射/折射: 
渲染有水的場景也是一種用到了幀緩存的紋理特效。上文介紹過,事實上場景被渲染 3 次。一次用於反應水下的折射,一次用於反應水上物體的折射,還有一次是從最終視圖。在 Lost Coast 中能在兩個小的窗口屏幕上看到反射和折射的場景。在折射中,我們逐像素計算您看穿的水有多深,然後製作水下立體霧,模擬顆粒物質。由於 HDR 需要把幀緩存改為 HDR 所需的精度格式,對於完整的 HDR 解決方案,必須仔細檢查整個引擎,並修改計算光線和顏色的代碼。 例如,這些水反射和折射渲染必須經過改進才能支持 HDR 完整範圍的對比值。 

20050624145113_98788
 
20050624145220_70380

當把所有的著色代碼改為 HDR 的格式後,就能精確的做一些後期的特效。因為眼睛會對你看到的亮度作出調節,虹膜會不斷地受到週遭光源環境的影響,而促使瞳孔擴張和收縮活動,眼鏡會讓瞳孔處在一個能接受所見景物最高亮度和最低亮度之間的一個平衡點上,並會不斷隨環境變換而作出調節,視覺區域中,和這個平衡點相距甚遠的色彩區域則不會表現該點的細節,而且會和背景以及光線在大氣中的折射產生豐富的色彩範圍,於是,泛光的區域便會出現。

Exposure Control(曝光控制):

Lost Coast 中的 HDR 解決方案的特色之一就是動態光澤貼圖。 簡單說來,動態光澤貼圖是一種模擬人眼對光的反應的方法。 在現實世界中,當您走進一間黑暗的房間中時,會發現您的眼睛會對黑暗進行調節,以使您在適應一段時間之後看得更清。 或者當您走進一個明亮的空間時,會感到陽光刺眼,只有眼睛調節之後才能正常用眼。 您的虹膜會自動調節以適應射入您眼睛中光的強度。 動態光澤貼圖模擬的就是這一過程,通過自動調節場景的曝光程度來模擬虹膜的調節過程。此外,相對於屏幕邊緣的光線,我們對屏幕中心的光線考慮的更多,從而能更好地模擬眼睛看到的幾何構造。

hdr_lc_21hdr_lc_22

在渲染的時候,按照 HDR 格式渲染到幀緩存以後,幀緩存被覆製為幾份。其中一份將被縮小為一個 1x1 像素的貼圖以測量整個場景的亮度。因為“眼睛”的瞳孔對視線不同位置的感受不同,所以不同位置的像素縮放是的亮度比例也是不同的,場景中央的像素對測量整個場景的亮度範圍影響最大。這個測量出的亮度值將對幀緩存中的圖像進行亮度調解,使之變得更亮或更暗,以保證儘可能多的像素亮度在 0 到 1.0 之間。這樣模擬了眼睛瞳孔在不同光線下的縮放,令我們在過亮或過暗的場景中看到更多細節。

Bloom(泛光): 
由於現在的顯示設備不能顯示亮度值大於 1.0 的亮度,所以一般使用泛光效果對這些亮度值超過 1.0 的像素進行處理,以模仿曝光過度。幀緩存另外被覆製出一份和前面測量出的亮度值進行比較,大於 1.0 的像素被選出進行橫向和縱向的高斯模糊處理,然後再與原來的渲染結果進行混合,也就是進行光澤貼圖的處理以生成最終的顯示結果。 

20050624144031_64677
 

20050624144154_21076

動畫、幾何體以及遊戲界面 
動畫以及角色面部造型

就如目前很多的現代遊戲引擎一樣,角色的動作效果由一個骨骼系統控制。事實上,Half-Life 1 是這個骨骼系統使用的先行者。通過這骨骼系統,多種的動畫以及細膩的動作可以被綁定到同一個角色上,由綁定在不同骨架部位的動畫互相作用而生成整個角色的動作。Source引擎還在這個骨架系統中整合進Havok先進的物理引擎。

對於玩家來說,使用附加有物理特性的骨骼系統會讓我們看到更加真實的角色動作以及怪物的運動動作。因為在這些骨骼上會混合地播放角色動畫,舉個例子,一個角色在跑動的時候向對手開槍,然後被對手還擊的子彈擊中手部,手中的槍脫手飛出……這一系列的動作都能在一個角色上同時上演,加入了物理特性的骨骼系統能對受傷的手部作出反應,比如是承受衝擊力時的晃動以及身體的扭轉。

面部動畫系統 

也許Source中最為神奇的特效來自於引擎中對面部表情的動畫,Valve覺得,Half-Life 2里面的角色應該擁有與現實無異的表情,為了達到這個目的,Valve建立了一個包含面部肌肉模擬系統以及一個基於文本文件的半自動聲音識別系統。 
在文本識別系統中,Source採用了“Keyshape”的動畫,類似於Valve裡面資深動畫師Bill Buren所描述的漸變以及頂點動畫。這套系統中包含了一系列預先設定的表情腳本,能控制角色面肌肉群產生相應的面部動畫,並進行相加/混合/插值運算以創建現實生活中所見的自然真實的角色表情。 
為了縮短整個角色動畫的構建過程,Valve在Source中創建了一套VRS (Voice Recognition Software,聲音識別軟件),有了準確自然的角色面部表情,就需要有精密的發音口形與之搭配,於是,一個文本文件會在語音對話中起到指揮的作用,關卡的設計師只需簡單地將一個聲效文件以及對應的腳本放進Source中,便能讓一個角色在適當的時候說話。所有的角色動作會由VRS作出發音單詞的判定然後調用該單詞的口形,這些基本的口型就被稱為Keyshape,通過骨骼系統和頂點漸變過程實現Keyshape之間的動畫,於是便產生了精確的發音口形。更為神奇的是這些Keyshape能綁定角色的情感因素,Keyshape根據權重來確定在面部產生表情的正確部位,Valve將其稱之為面部肌肉的仿真模擬。

snap324

Geometry

幾何模型的體現主要來自網格(例如表現一個物體或者角色的整體),靜態網格(非生成整個物體),BSP序列以及一些幾何建模實體。在Source中,所有的幾何體均被儲存在BSP序列中,當你查閱該序列的時候,你可以看到幾何體均在 Hammer 地圖編輯器中的光影關係,正如上面所說的場景合成一樣,所有的輻射度、漫反射係數、鏡面反射係數等均在編輯器中生成。

BSP序列 (Binary Space Partitioning tree) 用以儲存在Source裡面大量的固態幾何體(或許還有一些非固態的),不過,關於BSP序列的創建以及具體的操作將不在本文的討論範圍,各位有興趣的玩家可以在google上搜索有關地圖製作的知識,特別是關於Hammar地圖編輯器的使用幫助。

當我提及“mesh”網格的時候,具體指的是一些將幾何體儲存在地圖文件之外一些獨立的文件中,Source採用了.mdl文件來儲存mesh網格。mesh多數用在創建能移動的物體或者一些復合的靜態物體,主要是因為這種格式的文件能被XSI以及3DMax這些常用作三維建模以及動畫設計的軟件所導入。此外,.mdl文件還能多次地在場景中出現而避免重複地將幾何體插進地圖文件,採用了.mdl之後,你就不會再為已經夠複雜的BSP序列添加進更多的冗餘信息。

 

VGUI

console_2_tn

值得欣喜的是,Valve的圖形控制界面將在Source中獲得全新的定義。大家都知道,GUI (Graphic User Interface,用戶圖形界面) 就是和命令行(拚命地打字輸入命令)相對的概念,意味著你根本不需要輸入任何的命令,因為你有形象的圖形操作界面能實現交互的人機操作。VGUI更是讓開發者使用Source進行GUI的渲染,能在遊戲中任何的地方顯示並交互執行命令。這時,VGUI已經不再只限於一套菜單系統,他提供了程序接入窗口、按鈕、彈出窗口、內核控制命令行等等控件。所有的這一切能按照程序員甚至玩家的意願顯示或者放置。另一方面,由於Source的VGUI採用了兼容的Unicode編碼,能很方便地實現遊戲語言以及文字的本地化,在CS:Source中任何地方的中文輸入甚至全世界的語言文字輸入已經讓我們見識到了下一代VGUI的威力。

console_1

在Source中,VGUI能在遊戲中以及遊戲外面進行顯示,Steam的菜單就是一個很好的例子。在CS:Source出版之前,我們還不清楚,STEAM看似革命性的菜單是否就是採用了全新的VGUI編寫。到了今天,CS:Source正式公開測試,可以這樣下一個判斷,CS:S中的,才是真正的VGUI,因為他採用了遊戲的渲染引擎進行菜單的潤色渲染,並能通過菜單操作所有的界面命令以及內核命令(半透明以及淡出淡入的效果、Debug Option菜單、附著完整信息的Console控制台),而Steam的窗口菜單只是GUI菜單的一個比較完善的改進罷了。據稱,Valve將為Source制定的VGUI命名為VGUI2

物理系統

有一樣東西是Valve想在Half-Life 2中需要解決的首要問題,那就是一個能讓角色和場景中任何物體都能發生互動關係增加整個遊戲可玩性的物理引擎。Valve一直在找這樣一個引擎,能讓物品能和主角發生互動關係而不再是一個擺設,最後,他們將目光定格到 Havok 的物理引擎上。

當和Havok整合之後,Source的一切得到了重生般的感覺,引擎裡面所描述的所有東西都有了他們獨到的物理特性,包括了聲音、外觀、材質、AI以及角色動畫。當Valve被問道,是否會將Havok 1 引擎升級到Havok 2 的時候,Valve指出目前並不會作出此等舉動,因為二代的引擎並沒有比目前他們所擁有的引擎有更多的性能提升。

剛體動力學 以及 約束、關節鏈

正如名稱一樣,剛性物體不能在遊戲中被打碎、彎曲或者其他形式的扭曲。剛體的動力學是目前遊戲中最常見的一個物理引擎表現部分--僅僅是非形體變形上的物理模仿,通常會以小盒子、鐵桶、臨時的木板等形式出現,不過有時候也會以小玩偶的形式出現(通過關節鏈將剛體組成一個簡單的整體)

關節節點就是常說的活動連接,這種連接允許你將兩個物體有機地連接在一齊,你可以為這個關節添加約束,使他們只能在你規定的範圍之內活動而不會走遠。這就是在遊戲中NPC被創建出來的關鍵。在眾多角色中數不清的關節裡面,有一些是能夠被打斷的,而有一些不能,在這些能夠被打斷的關節上,在被折斷前就存在著一個受力的極限值。

explo

樓房被怪物擊中,整個牆壁以及上面掛著的招牌被怪物強勁的衝擊波銜翻,在Half-Life 2中似乎到處都充滿這種可以被破壞的牆體。

柔性體:

和剛性體相對的就是柔性以及彈性體,就和Source中的物理特性--作用力與反作用力一致,具體來說就是受力物體對施力物體的一種反作用,起到兩者的排斥反應,只不過在彈性力中,這種排斥力是柔和的,加速度相對較小。在和橡膠輪胎的碰撞中,我們就能體驗到這種彈性的反作用力。繩索的定義只需在地圖編輯器中簡單地連接兩點並指定其屬性即可,最重要的一個屬性就是取樣點的多少。當然,取樣點越多繩結看上去越真實,但會對顯示性能造成負面的影響。布料的模擬就類似於繩索,取樣點點的多少將直接對布料在物理環境(大風、皺褶等)中的真實程度。當然,布料的模擬將消耗比繩索更加多的CPU資源而且按照目前的3D設計來說,要真實地模擬出一塊布料在各種環境中的形態還是未能做到精確。 
車輛系統

車輛系統在Source中就是物理系統和建模系統的有機結合,加入了屬於專有的腳本系統以便能讓玩家操縱車輛。在Source引擎中的車輛系統由另外一群主要的專業設計師擔任設計,以在達到最真實效果之餘在最大限度上節省創作時間。畢竟Half-Life 2不是一個專業的賽車遊戲,玩家只會用方向鍵對車輛進行操控,因此,在遊戲中出現的航船、氣墊船等可操控的交通工具均保持了最高的易用度:和讀者想像中最簡單的操作方法一樣,WSAD控制方向,鼠標控制視覺以及槍械的瞄準。當然,車輛擁有屬於自己的一套獨立的腳本系統。

car_tn

水、火

Source中另外一個令人迷惑的東西就是水的模擬系統。當Valve說,水的效果會在物理的層面被模擬的時候,各界均推斷在Source中將會引入流體動力學並加入物體的浮力特性到其物理指標中。但到了後來,Valve在聲明中指出,在Source中並沒有內建流體動力學的模型,water區域只是一個加入了特別的物理特性的場地。當其他的物體進入到這個區域後,會作出與之正常行為不同的動作,比如是行動變得緩慢、並且會出現重力上的變化(浮力)。當然,如果是真正的流體動力學模擬的話,將會是完全的自身物體特性引起的行為動作而非單純的兩者之間的互相作用。

火,在Source系統中並非一個獨立的系統,但作為一個FPS遊戲中不可或缺的部分(特別是Half-Life 2這種充滿毀滅性鏡頭的大作),火的效果是最能吸引玩家的眼球的。在Source中,火混合了粒子系統和材質系統,按照道理來說,在遊戲關卡中的火苗如果在旁邊出現易燃的物體的話,火將會向這些物體蔓延出去,不過並沒有確切的消息肯定這個推斷。

粒子系統和材質系統

blood_tn

在Source中,粒子可以理解為“點狀物”,常在一些單面的幾何面上出現,這種粒子沒有區域,沒有體積,只能依靠坐標系或者某些參照物來描述其存在的位置,在字面上解,他們就是在空間上的點,是Source中比較容易進行模擬的物體。Source中的粒子除了長寬高的幾何尺寸和明顯的扭轉系統之外,擁有和剛體完全一致的物理特性,畢竟,例子系統並不需要和剛體系統那樣豐富的互動性。在遊戲中,使用粒子系統創建的最常見的物體就是煙霧、沙塵、火、雨、雪、血霧、火花以及飛濺的碎片。

材質系統

在Source引擎中的材質系統被賦予了物理特性以及貼圖外觀的特性。例如,給一塊板賦予木材的材質,這塊板便具有了木材的所有物理以及外觀特徵,重力、韌性、聲學特性等均被附加到這塊板上。材質特性方面保留了諸如密度、表面材質、耐壓度、以及當折斷或者敲擊時發出的聲響,雖然這些在眾人眼中看是一些很簡單很必然的事情,但在遊戲中,這些細節將會是遊戲增加其感染力的最佳途徑。此外,AI系統還能就材質系統提供的信息,反饋給NPC以使其對戰術作出相應的調整。當然,玩家也可以充分利用材質的特性從而得到更多的通關方法。

crack_tn

木板被賦予了物理剛節點之後,破裂之後的斷層不再如一般遊戲那樣刀切般平整。

AI系統

AI,Artificial Intelligence,近年來在電腦領域是一個突飛猛進的科研領域,一個優秀的AI系統,能對其周圍的環境智能地作出非線性的反應並採取相應的行動。最重要的是具有依據周圍環境參數的變更而決定系統行動的能力,否則,就不能稱之為真正的AI。一個慣常的AI系統,會使角色根據當前任務的可行性、對主要任務影響的重要性以及目前的週遭環境作出判斷以選擇達到預定目的的最佳途徑,而怎樣選擇最佳的動作以達到這個目的,就是一個AI系統優秀與否的體現。

cover_tn

聰明的NPC借助矮牆以及隊友在對角線火力的掩護下弓身前進。

老實地說句,筆者並不完全明瞭Source的AI系統所能夠完成的任務,Valve關於Half-Life 2的白皮書中就說過很多的東西表明Source裡面的AI系統是多麼的強大,大意就是下面的幾點(已經足夠讓目前每一個遊戲汗顏的了)。

  • 允許關卡設計師以I/O接口聯繫遊戲中的實體以控制AI系統。
  • 成熟的導航系統,可以實現角色的跑動、飛行、跳躍、下蹲、上樓梯和爬手扶梯甚至在地下的挖掘動作。我們在cz中的機器人已經見識過新一代的導航系統是如何地高超。
  • AI系統能依靠視覺、聽覺以及嗅覺感知到物體 的存在
  • AI的關係決定敵友以及其他實體的關係
  • 戰鬥AI允許AI角色進行團隊攻防,他們知道何時推進、何時撤退、何時伏倒尋找掩護等等的戰鬥動作
  • 如果一個NPC看到你向一個關鍵的物體跑去的時候,他會判斷你對該物體要作出的動作而採取相應的行動
  • 大規模的NPC進攻將一道橋弄垮這些細節將會脫離Valve的劇本隨時地,在不可預見的情況下發生
  • AI知道移動重型物體是難以被快速移動的。
  • AI能清楚地知道他看到你的最後位置,並會對你逃跑的路線作出判斷,以採取相應行動。
  • NPC會拒絕執行來自玩家的愚蠢的命令
  • AI能使用物理存在的物體。

在遊戲中NPC的AI著實讓筆者大吃一驚,比如在TrainStation的一幕,主角剛剛到達City17,被NPC帶到一個陰暗的長廊上準備接受洗腦,途中聽到一間房間內發出人類疼苦的哀嚎,好奇的主角把頭湊過去那個房門上打開的小窗戶想瞭解裡面發生什麼事情,這時,屋內的NPC會判定屋子裡面發生的事情不是主角所應該看到的