Diffuse Lighting(漫反射光)

來源:互聯網
上載者:User
Diffuse Lighting(漫反射光) 不同的材質表面反射光的方式也不同。在鏡面上光的反射角度與入身角度相等。當在一隻貓的眼睛裡看到一束怪異的光芒,這就是光的反射性:這是由於貓的眼睛反射光的方向與光源的照射方向平行,但是方向相反。漫反射表面對光的反射在各個方向上都一樣。
近似計算一個漫反射光,最簡單並且最常用的模型是Lambert’s cosine law(朗伯餘弦定律)。根據Lambert’s cosine law,照射到材質表面的光照亮度,與光源方向向量和面法線的夾角的餘弦成正比。光源向量描述了光的照射方向,法向量確定了表面的朝向。圖6.3說明了這些術語。
圖6.3 An illustration of a surface normal, a light vector, and Lambert’s cosine law.

回想一下第二章,“A 3D/Math Primer”,所討論的向量運算,通過向量的dot product可以得到光源向量與法向量(這兩個向量都是單位向量)夾角的餘弦值。一般的,法向量在3D object載入時由每一個vertex提供(或者通過triangle兩條邊的cross-product計算得出)。

Directional Lights
在3D圖形學中定義了三種常用的光源:directional lights,point lights,spotlights(方向光,點光源,聚光燈)。一個directional light表示距離3D objects無窮遠的光源,也就是說相對於objects沒有座標位置的意義。因此,照射到objects上的光線都是平行的,來自於同一個方向。Directional light的一個很好的例子是太陽光(雖然太陽不是嚴格意義上的無窮遠)。圖6.4描述了direction light的概念。
圖6.4 An illustration of a directional light.
類比一個directional light,只需要簡單的用一個三維向量定義光的方向。也可以像ambient light那樣,包括光的顏色和強度。列表6.2列出了一種diffuse lighting effect的代碼,該effect使用了一種簡單的directional light。本書中講述的十分有限,建議把代碼拷貝到NVIDIA FX Composer中,一步一步的測試該lighting effect。(也可以,從本書的配套網站上下載該代碼)
列表6.2 DiffuseLighting.fx
#include "include\\Common.fxh"/************* Resources *************/cbuffer CBufferPerFrame{float4 AmbientColor : AMBIENT <string UIName =  "Ambient Light";string UIWidget = "Color";> = {1.0f, 1.0f, 1.0f, 0.0f};float4 LightColor : COLOR <string Object = "LightColor0";string UIName =  "Light Color";string UIWidget = "Color";> = {1.0f, 1.0f, 1.0f, 1.0f};float3 LightDirection : DIRECTION <string Object = "DirectionalLight0";string UIName =  "Light Direction";string Space = "World";> = {0.0f, 0.0f, -1.0f};}cbuffer CBufferPerObject{float4x4 WorldViewProjection : WORLDVIEWPROJECTION < string UIWidget="None"; >;float4x4 World : WORLD < string UIWidget="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;float3 Normal : NORMAL;};struct VS_OUTPUT{    float4 Position : SV_Position;    float3 Normal : NORMAL;float2 TextureCoordinate : TEXCOORD0;float3 LightDirection : TEXCOORD1;};/************* 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); OUT.Normal = normalize(mul(float4(IN.Normal, 0), World).xyz);OUT.LightDirection = normalize(-LightDirection);return OUT;}/************* Pixel Shader *************/float4 pixel_shader(VS_OUTPUT IN) : SV_Target{float4 OUT = (float4)0;float3 normal = normalize(IN.Normal);    float3 lightDirection = normalize(IN.LightDirection);float n_dot_l = dot(lightDirection, normal);float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);float3 ambient = AmbientColor.rgb * AmbientColor.a * color.rgb;float3 diffuse = (float3)0;if (n_dot_l > 0){diffuse = LightColor.rgb * LightColor.a * n_dot_l * color.rgb;}OUT.rgb = ambient + diffuse;OUT.a = color.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);    }}


Diffuse Lighting Effect Preamble
DiffuseLighting.fx代碼的第一行使用了一種C風格的#include方法,包含了一個標頭檔,該標頭檔中提供了一些通用的函數,用于越來越豐富的effects中。列表6.3列出了標頭檔Common.fxh的內容,標頭檔中帶有_COMMON_FXH宏定義的標頭檔衛士,以及從之前的代碼中轉移過來的FLIP_TEXTURE_Y宏和get_corrected_texture_coordinate()函數。
列表6.3 Common.fxh
#ifndef _COMMON_FXH#define _COMMON_FXH/************* Constants *************/#define FLIP_TEXTURE_Y 1/************* Utility Functions *************/float2 get_corrected_texture_coordinate(float2 textureCoordinate){    #if FLIP_TEXTURE_Y        return float2(textureCoordinate.x, 1.0 - textureCoordinate.y);     #else        return textureCoordinate;     #endif}float3 get_vector_color_contribution(float4 light, float3 color){    // Color (.rgb) * Intensity (.a)    return light.rgb * light.a * color;}float3 get_scalar_color_contribution(float4 light, float color){    // Color (.rgb) * Intensity (.a)    return light.rgb * light.a * color;}#endif /* _COMMON_FXH */


另外需要注意的是兩個新的CBufferPerFrame成員:LightColor和LightDirection。LightColor常量與AmbientColor具有同樣的功能:用於表示directional light的顏色和強度。LightDirection儲存了光源在world space中的方向。這兩個新的shader constants都有對應的Object annotations,表示在NVIDIA FX Composer中可以把該變數與情境中的一個oject綁定。具體地說,就是可以在NVIDIA FX Composer的Render panel中放置光源,並把這些光源與帶有Object annotation的shader constants關聯起來。
最後需要注意的是,在CBufferPerObject中增加的World變數。該變數值與VS_INPUT結構體中新加的Normal成員變數有關。與object的vertices一樣,面的法線儲存在object space中。計算pixel的diffuse color是通過把法線向量與光的方向向量進行dot-product,但由於光源的方向是在world space中,因此法向量也要變換到wrold space中,而World矩陣就是用於這種變換。之所以不能使用組合矩陣World-View-Projection進行變換,是因為該矩陣是變換到homogeneous space而不僅僅是world space。World矩陣有可能包含了scaling變換,而面的法向量必須是一個正常化向量;因此,在變換後必須再進行normalizing(正常化)。
Diffuse Lighting Vertex Shader
接下來,講解VS_OUTPUT結構體的兩個新成員變數:Normal和LightDirection。Normal用於從CPU中傳遞變換後的面法線向量,而LightDirection有點特別,因為有一個shader constant叫LightDirection。LightDirection shader constant是一個global變數,儲存了光源的方向,而VS_OUTPUT中的LightDirection成員表示object表面的光線方向。因此,在vertex shader中對global LightDirection取反,並賦值給對應的輸出成員LightDirection。當然,可以在CPU中計算正確的光線方向,再傳遞到vertex shader中,這樣在vertex shader中就不用進行取反操作。但是在使用NVIDIA FX Composer的情況下,必須這樣做,因為光照的資料都由FX Composer發送到shader中,要想在Render panel中得到正確的預覽效果必須要在shader對光線方向向量進行取反。對LightDirection取反時,還進行了normalize()操作,這是為了保證light direction向量是正常化的,如果可以保證從CPU傳遞過來的資料已經是正常化的,就可以省略normalize()了。

Diffuse Lighting Pixel Shader
儘管與ambient lighting effect有一些相似的地方,但是diffuse lighting pixel shader(見列表6.4)包含了更多新的代碼。
列表6.4 The Pixel Shader from DiffuseLighting.fx
float4 pixel_shader(VS_OUTPUT IN) : SV_Target{float4 OUT = (float4)0;float3 normal = normalize(IN.Normal);    float3 lightDirection = normalize(IN.LightDirection);float n_dot_l = dot(lightDirection, normal);float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);float3 ambient = AmbientColor.rgb * AmbientColor.a * color.rgb;float3 diffuse = (float3)0;if (n_dot_l > 0){diffuse = LightColor.rgb * LightColor.a * n_dot_l * color.rgb;}OUT.rgb = ambient + diffuse;OUT.a = color.a;return OUT;}


首先,對於textrue sampling和calculation of the ambient引入了兩個局部變數color和ambient。雖然有一點點改進,但與之前的步驟基本一樣;其中的ambient變數被分離出來用於進一步計算最終的pixel color。
接下來介紹輸入參數的成員變數Normal和LightDirection的正常化。傳遞到rasterizer階段的資料是經過插值計算的,而該運算過程會導致向量變成非正常化的。與相應的視覺效果一樣,插值運算導致的差錯是微小的。因此,如果對效能有比較嚴格的要求,只需要簡單地省略這些正常化運算即可。
下一步,把light direction和surface normal向量的dot product結果賦值給局部變數n_dot_l,用於下面計算diffuse color。其中if-statement(if條件陳述式)確保變數n_dot_l的值大於0。如果dot product為負值,表示光源在表面的背面,因此就不需要計算。正確的dot product值應該介於0.0和1.0之間。值為0.0表示光源的方向與表面平行(也就沒有光照效果),值為1.0表示光源方向與表面垂直(能得到最在強度的光照)。而局部變數diffuse的值則由sampled color和directional light color,intensity以及前面計算的dot product相乘得到。

Diffuse Lighting Output
最終的pixel color由局部變數ambient和diffuse相加得到。Alpha通道值直接使用color texture的alpha值。圖6.5是把diffuse lighting effect應用到sphere的結果,同時使用了與圖6.1一樣的Earth texture。可以看到,當sphere的surfaces與光源的距離越遠,顯示的圖片變得越暗。
圖6.5 DiffuseLighting.fx applied to a sphere with the texture of Earth. (Original texture from Reto
Stöckli, NASA Earth Observatory. Additional texturing by Nick Zuccarello, Florida Interactive Entertainment
Academy.)


在圖片的下邊有一個diretional light。NVIDIA FX Composer支援在Render panel中建立ambient,point,spot和directional lights。要添加不同的光源,只需要點擊工具列上對應的按鈕或者在主菜單上點擊Create再選擇對應的選項。要在lighting effect使用directional light,必須把directional light與LightColor和LightDirection shader constant進行綁定。這就是之前說的Object annotations的作用。在Render panel中選中sphere,再在Properties panel中點擊MaterialInstance按鈕(如圖6.6,Properties panel工具列中的第5個按鈕),就可以綁定light。然後選擇directional light作為directionallight0和lightcolor0的綁定對象。把light綁定與shader constants綁定之後,light的任何修改都會立即在Render panel中顯示。例如,旋轉light並觀察sphere的光照地區相對light的方向如何變化。但是,如果只是平移改變light的位置,你會發現沒有影響,因為directional lights沒有真正意義上的座標位置。在Render panel中用於表示directional lights的模型所處的位置,對傳遞到shader effect的資料沒有影響。
Figure 6.6 Material Instance Properties within NVIDIA FX Composer.
在Render panel中選中light,並在Properties panel中開啟Color property的顏色選取器(color picker),就可以修改directional light的color和intensity(強度,使用alpha通道表示)。在圖6.5所示的輸出圖片中,directional light的顏色為純白色,強度為1.0,ambient light的強度為0(等同于禁用了ambient light)。

警告
NVIDIA FX Composer支援自動和手動兩種綁定方式。當自動綁定可用時,NVIDIA FX Composer會盡量把lights與最合適的shader constants綁定。但是,這種方式並不能保證問題成功的。查看material instance properties,並檢查哪些已經綁定了,如果有必要,就手動修改設定。但是要注意的是,一旦recomplile effect,所有手動綁定都會刪除。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.