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));// ambientvec3 ambient = light.ambient * diffuseTexColor;// diffusevec3 norm = normalize(Normal);vec3 lightDir = normalize(light.position-FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = diff * light.diffuse * diffuseTexColor;// specularvec3 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.hqwc.cn/news/685326.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Excel——项目管理,设置时间到期自动提醒及颜色高亮

效果图 第一步、自动获取合同到期日期 1、首先合同【签约日期】和【到期日期】下面的数据必须是日期格式,不能是其它的格式否则无法计算,如果是其它格式需要转换成标准的日期格式,如下图所示。 2、在“到期日期”下面的第一个单元格中输入公…

【初阶数据结构】顺序表OJ题讲解

前言 📚作者简介:爱编程的小马,正在学习C/C,Linux及MySQL。 📚本文收录与初阶数据结构系列,本专栏主要是针对时间、空间复杂度,顺序表和链表、栈和队列、二叉树以及各类排序算法,持…

传感器—超声波雷达

声波技术 在讲述超声波雷达之前,先了解一下声波的概念以及超声波和声波之间的关系 什么是声波? 声波是物体机械振动状态(或能量)的传播形式。所谓振动是指物质的质点在其平衡位置附近进行的往返运动形式,这种振动状…

新手小白入门股指期货必懂的知识点

股指期货,正式名称为股票价格指数期货,也被称作股价指数期货或期指,它是一种以股票指数为基础资产的标准化期货合约。 股指期货就像是一场关于股票指数的赌局。比如你和朋友打赌,比如你们赌的是“上证50指数”(就是上海…

Xinstall助力App地推监测,实现精准效果评估

在移动互联网时代,App的推广已经成为企业营销的重要手段。然而,如何有效地监测App地推效果,一直是广告主和开发者面临的难题。幸运的是,Xinstall作为国内专业的App全渠道统计服务商,为广告主和开发者提供了一站式的解决…

26 | 备库为什么会延迟好几个小时?

在官方的 5.6 版本之前,MySQL 只支持单线程复制,由此在主库并发高、TPS 高时就会出现严重的主备延迟问题。 coordinator 就是原来的 sql_thread, 不过现在它不再直接更新数据了,只负责读取中转日志和分发事务。真正更新日志的,变成了 worker 线程。而 work 线程的个数,就是…

企业如何做好数据安全治理?

在数字化时代,数据成为企业运营的核心资产,数据安全治理成为企业管理的重要组成部分。良好的数据安全治理不仅能保护企业信息不受侵犯,还能有效提升企业的运营效率和市场竞争力。下面是企业如何做好数据安全治理的几个关键步骤: 1…

【dnSpy】通过IL指令添加try catch语句

原始代码 我们需要将整体代码添加try catch语句 步骤 添加代码的保护区域,也就是{ } 获取Exception.Message方法 添加callvirt指令 添加提示框 添加call指令 最后让消息框显示 编译完点击确定即可 添加后的IL指令代码详解 如果看不清楚联系作者 参考文档 ht…

锚索测力计在岩土工程中的应用

随着现代工程建设的快速发展,岩土工程安全问题日益受到人们的关注。岩土工程中的锚索结构,作为保证工程稳定和安全的关键部分,其性能监测和评估显得尤为重要。近年来,锚索测力计作为一种先进的监测工具,在岩土工程安全…

【全开源】Java共享台信息共享系统源码

特色功能 信息整合与共享:该平台提供一站式信息整合服务,将各种类型的信息资源进行汇聚,方便用户快速查找和获取所需资源。多种共享功能:支持信息共享、共享车位、共享会议室、共享电动车等多种共享功能,提高资源利用…

Photoshop中选区工具的应用

Photoshop中选区工具的应用 前言Photoshop中选区工具的基本操作创建选区的工具及方法选择、取消、隐藏选区选区的增加、减少选区的应用变换扩大选取与选取相似 Photoshop中采用快速选择工具来创建选区Photoshop中采用色彩范围命令来创建选区Photoshop中采用快速蒙版来创建选区P…

wandb: - 0.000 MB of 0.011 MB uploaded持续出现的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…