OpenGL學習筆記《八》光照

  在理解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中的光照。學習的網站上經過一個章節來說解,主要就是圍繞對馮氏光照模型的三個份量,乘以不一樣的係數值,來達到模擬現實生活中不一樣的效果。

相關文章
相關標籤/搜索