QT+OPenGL十四之多光源混合的問題

我們前面已經(jīng)研究了各中不同光源,但是我們都是單獨(dú)實(shí)現(xiàn)的。一個(gè)場景中往往會(huì)有多種光源。所以我們不得不考慮這種情況。為了能看出效果,我們需要先來一個(gè)地形,下載地形模型直接按前面講的,繪制出來就好了。
效果:


image.png

這只是有定向光的情況,但是我們已經(jīng)能看到各個(gè)部分,背向光線有了陰影。

混合光的思路:

我們可以對每種光源處理后最后返回的顏色封裝成函數(shù)。然后對結(jié)果進(jìn)行結(jié)合。
封裝的好處:封裝好了更便于我們以后重用,而且能使shader中main函數(shù)更簡介更可讀。

注意:這里為了簡潔我們默認(rèn)每一種光的環(huán)境光因子,漫反射因子和鏡面反射因子都是相同的,我們就不在每種光結(jié)構(gòu)體中定義這三種因子了。首先shader和c語言及其相似,我們可以用c語言的方法來寫函數(shù)。

修復(fù)法向量:

經(jīng)過前面的學(xué)習(xí)我們法線光照往往是需要法線的,而法向量會(huì)隨著模型的旋轉(zhuǎn)和非等比縮放,就會(huì)發(fā)生改變,如果我們不修復(fù)就會(huì)出現(xiàn)光照的嚴(yán)重視覺錯(cuò)誤。當(dāng)旋轉(zhuǎn)后,我們可以直接使用:

Normal=vec3(model*vec4(normal,0.0));

把最后的法向量發(fā)送給片段著色器去處理光照,我們知道模型矩陣中包括了位移縮放和旋轉(zhuǎn),而向量沒有位移屬性因此我們必須排除掉,直接把w分量置0就能很好的解決,這樣法向量就和頂點(diǎn)坐標(biāo)一樣被放置到世界坐標(biāo)系中。當(dāng)然這種情況不適用于非等比縮放。非等縮放需要用到逆轉(zhuǎn)置矩陣,這個(gè)在以后來討論。

struct Factor
{
    float ambient;
    float diffuse;
    float specular;
};
uniform Factor factor;

封裝定向光:

struct DirLight
{
    vec3 direct;
    vec3 color;
};
uniform DirLight dirDight;

函數(shù),注意先聲明再使用,也可寫再main函數(shù)上面

vec3 getDirLightColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material){
//獲取漫反射貼圖,鏡面反射貼圖紋理采樣的顏色
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 lightDir=normalize(dirDight.direct);
vec3 normal=normalize(Normal);
//計(jì)算漫反射強(qiáng)度
float diffuseStringth = max(dot(normal,lightDir), 0.0);
//計(jì)算鏡面反射強(qiáng)度
vec3 viewDir = normalize(cameraPos - worldPos);
vec3 reflectDir = reflect(-lightDir, normal);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
//三種反射的顏色
vec3 ambient=factor.ambient*dirDight.color*diffuseColor;
vec3 diffuse =factor.diffuse* diffuseStringth *dirDight.color*diffuseColor;
vec3 specular = factor.specular * specularStringth*dirDight.color*specularColor;
//返回最終結(jié)合出的顏色
return vec3(ambient + diffuse+specular);
}

完整shader:這是個(gè)例子后面就省略了這些代碼

#version 430 
struct Material
{
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};
struct DirLight
{
    vec3 direct;
    vec3 color;
};
struct Factor
{
    float ambient;
    float diffuse;
    float specular;
};
uniform Material material;
uniform DirLight dirDight;
uniform Factor factor;
out vec4 color;
uniform vec3 cameraPos;
in vec2 texcoord;
in vec3 Normal;
in vec3 worldPos;
vec3 getDirLightPointColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material);
void main(void) 
{
color=vec4(getDirLightColor(texcoord,Normal,factor,dirDight,material),1.0);
}
vec3 getDirLightColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material){
//獲取漫反射貼圖,鏡面反射貼圖紋理采樣的顏色
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 lightDir=normalize(dirDight.direct);
vec3 normal=normalize(Normal);
//計(jì)算漫反射強(qiáng)度
float diffuseStringth = max(dot(normal,lightDir), 0.0);
//計(jì)算鏡面反射強(qiáng)度
vec3 viewDir = normalize(cameraPos - worldPos);
vec3 reflectDir = reflect(-lightDir, normal);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
//三種反射的顏色
vec3 ambient=factor.ambient*dirDight.color*diffuseColor;
vec3 diffuse =factor.diffuse* diffuseStringth *dirDight.color*diffuseColor;
vec3 specular = factor.specular * specularStringth*dirDight.color*specularColor;
//返回最終結(jié)合出的顏色
return vec3(ambient + diffuse+specular);
}

封裝點(diǎn)光源:

struct PointLight
{
   vec3 position;
    float constant;//1.0
    float linear;//0.045
    float quadratic; //0.0075,這個(gè)衰減距離是100時(shí)全部衰減
    vec3 color;
};
uniform PointLight pointLight;
vec3 getPointLightColor(vec2 texcoord,vec3 Normal,Factor factor,PointLight pointLight,Material material){
    //獲取漫反射貼圖,鏡面反射貼圖紋理采樣的顏色
    vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
    vec3 specularColor=vec3(texture(material.specular,texcoord));
    vec3 lightDir = normalize(pointLight.position-worldPos);
    vec3 normal=normalize(Normal);
    // 計(jì)算漫反射強(qiáng)度
    float diffuseStringth = max(dot(normal, lightDir), 0.0);
    // 計(jì)算鏡面反射
    vec3 reflectDir = reflect(-lightDir, normal);
    vec3 viewDir = normalize(cameraPos - worldPos);
    float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // 計(jì)算衰減
    float distance = length(pointLight.position - worldPos);
    float attenuation = 1.0f/(pointLight.constant + pointLight.linear * distance +
    pointLight.quadratic * (distance * distance));
    // 將各個(gè)分量合并
    vec3 ambient  = factor.ambient*attenuation*pointLight.color*diffuseColor;
    vec3 diffuse  = factor.diffuse*diffuseStringth*attenuation*pointLight.color*diffuseColor;
    vec3 specular = factor.specular*specularStringth*attenuation*pointLight.color*specularColor;

    return vec3(ambient + diffuse + specular);
}

單獨(dú)開啟點(diǎn)光源效果(光源放在龍模型上方五個(gè)像素處):


image.png

可以看見只有點(diǎn)光源周圍被點(diǎn)亮了。

封裝聚光燈:我們這里寫一個(gè)手電筒和一個(gè)類似與路燈的光源:

struct Spotlight
{
    vec3 position;
    vec3 direction;
    float cutOff;//Φ的余玄值
    float outerCutOff;//r的余玄
    vec3 color;
};
uniform Spotlight spotlight;
vec3 getSpotlightColor(vec2 texcoord,vec3 Normal,Factor factor,Spotlight spotlight,Material material){
  //獲取漫反射貼圖,鏡面反射貼圖紋理采樣的顏色
    vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
    vec3 specularColor=vec3(texture(material.specular,texcoord));
    vec3 lightDir = normalize(spotlight.position-worldPos);
    vec3 normal=normalize(Normal);
    //計(jì)算光照范圍強(qiáng)度
    float theta = dot(lightDir, normalize(spotlight.direction));
   float epsilon = spotlight.cutOff - spotlight.outerCutOff;
   float intensity = clamp((theta - spotlight.outerCutOff) / epsilon,0.0, 1.0);
   //計(jì)算漫反射強(qiáng)度
   float diffuseStringth = max(dot(normal,lightDir), 0.0);
   //計(jì)算鏡面反射強(qiáng)度
   vec3 viewDir = normalize(cameraPos - worldPos);
   vec3 reflectDir = reflect(-lightDir, normal);
   float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
   //三種反射最終顏色
      vec3 ambient  = factor.ambient*spotlight.color*diffuseColor;
      vec3 diffuse  = factor.diffuse*diffuseStringth*spotlight.color*intensity*diffuseColor;
      vec3 specular = factor.specular*specularStringth*spotlight.color*intensity*specularColor;
     return vec3(ambient + diffuse + specular);
    
}

效果:


image.png

可以看到斜坡別聚光燈照亮了,我們這里認(rèn)為聚光燈也不衰減。
當(dāng)然一般的模仿路燈的效果這樣就足夠了,如果做手電筒還是應(yīng)該做衰減的。我這里就不做衰減了,就是把點(diǎn)光源的衰減方法加在這里就好了。

手電筒的實(shí)現(xiàn)很簡單就是把光源的位置換成相機(jī)的位置,光源的方向換成視線的方向:
struct Torch
{
    float cutOff;//Φ的余玄值
    float outerCutOff;//r的余玄
    float constant;
    float linear;
    float quadratic; 
    vec3 color;
};
uniform Torch torch;
vec3 getElectricTorchColor(vec2 texcoord,vec3 Normal,Factor factor,Torch torch,Material material){
  //獲取漫反射貼圖,鏡面反射貼圖紋理采樣的顏色
    vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
    vec3 specularColor=vec3(texture(material.specular,texcoord));
    vec3 lightDir = normalize(cameraPos-worldPos);
     vec3 cameraViewDir = normalize(cameraPos - cameraView);
    vec3 normal=normalize(Normal);
    //計(jì)算光照范圍強(qiáng)度
    float theta = dot(lightDir, normalize(cameraViewDir));
   float epsilon = torch.cutOff - torch.outerCutOff;
   float intensity = clamp((theta - torch.outerCutOff) / epsilon,0.0, 1.0);
    // 計(jì)算衰減
    float distance = length(cameraPos - worldPos);
    float attenuation = 1.0f/(torch.constant + torch.linear * distance +
    torch.quadratic * (distance * distance));
   //計(jì)算漫反射強(qiáng)度
   float diffuseStringth = max(dot(normal,lightDir), 0.0);
   //計(jì)算鏡面反射強(qiáng)度
   vec3 viewDir = normalize(cameraPos - worldPos);
   vec3 reflectDir = reflect(-lightDir, normal);
   float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
   //三種反射最終顏色
      vec3 ambient  = factor.ambient*torch.color*diffuseColor*attenuation;
      vec3 diffuse  = factor.diffuse*diffuseStringth*torch.color*intensity*diffuseColor*attenuation;
      vec3 specular = factor.specular*specularStringth*torch.color*intensity*specularColor*attenuation;
     return vec3(ambient + diffuse + specular);
}

效果圖:


image.png

我們能看見近處被照亮遠(yuǎn)處亮度就不夠了,這就是我們要的效果。如果要開啟多個(gè)光源,直接把這些返回值相加就好了。
統(tǒng)一變量發(fā)送

 shaderProgram->setUniformValue("factor.ambient", ambientFactor);
    shaderProgram->setUniformValue("factor.diffuse", diffuseFactor);
    shaderProgram->setUniformValue("factor.specular", specularFactor);

    shaderProgram->setUniformValue("material.shininess", shininess);
    shaderProgram->setUniformValue("dirDight.direct", direct);
    shaderProgram->setUniformValue("dirDight.color", dirLightColor);

    shaderProgram->setUniformValue("pointLight.position", QVector3D(0.0,5.0,-30.0));
    shaderProgram->setUniformValue("pointLight.constant", 1.0f);
    shaderProgram->setUniformValue("pointLight.linear", linear);
    shaderProgram->setUniformValue("pointLight.quadratic", quadratic);
    shaderProgram->setUniformValue("pointLight.color", pointLightColor);

    shaderProgram->setUniformValue("spotlight.position", QVector3D(0.0,20.0,-30.0));
    shaderProgram->setUniformValue("spotlight.direction", QVector3D(0.0,1.0,0.0));
    shaderProgram->setUniformValue("spotlight.cutOff", 0.95f);
    shaderProgram->setUniformValue("spotlight.outerCutOff", 0.93f);
    shaderProgram->setUniformValue("spotlight.color", QVector3D(1.0,1.0,1.0));

    shaderProgram->setUniformValue("torch.constant", 1.0f);
    shaderProgram->setUniformValue("torch.linear", 0.014f);
    shaderProgram->setUniformValue("torch.quadratic", 0.0007f);
    shaderProgram->setUniformValue("torch.cutOff", 0.91f);
    shaderProgram->setUniformValue("torch.outerCutOff", 0.82f);
    shaderProgram->setUniformValue("torch.color", QVector3D(1.0, 1.0, 1.0));

開啟點(diǎn)光源和聚光燈:

vec3 spotlightColor=getSpotlightColor(texcoord,Normal,factor,spotlight,material);
vec3 pointLightColor=getPointLightColor(texcoord,Normal,factor,pointLight,material);
vec3 mixtureColor=spotlightColor+pointLightColor;
color=vec4(mixtureColor,1.0); 
image.png

可以看到龍被點(diǎn)光源照亮,光圈被聚光燈照亮。

目錄

VSC++2019+QT+OpenGL
QT+OpenGL一之繪制立方體(三角形圖元)
QT+OpenGL二之紋理貼圖
QT+OpenGL三之矩陣簡解
QT+OpenGL四之相機(jī)的移動(dòng)和旋轉(zhuǎn)
QT+OpenGL五之繪制不同的模型(vao,vbo機(jī)制)
QT+OpenGL六之天空盒
QT+OpenGL七之使用EBO
QT+OPenGL八之模型準(zhǔn)備
QT+OPenGL九之模型解碼
QT+OPenGL十之光照模型
QT+OPenGL十一之漫反射和鏡面反射貼圖
QT+OPenGL十二之定向光
QT+OPenGL十三之真正的點(diǎn)光源和聚光燈
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輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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