OpenGL(十三)——世界光照

news2024/11/16 16:25:21

目录

一、前言

二、平行光

 2.1 片段着色器

2.2 app渲染

三、点光源

3.1 距离衰减

 3.2 衰减片段着色器

四、聚光

4.1 片段着色器

4.2 光照入射方向 

4.3 平滑边缘


一、前言

Light Caster :光投射(Cast)到物体的光源。现实世界中通常多种不同的光源类型共同作用在物体表面,包括平行光(定向光)、点光源、聚光。

二、平行光

当光源处于一个很远地方,在物体上的光线可以看作是近似相互平行的。这种光线被称为定向光或者平行光,由于光线是相同的方向,所有它与光源位置是无关的。因此直接使用光线向量来替代位置向量来计算light Direction。

 2.1 片段着色器

将光结构体的位置向量去掉,使用direction变量,然后重新计算lightDir即可

#version 330 core

struct Material{
    sampler2D diffuse;//漫反射纹理贴图
    sampler2D specular;
    float shininess;
};

struct Light{
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

out vec4 FragColor;

in vec3 Normal;  //法向量:垂直于顶点表面的向量
in vec3 FragPos;  //片段位置向量:世界坐标系下
in vec2 TexCoords;

uniform vec3 lightPos; //光源位置向量
uniform vec3 viewPos; //观察者位置向量

uniform vec3 lightColor;
uniform Material material;
uniform Light light;

void main()
{
    // 环境光照
    vec3 ambient =light.ambient*texture(material.diffuse,TexCoords).rgb;
  	
    // 漫反射 
    vec3 norm = normalize(Normal);//世界坐标系法向量归一化,单位向量
    vec3 lightDir = normalize(-light.direction);//计算光源向量,取反表示从光源出发的全局方向
    float diff = max(dot(norm, lightDir), 0.0); //计算光源对当前片段影响
    vec3 diffuse = light.diffuse*diff * texture(material.diffuse,TexCoords).rgb ;
    
    // 镜面反射 + 反光强度Shininess(镜面高光散射半径)
    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);  
        
    FragColor = vec4(ambient + diffuse + specular, 1.0);
} 

2.2 app渲染

为了体现光照对不同姿态物体的影响,设置物体的全局位置和旋转角度,计算模型矩阵(从局部到世界空间)

//箱子全局位置
glm::vec3 cubePositions[] = {
    glm::vec3(0.0f,  0.0f,  0.0f),
    glm::vec3(2.0f,  5.0f, -15.0f),
    glm::vec3(-1.5f, -2.2f, -2.5f),
    glm::vec3(-3.8f, -2.0f, -12.3f),
    glm::vec3(2.4f, -0.4f, -3.5f),
    glm::vec3(-1.7f,  3.0f, -7.5f),
    glm::vec3(1.3f, -2.0f, -2.5f),
    glm::vec3(1.5f,  2.0f, -2.5f),
    glm::vec3(1.5f,  0.2f, -1.5f),
    glm::vec3(-1.3f,  1.0f, -1.5f)
};
//设置光源向量
ObjShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f);

//模型矩阵, 世界坐标不同位置的立方体
        glBindVertexArray(ObjVAO);
        for (unsigned int i = 0; i < 10; i++)
        {
            glm::mat4 model = glm::mat4(1.0f);
            model = glm::translate(model, cubePositions[i]);
            float angle = 20 * i;
            model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
            ObjShader.setMat4("model", model);
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

 

三、点光源

3.1 距离衰减

点光源是世界坐标中定义某一个位置的光源,它朝所有方向发光,但是光线会随着距离逐渐衰减。

 使用一个方程来表示随距离增长而线性减少光强度:

 d表示片段光源的距离,k是可配置的衰减常数项系数。Kc通常为1;一次项保证以线性方式减少强度,二次项在距离近时影响比一次项小很多,当距离远时影响比较大。下表是在指定3项的情况下能覆盖的距离:

 3.2 衰减片段着色器

  1. 在光源结构体中添加衰减项系数
  2. 计算光源位置到片段的距离
  3. 计算衰减值
  4. 计算环境光照、漫反射、镜面反射强度
  5. 使用衰减值和强度值计算最终颜色
#version 330 core

struct Material{
    sampler2D diffuse;//漫反射纹理贴图
    sampler2D specular;
    float shininess;
};

struct Light{
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};

out vec4 FragColor;

in vec3 Normal;  //法向量:垂直于顶点表面的向量
in vec3 FragPos;  //片段位置向量:世界坐标系下
in vec2 TexCoords;

uniform vec3 lightPos; //光源位置向量
uniform vec3 viewPos; //观察者位置向量

uniform vec3 lightColor;
uniform Material material;
uniform Light light;

void main()
{
    // 环境光照
    vec3 ambient =light.ambient*texture(material.diffuse,TexCoords).rgb;
  	
    // 漫反射 
    vec3 norm = normalize(Normal);//世界坐标系法向量归一化,单位向量
    //vec3 lightDir = normalize(-light.direction);//计算光源向量,取反表示从光源出发的全局方向
    vec3 lightDir = normalize(FragPos - light.position);//从片段到光源的光线方向
    float diff = max(dot(norm, lightDir), 0.0); //计算光源对当前片段影响
    vec3 diffuse = light.diffuse*diff * texture(material.diffuse,TexCoords).rgb ;
    
    // 镜面反射 + 反光强度Shininess(镜面高光散射半径)
    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);  
    
    //衰减
    float distance = length(light.position - FragPos );
    float attenuate =  1/(light.constant + light.linear*distance + light.quadratic*distance*distance);

    FragColor = vec4((ambient + diffuse + specular)*attenuate, 1.0);
} 

在app中设置衰减参数

        //ObjShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
        ObjShader.setVec3("lightPos", lightPos);
        //ObjShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f);
        ObjShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
        ObjShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // 将光照调暗了一些以搭配场景
        ObjShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
        //衰减
        ObjShader.setFloat("light.constant", 1.0f);
        ObjShader.setFloat("light.linear", 0.09f);
        ObjShader.setFloat("light.quadratic", 0.032f);

 

四、聚光

聚光是环境中某个位置的光源,它只朝某一个特定方向而不是所有方向照射光线。效果是只有在聚光方向的特定半径内的物体才会被照亮,其它物体都会保持黑暗,类似路灯或者手电筒。

  •  LightDir是片段到光源的向量
  • SpotDir是聚光所指向的方向
  • Φ是聚光半径的切光角,落在这个角度之外的物体都不会被这个聚光所照亮
  • θ是LightDir和SpotDir夹角

计算LightDir和SpotDir向量之间的点击,得到的结果与切光角对比。LightDir与direction向量之间的点积得到夹角余弦值。

 越接近90度表示的是0的余弦值

4.1 片段着色器

在光照中定义聚光信息,然后对比lightDir和SpotDir夹角是否在切光角范围内执行不同光照算法:

#version 330 core

struct Material{
    sampler2D diffuse;//漫反射纹理贴图
    sampler2D specular;
    float shininess;
};

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

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};

out vec4 FragColor;

in vec3 Normal;  //法向量:垂直于顶点表面的向量
in vec3 FragPos;  //片段位置向量:世界坐标系下
in vec2 TexCoords;

uniform vec3 lightPos; //光源位置向量
uniform vec3 viewPos; //观察者位置向量

//uniform vec3 lightColor;
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)
    {
        // 环境光照
        vec3 ambient =light.ambient*texture(material.diffuse,TexCoords).rgb;
  	
        // 漫反射 
        vec3 norm = normalize(Normal);//世界坐标系法向量归一化,单位向量
        //vec3 lightDir = normalize(-light.direction);//计算光源向量,取反表示从光源出发的全局方向
    
        float diff = max(dot(norm, lightDir), 0.0); //计算光源对当前片段影响
        vec3 diffuse = light.diffuse*diff * texture(material.diffuse,TexCoords).rgb ;
    
        // 镜面反射 + 反光强度Shininess(镜面高光散射半径)
        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);  
    
        //衰减
        float distance = length(light.position - FragPos );
        float attenuate =  1/(light.constant + light.linear*distance + light.quadratic*distance*distance);
        //ambient  *= attenuate;//导致较远的距离,在切光角内更黑
        diffuse   *= attenuate;
        specular *= attenuate;  
        FragColor = vec4(ambient + diffuse + specular, 1.0);
    }
    else
    {
        //使用环境光,让场景在聚光之外时不至于完全黑暗
        FragColor = vec4(light.ambient * texture(material.diffuse, TexCoords).rgb, 1.0);
    }
} 

在app中对聚光信息设值:

        ObjShader.setVec3("light.position", lightPos);
        ObjShader.setVec3("light.direction", camera.Front);
        ObjShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));

4.2 光照入射方向 

关于光源LightDir的疑惑,到底是light.position - FragPos还是 FragPos -light.position,取决于场景,如漫反射是从物体到光源方向的反射所以是light.position - FragPos,

模拟平行光是从光源出发的向量lightingShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f); 光的方向是朝下的,要参考右手定则,设为vec3 lightDir = normalize(-light.direction);对向量取反,得到一个从光源出发的全局方向。

4.3 平滑边缘

为了创建一个边缘平滑的聚光,模拟聚光有一个内圆锥、外圆锥。将内圆锥设置为上一部分中的那个圆锥,外圆锥来让光从内圆锥逐渐减暗,直到外圆锥的边界

定义一个余弦值来代表聚光方向向量 和 外圆锥向量(等于它的半径)夹角。

如果一个片段处于内外圆锥之间,将会给它计算出一个0.0到1.0之间的强度值。如果片段在内圆锥之内,它的强度就是1.0,如果在外圆锥之外强度值就是0.0。

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

 片段着色器:

#version 330 core

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;
};

out vec4 FragColor;

in vec3 Normal;  //法向量:垂直于顶点表面的向量
in vec3 FragPos;  //片段位置向量:世界坐标系下
in vec2 TexCoords;

uniform vec3 lightPos; //光源位置向量
uniform vec3 viewPos; //观察者位置向量

//uniform vec3 lightColor;
uniform Material material;
uniform Light light;

void main()
{
    // 环境光照
    vec3 ambient =light.ambient*texture(material.diffuse,TexCoords).rgb;
  	
    // 漫反射 
    vec3 norm = normalize(Normal);//世界坐标系法向量归一化,单位向量
    //vec3 lightDir = normalize(-light.direction);//计算光源向量,取反表示从光源出发的全局方向
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0); //计算光源对当前片段影响
    vec3 diffuse = light.diffuse*diff * texture(material.diffuse,TexCoords).rgb ;
    
    // 镜面反射 + 反光强度Shininess(镜面高光散射半径)
    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;  
    
    //聚光+平滑
    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;

    //衰减
    float distance = length(light.position - FragPos );
    float attenuate =  1/(light.constant + light.linear*distance + light.quadratic*distance*distance);
    ambient  *= attenuate;
    diffuse   *= attenuate;
    specular *= attenuate;  
    FragColor = vec4(ambient + diffuse + specular, 1.0);
} 

app设置外圆锥切角:

ObjShader.setFloat("light.outerCutOff", glm::cos(glm::radians(17.5f)));

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/509764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面对复杂的系统与众多的插件,如何确保Jenkins项目的安全性?

CloudBees在Jenkins/CBCI生态系统上建立了一个专门的安全团队。关于该团队的公开信息可以在从此链接中找到&#xff1a;https://www.jenkins.io/security/。由于所涉及的系统复杂且插件数量众多&#xff08;见下文&#xff09;&#xff0c;许多扫描提供的信息缺少有价值的上下文…

1015. 可被 K 整除的最小整数(leetcode)取模技巧题-------------------c++实现

1015. 可被 K 整除的最小整数&#xff08;leetcode&#xff09;取模技巧题-------------------c实现 题目表述 给定正整数 k &#xff0c;你需要找出可以被 k 整除的、仅包含数字 1 的最 小 正整数 n 的长度。 返回 n 的长度。如果不存在这样的 n &#xff0c;就返回-1。 注…

看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动EPIT定时器中断《按键消抖》) 2023.5.10

前言 首先我们在使用开发板进行开发时&#xff0c;自然而然会使用到定时器这个外设&#xff0c;因为我们需要它来完成精准的定时功能&#xff0c;但是说到精准&#xff0c;我会在下一篇文章中使用其他的定时器来完成这个功能即GPT定时器。在本文章中我们会利用定时器中断来解决…

LeetCode2. 两数相加

写在前面&#xff1a; 题目链接&#xff1a;LeetCode2两数相加 编程语言&#xff1a;C 题目难度&#xff1a;中等 一、题目描述 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 …

光学液氮恒温器T9120-4W的技术参数

液氮型低温恒温器&#xff0c;利用液氮作为降温媒介&#xff0c;标准恒温器可实现快速降温至液氮温度&#xff08;约20min&#xff09;&#xff0c;其工作原理是在恒温器内部液氮腔内装入液氮&#xff0c;通过调整控温塞与冷指的间隙来保持冷指的漏热稳定在一定值上&#xff0c…

lua实战(1)

目录 IDELua中的名称Lua是一种区分大小写的语言 Lua 是一个小巧的脚本语言。它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo三人所组成的研究小组于1993年开…

vue3+electron开发桌面软件(9)——选中多个文件,右键上传

系列文章目录 系列第一篇&#xff1a; vue3electron开发桌面软件入门与实战&#xff08;0&#xff09;——创建electron应用 文章目录 系列文章目录前言一、我们如何思考二、解决问题1.选择方案2. 发现electron多开窗口监听3.查找可使用的官方参数4.示例代码 总结 前言 从本系…

Docker 安全及日志管理

Docker 安全及日志管理 Docker 容器与虚拟机的区别隔离与共享性能与损耗 Docker 存在的安全问题Docker 自身漏洞Docker 源码问题Docker 架构缺陷与安全机制Docker 安全基线标准 容器相关的常用安全配置方法容器最小化Docker 远程 API 访问控制重启 Docker在宿主机的 firewalld …

ASEMI代理ADI亚德诺ADXL345BCCZ-RL7车规级芯片

编辑-Z ADXL345BCCZ-RL7特点&#xff1a; 超低功率&#xff1a;在测量模式下低至23A 在VS2.5 V的待机模式下为0.1A&#xff08;典型&#xff09; 功耗会随带宽自动调整 用户可选分辨率 固定的10位分辨率 全分辨率&#xff0c;其中分辨率随着g范围的增加而增加&#xff0…

00后才是内卷之王,被卷的头皮发麻....

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…

Redis主从复制(搭建集群的一种方式)【故障转移,内存,回收】

做一个伪集群 配置文件&#xff1a; daemonize yes port 7777 logfile .redis-7777.log dir ./ bind 0.0.0.0启动6666 and 7777 现在设置主从表 但是有个问题我把服务器停掉 关系就会解除 还可以手动解除 slaveof no one 命令 配置Sentinel&#xff08;哨兵&#…

基于络达SOC AB1562A TWS蓝牙耳机设计

V hezkz17进数字音频答疑 一 原理框图 二 电子电路设计 (1)SOC主芯片 (2) 最小系统晶振电路设计26MHZ (3) 电池电路设计 4 充电电路与充电保护设计 5 LED输出电路设计</

hexo stellar设置目录跳转记录

1. 使用hexo-toc插件 一开始使用的是hexo-toc的插件&#xff1a;参考hexo安装toc插件 详细的可以看github的项目&#xff1a; github-hexo-toc 更加详细的配置&#xff1a; Hexo添加Toc支持&#xff0c;生成文章目录 2. 官网的方式&#xff08;推荐&#xff09; stellar博…

flink cdc原理与使用

flink cdc原理与使用 1 cdc 介绍1.1 cdc简介与对比1.2 基于日志的 CDC 方案介绍 2 基于 Flink SQL CDC 的数据同步方案实践2.1 案例 1 : Flink SQL CDC JDBC Connector2.2 案例 2 : CDC Streaming ETL2.3 案例 3 : Streaming Changes to Kafka 3 Flink SQL CDC 的更多应用场景…

Java EE企业级应用开发(SSM)第10章

第10章MyBatis核心配置及动态SQL 一.预习笔记 1.第九章的细节处理 1-1.mappers标签中的配置 1-2.jdbc属性文件的配置 1-3.包的别名配置 2.Mybatis核心配置文件 2-1&#xff1a;settings标签&#xff08;P145-146中的表10-1&#xff09; 2-2.类型别名 3.Mybatis映射文件 3-1…

项目集管理绩效领域

项目集管理绩效领域是对活动或职能相关领域的补充分组&#xff0c;这些活动或职能在项目集管理工作的 整个范围内&#xff0c;专门描述和区分一个绩效领域中的活动。 本章包括&#xff1a; 项目集管理绩效领域的定义项目集管理绩效领域的交互组织战略、项目组合管理和项目集管…

vue 水印组件

效果图展示 Watermark 参数说明类型默认值版本width水印的宽度&#xff0c;content 的默认值为自身的宽度number120height水印的高度&#xff0c;content 的默认值为自身的高度number64rotate水印绘制时&#xff0c;旋转的角度&#xff0c;单位 number-22zIndex追加的水印元素…

24.eslint

eslint是约束代码写法的插件&#xff0c;比如组件的命名必须要用驼峰命名这种 eslint官网 检测并修复 JavaScript 代码中的问题。 - ESLint - 插件化的 JavaScript 代码检查工具 目录 1 vue-cli的eslint 2 标准规则 2.1 不能连续出现两个空行 2.2 结尾必须有空行 2.3…

深入了解Dubbo SPI 工作机制——@Activate (5)

在上一篇Dubbo 基于xml文件分析主流程源码 &#xff08;4&#xff09;_chen_yao_kerr的博客-CSDN博客中, 我们已经初步了解了Dubbo SPI的 key - value 结构。接下来将会继续分享Dubbo SPI其他功能的使用方式&#xff0c;并且从源码的角度去一谈究竟。 Activate注解 参数名 …

【数据结构】链表OJ:力扣141.环形链表、142.环形链表II

今天要分享的关于链表的题目是环形链表 目录 题目141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; 题解 关于快慢指针的深入研究 题目2&#xff1a;142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; 题解 以下是题目链接 141. 环形链表 - 力扣&#xff…