在理解opengl中的光照以前,先理解一下顏色。在咱們現實生活中,咱們看到的物體顏色,是物體反射的顏色,即沒有被物體吸取掉的部分。opengl利用這個,在系統中模擬的顏色,就是定義一個表示對象的顏色向量A,再頂一個一個表示光源的顏色向量B,而後在片斷着色器中將兩個向量相乘獲得表示物體的顏色。所以大體能夠理解爲,opengl中最終要渲染出的顏色,實際上是表示對象的顏色乘以某個影響係數,得出來的。函數
由於現實生活中的光照很是的複雜,早期計算設備條件不足的狀況下不能作很複雜的計算,得出的光照效果比較粗糙。近年來隨着計算設備能力的提高,開始出現光照追蹤技術,儘量的模擬出現實生活中的光照效果。學習
opengl中採用的光照模型,叫作馮氏光照模型(Phong lighting model),由三個份量組成:環境光(ambient),漫散射光(diffuse),鏡面反射光(specular),三個份量在opengl中分別使用一個vec3向量表示,vec3三個份量x,y,z分別表示r,g,b。單獨一個份量對最終渲染的影響,就是用表示份量顏色的向量去乘以對象顏色的向量,獲得的結果就是opengl系統模擬的對象在受光照影響下的效果。而馮氏光照模型,就是將三個份量的效果組合起來,獲得最終的影響效果。三種模式效果以下:網站
環境光(ambient)spa
在現實生活中光照的來源有不少,如別的物體反射過來的,所以可能在咱們並無直接看到光源的狀況下,咱們也能看到眼前的某些物體(即這些物體有反射光線過來)。馮氏光照模型中的環境光份量就是用來模擬這種效果,咱們在這裏簡單的模擬,即在光源顏色的基礎上乘以一個常量係數,獲得的就是環境光顏色,再將這個顏色乘以對象顏色,獲得模擬出來的效果,在這裏假定這個常量爲0.1,調整片元着色器代碼:3d
void main() { float ambientStrength = 0.1; vec3 ambient = ambientStrength * lightColor; vec3 result = ambient * objectColor; FragColor = vec4(result, 1.0); }
獲得的效果以下:code
經過調整常量的值,咱們能夠模擬出在不一樣的光照強度下,對象的不一樣表現。orm
漫散射光(diffuse)對象
在現實生活中,以燈泡爲光源,咱們拿一個物體放到離燈泡不一樣的位置,物體表現出來的顏色效果會有不一樣,若是離的近、與燈泡垂直了,那麼物體表面就會更亮,若是離的遠那麼就會相對暗點。馮氏光照模型中的漫散射光份量模擬的就是這種效果,如下圖爲例:blog
咱們提到的與燈泡垂直,離燈泡遠,其實能夠理解爲上圖中θ角度的變化,θ角度越小,即接近於垂直,那麼此時物體表現的就會越亮,反之則暗,參考上文中提到的環境光係數的影響,在這裏咱們也能夠模擬一個係數,來達到效果。從數學角度出發,兩個單位向量的點乘,結果等於兩個單位向量的模乘以兩個向量夾角的餘弦值,0-90度範圍內,夾角越小,值越大。所以在這裏咱們能夠根據這個特性,來模擬漫散射效果。數學
根據光源位置,頂點座標,咱們能夠獲得表示光源方向的環境向量,而後咱們再引入法線向量這個概念(垂直於表面的向量),用兩個向量相乘獲得的值,乘以光源的顏色就能夠獲得模擬漫反射的光源顏色,再將這個顏色乘以對象的顏色,就能夠模擬出漫反射影響下的對象表現效果。
兩個向量相減,能夠獲得一個表示方向的向量,在這裏咱們是用頂點的位置減去光源的位置,獲得一個從頂點位置指向光源的單位向量,而法線向量咱們能夠根據對象的形狀直接獲得,調整片元着色器代碼:
void main(){ vec3 norm = normalize(oNormal); vec3 lightDir = normalize(u_lightPos - oFragPos); float diff = max(dot(norm, lightDir), 0.0f); vec3 diffuse = diff * u_lightColor; float ambientStrength = 0.1f; vec3 ambient = ambientStrength * u_lightColor; vec3 result = (ambient + diffuse) * u_objectColor; FragColor = vec4(result, 1.0f); }
由於咱們提到了是用單位向量進行計算,因此保險起見對於咱們要使用到的向量,都調用normalize處理一下。最終獲得的效果:
光源的位置,是白色矩形的位置,從上圖就能夠看到與光源夾角越小的就更亮,不然就更暗。
鏡面反射光(specular)
在現實生活中,咱們也有這樣的體會,咱們從不一樣的角度去觀察物體,看到的效果也會不同。在馮氏光照模型中,使用鏡面反射光來模擬這個效果,以下圖所示:
在這裏使用到的是光源向量基於法線對稱後,與表示視線方向向量的夾角,來模擬鏡面反射的效果,用兩個向量點乘後獲得的值,再乘以光源顏色,獲得表示鏡面反射光份量的顏色向量,再去乘以對象的顏色,獲得模擬出來的效果。調整後的片元着色器代碼:
void main(){ float ambientStrength = 0.1f; vec3 ambient = ambientStrength * u_lightColor; vec3 norm = normalize(oNormal); vec3 lightDir = normalize(u_lightPos - oFragPos); float diff = max(dot(norm, lightDir), 0.0f); vec3 diffuse = diff * u_lightColor; float specularStrength = 0.5f; vec3 viewDir = normalize(u_viewPos - oFragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0f), 32); vec3 specular = specularStrength * spec * u_lightColor; vec3 result = (ambient + diffuse + specular) * u_objectColor; FragColor = vec4(result, 1.0f); }
咱們使用GLSL內置的reflect函數計算反射後的光源方向,而後計算與視線方向單位向量的夾角值,最後咱們取這個值的32次方值,取的次方值越高,在表現上就越像聚焦的效果,以下圖:
最終咱們將三個份量的值相加,再去乘以對象的顏色,就獲得了馮氏光照模型下模擬出來的效果。
材質(Material)、Lighting map,投光物(Light caster)
現實生活中,光照的效果很是複雜,好比一組物體,離光源的遠近,會影響物體的亮度;木頭和鐵塊表現出來的效果不一樣;以聚光燈爲光源照射一組物體,中心區域的物體表現的會亮一點,周圍區域的會表現的暗一點。咱們上面提到的馮氏光照模型其實也能夠模擬出這種效果,咱們能夠看到上面的片元着色器代碼中,環境光、漫散射光、鏡面反射光都有乘以一個常量,若是咱們動態的去調整這個常量,就能夠獲得不一樣的效果。
如,咱們模擬一個離光源遠近,物體表現不一樣的效果,咱們能夠乘以一個衰減係數,這個衰減係數的計算有一個公式:
\begin{equation} F_{att} = \frac{1.0}{K_c + K_l * d + K_q * d^2} \end{equation}
其中,分子部分爲常量1,保證最終獲得的係數值須要小於1,分母由三個部分組成,一個常量項,一個線性項,一個二次項部分;d則表示距離光源的遠近,用來模擬下圖的係數值:
即隨着距離的增長,係數值變小,到必定距離以後這個係數值基本維持在一個比較小的範圍。調整後的片元着色器代碼:
// attenuation float distance = length(u_light.position - oFragPos); float attenuation = 1.0 / (u_light.constant + u_light.linear * distance + u_light.quadratic * (distance * distance)); // ambient vec3 ambient = u_light.ambient * vec3(texture(u_material.diffuse, oTexCoords)); ambient *= attenuation; // diffuse vec3 norm = normalize(oNormal); vec3 lightDir = normalize(u_light.position - oFragPos); float diff = max(dot(norm, lightDir), 0.0f); vec3 diffuse = u_light.diffuse * (diff * vec3(texture(u_material.diffuse, oTexCoords))); diffuse *= attenuation; // specular vec3 viewDir = normalize(u_viewPos - oFragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0f), u_material.shininess); vec3 specular = u_light.specular * (spec * vec3(texture(u_material.specular, oTexCoords))); specular *= attenuation; vec3 result = (ambient + diffuse + specular); FragColor = vec4(result, 1.0f);
獲得的效果以下:
以上就是簡單的理解一下opengl中的光照。學習的網站上經過一個章節來說解,主要就是圍繞對馮氏光照模型的三個份量,乘以不一樣的係數值,來達到模擬現實生活中不一樣的效果。