OpenGL实现多光源(平行光、点光源、聚光灯)光照效果

发表于2017-05-17
评论0 5.3k浏览

常规的光源类型有三种。最简单的自然是平行光,稍复杂些的为点光源,最复杂的为聚光灯。总体说来,在可编程管线中原理是一样的。在OpenGL代码中传入必要的参数,在shader中进行相关的计算绘制出效果。本文分别介绍三种光源效果的shader实现。


平行光

平行光我们已经写的轻车熟路了。它只需要一个光源方向就够了。通常我们会定义一个光源点,它与坐标轴原点的连线就是光线方向。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//GL Code 
float lightPos[] = { 0.0f,1.5f,0.0f,0.0f }; 
float diffuseLightColor[] = { 1.0f,1.0f,1.0f,1.0f }; 
   
glUniformMatrix4fv(gpuProgram.GetLocation("M"), 1,GL_FALSE, glm::value_ptr(model)); 
glUniformMatrix4fv(gpuProgram.GetLocation("V"), 1, GL_FALSE, identity); 
glUniformMatrix4fv(gpuProgram.GetLocation("P"), 1, GL_FALSE, glm::value_ptr(projectionMatrix)); 
glUniformMatrix4fv(gpuProgram.GetLocation("NM"), 1, GL_FALSE, glm::value_ptr(normalMatrix)); 
glUniform4fv(gpuProgram.GetLocation("U_LightPos"), 1, lightPos); 
glUniform4fv(gpuProgram.GetLocation("U_DiffuseLightColor"), 1, diffuseLightColor); 
   
//fs 
void main(){ 
    //... 
    vec3 n = normalize(V_Normal); 
    vec3 L = U_LightPos.xyz; 
    float diffuseIntensity=max(0.0,dot(L,n)); 
    vec4 diffuseColor=U_DiffuseLightColor*diffuseIntensity; 
    //... 
    gl_FragColor=ambientColor+diffuseColor; 
}

获取的光线方向直接与法线点乘,即为光照强度。


点光源

在平行光的基础上,根据模型与光源之间的距离进行衰减。衰减为与距离相关的二次函数。因此需要加入三个因数,分别为expFactorlinearFactorconstantFactor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//GL Code 
float constantFactor = 1.0; 
float linearFactor = 0.2; 
float expFactor=0.0; 
   
glUniform1f(gpuProgram.GetLocation("U_ConstantFactorn"), 1, constantFactor); 
glUniform1f(gpuProgram.GetLocation("U_LinearFactor"), 1, linearFactor); 
glUniform1f(gpuProgram.GetLocation("U_ExpFactor"), 1, expFactor); 
   
   
//fs 
float attenuation =  1.0; 
   
void main(){ 
    //... 
    vec3 n = normalize(V_Normal); 
    vec3 L = U_LightPos.xyz - V_WorldPos; 
    float distance = length(L); 
    attenuation = 1.0/(U_ExpFactor*distance*distance + U_LinearFactor*distance + U_ConstantFactor); 
    float diffuseIntensity = max(0.0,dot(L,n)); 
    vec4 diffuseColor = U_DiffuseLightColor*attenuation*diffuseIntensity; 
    //... 
    gl_FragColor=ambientColor+diffuseColor; 
}


聚光灯

在点光源的基础上,添加一个带角度的锥形区域的检测。只有在区域内的部分才有照亮处理。因此需要传入shader两个参数,分别为光照方向和范围角度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//GL Code 
//... 
float spotLightDirection[] = { 0.0f,-1.0f,0.0f,128.0f }; 
float spotLightCutoff = 15.0f; 
   
glUniform1f(gpuProgram.GetLocation("U_Cutoff"), spotLightCutoff); 
glUniform4fv(gpuProgram.GetLocation("U_LightDirection"), 1, spotLightDirection); 
   
//... 
   
//fs 
float attenuation =  1.0; 
   
void main(){ 
    //... 
   
    float radianCutoff = U_Cutoff*3.14/180; 
    float cosThta = cos(radianCutoff); 
    vec3 spotLightDirection = normalize(U_LightDirection.xyz); 
   
    vec3 n = normalize(V_Normal); 
    vec3 L = U_LightPos.xyz - V_WorldPos; 
    float distance = length(L); 
    attenuation = 1.0/(U_ExpFactor*distance*distance + U_LinearFactor*distance + U_ConstantFactor); 
       
    float currentCosThta=max(0.0,dot(-L,spotLightDirection)); 
    float diffuseIntensity = 0.0; 
    if(currentCosThta > cosThta) 
    
        if(dot(L,n) >0.0) 
        
            diffuseIntensity = pow(currentCosThta,U_LightDirection.w);  
        
    
    vec4 diffuseColor = U_DiffuseLightColor*attenuation*diffuseIntensity; 
   
    //... 
    gl_FragColor=ambientColor+diffuseColor; 
}


总结

以上为三种光源的OpenGL实现,可以看出,计算量最大的为聚光灯效果,而且为了让边缘柔和,需要pow的大量计算。移动端跑实时运算估计要优化算法了。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引