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

2021-09-07 14:16:07 字數 3133 閱讀 8667

原文:

csdn完整版專欄:

聚光燈光源是眼下這裡要介紹的第三種也是最後一種光源型別了,它比平行光和點光源要複雜,但聚光燈光源事實上是具有平行光和點光源核心特徵的一種特殊光源。聚光燈光源也會隨著距離衰減。但它不是像點光源照向四面八方的而是像平行光那樣有乙個聚光方向(相當於取點光源的乙個錐形的一小部分),聚光燈光源呈錐形,因此有乙個新的屬性。就是離光源越遠。照亮的圓形區域會越大(光源位於錐形體的尖端)。

聚光燈光源,顧名思義,相應於現實中的聚光燈,比如:手電筒。

在遊戲中。聚光燈主要用於某些場景。比如:主角拿著手電筒在黑暗的地道裡探索或者逃離監獄。

我們已經知道了建立聚光燈光源的全部技術,這裡最後還要另外學一下怎樣實現這個光源型別的錐形效果。例如以下圖:

圖中垂直指向地面的黑色尖頭指的是光源方向,這裡想實現讓光源僅僅照亮兩條紅線夾角之間的區域。這裡仍然能夠使用點積來實現。我們能夠定義光錐為光線方向l和紅線之間的那個夾角(兩條紅線之間夾角的一半)。計算那個夾角的余弦值『c』(點積計算得到)以及l和v夾角的余弦。當中v指的是光源到某個畫素的向量,假設後者的值大於余弦值『c』(夾角越小余弦越大),說明l和v之間的夾角偏小,該畫素就位於被照亮的區域內。反之,畫素位於區域外就不會被該光源照亮。

假設我們緊緊依照上面說的在照亮區域內就點亮畫素,否則就不點亮。那樣就會看上去非常假,由於照亮區域和未照亮區域之間的邊界邊緣會非常明顯(沒有乙個自然的過渡),看上去會是乙個清晰的圓形畫在乙個黑色區域(假設沒有其它光源的話)。乙個真實的聚光燈光源會從照亮區域的中心向圓形邊緣慢慢衰減。這裡我們能夠利用上面計算得到的那些點積作為乙個衰減的引數。

首先我們知道,當l和v兩個向量相等重合時,點積為『1』。可是用余弦來做衰減引數會有問題,由於聚光燈光源的夾角不能太大,否則範圍太廣就失去了聚光燈的效果,可是在夾角從0到乙個比較小的角度範圍內。cos值得變化是非常緩慢的,導致衰減不明顯。比如:讓聚光燈的夾角為20度,余弦值就為0.939,[0.939,1.0]這個變化範圍就不好作為衰減引數了。在這個範圍內進行插值的空間不足,造成的衰減程度不足以讓眼睛察覺到。要想衰減效果明顯這個引數範圍應該是[0,1]。解決方法是將這個引數的小範圍對映到[0,1]的範圍。方法例如以下:

原理非常easy:計算大的範圍和小的範圍的比例,然後依據那個比例對小範圍進行對映擴張就可以。

(lighting_technique.h:68)

struct spotlight : public pointlight

};

聚光燈光源的結構體繼承自點光源的結構體。並加入了兩個屬性和點光源差別開:乙個是光源的方向向量,還有乙個是截斷光源照亮範圍的乙個閾值。

閾值代表的是光源方向向量和光源到可照亮畫素之間的最大夾角。

比這個閾值夾角大的畫素是不會被該光源照亮的。這裡還在lightingtechnique類中為shader加入了乙個位置陣列,用來獲取shader中的聚光燈光源陣列。

(lighting.fs:39)

struct spotlight

;...

uniform int gnumspotlights;

...uniform spotlight gspotlights[max_spot_lights];

在glsl中有乙個聚光燈光源型別的相似的結構體。由於這裡我們不能夠在c++**中進行繼承,所以這裡將乙個點光源結構體物件作為乙個成員物件變數,並在後面加入新的屬性。

有乙個不一樣的地方是在c++**中那個閾值是夾角本身。而在shader中這個閾值是那個夾角的余弦值。

shader著色器僅僅關心夾角的余弦值,因此計算一次並儲存比為每個畫素都又一次計算余弦值要高效得多。

這裡還定義了乙個聚關燈光源的陣列,並使用乙個叫做』gnumspotlights』的計數器來限制同意應用去建立使用的聚光燈光源的數量。

(lighting.fs:85)

vec4 calcpointlight(struct pointlight l, vec3 normal)

點光源的函式有了輕微的修改:將乙個點光源的結構體作為乙個引數,而不是直接獲取全域性陣列。這樣更easy將它分享給聚光燈光源物件使用。其它的這裡沒有做修改。

(lighting.cpp:fs)

vec4 calcspotlight(struct spotlight l, vec3 normal)

else

}

這裡這個函式是我們計算聚光燈光源效果的地方。首先得到光源到某個畫素的向量,將向量單位化方便點積運算,然後和單位化了的光源方向向量進行點積運算得到他們之間夾角的余弦值。將得到的余弦值和光源的閾值(定義光源範圍的最大夾角的余弦值)進行比較。假設余弦值比閾值小,說明夾角太大畫素在照亮圓區域的外面,這樣畫素就不會被該光源點亮。這樣那個閾值就能夠將聚光燈光源的照亮範圍限制在乙個大的或者小的圓圈內。反之假設畫素在照亮區域內,我們就先像點光源那樣計算光源的基礎顏色。然後將那個點積計算得到的引數』spotfactor』放到上面的公式中,將這個引數線性插值到0到1的範圍。最後和點光源顏色相乘計算得到終於的聚光燈顏色值。

(lighting.fs:122)

...

for (int i = 0 ; i < gnumspotlights ; i++)

...

和點光源的計算模式一樣我們在主函式通過迴圈遍歷累加全部聚光燈光源的效果得到相應畫素的終於顏色值。

(lighting_technique.cpp:367)

void lightingtechnique::setspotlights(unsigned int numlights, const spotlight* plights)

}

這個函式依據聚光燈光源的結構體陣列來繼續更新著色器程式,基本上和點光源中相應的這個函式一樣,除了額外又加入了兩個引數,光的方向單位化後也傳給了shader,另外那個閾值夾角裝換成它的余弦值之後也傳給了shader(方便shader直接用它和點積運算的結果進行比較)。注意庫函式cosf()使用的是弧度值引數,是先用toradian巨集將角度轉換成的弧度值。

一步步學ROS

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

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

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

一步步學OpenGL 20 《點光源》

原文 csdn完整版專欄 之前已經學習了三個主要的光照模型 環境光,漫射光和鏡面反射光 這三種模型都是基於平行光的。平行光僅僅是通過乙個向量來表示,沒有光源起點,因此它不會隨著距離的增大而衰減 實際上沒有起點根本無法定義光源和某個物體的距離 如今我們再來看點光源型別,它有光源起點並且有衰減效果。距離...