第六章 Lighting Models
在現實世界中,沒有光照是無法看見東西的;一個物體能被看見,要麼是通過反射光源,要麼是自身發光。在使用電腦渲染時,類比光線的互動可以使3D objects更逼真。但是光照的互動是一個非常複雜的過程,不能簡單的在各種交叉的幀率之間進行複製(至少目前階段還不行)。因此,使用光照與3D objects互動的近似值或光照模型,在情境中實現更多的細節。本章主要介紹一些基礎的光照模型。
Ambient Lighting(環境光線)
在一個光照環境中,環境光線看起來遠處不在。例如,即使沒有光源直接照向桌底下最遠的角落裡,你依然可以看清角落的細節。這種環境光線,是由物體表面上無數種光線相互作用的結果。當一束光照射到物體表面時,光線的全部或部分要麼被反射要麼被吸收,而且會一直持續。因此部分光線能照射到桌底的角落裡,即使只有一點點。
通過改變一些常量值來修改某個pixel的顏色值可以實現環境光線的簡單近似。可以把這些常量看成光照亮度或強度係數,介於0和1之間,每一個pixel都與該係數相乘就能得到環境光線的近似值。環境光線強度值越接近於0,物體看起來就越暗。此外,可以在環境光線模型中包含一種顏色,用於類比非純白色的光源。這需要計算一個pixel的紅,綠,藍各個通道值。列表6.1列出了一種環境光線effect的代碼。
列表6.1 AmbientLighting.fx
/************* Resources *************/#define FLIP_TEXTURE_Y 1cbuffer CBufferPerFrame{float4 AmbientColor : AMBIENT <string UIName = "Ambient Light";string UIWidget = "Color";> = { 1.0f, 1.0f, 1.0f, 1.0f };}cbuffer CBufferPerObject{float4x4 WorldViewProjection : WORLDVIEWPROJECTION < stringUIWidget = "None";>;}Texture2D ColorTexture <string ResourceName = "default_color.dds";string UIName = "Color Texture";string ResourceType = "2D";>;SamplerState ColorSampler{Filter = MIN_MAG_MIP_LINEAR;AddressU = WRAP;AddressV = WRAP;};RasterizerState DisableCulling{CullMode = NONE;};/************* Data Structures *************/struct VS_INPUT{float4 ObjectPosition : POSITION;float2 TextureCoordinate : TEXCOORD;};struct VS_OUTPUT{float4 Position : SV_Position;float2 TextureCoordinate : TEXCOORD;};/************* Utility Functions *************/float2 get_corrected_texture_coordinate(float2 textureCoordinate){#if FLIP_TEXTURE_Yreturn float2(textureCoordinate.x, 1.0 - textureCoordinate.y);#elsereturn textureCoordinate;#endif}/************* Vertex Shader *************/VS_OUTPUT vertex_shader(VS_INPUT IN){VS_OUTPUT OUT = (VS_OUTPUT)0;OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);OUT.TextureCoordinate = get_corrected_texture_coordinate(IN.TextureCoordinate);return OUT;}/************* Pixel Shader *************/float4 pixel_shader(VS_OUTPUT IN) : SV_Target{float4 OUT = (float4)0;OUT = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);OUT.rgb *= AmbientColor.rgb * AmbientColor.a; // Color (.rgb) *Intensity(.a)return OUT;}/************* Techniques *************/technique10 main10{pass p0{SetVertexShader(CompileShader(vs_4_0, vertex_shader()));SetGeometryShader(NULL);SetPixelShader(CompileShader(ps_4_0, pixel_shader()));SetRasterizerState(DisableCulling);}}
AmbientColor Shader Constant
可能你已經注意到了這段代碼與上一章紋理映射effect中的大部分代碼都一樣。使用以前的effects代碼,是為了更好的學習後面的知識。
第一處與上一章不同的是,增加了一個float4類型的shader常量AmbientColor,表示環境的顏色和強度。光的顏色值儲存在RGB通道中,而且強度值儲存在alpha通道中。而且AmbientColor使用一種新cbuffer,CBufferPerFrame。回顧一下第四章,“Hello,Shaders”所討論的constant buffers,通常是根據cbuffer中包含的資料所期望的更新頻率來選擇cbuffers類型。在本例中,AmbientColor值不僅要用於多個objects中,還要於每一幀都進行更新。這與WorldViewProjection對象中使用的CBufferPerObject相反,CBufferPerObject表示把AmbientLighting effect用於每個object,而不是每一幀。
AmbientColor常量有一個對應的AMBIENT語義。與其他任何shader常量一樣,AMBIENT語義也是可選的,但給變數設定語義是一種好的習慣,因為有了語義CPU端的應用程式就可以通過語義訪問該變數值,而不是使用hard-coded name。
最後需要注意的是,AmbientColor常量被初始化為{1.0f, 1.0f, 1.0f, 1.0f}。表示這是一個純白色,最大強度的環境光線,不會改變objects的輸出顏色。因此,即使在CPU端的應用程式中忽略該變數,對輸出結果也沒有負面影響。
其餘的代碼,都與紋理映射effect中的代碼一樣,直到pixel shaderf才有差異。
Ambient Lighting Pixel Shader
環境光線照模型的pixel shader首先sample紋理顏色,然後計算光照輸出。具體地說,就是對於紋理採樣所輸出的顏色的RGB通道值都要與AmbientColor.rgb * AmbientColor.a的乘積相乘。一個向量(AmbientColor.rgb float3類型)與一個標量(AmbientColor.a float類型)相乘,要對向量的每一個分量都與標量相乘。因此,環境光線的顏色首先通過光照強度進行調整(也就是光照顏色與光照強度相乘),然後再把採樣輸出的RGB通道值與該乘積相乘。
Ambient Lighting Output
圖6.1是在紋理映射effect的基礎上再加上ambient lighting effect應用到一個sphere的輸出結果。左圖中使用了純白色的光,並且光照強度係數為0.5(即alpha通道值為0.5)。而右圖中,則是紅色光(紅色通道值為1.0,綠色和藍色通道值為0),強度為1(alpha通道值為1.0)。
圖6.1 AmbientLighting.fx applied to a sphere with the texture of Earth with a pure-white,