1. 程式人生 > >天空盒(SkyBox)的實現原理與細節

天空盒(SkyBox)的實現原理與細節

天空盒的原理

在實時渲染中,如果要繪製非常遠的物體,例如遠處的山、天空等,隨著觀察者的距離的移動,這個物體的大小是幾乎沒有什麼變化的,想象一下遠處有一座山,即使人走進十米、百米、甚至千米,這座山的大小也是幾乎不怎麼改變的,這個時候可以考慮採用天空盒技術。
這裡寫圖片描述
所謂的天空盒其實就是將一個立方體展開,然後在六個面上貼上相應的貼圖,如上圖所示。
在實際的渲染中,將這個立方體始終罩在攝像機的周圍,讓攝像機始終處於這個立方體的中心位置,然後根據視線與立方體的交點的座標,來確定究竟要在哪一個面上進行紋理取樣。具體的對映方法為:設視線與立方體的交點為(x,y,z),在xyz

中取絕對值最大的那個分量,根據它的符號來判定在哪個面上取樣。
這裡寫圖片描述
然後讓其他兩個分量都除以最大分量的絕對值,這樣就讓另外兩個分量都對映到了[0,1]內,然後就可以直接在對應的紋理上做紋理對映就行了,這個方法就是所謂的Cube Map,是天空盒方法的核心。以下是它的Vertex Shader和Pixel Shader,在Direct3D中可以直接用TextureCube,這樣就省去了做Cube Map的步驟。

cbuffer MatrixType
{
    matrix WorldMatrix;
    matrix ViewMatrix;
    matrix ProjectionMatrix;
}; struct VertexInputType { float4 position : POSITION; float2 tex : TEXCOORD0; }; struct PixelInputType { float4 position : SV_POSITION; float3 tex : TEXCOORD0; }; PixelInputType main(VertexInputType input) { PixelInputType output; input.position.w = 1.0f; output.position
= mul(input.position,WorldMatrix); output.position = mul(output.position,ViewMatrix); output.position = mul(output.position,ProjectionMatrix); output.position.z = output.position.w; output.tex.x = input.position.x; output.tex.y = input.position.y; output.tex.z = input.position.z; return output; };
TextureCube shaderTexture;
SamplerState SampleType;

struct PixelInputType
{
    float4 position : SV_POSITION;
    float3 tex : TEXCOORD0;
};

float4 PixelFunc(PixelInputType input) : SV_TARGET
{
    return shaderTexture.Sample(SampleType,input.tex);
};

天空盒的原理非常簡單,下面來探討關於天空盒的幾個細節問題。

z = w

在投影變換之後,會做一步透視除法,即讓四元向量的所有分量都除以它的W分量,從而使視錐體內的區域的x、y對映到[1,1],z對映到[0,1],從而根據透視除法之後的xyz的範圍直接剔除掉那些不可見的頂點,如果令z=w,就表示透視除法後的z=1,也就是讓天空盒始終處於遠平面的位置,從而讓它被之後所有要繪製的物體遮擋住。由於z=1,要修改深度測試的比較函式,即令 CD3D11_DEPTH_STENCIL_DESC 的desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL,如果比較函式是小於的話,由於深度緩衝的最大值本來就是1,無法通過深度測試的比較函式,最終天空盒是沒辦法被繪製出來的。

消隱問題

由於攝像機位於物體的內部,這個時候消隱的設定尤為重要,模型本身是沒有發生變化的,它的法線始終朝向外部,那麼視線與法線的夾角就變成了銳角,如果採用背面消隱,整個模型就會都被剔除掉,所以在繪製天空盒的時候,要麼取消消隱,要麼將消隱設定為正面消隱。

天空盒模型大小的限制

原則上模型的大小對於最終的結果沒有影響,因為在模型增大的時候,雖然它的面是變大了,但它離視錐也變遠了,因此最終視野內的大小是保持不變的,但是一定要保證模型始終處在視錐體的內部,即讓模型離攝像機最遠的點到攝像機的距離不超過攝像機到遠平面的距離,對於一個立方體模型來說,離中心最遠的點顯然是它的頂點,

d=32aa為立方體的邊長,由dZfara23Zfar因此天空盒的模型邊長有一個上界,在這個最大值以內都是可以的。

天空盒紋理的最佳大小

最終渲染的效果很大程度上取決於紋理大小與螢幕大小之間的關係,如果要達到最佳的效果,肯定是要讓紋理中的每一個紋素對應視窗中的每一個畫素,此時紋理大小與視窗大小的關係為:

Tsize=Rwidthtan(fov/2)其中Tsize為紋理的大小,Rwidth為視窗的寬度,fov為視錐體的視角。
下面來進行推導:
這裡寫圖片描述
取遠平面上的兩點,設它們的x座標分別為x1x2,這兩點顯然就是天空盒上的兩點。
現在計算它們歸一化後在視窗螢幕上的座標:
這裡寫圖片描述
rx1=(x1dtan(fov/2)+1.0)2rwidth
解釋一下:其中x1dtan(fov/2)x1對映到[1,1],再加1併除以2就對映到了[0,1]
同理rx