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.mzph.cn/news/834948.shtml

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

相关文章

流媒体服务器(20)—— mediasoup 之媒体流score评分计算(一)

目录 前言 正文 《流媒体服务器》专栏总览丨蓄力计划_开源流媒体服务器对比-CSDN博客 前言 mediasoup 有一套评估媒体传输通道优劣的机制,主要是通过 score 评分来判断的。今天就先介绍一下这个机制的大体逻辑,后面的文章再详细介绍具体计算的算法。 正文 mediasoup 的…

开源AlphaFold3来啦!快来亲自尝试预测蛋白质结构!

引言 随着AlphaFold2的显著成就,DeepMind的AlphaFold3引发了科学界的广泛关注。尽管官方尚未开源AlphaFold3的代码,一些社区开发者已开始基于现有的科学论文尝试复现。本文将介绍如何使用一个名为AlphaFold3复现项目的GitHub代码仓库来进行蛋白质结构预…

CTF—AWD防御起手式

前言 AWD (Attack With Defence),比赛中每个队伍维护多台服务器,服务器中存在多个漏洞,利用漏洞攻击其他队伍可以进行得分,修复漏洞可以避免被其他队伍攻击失分。 改SSH密码 官方在给出服务器密码时,很有可能是默认…

如何确保个人域名的安全性?

确保个人域名的安全性是网络身份和在线资产保护的重要方面。以下是一些关键措施和最佳实践,可以帮助您提高个人域名的安全性: 选择可靠的域名注册商:选择一个有良好口碑和信誉的域名注册商至关重要。确保注册商提供强大的安全功能&#xff0…

.NET WebService \ WCF \ WebAPI 部署总结 以及 window 服务 调试

一、webservice 部署只能部署IIS上, 比较简单,就不做说明了 二、 WCF 部署 1 部署到IIS 跟部署 webservice 部署方法一样的 wcf 部署2 部署到控制台 要以管理员运行vs,或者 管理员运行 控制台的exe 在控制器项目中 创建IUserInfoService 接口…

资源管理游戏模版进入The Sandbox

我们非常高兴地向您介绍 Game Maker 的最新模板:资源管理游戏! 这一全新的模板让您能够深入身临其境的游戏体验中,同时掌握令人兴奋的新机制。通过揭开模板的神秘面纱,您可以锤炼您的游戏设计技能。 什么是资源管理游戏&#xff1…

Hive Aggregation 聚合函数

Hive Aggregation 聚合函数 基础聚合 增强聚合

必备软件管理工具——Applite!!【送源码】

Mac 用户都知道,我们可以通过一个非常好用的一个工具 Homebrew 快速的使用命令下载海量的工具和软件。然而对于非技术人员来说,命令行的交互还是不太方便,如果有界面可以查看从 Homebrew 安装的软件,或者浏览 Homebrew 软件库就好…

抖音直播间怎么涨流量?掌握巨量千川投放技巧拓客事半功倍

抖音直播间作为一种新兴的社交娱乐形式,吸引了越来越多的用户参与和关注。然而,如何在抖音直播间中获得更多的流量和粉丝成为了很多主播面临的挑战。幸运的是,通过掌握巨量千川的投放技巧,拓客事半功倍是可能的。 首先&#xff0c…

Github新手入门使用方法

**存在问题:**新手如何快速入门github,能够下载开源文件,并且修改后更新远程github仓库; 解决方案: 参考: http://www.360doc.com/content/24/0301/12/60419_1115656653.shtml https://blog.csdn.net/gongd…

很多人做的 Google Play 马甲包有啥用

大家或多或少的听说过马甲包,尤其在 Google Play 市场,那么大家为什么上传一堆差不多功能的包到市场呢? 高级一些来说:为了覆盖更多的关键词。 大部分伙伴应该知道,一个应用的核心关键词就是它的标题,标题…

Kafka分级存储概念(一)

Kafka分级存储及实现原理 概述 Kafka社区在3.6版本引入了一个十分重要的特性: 分级存储,本系列文章主要旨在介绍Kafka分级存储的设计理念、设计细节以及具体的代码实现 背景:为什么要有分级存储? 场景 作为一款具有高吞吐及高性能的消息中间件,Kafka被广泛应用在大数据、…

pytorch中统计一个数在tensor中出现了几次

pytorch中统计一个数在tensor中出现了几次 在PyTorch中,可以使用torch.eq()函数配合torch.sum()来统计某个数值在Tensor中出现的次数。torch.eq()函数会返回一个新的Tensor,其中对于每个元素来说,如果和指定的数值相等,则该位置为…

本安防爆手机在电力行业中的应用

在电力行业这一充满挑战与风险的领域中,安全始终是最为首要的考量。电力巡检、维修等作业往往涉及易燃、易爆环境,这就要求工作人员配备能够在极端条件下保障通讯和作业安全的专业设备。防爆手机应运而生,以其独特的设计和卓越的性能&#xf…

百度AMIS前端低代码框架本地部署运行记录

1、下载使用 1.1 前置环境: 软件: 1、visual studio code 官网下载特别慢,本次使用的下载地址是:http://www.downcc.com/soft/230273.html 2、nodejs 下载教程参考:https://www.cnblogs.com/18sui/p/17162016.html …

PyTorch生成随机张量的函数

1、torch.rand() 该函数用于生成一个张量,其中的元素是在区间[0, 1)内均匀分布的随机数 import torchrandom_tensor torch.rand(2, 3) random_like_input torch.rand_like(random_tensor) 2、torch.randn() 该函数生成一个张量,其中的元素是从标准…

js 在一个字符串中查找 从某个字符开始到另一个字符的子字符串

1、在这个例子中,findSubstringBetween函数接收一个字符串str,以及要查找的起始字符startChar和终止字符endChar; 2、函数首先找到startChar的位置,并从该位置加上startChar的长度获取开始子字符串的起始索引; 3、然后从这个起始索引开始&…

Go 多模块工作区处理一个go项目下有多个module(即多个go.mod)的情况

背景 在现在微服务盛行的年代,一个人会维护多个代码仓库,很多的时候是多个仓库进行同时开发,也就是在当前项目下有多个目录,每个目录对应一个微服务,每个微服务都有一个go.mod文件。那么我在其中一个目录下要怎么导入…

L6201PSTR DMOS全桥驱动器

L6201PSTR是采用Multipower-BCD技术实现的电机控制应用的全桥式驱动器,该技术将隔离的DMOS功率晶体管与CMOS和双极电路结合在同一芯片上。通过使用混合技术,可以优化逻辑电路和功率级,以达到最佳性能。DMOS输出晶体管可以在高达42V的电源电压…

如何使用jmeter进行接口测试?jmeter接口测试流程是怎样的

前言 我们学习自动化测试都会用到不同的工具,那么今天笔者呢,想给大家聊聊Jmeter接口测试流程详解,废话不多说直接进入正题。 一、jmeter简介 Jmeter是由Apache公司开发的java开源项目,所以想要使用它必须基于java环境才可以&am…