【GLSL教程】(六)逐頂點的光照
阿新 • • 發佈:2018-11-22
引言
在OpenGL中有三種類型的光:方向光(directional)、點光(point)、聚光(spotlight)。本教程將從方向光講起,首先我們將使用GLSL來模仿OpenGL中的光。
我們將向shader中逐漸新增環境光、散射光和高光效果。
後面的教程中我們將使用逐畫素光照以獲得更好的效果。
接下來我們將實現逐畫素的點光和聚光。這些內容與方向光很相近,大部分程式碼都是通用的。
在卡通著色的教程中我們接觸過在GLSL中如何訪問OpenGL狀態中關於光源的部分,這些資料描述了每個光源的引數。
方向光I
本節的公式來自《OpenGL程式設計指南》中“和光照有關的數學知識”這一章。
我們從散射光開始討論。在OpenGL中假定,不管觀察者的角度如何,得到的散射光強度總是相同的。散射光的強度與光源中散射光成分以及材質中散射光反射係數相關,此外也和入射光角度與物體表面法線的夾角相關。
OpenGL用下面的公式計算散射光成分:
I是反射光的強度,Ld是光源的散射成分(gl_LightSource[0].diffuse),Md是材質的散射係數(gl_FrontMaterial.diffuse)。
這個公式就是Lambert漫反射模型。Lambert餘弦定律描述了平面散射光的亮度,正比於平面法線與入射光線夾角的餘弦,這一理論提出已經超過200年了。
在頂點shader中要實現這個公式,需要用到光源引數中的方向、散射成分強度,還要用到材質中的散射成分值。因此使用此shader時,在OpenGL中需要像在平時一樣設定好光源。注意:由於沒有使用固定功能流水線,所以不需要對光源呼叫glEnable。
要計算餘弦值,首先要確保光線方向向量(gl_LightSource[0].position)與法線向量都是歸一化的,然後就可以使用點積得到餘弦值。注意:對方向光,OpenGL中儲存的方向是從頂點指向光源,與上面圖中畫的相反。
OpenGL將光源的方向儲存在視點空間座標系內,因此我們需要把法線也變換到視點空間。完成這個變換可以用預先定義的一致變數gl_NormalMatrix。這個矩陣是模型檢視變換矩陣的左上3×3子矩陣的逆矩陣的轉置。
以下就是上述內容的頂點shader程式碼:
加入環境光非常容易,只需要使用一個全域性的環境光引數以及光源的環境光引數即可,公式如下所示:
前面的頂點shader中需要加入幾條語句完成環境光的計算:
方向光II
下面介紹OpenGL方向光中的鏡面反射部分。我們使用稱為Blin-Phong模型的光照模型,這是Phong模型的簡化版。在這之前,我們有必要先看看Phong模型,以便於更好地理解Blin-Phong模型。
在Phong模型中,鏡面反射成分和反射光線與視線夾角的餘弦相關,如下圖:
L表示入射光,N表示法線,Eye表示從頂點指向觀察點的視線,R是L經鏡面反射後的結果,鏡面反射成分與α角的餘弦相關。
如果視線正好和反射光重合,我們將接收到最大的反射強度。當視線與反射光相分離時,反射強度將隨之下降,下降速率可以由一個稱為shininess的因子控制,shininess的值越大,下降速率越快。也就是說,shininess越大的話,鏡面反射產生的亮點就越小。在OpenGL中這個值的範圍是0到128。
計算反射光向量的公式:
OpenGL中使用Phong模型計算鏡面反射成分的公式:
式中指數s就是shininess因子,Ls是光源中鏡面反射強度,Ms是材質中的鏡面反射係數。
Blinn提出了一種簡化的模型,也就是Blinn-Phong模型。它基於半向量(half-vector),也就是方向處在觀察向量以及光線向量之間的一個向量:
現在可以利用半向量和法線之間夾角的餘弦來計算鏡面反射成分。OpenGL所使用的Blinn-Phong模型計算鏡面反射的公式如下:
這個方法與顯示卡的固定流水線中使用的方法相同。因為我們要模擬OpenGL中的方向光,所以在shader中也使用此公式。幸運的是:OpenGL會幫我們算半向量,我們只需要使用下面的程式碼:
http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/ogldirsd.zip
在OpenGL中有三種類型的光:方向光(directional)、點光(point)、聚光(spotlight)。本教程將從方向光講起,首先我們將使用GLSL來模仿OpenGL中的光。
我們將向shader中逐漸新增環境光、散射光和高光效果。
後面的教程中我們將使用逐畫素光照以獲得更好的效果。
接下來我們將實現逐畫素的點光和聚光。這些內容與方向光很相近,大部分程式碼都是通用的。
在卡通著色的教程中我們接觸過在GLSL中如何訪問OpenGL狀態中關於光源的部分,這些資料描述了每個光源的引數。
-
struct
- {
- vec4 ambient;
- vec4 diffuse;
- vec4 specular;
- vec4 position;
- vec4 halfVector;
- vec3 spotDirection;
- float spotExponent;
- float spotCutoff; // (range: [0.0,90.0], 180.0)
- float spotCosCutoff; // (range: [1.0,0.0],-1.0)
-
float
- float linearAttenuation;
- float quadraticAttenuation;
- };
- uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];
- struct gl_LightModelParameters
- {
- vec4 ambient;
- };
- uniform gl_LightModelParameters gl_LightModel;
- struct gl_MaterialParameters
- {
- vec4 emission;
- vec4 ambient;
- vec4 diffuse;
- vec4 specular;
- float shininess;
- };
- uniform gl_MaterialParameters gl_FrontMaterial;
- uniform gl_MaterialParameters gl_BackMaterial;
方向光I
本節的公式來自《OpenGL程式設計指南》中“和光照有關的數學知識”這一章。
我們從散射光開始討論。在OpenGL中假定,不管觀察者的角度如何,得到的散射光強度總是相同的。散射光的強度與光源中散射光成分以及材質中散射光反射係數相關,此外也和入射光角度與物體表面法線的夾角相關。
OpenGL用下面的公式計算散射光成分:
I是反射光的強度,Ld是光源的散射成分(gl_LightSource[0].diffuse),Md是材質的散射係數(gl_FrontMaterial.diffuse)。
這個公式就是Lambert漫反射模型。Lambert餘弦定律描述了平面散射光的亮度,正比於平面法線與入射光線夾角的餘弦,這一理論提出已經超過200年了。
在頂點shader中要實現這個公式,需要用到光源引數中的方向、散射成分強度,還要用到材質中的散射成分值。因此使用此shader時,在OpenGL中需要像在平時一樣設定好光源。注意:由於沒有使用固定功能流水線,所以不需要對光源呼叫glEnable。
要計算餘弦值,首先要確保光線方向向量(gl_LightSource[0].position)與法線向量都是歸一化的,然後就可以使用點積得到餘弦值。注意:對方向光,OpenGL中儲存的方向是從頂點指向光源,與上面圖中畫的相反。
OpenGL將光源的方向儲存在視點空間座標系內,因此我們需要把法線也變換到視點空間。完成這個變換可以用預先定義的一致變數gl_NormalMatrix。這個矩陣是模型檢視變換矩陣的左上3×3子矩陣的逆矩陣的轉置。
以下就是上述內容的頂點shader程式碼:
- void main()
- {
- vec3 normal, lightDir;
- vec4 diffuse;
- float NdotL;
- /* 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));
- /* compute the cos of the angle between the normal and lights direction.
- The light is directional so the direction is constant for every vertex.
- Since these two are normalized the cosine is the dot product. We also
- need to clamp the result to the [0,1] range. */
- NdotL = max(dot(normal, lightDir), 0.0);
- /* Compute the diffuse term */
- diffuse = gl_FrontMaterial.diffuse * gl_LightSource[ 0].diffuse;
- gl_FrontColor = NdotL * diffuse;
- gl_Position = ftransform();
- }
- void main()
- {
- gl_FragColor = gl_Color;
- }
加入環境光非常容易,只需要使用一個全域性的環境光引數以及光源的環境光引數即可,公式如下所示:
前面的頂點shader中需要加入幾條語句完成環境光的計算:
- void main()
- {
- vec3 normal, lightDir;
- vec4 diffuse, ambient, globalAmbient;
- float NdotL;
- normal = normalize(gl_NormalMatrix * gl_Normal);
- lightDir = normalize(vec3(gl_LightSource[ 0].position));
- NdotL = max(dot(normal, lightDir), 0.0);
- diffuse = gl_FrontMaterial.diffuse * gl_LightSource[ 0].diffuse;
- /* Compute the ambient and globalAmbient terms */
- ambient = gl_FrontMaterial.ambient * gl_LightSource[ 0].ambient;
- globalAmbient = gl_FrontMaterial.ambient * gl_LightModel.ambient;
- gl_FrontColor = NdotL * diffuse + globalAmbient + ambient;
- gl_Position = ftransform();
- }
方向光II
下面介紹OpenGL方向光中的鏡面反射部分。我們使用稱為Blin-Phong模型的光照模型,這是Phong模型的簡化版。在這之前,我們有必要先看看Phong模型,以便於更好地理解Blin-Phong模型。
在Phong模型中,鏡面反射成分和反射光線與視線夾角的餘弦相關,如下圖:
L表示入射光,N表示法線,Eye表示從頂點指向觀察點的視線,R是L經鏡面反射後的結果,鏡面反射成分與α角的餘弦相關。
如果視線正好和反射光重合,我們將接收到最大的反射強度。當視線與反射光相分離時,反射強度將隨之下降,下降速率可以由一個稱為shininess的因子控制,shininess的值越大,下降速率越快。也就是說,shininess越大的話,鏡面反射產生的亮點就越小。在OpenGL中這個值的範圍是0到128。
計算反射光向量的公式:
OpenGL中使用Phong模型計算鏡面反射成分的公式:
式中指數s就是shininess因子,Ls是光源中鏡面反射強度,Ms是材質中的鏡面反射係數。
Blinn提出了一種簡化的模型,也就是Blinn-Phong模型。它基於半向量(half-vector),也就是方向處在觀察向量以及光線向量之間的一個向量:
現在可以利用半向量和法線之間夾角的餘弦來計算鏡面反射成分。OpenGL所使用的Blinn-Phong模型計算鏡面反射的公式如下:
這個方法與顯示卡的固定流水線中使用的方法相同。因為我們要模擬OpenGL中的方向光,所以在shader中也使用此公式。幸運的是:OpenGL會幫我們算半向量,我們只需要使用下面的程式碼:
- /* compute the specular term if NdotL is larger than zero */
- if (NdotL > 0.0)
- {
- // normalize the half-vector, and then compute the
- // cosine (dot product) with the normal
- NdotHV = max(dot(normal, gl_LightSource[ 0].halfVector.xyz), 0.0);
- specular = gl_FrontMaterial.specular * gl_LightSource[ 0].specular *
- pow(NdotHV,gl_FrontMaterial.shininess);
- }
http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/ogldirsd.zip