QT+OPenGL十三之真正的點光源和聚光燈

上一章研究了定向光,很顯然光有太陽光無法滿足現(xiàn)實的真實需求,現(xiàn)實中還會有其他類型的光源,比如點光源。我們以前使用的白熾燈炮就是點光源。
在learnOpenGL中:


image.png

點光源:

這時一種放射狀的光線。我們研究光照模型時用的就是點光源,他需要位置,當(dāng)我們沒有做光的衰減。距離點光源越遠的地方光的因子應(yīng)該越弱。需要做衰減。

點光源的特點:

有位置,可與每個頂點的位置計算出光的方向,在現(xiàn)實中距離很近時衰減得很快,在距離較遠時衰減得很慢。這時就需要一種滿足需求的算法:


image.png

d:距離光源的距離,肯定是正數(shù)(大于等于0)。
Kc:產(chǎn)量因子,通常為1.0,它的作用是保證分母永遠不會比1小。
Kl:一次項因子,影響線性的亮度變化。
Kq:二次項因子,影響衰減的速度,距離越遠衰減越慢。


image.png

一般選用100的距離就足夠了。這個表在learnOpenGL中是經(jīng)過實驗證明的。
struct Light
{
    vec3 position;
    vec3 ambientFactor;
    vec3 diffuseFactor;
    vec3 specularFactor;
    float constant;//常數(shù)項
    float linear;//一次項
    float quadratic;//二次項
};

發(fā)送變量的時候選一種距離的這些項發(fā)送就好。

float distance = length(light.position - FragPos);
float attenuation = 1.0f / (light.constant + light.linear*distance +light.quadratic*(distance*distance));

左后去作用于每一種反射光的顏色就好。前面章節(jié)怎么去計算反射光的顏色說得很清楚了這里就省略了。

聚光燈:

在生活中聚光燈實際上可以看成白熾燈加一個燈罩,這樣光的方向就被控制在一定范圍內(nèi)了。生活中案例很多,如手電筒,路燈,探照燈甚至歌廳舞臺的射燈。我們可以去看聚光燈的OpenGL中的工作機制:


image.png

Φ:默認的允許照射的范圍
θ:計算出的光的方向和聚光燈方向的夾角
當(dāng)θ小于等于Φ我們認為在范圍內(nèi)允許片段點亮,如藍色和綠色光線。否則被認為在范圍外不被點亮,如棕色光。

聚光燈特點:

這里就能看出聚光燈需要一個方向,需要一個默認角度范,需要一個位置,其他的就可以通過計算獲得結(jié)果。

struct Light
{
    vec3 position;
    vec3 direction;
    float cutOff;//Φ的余玄值,通常我們都是使用余玄值去運算
    ...//這些因子與之前相同
};
float theta = dot(lightDir, normalize(-light.direction));
if(theta > light.cutOff)
{
    // 執(zhí)行光照計算
}
else // 否則使用環(huán)境光,使得場景不至于完全黑暗
color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f);

可以看出使用余玄值的好處:角度越大,余玄值越小。我們只需認可余玄值小的讓他點亮。
我們還可以結(jié)合上面的衰減的原理使聚光燈更真實。更可以通過傳入相機位置,來做相機的手電筒,這時聚光的的方向就要變化,始終是相機的視線方向(視點-相機位置)

邊緣的模糊化:

上面的聚光燈還不夠真實,生活中的聚光燈邊緣不會有特別清晰的分界線,而是模糊的,逐漸變?nèi)醯?。那么怎么能夠模擬這種效果喃。


image.png

我們在加一個半徑范圍r,當(dāng)角度θ小于Φ光強乘以1.0,如果大于Φ小于r就逐漸變暗(從1.0遞減),如果大于r就什么都不做。


image.png

I:就是衰弱的強度。
ε:內(nèi)切的余玄值與外切的余玄值之差,Φ-r。
注意:公式里都是余玄值,我們不會直接拿角來算。
這樣就保證了衰減性,而且值在0.0到1.0之間。
為了避免運算出來的片段完全黑暗我們把衰減因子的最小值設(shè)置為環(huán)境光因子的大小。

float intensity=light.ambientFactor;
float epsilon = light.cutOff - light.outerCutOff;//light.outerCutOff是r。
float theta = dot(lightDir, normalize(-light.direction));
if(theta<light.outerCutOff){
//執(zhí)行全局光(環(huán)境光)
}
else if(theta >outerCutOff&&theta < light.cutOff){
intensity=(theta - light.outerCutOff) / epsilon ;
if(intensity<light.ambientFactor)
intensity=light.ambientFactor;
//執(zhí)行衰減。
}
else{
intensity=1.0;
//執(zhí)行光照
}

經(jīng)過巧妙的處理我們發(fā)現(xiàn)上面的代碼可以用下面的代替。避免了做判斷。clamp函數(shù)是用來固定,保證值在0.0到1.0之內(nèi)。

float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;//light.outerCutOff是r。
float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);//當(dāng)然0.0就不要去相乘了,我們可以把強度給寫成環(huán)境光因子的強度到1.0。這樣來規(guī)避完全黑暗。

最終修改。

float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon,light.ambientFactor, 1.0);

是不是這個函數(shù)簡化了很多步驟喃。當(dāng)然他是shader的函數(shù)。

目錄

VSC++2019+QT+OpenGL
QT+OpenGL一之繪制立方體(三角形圖元)
QT+OpenGL二之紋理貼圖
QT+OpenGL三之矩陣簡解
QT+OpenGL四之相機的移動和旋轉(zhuǎn)
QT+OpenGL五之繪制不同的模型(vao,vbo機制)
QT+OpenGL六之天空盒
QT+OpenGL七之使用EBO
QT+OPenGL八之模型準(zhǔn)備
QT+OPenGL九之模型解碼
QT+OPenGL十之光照模型
QT+OPenGL十一之漫反射和鏡面反射貼圖
QT+OPenGL十二之定向光
QT+OPenGL十三之真正的點光源和聚光燈
QT+OPenGL十四之多光源混合的問題
QT+OPenGL十五之深度緩沖區(qū)
QT+OPenGL十六之模板緩沖區(qū)
QT+OPenGL十七幀緩沖區(qū)(離屏渲染)
QT+OPenGL十八抗鋸齒
QT+OPenGL十九鏡面反射效率調(diào)整
QT+OPenGL二十Gamma校正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容