【GLSL教程】(七)逐畫素的光照
阿新 • • 發佈:2018-11-22
逐畫素的方向光(Directional Light per Pixel)
這一節將把前面的shader程式碼改為逐畫素計算的方向光。我們需要將工作按照兩個shader拆分,以確定哪些是需要逐畫素操作的。
首先看看每個頂點接收到的資訊:
•法線
•半向量
•光源方向
我們需要將法線變換到視點空間然後歸一化。我們還需要將半向量和光源方向也歸一化,不過它們已經位於視點空間中了。這些歸一化之後的向量會進行插值,然後送入片斷shader,所以需要宣告易變變數儲存這些向量。
我們也可以在頂點shader中完成一些與光和材質相關的計算,這樣可以幫助平衡頂點shader和片斷shader的負載。
頂點shader程式碼可以寫成如下形式:
本節內容Shader Designer的工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/dirpixsd.zip
逐畫素的點光(Point Light Per Pixel)
本節基於前面有關方向光的內容,大部分程式碼都相同。本節內容主要涉及方向光和點光的不同之處。方向光一般假設光源在無限遠的地方,所以到達物體時是平行光。相反,點光源有一個空間中的位置,並向四面八方輻射光線。此外,點光的強度會隨到達頂點的距離而衰弱。
對於OpenGL程式來說,這兩種光的區別主要有:
•光源的position域的w分量:對方向光來說它是0,表面這個position實際是一個方向(direction);對點光來說,這個分量是1。
•點光源的衰減由三個係數決定:一個常數項,一個線性項和一個二次項。
對方向光來說,光線的方向對所有頂點相同,但是對點光來說,方向是從頂點指向光源位置的向量。因此對我們來說需要修改的就是在頂點shader中加入計算光線方向的內容。
在OpenGL中衰減是按照如下公式計算的:
式中k0是常數衰減係數,k1是線性衰減係數,k2是二次衰減係數,d是光源位置到頂點的距離。
注意衰減與距離是非線性關係,所以我們不能逐頂點計算衰減再在片斷shader中使用插值結果,不過我們可以在頂點shader中計算距離,然後在片斷shader中使用距離的插值計算衰減。
使用點光計算顏色值的公式為:
在上面公式中,環境光部分必須分解為兩項:使用光照模型的全域性環境光設定和光源中的環境光設定。頂點shader也必須分別計算這兩個環境光成分。新的頂點shader如下:
本節內容Shader Designer工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/pointlightsd.zip
逐畫素的聚光(Spot Light Per Pixel)
本節內容與上一節基本一致,唯一不同的就是聚光不同於點光,其發出的光線被限制在一個圓錐體中。
對於OpenGL程式來說,這兩種光的區別主要有:
•聚光包含一個方向向量spotDirection,表示圓錐體的軸。
•圓錐體包含一個角度,在GLSL中可以使用應用程式設定的角度值以及對應的餘弦值spotCosCutoff。
•最後還有一個衰減速率spotExponent,它表示從圓錐的中心軸向外表面變化時光強度的衰減。
聚光的頂點shader與點光完全相同,我們只需要對片斷shader進行一些修改。只有噹噹前片斷位於聚光的光錐內時,才需要對散射光、鏡面反射光和環境光成分進行著色。所以我們首先要檢查這個條件。
光源與某點連線向量以及聚光方向向量(spotDirection)之間夾角的餘弦值必須大於spotCosCutoff,否則此點位於聚光之外,只能接收到全域性環境光。
上式中spotDirection來自OpenGL中設定的狀態,lightDir是光源到某點的向量,spotExp是聚光衰減率,這個值也是在OpenGL程式中設定的,它用來控制從聚光光錐中心到邊緣的衰減。spotExp越大衰減越快,如果為0表示在光錐內光強是常數。
本節內容Shader Designer的工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/spotlightsd.zip
這一節將把前面的shader程式碼改為逐畫素計算的方向光。我們需要將工作按照兩個shader拆分,以確定哪些是需要逐畫素操作的。
首先看看每個頂點接收到的資訊:
•法線
•半向量
•光源方向
我們需要將法線變換到視點空間然後歸一化。我們還需要將半向量和光源方向也歸一化,不過它們已經位於視點空間中了。這些歸一化之後的向量會進行插值,然後送入片斷shader,所以需要宣告易變變數儲存這些向量。
我們也可以在頂點shader中完成一些與光和材質相關的計算,這樣可以幫助平衡頂點shader和片斷shader的負載。
頂點shader程式碼可以寫成如下形式:
- varying vec4 diffuse,ambient;
- varying vec3 normal,lightDir,halfVector;
- void main()
- {
-
/* first transform the normal into eye space and
- normalize the result */
- normal = normalize(gl_NormalMatrix * gl_Normal);
- /* now normalize the light's direction. Note that
-
according to the OpenGL specification, the light
- is stored in eye space. Also since we're talking about
- a directional light, the position field is actually direction */
- lightDir = normalize(vec3(gl_LightSource[ 0].position));
- /* Normalize the halfVector to pass it to the fragment shader */
- halfVector = normalize(gl_LightSource[ 0].halfVector.xyz);
- /* Compute the diffuse, ambient and globalAmbient terms */
- diffuse = gl_FrontMaterial.diffuse * gl_LightSource[ 0].diffuse;
- ambient = gl_FrontMaterial.ambient * gl_LightSource[ 0].ambient;
- ambient += gl_FrontMaterial.ambient * gl_LightModel.ambient;
- gl_Position = ftransform();
- }
- varying vec4 diffuse,ambient;
- varying vec3 normal,lightDir,halfVector;
- void main()
- {
- vec3 n,halfV;
- float NdotL,NdotHV;
- /* The ambient term will always be present */
- vec4 color = ambient;
- /* a fragment shader can't write a varying variable, hence we need
- a new variable to store the normalized interpolated normal */
- n = normalize(normal);
- /* compute the dot product between normal and ldir */
- NdotL = max(dot(n,lightDir), 0.0);
- ...
- ...
- if (NdotL > 0.0)
- {
- color += diffuse * NdotL;
- halfV = normalize(halfVector);
- NdotHV = max(dot(n,halfV), 0.0);
- color += gl_FrontMaterial.specular *
- gl_LightSource[ 0].specular *
- pow(NdotHV, gl_FrontMaterial.shininess);
- }
- gl_FragColor = color;
- }
本節內容Shader Designer的工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/dirpixsd.zip
逐畫素的點光(Point Light Per Pixel)
本節基於前面有關方向光的內容,大部分程式碼都相同。本節內容主要涉及方向光和點光的不同之處。方向光一般假設光源在無限遠的地方,所以到達物體時是平行光。相反,點光源有一個空間中的位置,並向四面八方輻射光線。此外,點光的強度會隨到達頂點的距離而衰弱。
對於OpenGL程式來說,這兩種光的區別主要有:
•光源的position域的w分量:對方向光來說它是0,表面這個position實際是一個方向(direction);對點光來說,這個分量是1。
•點光源的衰減由三個係數決定:一個常數項,一個線性項和一個二次項。
對方向光來說,光線的方向對所有頂點相同,但是對點光來說,方向是從頂點指向光源位置的向量。因此對我們來說需要修改的就是在頂點shader中加入計算光線方向的內容。
在OpenGL中衰減是按照如下公式計算的:
式中k0是常數衰減係數,k1是線性衰減係數,k2是二次衰減係數,d是光源位置到頂點的距離。
注意衰減與距離是非線性關係,所以我們不能逐頂點計算衰減再在片斷shader中使用插值結果,不過我們可以在頂點shader中計算距離,然後在片斷shader中使用距離的插值計算衰減。
使用點光計算顏色值的公式為:
在上面公式中,環境光部分必須分解為兩項:使用光照模型的全域性環境光設定和光源中的環境光設定。頂點shader也必須分別計算這兩個環境光成分。新的頂點shader如下:
- varying vec4 diffuse,ambientGlobal,ambient;
- varying vec3 normal,lightDir,halfVector;
- varying float dist;
- void main()
- {
- vec4 ecPos;
- vec3 aux;
- normal = normalize(gl_NormalMatrix * gl_Normal);
- /* these are the new lines of code to compute the light's direction */
- ecPos = gl_ModelViewMatrix * gl_Vertex;
- aux = vec3(gl_LightSource[ 0].position-ecPos);
- lightDir = normalize(aux);
- dist = length(aux);
- halfVector = normalize(gl_LightSource[ 0].halfVector.xyz);
- /* Compute the diffuse, ambient and globalAmbient terms */
- diffuse = gl_FrontMaterial.diffuse * gl_LightSource[ 0].diffuse;
- /* The ambient terms have been separated since one of them */
- /* suffers attenuation */
- ambient = gl_FrontMaterial.ambient * gl_LightSource[ 0].ambient;
- ambientGlobal = gl_FrontMaterial.ambient * gl_LightModel.ambient;
- gl_Position = ftransform();
- }
- varying vec4 diffuse,ambientGlobal, ambient;
- varying vec3 normal,lightDir,halfVector;
- varying float dist;
- void main()
- {
- vec3 n,halfV,viewV,ldir;
- float NdotL,NdotHV;
- vec4 color = ambientGlobal;
- float att;
- /* a fragment shader can't write a varying variable, hence we need
- a new variable to store the normalized interpolated normal */
- n = normalize(normal);
- /* compute the dot product between normal and normalized lightdir */
- NdotL = max(dot(n,normalize(lightDir)), 0.0);
- if (NdotL > 0.0)
- {
- att = 1.0 / (gl_LightSource[ 0].constantAttenuation +
- gl_LightSource[ 0].linearAttenuation * dist +
- gl_LightSource[ 0].quadraticAttenuation * dist * dist);
- color += att * (diffuse * NdotL + ambient);
- halfV = normalize(halfVector);
- NdotHV = max(dot(n,halfV), 0.0);
- color += att * gl_FrontMaterial.specular * gl_LightSource[ 0].specular *
- pow(NdotHV,gl_FrontMaterial.shininess);
- }
- gl_FragColor = color;
- }
本節內容Shader Designer工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/pointlightsd.zip
逐畫素的聚光(Spot Light Per Pixel)
本節內容與上一節基本一致,唯一不同的就是聚光不同於點光,其發出的光線被限制在一個圓錐體中。
對於OpenGL程式來說,這兩種光的區別主要有:
•聚光包含一個方向向量spotDirection,表示圓錐體的軸。
•圓錐體包含一個角度,在GLSL中可以使用應用程式設定的角度值以及對應的餘弦值spotCosCutoff。
•最後還有一個衰減速率spotExponent,它表示從圓錐的中心軸向外表面變化時光強度的衰減。
聚光的頂點shader與點光完全相同,我們只需要對片斷shader進行一些修改。只有噹噹前片斷位於聚光的光錐內時,才需要對散射光、鏡面反射光和環境光成分進行著色。所以我們首先要檢查這個條件。
光源與某點連線向量以及聚光方向向量(spotDirection)之間夾角的餘弦值必須大於spotCosCutoff,否則此點位於聚光之外,只能接收到全域性環境光。
- ...
- n = normalize(normal);
- /* compute the dot product between normal and ldir */
- NdotL = max(dot(n,normalize(lightDir)), 0.0);
- if (NdotL > 0.0)
- {
- spotEffect = dot(normalize(gl_LightSource[ 0].spotDirection),
- normalize(-lightDir));
- if (spotEffect > gl_LightSource[ 0].spotCosCutoff)
- {
- /* compute the illumination in here */
- }
- }
- gl_FragColor = ...
上式中spotDirection來自OpenGL中設定的狀態,lightDir是光源到某點的向量,spotExp是聚光衰減率,這個值也是在OpenGL程式中設定的,它用來控制從聚光光錐中心到邊緣的衰減。spotExp越大衰減越快,如果為0表示在光錐內光強是常數。
- spotEffect = pow(spotEffect, gl_LightSource[ 0].spotExponent);
- att = spotEffect / (gl_LightSource[ 0].constantAttenuation +
- gl_LightSource[ 0].linearAttenuation * dist +
- gl_LightSource[ 0].quadraticAttenuation * dist * dist);
- color += att * (diffuse * NdotL + ambient);
- halfV = normalize(halfVector);
- NdotHV = max(dot(n,halfV), 0.0);
- color += att * gl_FrontMaterial.specular *
- gl_LightSource[ 0].specular *
- pow(NdotHV,gl_FrontMaterial.shininess);
本節內容Shader Designer的工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/spotlightsd.zip