前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >11.opengl光照-常见的三种光照实现

11.opengl光照-常见的三种光照实现

作者头像
公号sumsmile
发布2020-06-19 16:54:46
1.9K0
发布2020-06-19 16:54:46
举报

对glBindVertexArray的理解,学习这一章节时,开始run出来的效果是黑色的立方体,检查发现在while循环中少了'glBindVertexArray(VAO)', 每执行一个不同位置的模型渲染之前,都需要重新glBindVertexArray,否则glDrawArrays等操作都是基于之前glBindVertexArray的数据。黑色的其实是关联到了lightCubeVAO(灯模型),灯模型是没有纹理贴纸的。 这一篇没有复杂的原理,对三种常见的光照做了简单的实现,还是用的opengl中光照的基本原理

一、平行光

平行光

1. 定义一个光线方向向量而不是位置向量来模拟一个定向光,用一个方向代替position。注意direction的方向取反,入射方向是朝向物体的,而夹角求的是入射角和法线之间的锐角。
代码语言:javascript
复制
for(unsigned int i = 0; i < 10; i++)
{
    glm::mat4 model;
    model = glm::translate(model, cubePositions[i]);
    float angle = 20.0f * i;
    model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
    lightingShader.setMat4("model", model);

    glDrawArrays(GL_TRIANGLES, 0, 36);
}
2. 观察多个箱子对光照的反应,把前面章节中的箱子坐标copy一份
代码语言:javascript
复制
struct Light {
    // vec3 position; // 使用定向光就不再需要了
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
...
void main()
{
  vec3 lightDir = normalize(-light.direction);
  ...
}
3. 实现效果

平行光

注意光照的参数改变了:

代码语言:javascript
复制
lightingShader.setVec3("light.direction", -0.2, -1.0f, -0.3f);
二、点光源
1. 原理介绍

1.1. 点光源:

点光源示意图

1.2.点光源特征: 点光源随着距离增大,一开始快速衰减,达到一定距离后衰减降低,100的距离几乎衰减为0.

衰减

1.3. 衰减公式:

点光源衰减

1.4. 可以通过查表来确定参数k

参数

2. 代码说明,代码改动不大,不贴完整代码了,注意平行光是directiion计算,点光源是基于position计算

2.1 片元着色器中修改光照参数

代码语言:javascript
复制
struct Light {
    vec3 position;  

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};

2.2 在主程序中设置常量参数,这几个参数覆盖到50的范围内了

代码语言:javascript
复制
lightingShader.setFloat("light.constant",  1.0f);
lightingShader.setFloat("light.linear",    0.09f);
lightingShader.setFloat("light.quadratic", 0.032f);

2.3 计算光源离片元的距离

代码语言:javascript
复制
float distance    = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + 
                light.quadratic * (distance * distance));

2.4 光照计算,注意环境光也需要跟着一起衰减,因为可能有多个光源,一起衰减更逼真

代码语言:javascript
复制
ambient  *= attenuation; 
diffuse  *= attenuation;
specular *= attenuation;
3. 效果

点光源效果

三、聚光,如手电筒

聚光的特点是,聚光方向特定半径内的物体会被照亮,其他部分保持黑暗。 四个参数定义聚光:1)聚光光源position; 2)灯光方向;3)切光角

1. 示意图:

聚光

LightDir:从片段指向光源的向量。 SpotDir:聚光所指向的方向。 Phiϕ:指定了聚光半径的切光角。落在这个角度之外的物体都不会被这个聚光所照亮。 Thetaθ:LightDir向量和SpotDir向量之间的夹角。在聚光内部的话θ值应该比ϕ值小。

2. 实现效果

聚光

3. 代码说明
  1. 着色器中增加聚光的参数
代码语言:javascript
复制
struct Light {
    vec3  position; //灯光位置
    vec3  direction; // 灯光方向
    float cutOff; // 灯光的开合角度
    ...
};
  1. 设置参数到着色器
代码语言:javascript
复制
// 简单处理,用camera的位置作为光照
lightingShader.setVec3("light.position",  camera.Position);
// front的默认值是(0, 0, -1.0),指向屏幕里面,>0 指向屏幕外
lightingShader.setVec3("light.direction", camera.Front);
// 直接比较角度需要对余弦求反,余弦求角的开销很大,直接比较余弦值比较合适
lightingShader.setFloat("light.cutOff",   glm::cos(glm::radians(12.5f)));
  1. 主程序代码改动较小,仅附上着色器完整代码
代码语言:javascript
复制
#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};

struct Light {
    vec3 position;
    vec3 direction;
    float cutOff;
    float outerCutOff;
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    
    float constant;
    float linear;
    float quadratic; // 二次方
};

in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;

uniform vec3 viewPos;
uniform Material material;
uniform Light light;

void main()
{
    vec3 lightDir = normalize(light.position - FragPos);
    float theta = dot(lightDir, normalize(-light.direction));
    
    if (theta > light.cutOff)
    {
        // ambient
        vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
        
        // diffuse
        vec3 norm = normalize(Normal);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;
        
        // specular
        vec3 viewDir = normalize(viewPos - FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;
        
        // attenuation 衰减
        float distance = length(light.position - FragPos);
        float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));
        
        diffuse *= attenuation;
        specular *= attenuation;
        
        
        vec3 result = ambient + diffuse + specular;
        FragColor = vec4(result, 1.0);
    }
    else
    {
        FragColor = vec4(light.ambient * texture(material.diffuse, TexCoords).rgb, 1.0);
    }
}
四、聚光-平滑/软化边缘
1. 原理

上面实现的聚光,边界太清晰,和实际的光照不符,需要在边界进行平滑过渡处理。

我们可以用下面这个公式来计算这个值:

这里ϵ(Epsilon)是内(ϕ)和外圆锥(γ)之间的余弦值差(ϵ=ϕ−γ)。最终的I值就是在当前片段聚光的强度。

很难去表现这个公式是怎么工作的,所以我们用一些实例值来看看:

按照余弦值来计算比较方便,单位向量的点乘结果就是余弦值,有其他的线性差值实现也是可行的,比如按角度渐进。

这里有个技巧,用clamp可以把值归一化到0-1之间

代码语言:javascript
复制
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);    

下面的demo中,内切光是12.5,外切光角是17.5

2. 实现效果

平滑

3. 详细代码,主要是着色器中对聚光灯边缘处环状做了线性渐进的效果
代码语言:javascript
复制
#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D diffuse;
    sampler2D specular;    
    float shininess;
}; 

struct Light {
    vec3 position;  
    vec3 direction;
    float cutOff;
    float outerCutOff;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};

in vec3 FragPos;  
in vec3 Normal;  
in vec2 TexCoords;

uniform vec3 viewPos;
uniform Material material;
uniform Light light;

void main()
{
    // ambient
    vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;

    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  

    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;  

    // spotlight (soft edges)
    float theta = dot(lightDir, normalize(-light.direction)); 
    float epsilon = (light.cutOff - light.outerCutOff);
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
    diffuse  *= intensity;
    specular *= intensity;

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

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
} 

主程序中增加几个角度的设置

代码语言:javascript
复制
        lightingShader.use();
        lightingShader.setVec3("light.position", camera.Position);
        lightingShader.setVec3("light.direction", camera.Front);
        lightingShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));
        lightingShader.setFloat("light.outerCutOff", glm::cos(glm::radians(17.5f)));
        lightingShader.setVec3("viewPos", camera.Position);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、平行光
    • 1. 定义一个光线方向向量而不是位置向量来模拟一个定向光,用一个方向代替position。注意direction的方向取反,入射方向是朝向物体的,而夹角求的是入射角和法线之间的锐角。
      • 2. 观察多个箱子对光照的反应,把前面章节中的箱子坐标copy一份
        • 3. 实现效果
        • 二、点光源
          • 1. 原理介绍
            • 2. 代码说明,代码改动不大,不贴完整代码了,注意平行光是directiion计算,点光源是基于position计算
              • 3. 效果
              • 三、聚光,如手电筒
                • 1. 示意图:
                  • 2. 实现效果
                    • 3. 代码说明
                    • 四、聚光-平滑/软化边缘
                      • 1. 原理
                        • 2. 实现效果
                          • 3. 详细代码,主要是着色器中对聚光灯边缘处环状做了线性渐进的效果
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档