13
cTerrain::generate_texture方法會調用cTerrain::light_terrain,顧名思義,光照使地形更接近于現實。當我們已經計算完地形紋理以后,我們只需要計算陰影系數(shade factor),使一個定義了光源的地形區域變亮或變暗。你會驚訝于為什么我們照亮地圖卻沒有讓Direct3D來做。我們自己來計算有三個好處:
內存中不必保存頂點法線。
因為紋理是靜態的,所以不能隨意的移動光源。雖然我們可以重新計算光源,但因此采用Direct3D實時的照亮地形是很耗時的。
我們獲得了一些數學上的經驗,熟悉了一些基本的光照概念,并且是用Direct3D函數實踐的。
13.4.1概覽(OVERVIEW)
光照是計算地形陰影(shade)的一個最基本的技巧之一,一般認為的光是漫射光(diffuse lighting),給定一個平行光源,我們用“到達光源的方向(direction to light)”(該光源發出的平行光的傳播方向的反方向)。因此,如果我們想讓光線從空中筆直落下,那么lightRaysDirection = (0, -1, 0),按相反的方向:directionToLight = (0, 1, 0)。注意:創建光照向量要使用單位向量。
注意:雖然指定方向的光是從光源發射出來的,這么說更直接一點,指定到達光源的反向在計算上要比漫謝光更合得來。
對于地形中的每個方格,我們計算光的向量與方格的面法線之間的角度。
在圖13.7中我們看到,當角度變得比較大時,方格的面離光源越來越遠,接收的光越少。反過來說,角度變小,方格的面則離光源越來越近,相應的會接收更多的光。注意:一旦光向量與法線角度大于90度,表面就接收不到光。

我們能夠創建一個陰影(shading)標量,用0..1之間的范圍來表示表面能接收到光的多少。使用陰影標量,角度大則標量接近于0。當顏色與一個陰影標量接近0的值相乘時,得到的結果是:顏色變暗。相反,乘以一個陰影標量的值接近1的值時,顏色則接近于原始亮度。
13.4.2 計算方格的陰影(Shade)
光源的方向是一個單位向量,為了計算光源方向與面法線間的夾角,首先需要找到面法線,這是叉積的一小部分應用,但首先必須在方格里找到二個共面的非0并且不平行的向量。看圖 13.8有兩個這樣的向量:


給指定的格子計算陰影系數用cTerrain::compute_shade方法,它需要參數:行和列來確定方格,還有平行方向光的光源。
float cTerrain::compute_shade(int cell_row, int cell_col, D3DXVECTOR3* dir_to_light)
{
// get heights of three vertices on the quad
float height_a = get_height_map_entry(cell_row, cell_col);
float height_b = get_height_map_entry(cell_row, cell_col+1);
float height_c = get_height_map_entry(cell_row+1, cell_col);
// build two vectors on the quad
D3DXVECTOR3 u(m_cell_spacing, height_b - height_a, 0.0f);
D3DXVECTOR3 v(0.0f, height_c - height_a, -m_cell_spacing);
// find the normal by taking the cross product of two vectors on the quad
D3DXVECTOR3 n;
D3DXVec3Cross(&n, &u, &v);
D3DXVec3Normalize(&n, &n);
float cosine = D3DXVec3Dot(&n, dir_to_light);
if(cosine < 0.0f)
cosine = 0.0f;
return cosine;
}
13.4.3 地形陰影(Shading)
一旦知道了如何給指定的方格加陰影,我們就能給地形上所有的方格加陰影。只要遍例每一個方格,計算方格的陰影值,并測量方格對應的texel顏色。光照少則方格會變暗。
bool cTerrain::light_terrain(D3DXVECTOR3* dir_to_light)
{
D3DSURFACE_DESC texture_desc;
m_texture->GetLevelDesc(0, &texture_desc);
// make sure we got the requested format because our code that fills the texture is
// hard coded to a 32 bit pixel depth.
if(texture_desc.Format != D3DFMT_X8R8G8B8)
return false;
D3DLOCKED_RECT locked_rect;
m_texture->LockRect(0, &locked_rect, NULL, 0);
DWORD* image_data = (DWORD*) locked_rect.pBits;
for(UINT row = 0; row < texture_desc.Height; row++)
{
for(UINT col = 0; col < texture_desc.Width; col++)
{
// Index into texture, note we use the pitch and divide by four since the pitch is given
// in bytes and there are 4 bytes per DWORD.
int index = row * (locked_rect.Pitch / 4) + col;
// get current color of quad
D3DXCOLOR color(image_data[index]);
// shade current quad
color *= compute_shade(row, col, dir_to_light);
// save shade color
image_data[index] = (D3DCOLOR) color;
}
}
m_texture->UnlockRect(0);
return true;
}