LearnOpenGL(十一)之光源

一、投光物

将光投射(Cast)到物体的光源叫做投光物(Light Caster)。

二、平行光

当一个光源处于很远的地方时,来自光源的每条光线就会近似于互相平行,我们可以称这些光为平行光。当我们使用一个假设光源处于无限远处的模型时,它就被称为定向光,因为它的所有光线都有着相同的方向,它与光源的位置是没有关系的。定向光非常好的一个例子就是太阳:

我们可以定义一个光线方向向量而不是位置向量来模拟一个定向光,并直接使用光的direction向量而不是通过position来计算lightDir向量:

struct Light {
    // vec3 position; // 使用定向光就不再需要了
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
...
void main()
{
  vec3 lightDir = normalize(-light.direction);
  ...
}

我们目前使用的光照计算需求一个从片段光源的光线方向,但人们更习惯定义定向光为一个光源出发的全局方向。所以我们需要对全局光照方向向量取反来改变它的方向,它现在是一个指向光源的方向向量了。

然后,定义光源的方向就可以了:

lightingShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f);

三、点光源

点光源是处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰减,如生活中的灯泡和火把:

其实,我们一直都在使用一个(简化的)点光源。我们在给定位置有一个光源,它会从它的光源位置开始朝着所有方向散射光线。然而,我们定义的光源模拟的是永远不会衰减的光线,这看起来像是光源亮度非常的强。现在我们要做的就是将光随距离来衰减。

四、衰减

随着光线传播距离的增长逐渐削减光的强度通常叫做衰减(Attenuation)。在现实世界中,灯在近处通常会非常亮,但随着距离的增加光源的亮度一开始会下降非常快,但在远处时剩余的光强度就会下降的非常缓慢了。所以,我们可以通过以下公式实现该效果:

代表片段到光源的距离

常数项 Kc 通常是1.0,它的作用是保证分母永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。

一次项 Kl 用于与距离值相乘,这会以线性的方式减少亮度。

二次项 Kq 用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大。

为了实现衰减,在片段着色器中我们需要公式中的常数项、一次项和二次项这3个值:

struct Light {
    vec3 position;  

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};

然后,在OpenGL中设置这些项:

lightingShader.setFloat("light.constant",  1.0f);
lightingShader.setFloat("light.linear",    0.09f);
lightingShader.setFloat("light.quadratic", 0.032f);

接着,计算距光源的距离,进而计算衰减值:

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;

运行效果如下:

五、聚光

聚光是位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线,如路灯或手电筒。

OpenGL中聚光是用一个世界空间位置、一个方向和一个切光角(Cutoff Angle)来表示的:

LightDir:从片段指向光源的向量。

SpotDir:聚光所指向的方向。

ϕ:定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。

θ:LightDir向量和SpotDir向量之间的角度。θ值应该比Φ值小,这样才会在聚光内。

我们大致要做的是,计算LightDir向量和SpotDir向量的点乘,然后再和切光角ϕ对比,以手电筒为例。

手电筒(Flashlight)是一个坐落在观察者位置的聚光,通常瞄准玩家透视图的前面。

我们需要为片段着色器提供聚光的位置向量(来计算光的方向坐标),聚光的方向向量和切光角:

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

设置适当的值传给着色器:

glUniform3f(lightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
glUniform3f(lightSpotdirLoc, camera.Front.x, camera.Front.y, camera.Front.z);
glUniform1f(lightSpotCutOffLoc, glm::cos(glm::radians(12.5f)));

最后,计算θ值,用它和ϕ值对比,以决定我们是否在或不在聚光的内部:

void main()
{
    vec3 lightDir=normalize(FragPos-light.position);
    float theta = dot(lightDir, normalize(light.direction));
    vec3 diffuseTexColor = vec3(texture(material.diffuse,TexCoords));
    vec3 specularTexColor = vec3(texture(material.specular,TexCoords));
    if(theta > light.cutOff)
    {
        // 执行光照计算
        float distance = length(light.position - FragPos);
        float attenuation = 1.0 / (light.constant + light.linear * distance +
                                   light.quadratic * (distance * distance));
        // ambient
        vec3 ambient = light.ambient * diffuseTexColor;
        // diffuse
        vec3 norm = normalize(Normal);
        vec3 lightDir = normalize(light.position-FragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = diff * light.diffuse * diffuseTexColor;
        // specular
        vec3 viewDir = normalize(light.position - FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        vec3 specular = spec * light.specular * specularTexColor;

//        ambient *= attenuation;
        diffuse *= attenuation;
        specular *= attenuation;

        vec3 result = (ambient + diffuse + specular);
        FragColor = vec4(result, 1.0);
    }
    else // 否则使用环境光,使得场景不至于完全黑暗
        FragColor = vec4(light.ambient * diffuseTexColor, 1.0);

}

效果如下:

demo下载:迟点再传

觉得有帮助的话,打赏一下呗。。

           

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

django显示网页步骤

显示网页步骤 小白的django学习笔记 2024/5/6 8:30 文章目录 显示网页步骤创建输入框(文本、单选、多选)效果如何在django中显示网页写函数配置地址运行,要选择这个工程名的,使用socket复制ip,后面在加上名字,成功&…

Final Draft 12 for Mac:高效专业剧本创作软件

对于剧本创作者来说,一款高效、专业的写作工具是不可或缺的。Final Draft 12 for Mac就是这样一款完美的选择。这款专为Mac用户设计的剧本创作软件,凭借其卓越的性能和丰富的功能,让您的剧本创作更加得心应手。 Final Draft 12支持多种剧本格…

react+antd --- 日期选择器,动态生成日期表格表头

先看一下效果---有当前月的日期 技术: 1: react 2:antd-UI库 -- table 3:moment--时间处理库 代码效果: import { Button, DatePicker, Table } from antd; import { useEffect, useState } from react; import moment from moment;function Club() {const [selecte…

Vue3自定义封装音频播放组件(带拖拽进度条)

Vue3自定义封装音频播放组件(带拖拽进度条) 描述 该款自定义组件可作为音频、视频播放的进度条,用于控制音频、视频的播放进度、暂停开始、拖拽进度条拓展性极高。 实现效果 具体效果可以根据自定义内容进行位置调整 项目需求 有播放暂停…

特征融合篇 | YOLOv8改进之利用ASF-YOLO重构特征融合层 | 助力小目标检测

前言:Hello大家好,我是小哥谈。ASF-YOLO是一个目标检测模型,它基于YOLOv3算法,并引入了ASF(Anchor-Free Spatial Attention)模块。ASF模块可以自适应地学习特征图上每个位置的不同感受野,提高了模型对于小目标的检测能力。相比于YOLOv3,ASF-YOLO在保持准确率的同时大大…

3d里如何做螺旋状模型?---模大狮模型网

螺旋状模型在3D设计中常常被运用,不仅可以用于创造独特的装饰品和艺术品,还可以用于建筑设计、工程模拟等领域。然而,对于初学者而言,如何在3D软件中创建螺旋状模型可能是一个挑战。在本文中,我们将分享几种简单而有效…

Linux diff命令(比较两个文件或目录的内容差异)

文章目录 Linux diff 命令详解教程基本用法比较文件输出解释 递归比较(-r)示例代码 控制输出格式统一格式(-u)上下文格式(-c) 高级选项忽略所有空白差异(-w)仅报告文件是否不同 Linu…

【Web】CTFSHOW 单身杯 题解

目录 web签到 easyPHP 姻缘测试 web签到 用data协议包含php标签闭合 payload: filedata://text/plain,<?php system($_GET[1]);?>>?;)]1[TEG_$(metsys php?<,nialp/txet//:atadeasyPHP 一眼awk命令执行 payload: cmdawk&param{system("ta…

MySQL数据库连接 提示错误 10060

错误现象 Cant connect to server on (10060) 有3个原因&#xff1a; 1、防火墙没有关闭 &#xff08;注意如果配置开放了3306端口&#xff0c;则不可以关闭防火墙&#xff0c;需删除规则以后才能关闭&#xff09; 关闭防火墙&#xff08;1次&#xff09;systemctl stop fir…

数据结构_顺序表中基本操作的实现_代码

学习笔记&#xff0c;仅供参考 1.头文件 2.初始化 3.增加值 4.根据下标取值 5.查找 6.插入 7.删除 8.动态增加数组的长度 9.所有代码 10.运行结果 1.头文件 //顺序表的实现——动态分配 #include<stdio.h> #include<stdlib.h> #define InitSize 10 type…

Arduino-点亮TFT触摸屏一

Arduino-点亮TFT触摸屏一 1.概述 这篇文章主要介绍Arduino操作TFT触摸屏入门操作&#xff0c;通过SPI通信协议方式点亮TFT触摸屏。 2.硬件电路 2.1.硬件列表 名称数量Arduino Uno12.8" TFT彩色液晶触摸屏模块&#xff08;ILI9431&#xff09;110K 电阻5面包板1杜邦线…

ICode国际青少年编程竞赛- Python-3级训练场-if语句入门

ICode国际青少年编程竞赛- Python-3级训练场-if语句入门 1、 for i in range(5):Spaceship.step(i 1)Spaceship.turnRight()if i 1:Dev.step(1)Dev.step(-1)2、 for i in range(4):Dev.step(2)if i ! 1:Dev.turnLeft()Dev.step(3)Dev.step(-3)Dev.turnRight()3、 for i…

springboot3项目练习详细步骤(第三部分:文章管理模块)

目录 发布文章 接口文档 业务实现 自定义参数校验 项目参数要求 实现思路 实现步骤 文章列表(条件分页) 接口文档 业务实现 mapper映射 更新文章 接口文档 业务实现 获取文章详情 接口文档 业务实现 删除文章 接口文档 业务实现 文章管理业务表结构…

OpenHarmony实战开发——WLAN驱动框架介绍及适配方法

1. WLAN 驱动框架概述 WLAN 是基于 HDF(Hardware Driver Foundation)驱动框架开发的模块&#xff0c;该模块可实现跨操作系统迁移、自适应器件差异、模块化拼装编译等功能。从而降低 WLAN 驱动开发的难度&#xff0c;减少 WLAN 驱动移植和开发的工作量。 本文主要分析 WLAN 驱…

gif压缩大小但不改变画质怎么做?分享5个压缩GIF原理~

GIF&#xff08;图形互换格式&#xff09;是网络上广泛使用的一种图像格式&#xff0c;因其支持动画而备受欢迎。然而&#xff0c;随着动画越来越复杂和高分辨率&#xff0c;GIF 文件大小也随之增加&#xff0c;可能导致加载速度变慢和带宽消耗增加。在这篇文章中&#xff0c;我…

easypoi动态表头导出数据

需求&#xff1a;动态导出某年某月用户和用户评分数据信息&#xff0c;表头(序号、姓名、用户姓名)&#xff0c;数据(所有用户对应的评分以及平均分)&#xff1b; 分析&#xff1a;1、表头除过序号、姓名&#xff0c;用户姓名要动态生成&#xff1b; 2、用户评分信息要和表头中…

Nginx+GateWay

目录 Nginx nginx如何配置负载均衡 负载均衡有哪些策略 1、轮询&#xff08;默认&#xff09; 2、指定权重 3、ip_hash&#xff08;客户端ip绑定&#xff09; 4、least_conn&#xff08;最少连接&#xff09; 5、fair 6、url_hash Nginx为什么效率高 gateway 使用gat…

Lobe Chat–在线AI对话聊天机器人,一键部署,免费开源

Lobe Chat 现代化设计的开源 ChatGPT/LLMs 聊天应用与开发框架 支持语音合成、多模态、可扩展的&#xff08;function call&#xff09;插件系统 一键免费拥有你自己的 ChatGPT/Gemini/Claude/Ollama 应用 项目演示 支持多种模型接口 支持语音输入输出 支持云端同步 丰富多彩非…

1013: 哈希表(开放定址法处理冲突)

解法&#xff1a; 线性探测是一种解决哈希冲突的方法&#xff0c;当发生哈希冲突时&#xff0c;它会依次往后查找空的槽位&#xff0c;直到找到一个空的槽位或者达到数组的末尾。 下面是处理哈希冲突的线性探测的步骤&#xff1a; 创建一个哈希表&#xff0c;里面包含一定数量的…

Ps 滤镜:视频

Ps菜单&#xff1a;滤镜/视频 Filter/Video “视频”滤镜子菜单中包含了“NTSC 颜色”和“逐行”两个滤镜。 这两个滤镜都是针对视频和电视播放的特定需求设计的。 “逐行”滤镜主要解决交错视频的视觉问题&#xff0c;而“NTSC 颜色”滤镜则确保色彩在电视播放时的兼容性和准确…
最新文章