当前位置: 首页 > news >正文

OpenGL学习笔记(十二):初级光照:投光物/多光源(平行光、点光源、聚光)

文章目录

  • 平行光
  • 点光源
  • 聚光
  • 多光源


现实世界中,我们有很多种类的光照,每种的表现都不同。将光投射(Cast)到物体的光源叫做投光物(Light Caster)。

  • 平行光/定向光(Directional Light)
  • 点光源(Point Light)
  • 聚光(Spotlight)

平行光

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

定向光非常好的一个例子就是太阳。太阳距离我们并不是无限远,但它已经远到在光照计算中可以把它视为无限远了。

因为对场景中每一个物体光的方向都是一致的。我们可以定义一个光线 方向向量 而不是 位置向量 来模拟一个定向光。着色器的计算基本保持不变,但这次我们将直接使用光的direction向量而不是通过position来计算lightDir向量。

struct Light {// vec3 position; // 使用定向光就不再需要了vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;
};
...
void main()
{vec3 lightDir = normalize(-light.direction);...
}

首先对light.direction向量取反。我们目前使用的光照计算需求一个从片段至光源的光线方向,但人们更习惯定义定向光为一个从光源出发的全局方向。所以我们需要对全局光照方向向量取反来改变它的方向,它现在是一个指向光源的方向向量了。而且,记得对向量进行标准化,假设输入向量为一个单位向量是很不明智的。

最终的lightDir向量将和以前一样用在漫反射和镜面光计算中:

	// diffuse vec3 norm = normalize(Normal);//vec3 lightDir = normalize(light.position - FragPos);vec3 lightDir = normalize(-light.direction);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  // specularvec3 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; 

同时,不要忘记定义光源的方向(注意我们将方向定义为从光源出发的方向,你可以很容易看到光的方向朝下)。

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

为了清楚地展示定向光对多个物体具有相同的影响,我们将会再次定义了十个不同的箱子位置,并对每个箱子都生成了一个不同的模型矩阵,每个模型矩阵都包含了对应的局部-世界坐标变换

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);
}
  • 我们一直将光的位置和位置向量定义为vec3,但一些人会喜欢将所有的向量都定义为vec4
  • 当我们将位置向量定义为一个vec4时,很重要的一点是要将w分量设置为1.0,这样 变换和投影 才能正确应用。
  • 然而,当我们定义一个方向向量为vec4的时候,我们不想让位移有任何的效果(因为它仅仅代表的是方向),所以我们将w分量设置为0.0
  • 这也可以作为一个快速检测光照类型的工具:你可以检测w分量是否等于1.0,来检测它是否是光的位置向量;w分量等于0.0,则它是光的方向向量,这样就能根据这个来调整光照计算了。
  • 这正是旧OpenGL决定光源是定向光还是位置光源(Positional Light Source)的方法,并根据它来调整光照。

完整代码参考

在这里插入图片描述

点光源

定向光对于照亮整个场景的全局光源是非常棒的,但除了定向光之外我们也需要一些分散在场景中的点光源(Point Light)。点光源是处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰减

想象作为投光物的灯泡和火把,它们都是点光源。

之前我们一直都在使用一个小的立方体当作简易的点光源。但我们定义的光源模拟的是永远不会衰减的光线,这看起来像是光源亮度非常的强。在大部分的3D模拟中,我们都希望模拟的光源仅照亮光源附近的区域而不是整个场景。

随着光线传播距离的增长逐渐削减光的强度通常叫做衰减(Attenuation)。随距离减少光强度的一种方式是使用一个线性方程。接下来为了计算衰减值,我们定义3个项:常数项Kc、一次项Kl和二次项Kq

在这里插入图片描述

  • 常数项通常保持为1.0,它的主要作用是保证分母永远不会比1小,否则的话在某些距离上它反而会增加强度,这肯定不是我们想要的效果。
  • 一次项会与距离值相乘,以线性的方式减少强度。
  • 二次项会与距离的平方相乘,让光源以二次递减的方式减少强度。二次项在距离比较小的时候影响会比一次项小很多,但当距离值比较大的时候它就会比一次项更大了。

更新 片段着色器 中的Light结构体:

struct Light {vec3 position;  vec3 ambient;vec3 diffuse;vec3 specular;float constant;float linear;float quadratic;
};

我们希望光源能够覆盖50的距离(覆盖距离和系数关系可以由Ogre3D的Wiki查到),然后我们将在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;

点光源就是一个能够配置位置和衰减的光源。它是我们光照工具箱中的又一个光照类型。完整代码参考

在这里插入图片描述

聚光

聚光(Spotlight)是位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线。这样的结果就是只有在聚光方向的特定半径内的物体才会被照亮,其它的物体都会保持黑暗。

聚光很好的例子就是路灯或手电筒。

OpenGL中聚光是用一个世界空间位置一个方向一个切光角(Cutoff Angle) 来表示的,切光角指定了聚光的半径。对于每个片段,我们会计算片段是否位于聚光的切光方向之间(也就是在锥形内),如果是的话,我们就会相应地照亮片段。

在这里插入图片描述

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

所以我们要做的就是计算LightDir向量和SpotDir向量之间的点积(它会返回两个单位向量夹角的余弦值),并将它与切光角ϕ值对比。你现在应该了解聚光究竟是什么了,下面我们将以手电筒的形式创建一个聚光。


手电筒(Flashlight) 是一个位于观察者位置的聚光,通常它都会瞄准玩家视角的正前方。基本上说,手电筒就是普通的聚光,但它的位置和方向会随着玩家的位置和朝向不断更新。

更新片段着色器,我们需要的值有聚光的位置向量(来计算光的方向向量)聚光的方向向量和一个切光角。将它们储存在Light结构体中:

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

将合适的值传到着色器中:

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

我们并没有给切光角设置一个角度值,反而是用角度值计算了一个余弦值,将余弦结果传递到片段着色器中。
这样做的原因是在片段着色器中,我们会计算LightDirSpotDir向量的点积,这个点积返回的将是一个余弦值而不是角度值,所以我们不能直接使用角度值和余弦值进行比较。为了获取角度值我们需要计算点积结果的反余弦,这是一个开销很大的计算。所以为了节约一点性能开销,我们将会计算切光角对应的余弦值,并将它的结果传入片段着色器中。由于这两个角度现在都由余弦角来表示了,我们可以直接对它们进行比较而不用进行任何开销高昂的计算。

接下来就是计算θ值,并将它和切光角ϕ对比,来决定是否在聚光的内部:

float theta = dot(lightDir, normalize(-light.direction));if(theta > light.cutOff) 
{       // 执行光照计算
}
else  // 否则,使用环境光,让场景在聚光之外时不至于完全黑暗color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0);

我们首先计算了lightDir和取反的direction向量(取反的是因为让向量指向光源而不是从光源出发)之间的点积。对所有的相关向量标准化。

因为比较的是余弦cos值,θ比切光角的小,θ的余弦比切光角的余弦大。

完整代码参考

在这里插入图片描述

与真实的手电筒看起来还是有区别。真实情况下边缘应该是逐渐变暗的,而不是界限分明的。

为了创建一种看起来边缘平滑的聚光,我们需要模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)。我们可以将内圆锥设置为上一部分中的那个圆锥,但我们也需要一个外圆锥,来让光从内圆锥逐渐减暗,直到外圆锥的边界。

实现一个在聚光外是负的,在内圆锥内大于1.0的,在边缘处于两者之间的强度值。如果我们正确地约束(Clamp)这个值,在片段着色器中就不再需要if-else了,我们能够使用计算出来的强度值直接乘以光照分量:

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

使用了clamp函数把第一个参数约束(Clamp)在了0.0到1.0之间。这保证强度值不会在[0, 1]区间之外。

使用的内切光角是12.5,外切光角是17.5效果:完整代码参考

在这里插入图片描述

多光源

结合之前学过的所有知识,我们可以将多种光源结合起来布置在同一场景中。

为了在场景中使用多个光源,我们希望将光照计算封装到GLSL函数中。这样做的原因是,每一种光源都需要一种不同的计算方法,而一旦我们想对多个光源进行光照计算时,代码很快就会变得非常复杂。如果我们只在main函数中进行所有的这些计算,代码很快就会变得难以理解。

GLSL中的函数和C函数很相似,它有一个函数名、一个返回值类型,如果函数不是在main函数之前声明的,我们还必须在代码文件顶部声明一个原型。我们对每个光照类型都创建一个不同的函数:定向光、点光源和聚光。

当我们在场景中使用多个光源时,通常使用以下方法:我们需要有一个单独的颜色向量代表片段的输出颜色。对于每一个光源,它对片段的贡献颜色将会加到片段的输出颜色向量上。所以场景中的每个光源都会计算它们各自对片段的影响,并结合为一个最终的输出颜色。大体的结构会像是这样:

out vec4 FragColor;void main()
{// 定义一个输出颜色值vec3 output;// 将定向光的贡献加到输出中output += someFunctionToCalculateDirectionalLight();// 对所有的点光源也做相同的事情for(int i = 0; i < nr_of_point_lights; i++)output += someFunctionToCalculatePointLight();// 也加上其它的光源(比如聚光)output += someFunctionToCalculateSpotLight();FragColor = vec4(output, 1.0);
}

封装后的片段着色器:(完整源码参考)

#version 330 core
out vec4 FragColor;struct Material {sampler2D diffuse;sampler2D specular;float shininess;
}; struct DirLight {vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;
};struct PointLight {vec3 position;float constant;float linear;float quadratic;vec3 ambient;vec3 diffuse;vec3 specular;
};struct SpotLight {vec3 position;vec3 direction;float cutOff;float outerCutOff;float constant;float linear;float quadratic;vec3 ambient;vec3 diffuse;vec3 specular;       
};#define NR_POINT_LIGHTS 4in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;uniform vec3 viewPos;
uniform DirLight dirLight;
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLight;
uniform Material material;// function prototypes
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);void main()
{    // propertiesvec3 norm = normalize(Normal);vec3 viewDir = normalize(viewPos - FragPos);// == =====================================================// Our lighting is set up in 3 phases: directional, point lights and an optional flashlight// For each phase, a calculate function is defined that calculates the corresponding color// per lamp. In the main() function we take all the calculated colors and sum them up for// this fragment's final color.// == =====================================================// phase 1: directional lightingvec3 result = CalcDirLight(dirLight, norm, viewDir);// phase 2: point lightsfor(int i = 0; i < NR_POINT_LIGHTS; i++)result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);    // phase 3: spot lightresult += CalcSpotLight(spotLight, norm, FragPos, viewDir);    FragColor = vec4(result, 1.0);
}// calculates the color when using a directional light.
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{vec3 lightDir = normalize(-light.direction);// diffuse shadingfloat diff = max(dot(normal, lightDir), 0.0);// specular shadingvec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// combine resultsvec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));return (ambient + diffuse + specular);
}// calculates the color when using a point light.
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);// diffuse shadingfloat diff = max(dot(normal, lightDir), 0.0);// specular shadingvec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// attenuationfloat distance = length(light.position - fragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    // combine resultsvec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));ambient *= attenuation;diffuse *= attenuation;specular *= attenuation;return (ambient + diffuse + specular);
}// calculates the color when using a spot light.
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);// diffuse shadingfloat diff = max(dot(normal, lightDir), 0.0);// specular shadingvec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// attenuationfloat distance = length(light.position - fragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    // spotlight intensityfloat theta = dot(lightDir, normalize(-light.direction)); float epsilon = light.cutOff - light.outerCutOff;float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);// combine resultsvec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));ambient *= attenuation * intensity;diffuse *= attenuation * intensity;specular *= attenuation * intensity;return (ambient + diffuse + specular);
}

相关文章:

OpenGL学习笔记(十二):初级光照:投光物/多光源(平行光、点光源、聚光)

文章目录 平行光点光源聚光多光源 现实世界中&#xff0c;我们有很多种类的光照&#xff0c;每种的表现都不同。将光投射(Cast)到物体的光源叫做投光物(Light Caster)。 平行光/定向光(Directional Light)点光源(Point Light)聚光(Spotlight) 平行光 当一个光源处于很远的地…...

redis底层数据结构——整数集合

文章目录 定义内部实现升级升级的好处提升灵活性节约内存 降级总结 定义 整数集合&#xff08;intset&#xff09;是集合键的底层实现之一&#xff0c;当一个集合只包含整数值元素&#xff0c;并且这个集合的元素数量不多时&#xff0c;Redis就会使用整数集合作为集合键的底层…...

w198基于Springboot的智能家居系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…...

【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter2-HTML 中的 JavaScript

二、HTML 中的 JavaScript 将 JavaScript 插入 HTML 的主要方法是使用<script>元素。 <script>元素有下列 8 个属性。 async&#xff1a;可选。表示应该立即开始下载脚本&#xff0c;但不能阻止其他页面动作&#xff0c;比如下载资源或等待其他脚本加载。只对外部…...

【5】阿里面试题整理

[1]. 介绍一下ZooKeeper ZooKeeper是一个开源的分布式协调服务&#xff0c;核心功能是通过树形数据模型&#xff08;ZNode&#xff09;和Watch机制&#xff0c;解决分布式系统的一致性问题。 它使用ZAB协议保障数据一致性&#xff0c;典型场景包括分布式锁、配置管理和服务注…...

系统思考—自我超越

“人们往往认为是个人的能力限制了他们&#xff0c;但事实上&#xff0c;是组织的结构和惯性思维限制了他们的潜力。”—彼得圣吉 最近和一家行业隐形冠军交流&#xff0c;他们已经是领域第一&#xff0c;老板却依然要求&#xff1a;核心团队都要自我超越&#xff0c;攻坚克难…...

大模型-ALIGN 详细介绍

ALIGN模型&#xff08;A Large-scale ImaGe and Noisy-text embedding&#xff09;是一种大规模图像和噪声文本嵌入模型&#xff0c;它通过对比学习的方式将图像和文本嵌入到同一个向量空间中&#xff0c;使得匹配的图像-文本对的嵌入向量接近&#xff0c;不匹配的则远离。这种…...

【C++高并发服务器WebServer】-15:poll、epoll详解及实现

本文目录 一、poll二、epoll2.1 相对poll和select的优点2.2 epoll的api2.3 epoll的demo实现2.5 epoll的工作模式 一、poll poll是对select的一个改进&#xff0c;我们先来看看select的缺点。 我们来看看poll的实现。 struct pollfd {int fd; /* 委托内核检测的文件描述符 */s…...

【算法】动态规划专题⑩ —— 混合背包问题 python

目录 前置知识进入正题总结 前置知识 【算法】动态规划专题⑤ —— 0-1背包问题 滚动数组优化 【算法】动态规划专题⑥ —— 完全背包问题 python 【算法】动态规划专题⑦ —— 多重背包问题 二进制分解优化 python 混合背包结合了三种不同类型的背包问题&#xff1a;0/1背包…...

Java高频面试之SE-20

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天又来了&#xff01;哈哈哈哈哈嗝&#x1f436; Java的泛型是什么&#xff1f; Java 泛型&#xff08;Generics&#xff09; 是 Java 5 引入的一项重要特性&#xff0c;用于增强代码的类型安…...

springboot 事务管理

在Spring Boot中&#xff0c;事务管理是通过Spring框架的事务管理模块来实现的。Spring提供了声明式事务管理和编程式事务管理两种方式。通常&#xff0c;我们使用声明式事务管理&#xff0c;因为它更简洁且易于维护。 1. 声明式事务管理 声明式事务管理是通过注解来实现的。…...

opentelemetry-collector 配置elasticsearch

一、修改otelcol-config.yaml receivers:otlp:protocols:grpc:endpoint: 0.0.0.0:4317http:endpoint: 0.0.0.0:4318 exporters:debug:verbosity: detailedotlp/jaeger: # Jaeger supports OTLP directlyendpoint: 192.168.31.161:4317tls:insecure: trueotlphttp/prometheus: …...

IDEA关联Tomcat,部署JavaWeb项目

将IDEA与Tomcat关联 创建JavaWeb项目 创建Demo项目 将Tomcat作为依赖引入到Demo中 添加 Web Application 编写前端和后端代码 配置Tomcat server&#xff0c;并运行...

位图与位运算的深度联系:从图像处理到高效数据结构的C++实现与优化

在学习优选算法课程的时候&#xff0c;博主学习位运算了解到位运算的这个概念&#xff0c;之前没有接触过&#xff0c;就查找了相关的资料&#xff0c;丰富一下自身&#xff0c;当作课外知识来了解一下。 位图&#xff08;Bitmap&#xff09;&#xff1a; 在计算机科学中有两种…...

运维_Mac环境单体服务Docker部署实战手册

Docker部署 本小节&#xff0c;讲解如何将前端 后端项目&#xff0c;使用 Docker 容器&#xff0c;部署到 dev 开发环境下的一台 Mac 电脑上。 1 环境准备 需要安装如下环境&#xff1a; Docker&#xff1a;容器MySQL&#xff1a;数据库Redis&#xff1a;缓存Nginx&#x…...

DeepSeek-V3 论文解读:大语言模型领域的创新先锋与性能强者

论文链接&#xff1a;DeepSeek-V3 Technical Report 目录 一、引言二、模型架构&#xff1a;创新驱动性能提升&#xff08;一&#xff09;基本架构&#xff08;Basic Architecture&#xff09;&#xff08;二&#xff09;多令牌预测&#xff08;Multi-Token Prediction&#xf…...

react使用if判断

1、第一种 function Dade(req:any){console.log(req)if(req.data.id 1){return <span>66666</span>}return <span style{{color:"red"}}>8888</span>}2、使用 {win.map((req,index) > ( <> <Dade data{req}/>{req.id 1 ?…...

opencv:基于暗通道先验(DCP)的内窥镜图像去雾

目录 项目大体情况 暗通道先验&#xff08;Dark Channel Prior, DCP&#xff09;原理 项目代码解析 该项目是由我和我导师与舟山某医院合作开发的一个基于暗通道先验&#xff08;Dark Channel Prior&#xff0c;DCP&#xff09;的内窥镜图像去雾方法。具体来说&#xff0c;…...

2025年物联网相关专业毕业论文选题参考,文末联系,选题相关资料提供

一、智能穿戴解决方案研究方向 序号解决方案论文选题论文研究方向1智能腰带健康监测基于SpringBoot和Vue的智能腰带健康监测数据可视化平台开发研究如何利用SpringBoot和Vue技术栈开发一个数据可视化平台&#xff0c;用于展示智能腰带健康监测采集的数据&#xff0c;如心率、血…...

npm无法加载文件 因为此系统禁止运行脚本

安装nodejs后遇到问题&#xff1a; 在项目里【node -v】可以打印出来&#xff0c;【npm -v】打印不出来&#xff0c;显示npm无法加载文件 因为此系统禁止运行脚本。 但是在winr&#xff0c;cmd里【node -v】,【npm -v】都也可打印出来。 解决方法&#xff1a; cmd里可以打印出…...

使用PyCharm创建项目以及如何注释代码

创建好项目后会出现如下图所示的画面&#xff0c;我们可以通过在项目文件夹上点击鼠标右键&#xff0c;选择“New”菜单下的“Python File”来创建一个 Python 文件&#xff0c;在给文件命名时建议使用英文字母和下划线的组合&#xff0c;创建好的 Python 文件会自动打开&#…...

Qt中QFile文件读写操作和QFileInfo文件信息读取方法(详细图文教程)

&#x1f4aa; 图像算法工程师&#xff0c;专业从事且热爱图像处理&#xff0c;图像处理专栏更新如下&#x1f447;&#xff1a; &#x1f4dd;《图像去噪》 &#x1f4dd;《超分辨率重建》 &#x1f4dd;《语义分割》 &#x1f4dd;《风格迁移》 &#x1f4dd;《目标检测》 &a…...

CF998A Balloons​ 构造 ​

Balloons 算法&#xff1a;构造 rating : 1000 思路&#xff1a; 分情况讨论&#xff1a; 1. 当只有一个气球包时&#xff0c;肯定不行 2.当有两个气球包时&#xff0c;若两个气球包的气球个数相同则不行 3.其余的情况都是可以的&#xff0c;题目问给格里高利的气球包数…...

python基础入门:3.5实战:词频统计工具

Python词频统计终极指南&#xff1a;字典与排序的完美结合 import re from collections import defaultdictdef word_frequency_analysis(file_path, top_n10):"""完整的词频统计解决方案:param file_path: 文本文件路径:param top_n: 显示前N个高频词:return:…...

面试准备——Java理论高级【笔试,面试的核心重点】

集合框架 Java集合框架是面试中的重中之重&#xff0c;尤其是对List、Set、Map的实现类及其底层原理的考察。 1. List ArrayList&#xff1a; 底层是动态数组&#xff0c;支持随机访问&#xff08;通过索引&#xff09;&#xff0c;时间复杂度为O(1)。插入和删除元素时&#…...

Docker 部署 verdaccio 搭建 npm 私服

一、镜像获取 # 获取 verdaccio 镜像 docker pull verdaccio/verdaccio 二、修改配置文件 cd /wwwroot/opt/docker/verdaccio/conf vim config.yaml config.yaml 配置文件如下&#xff0c;可以根据自己的需要进行修改 # # This is the default configuration file. It all…...

每日一题--数组中只出现一次的两个数字

数组中只出现一次的两个数字 题目描述数据范围提示 示例示例1示例2 题解解题思路位运算方法步骤&#xff1a; 代码实现代码解析时间与空间复杂度按位与操作获取最小位1的原理为什么选择最低有效的 1 位而不是其他位&#xff1f; 题目描述 一个整型数组里除了两个数字只出现一次…...

蓝耘智算平台与DeepSeek R1模型:推动深度学习发展

公主请阅 前言何为DeepSeek R1DeepSeek R1 的特点DeepSeek R1 的应用领域DeepSeek R1 与其他模型的对比 何为蓝耘智算平台使用蓝耘智算平台深度使用DeepSeek R1代码解释&#xff1a;处理示例输入&#xff1a;输出结果&#xff1a; 前言 在深度学习领域&#xff0c;创新迭代日新…...

数据中台是什么?:架构演进、业务整合、方向演进

文章目录 1. 引言2. 数据中台的概念与沿革2.1 概念定义2.2 历史沿革 3. 数据中台的架构组成与关键技术要素解析3.1 架构组成3.2 关键技术要素 4. 数据中台与其他平台的对比详细解析 5. 综合案例&#xff1a;金融行业数据中台落地实践5.1 背景5.2 解决方案5.3 成果与价值 6. 方向…...

告别2023~2024

时间过得真快&#xff0c;距离上次写作2年多了。2023年&#xff5e;2024年的这两年时光里经历太多人生大事&#xff1a; 房贷&#xff0c;提前还贷买车&#xff0c;全款拿下租房搬家媳妇怀孕&#xff0c;独自照顾&#xff0c;……老人离世开盲盒喜提千金&#xff0c;百岁宴&am…...

PMO项目管理规范标准

这份文档是一份关于 PMO 项目管理规范标准的 V3.0 版本。以下是该文档的主要内容&#xff1a; 1. 立项管理 - 立项标准、级别划分和管理&#xff1a;定义了立项管理的标准、级别划分和管理&#xff0c;包括立项的审批流程、产品可行性分析、立项建议书、产品需求文档等。 - 立项…...

通过类加载和初始化的一些题目理解Java类加载过程

通过题目重点理解&#xff1a;Class加载流程和运行时区域 目录 子类和父类static变量父子类加载顺序2class.forName初始化 子类和父类static变量 class Parent {static int a 1;static int b 2;static int c;static {c 3;System.out.println("parent static block&quo…...

【人工智能】解码语言之谜:使用Python构建神经机器翻译系统

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 神经机器翻译(NMT)是近年来机器翻译领域的一项重大突破。它利用深度学习模型,特别是循环神经网络(RNN)和Transformer网络,以端到端的…...

瑞芯微 Rockchip 系列 RK3588 主流深度学习框架模型转成 rknn 模型教程

前言 在瑞芯微 Rockchip 芯片上进行 NPU 推理&#xff0c;需要先将模型文件转换成 rknn 模型文件&#xff0c;才能执行各种推理任务。本文将介绍如何安装各种工具&#xff0c;并最终实现将各种深度学习框架的模型文件转换成 rknn 文件。 本教程不仅适合 RK3588 平台&#xff…...

51单片机俄罗斯方块计分函数

/************************************************************************************************************** * 名称&#xff1a;scoring * 功能&#xff1a;计分 * 参数&#xff1a;NULL * 返回&#xff1a;NULL * 备注&#xff1a;采用非阻塞延时 ****************…...

C++线程池

使用线程情况比较频繁的地方&#xff0c;由于线程的创建及销毁都会产生对资源的占用及性能的损耗。为了优化性能&#xff0c;提升效率&#xff0c;在这种场景中&#xff0c;就应该使用线程池来处理任务。 线程池创建的关键点&#xff1a; 装载线程的容器&#xff0c;在C中使用…...

Golang GORM系列:定义GORM模型及关系指南

使用GORM进行数据库管理的核心是定义模型的技能。模型是程序的面向对象结构和数据库的关系世界之间的纽带。本文深入研究了在GORM中创建成功模型的艺术&#xff0c;研究了如何设计结构化的Go结构&#xff0c;用标记注释字段&#xff0c;以及开发跨模型的链接&#xff0c;以便最…...

ssm校园二手交易平台小程序

博主介绍&#xff1a;✌程序猿徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…...

【虚幻引擎UE】AOI算法介绍与实现案例

【虚幻引擎UE】AOI算法介绍与实现 一、AOI算法介绍AOI算法的典型应用场景二、AOI相关算法1. 边界框法(Bounding Box Method)2. 动态AOI算法3. 布尔运算(Boolean Operations)4. 四叉树(Quadtree)5. R树(R-Tree)6. 圆形AOI算法7. 网格分割(Grid Partitioning)8. 多边形…...

JavaScript 基础语法:变量、数据类型、运算符、条件语句、循环

JavaScript 是一种动态类型的脚本语言&#xff0c;广泛用于前端开发。以下是 JavaScript 基础语法的核心内容&#xff0c;包括变量、数据类型、运算符、条件语句和循环。 --- ### 1. 变量 变量用于存储数据。JavaScript 中有三种声明变量的方式&#xff1a; - **var**&…...

ASP.NET Core 如何使用 C# 从端点发出 GET 请求

使用 C#&#xff0c;从 REST API 端点获取 JSON&#xff1b;如何从 REST API 接收 JSON 数据。 本文需要 ASP .NET Core&#xff0c;并兼容 .NET Core 3.1、.NET 6和.NET 8。 要将数据发布到端点&#xff0c;请参阅本文。 使用 . 从端点发布 GET 数据非常容易HttpClient&…...

Docker 部署 MinIO | 国内阿里镜像

一、导读 Minio 是个基于 Golang 编写的开源对象存储套件&#xff0c;基于Apache License v2.0开源协议&#xff0c;虽然轻量&#xff0c;却拥有着不错的性能。它兼容亚马逊S3云存储服务接口。可以很简单的和其他应用结合使用&#xff0c;例如 NodeJS、Redis、MySQL等。 二、…...

量化交易数据获取:xtquant库的高效应用

量化交易数据获取&#xff1a;xtquant库的高效应用 在量化交易领域&#xff0c;历史行情数据的重要性不言而喻。它不仅为策略回测提供基础&#xff0c;也是实时交易决策的重要参考。本文将介绍如何使用xtquant库来高效获取和处理历史行情数据。 技术背景与应用场景 对于量化…...

Mysql中存储引擎各种介绍以及应用场景、优缺点

概述 MySQL 提供了多种存储引擎&#xff0c;每种引擎有不同的特点和适用场景。以下是几种常见的 MySQL 存储引擎的详细介绍&#xff0c;包括它们的底层工作原理、优缺点&#xff0c;以及为什么 MySQL 默认选择某种引擎。 1. InnoDB 底层工作原理&#xff1a; 事务支持&#…...

面试题整理:Java虚拟机 JVM 内存区域、垃圾回收、类加载器

文章目录 JVM虚拟机内存区域1. ⭐JVM的内存区域有哪些&#xff1f;每个区域的作用是什么&#xff1f;2. 堆和栈的区别是什么&#xff1f;3. 堆内存是如何划分的&#xff1f;4. 永久代和元空间是什么关系&#xff1f;5. 对JVM常量池的理解&#xff1f;6. ⭐Java 对象的创建过程&…...

ASP.NET Core 使用 WebClient 从 URL 下载

本文使用 ASP .NET Core 3.1&#xff0c;但它在.NET 5、 .NET 6和.NET 8上也同样适用。如果使用较旧的.NET Framework&#xff0c;请参阅本文&#xff0c;不过&#xff0c;变化不大。 如果想要从 URL 下载任何数据类型&#xff0c;请参阅本文&#xff1a;HttpClient 使用WebC…...

第六届MathorCup高校数学建模挑战赛-A题:淡水养殖池塘水华发生及池水自净化研究

目录 摘要 1 问题的重述 2 问题的分析 2.1 问题一的分析 2.2 问题二的分析 2.3 问题三的分析 2.4 问题四的分析 2.5 问题五的分析 3. 问题的假设 4. 符号说明 5. 模型的建立与求解 5.1 问题一的建模与求解 5.1.1 分析对象与指标的选取 5.1.2 折线图分析 5.1.3 相关性分析 5.1.4…...

GnuTLS: 在 pull 函数中出错。 无法建立 SSL 连接。

提示信息 [root@localhost ~]# wget https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz --2025-02-06 12:45:34-- https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz 正在解析主机 download.docker.com (download.docker.…...

OpenAI 实战进阶教程 - 第十二节 : 多模态任务开发(文本、图像、音频)

适用读者与目标 适用读者&#xff1a;已经熟悉基础的 OpenAI API 调用方式&#xff0c;对文本生成或数据处理有一定经验的计算机从业人员。目标&#xff1a;在本节中&#xff0c;你将学会如何使用 OpenAI 提供的多模态接口&#xff08;图像生成、语音转录等&#xff09;开发更…...

《qt easy3d中添加孔洞填充》

《qt easy3d中添加孔洞填充》 效果展示一、创建流程二、核心代码效果展示 参考链接Easy3D开发——点云孔洞填充 一、创建流程 创建动作,并转到槽函数,并将动作放置菜单栏,可以参考前文 其中,槽函数on_actionHoleFill_triggered实现如下:...