一步步學OpenGL 20 《點光源》

2022-07-23 20:54:17 字數 3964 閱讀 2018

原文:

csdn完整版專欄:

之前已經學習了三個主要的光照模型(環境光,漫射光和鏡面反射光),這三種模型都是基於平行光的。平行光僅僅是通過乙個向量來表示,沒有光源起點,因此它不會隨著距離的增大而衰減(實際上沒有起點根本無法定義光源和某個物體的距離)。

如今我們再來看點光源型別,它有光源起點並且有衰減效果。距離光源越遠光線越弱。點光源的經典樣例是燈泡。燈泡在屋子裡可能效果不明顯,可是拿到室外就會明顯看出它的衰減效果了。注意之前平行光的方向是恆定的。但點光源光線的方向是變化的,四處擴散。

點光源想各個方向均勻照耀,因此點光源的方向要通過計算物體到點光源之間的向量得到,這就是為什麼要定義點光源的起點而不是它的方向。

點光源光線慢慢變淡的的想象叫做『衰減』。真實光線的衰減是依照平方反比定律的。也就是說光線的強度和離光源的距離的平方成反比。數學原理例如以下圖中的公式:

但3d圖形中這個公式計算的結果看上去效果並不好。比如:當距離非常近時,光的強度接近無窮大了。

另外,開發人員除了通過設定光的起始強度外無法控制點光源的亮度,這樣就太受限制了。因此我們加入了幾個新的因素到公式中使對其的控制更加靈活:

我們在分母上加入了三個光衰減的引數因子。乙個常量引數,乙個線性引數和乙個指數引數。當將常量引數和線性引數設定為零且指數引數設定為1時,就和實際的物理公式是相應的了。也就是這個特殊情況下在物理上是準確的。

當設定常量因子引數為1時。調節另外兩個引數總體上就有比較好的衰減變化效果了。

常量引數的設定是要保證當距離為0時光照強度達到最大(這個要在程式內進行配置),然後隨著距離的增大光照強度要慢慢減弱,因分母在慢慢變大。控制好線性引數因子和指數引數因子的變化,就能夠實現想要的衰減效果。線性引數主要用於實現緩慢的衰減效果而指數因子能夠控制光強度的迅速衰減。

如今總結計算點光源須要的步驟:

(lighting_technique.h:24)

struct baselight;.

..struct pointlight : public baselight

attenuation;

}

平行光儘管和點光源不一樣。但它們仍然有非常多共同之處。它們共同的部分都放到了baselight結構體中,而點光源和平行光的結構體則繼承自baselight。平行光額外加入了方向屬性到它的類中,而點光源則加入了世界座標系中的位置變數和那三個衰減引數因子。

(lighting_technique.h:81)

void setpointlights(unsigned int numlights, const pointlight* plights);

這個教程除了展示如何實現點光源。還展示如何使用多光源。通常僅僅存在乙個平行光光源,也就是太陽光,另外可能還會有一些點光源(屋子裡的燈泡。地牢裡的火把等等)。

這個函式引數有乙個點光源資料結構的陣列和陣列的長度。使用結構體的值來更新shader。

(lighting_technique.h:103)

struct  atten;

} m_pointlightslocation[max_point_lights];

為了支援多個點光源,shader須要包括乙個和點光源結構體(僅僅在glsl中)內容一樣的結構體陣列。主要有兩種方法來更新shader中的結構體陣列:

第一種方法由於要維護大量的位置一致變數因此非常浪費資源。可是會更加靈活。由於你能夠通過位置一致變數訪問更新陣列中的不論什麼乙個元素,不須要像另外一種方法那樣先要轉換輸入的資料。

另外一種方法不須要管理那麼多的位置一致變數。可是假設想要同一時候更新陣列中的幾個元素的話,同一時候使用者傳入的又是乙個結果體陣列(像setpointlights()),你就要先將這個結構體陣列轉換成多個欄位的陣列結構,由於結構體中每乙個位置的字段資料都要使用乙個同型別的陣列來更新。當使用結構體陣列時,在陣列中兩個連續元素(結構體)中的同乙個字段之間存在記憶體間隔(被其它字段間隔開了,我們是想要同乙個欄位的連續字段陣列)。須要將它們收集到它們自己的同型別陣列中。本教程中,我們將使用第一種方法。最好兩個都實現一下,看你認為哪乙個方法更好用。

max_point_lights是乙個常量,用於限制能夠使用的點光源的最大數量,並且必須和著色器中的相應值同步一致。預設值為2。當你新增應用中光的數量,隨著光源的新增會發現效能越來越差。這個問題能夠使用一種稱為「延遲著色」的技術來優化解決,這個後面再**。

(lighting.fs:46)

vec4 calclightinternal(baselight light, vec3 lightdirection, vec3 normal)

}return (ambientcolor + diffusecolor + specularcolor);

}

這裡在平行光和點光源之間實現非常多著色器**的共享就不算什麼新技術了。大多數演算法是同樣的。不同的是,我們僅僅須要考慮點光源的衰減因素。 此外,針對平行光,光的方向是由應用提供的。而對點光源,須要計算每乙個畫素的光的方向。

上面的函式封裝了兩種光型別之間的共用部分。 baselight結構體包括光強度和顏色。

lightdirection是額外單獨提供的,原因上面剛剛已經提到。 另外還提供了頂點法線,由於我們在進入片段著色器時要對其進行一次單位化處理。然後在每次呼叫此函式時使用它。

(lighting.fs:70)

vec4 calcdirectionallight(vec3 normal)

有了公共的封裝函式,定義函式簡單的包裝呼叫一下就能夠計算出平行光了,引數多數來自全域性變數。

(lighting.fs:75)

vec4 calcpointlight(int index, vec3 normal)

計算點光比定向光要複雜一點。每乙個點光源的配置都要呼叫這個函式。因此它將光的索引作為引數,在全域性點光源陣列中找到相應的點光源。

它依據光源位置(由應用程式在世界空間中提供)和由頂點著色器傳遞過來的頂點世界空間位置來計算光源方向向量。使用內建函式length()計算從點光源到每乙個畫素的距離。 一旦我們有了這個距離。就能夠對光的方向向量進行單位化處理。

注意,calclightinternal()是須要乙個單位化的光方向向量的。平行光的單位化由lightingtechnique類來負責。 我們使用calcinternallight()函式獲得顏色值。並使用我們之前得到的距離來計算光的衰減。終於點光源的顏色是通過將顏色和衰減值相除計算得到的。

(lighting.fs:89)

void main()

fragcolor = texture2d(gsampler, texcoord0.xy) * totallight;

}

有了前面的基礎。片段著色器方面就變得非常easy了。簡單地將頂點法線單位化。然後將全部型別光的效果疊加在一起。結果再乘以取樣的顏色,就得到終於的畫素顏色了。

(lighting_technique.cpp:279)

void lightingtechnique::setpointlights(unsigned int numlights, const pointlight* plights)

}

此函式通過迭代遍歷陣列元素並依次傳遞每乙個元素的屬性值。然後使用點光源的值更新著色器。 這是前面所說的「方法1」。

本教程的demo顯示兩個點光源在乙個場景區域中互相追逐。

乙個光源基於余弦函式,而還有乙個光源基於正弦函式。該場景區域是由兩個三角形組成的非常easy的四邊形平面,法線是乙個垂直的向量。

一步步學OpenGL 21 《聚光燈光源》

原文 csdn完整版專欄 聚光燈光源是眼下這裡要介紹的第三種也是最後一種光源型別了,它比平行光和點光源要複雜,但聚光燈光源事實上是具有平行光和點光源核心特徵的一種特殊光源。聚光燈光源也會隨著距離衰減。但它不是像點光源照向四面八方的而是像平行光那樣有乙個聚光方向 相當於取點光源的乙個錐形的一小部分 聚...

一步步學ROS

最近因為看svo的 裡面用到catkin決定要好好看ros,年前學會基本操作。啟動節點 rosrun package name executable name 檢視節點 rosnode list 注 rosout 節點是乙個特殊的節點,通過 roscore 自動啟動 檢視特定節點的資訊 rosnod...

一步步學OpenGL 12 《透視投影》

原文 csdn完整版專欄 透視投影原理其他文章 總算到了如何實現最優化顯示3d圖形的階段了 在保留物體深度立體感的前提下將3d世界的物體投影到2d平面上。乙個很典型的例子就是3d世界中往遠方延伸的公路,2d螢幕上看上去會越來越窄最後在很遠的地平線上交匯成了乙個點。我們現在要建立一種滿足上面要求的一種...