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

tinyrenderer笔记(Shadow Mapping)

  • tinyrenderer
  • 个人代码仓库:tinyrenderer个人练习代码

前言

阴影是光线被阻挡的结果;当光源的光线由于其他物体的阻挡而无法到达物体表面时,该物体就会产生阴影。阴影能使场景看起来更真实,并让观察者获得物体之间的空间位置关系。场景和物体的深度感因此能得到极大提升。下图展示了有阴影和没有阴影的情况下的不同:

image.png

可以看到,有阴影时,你能更容易地区分物体之间的位置关系。例如,使用阴影时,浮在地板上的立方体的真实感更加清晰。

本节我们会手动实现一种硬阴影算法——Shadow Mapping。

Shadow Mapping

Shadow Mapping 背后的思路非常简单:我们以光源的位置为视角进行渲染,能被光源看到的东西都将被照亮,看不见的则处于阴影之中。假设有一个地板,光源和地板之间有一个大盒子。由于从光源处向光线方向看去,可以看到这个盒子,但看不到地板的一部分,这部分就应该在阴影之中。

下图中的所有蓝线代表光源可以看到的片段。黑线代表被遮挡的片段:它们应该渲染为带阴影的。如果我们绘制一条从光源出发,到达最右边盒子上的一个片段上的线段或射线,那么射线将先击中悬浮的盒子,随后才会到达最右侧的盒子。结果就是悬浮的盒子被照亮,而最右侧的盒子将处于阴影之中。

image.png

我们希望得到射线击中物体的第一个点,然后用这个最近点和射线上其他点进行对比。然后我们会做一个测试,判断测试点是否比射线上对应的最近点距离更远,如果是的话,这个点就在阴影中。

那么如何获得这个最近点呢?我们先将摄像机放在光源的位置,然后对整个场景进行渲染,进行深度测试但不着色,待渲染结束之后将此时 zbuffer 的数据写入纹理中,这个纹理就被称为 Shadow Map 或 Depth Map。

image.png

左图展示了一个定向光源(所有光线都是平行的)在立方体下方表面投射的阴影。通过存储在 Depth Map 中的深度值,我们可以找到最近点,并用它来决定片段是否在阴影中。我们可以使用来自光源的视图和投影矩阵来渲染场景,从而创建一个 Depth Map。这个投影和视图矩阵结合在一起形成一个 T 变换,它可以将任何三维位置转换到光源的可见坐标空间。

在右图中,我们展示了相同的平行光和观察者。我们渲染一个 P ‾ \overline{P} P 点处的片段,需要确定它是否在阴影中。我们首先使用 T P ‾ \overline{P} P 变换到光源的坐标空间中。由于点 P ‾ \overline{P} P 是从光的透视图中看到的,它的 z 坐标对应于它的深度,在此例中,该值为 0.9。使用点 P ‾ \overline{P} P 在光源坐标空间中的坐标,我们可以索引 Depth Map,以获得从光视角看到的最近可见深度,结果是点 C ‾ \overline{C} C,最近深度为 0.4。由于索引 Depth Map 的结果小于点 P ‾ \overline{P} P 的深度,我们可以断定 P ‾ \overline{P} P 被遮挡,它在阴影中。

总结下来,Shadow Mapping 由两个步骤组成:

  1. 首先,我们渲染 Depth Map ;
  2. 然后,我们像往常一样渲染场景,并使用生成的 Depth Map 来计算片段是否在阴影中。

准备工作

为了更好地测试阴影的效果,设定一些参数如下:

Vec3f light_dir = Vec3f(-1, 0, 0);// 光线方向
Vec3f light_color = Vec3f(1, 1, 1);// 光线的颜色
Vec3f cameraPos = Vec3f(0, 0, 2);
Vec3f targetPos = Vec3f(0, 0, 0);

head 的模型矩阵如下:

glm::mat4 modelMatrix;
modelMatrix = glm::rotate(modelMatrix, 22.5, Vec3f(0, 1, 0));

注意定向光没有位置,因为它被定义为无穷远。然而,为了实现 Depth Map,我们必须从光的透视图渲染场景,因此需要在光的方向上的某个点渲染场景。假定光源位于 -light_dir * 10 的位置并朝原点看去,LookAt 矩阵如下:

glm::mat4 lightView = glm::lookAt(-light_dir * 10, Vec3f(0, 0, 0), Vec3f(0, 1, 0));

我们使用的是定向光,所以使用正交投影来为光源创建透视图:

glm::mat4 lightProjection = glm::ortho(-5.f, 5.f, -5.f, 5.f, 1.0f, 20.f);

为了更好地表达阴影的效果,在光源与 head 模型之间渲染一个长方体来遮挡光照:

glm::mat4 bricksModelMat;
bricksModelMat = glm::translate(bricksModelMat, Vec3f(1, 0, 0));
bricksModelMat = glm::rotate(bricksModelMat, -45, Vec3f(0, 1, 0));
bricksModelMat = glm::scale(bricksModelMat, Vec3f(2, 1, 1));

现在渲染的效果如下,最后我们期望在 head 模型上看到长方体遮挡出来的阴影:

image.png

Depth Map

为了生成 depth map,我们需要定义一组新的 shader,它的顶点着色器如下:

class DepthVertexShader : public VertexShader
{
public:virtual void ConfirmVaryingVar() override {}virtual Vec4f vertex(std::shared_ptr<VertexInfoBase> vertexInfos) override{glm::mat4 modelMatrix = GetUniformVar<glm::mat4>("Model");glm::mat4 lightSpaceMatrix = GetUniformVar<glm::mat4>("lightSpaceMatrix");return lightSpaceMatrix * modelMatrix * Vec4f(vertexInfos->location, 1.f);}
};

顶点着色器只将顶点转换到光空间即可。lightSpaceMatrix 就是将顶点转换到光空间的变换矩阵:

depthProgram->SetUniformVar<glm::mat4>("lightSpaceMatrix", lightProjection * lightView);

片段着色器也很简单,因为我们只关心渲染完成之后的 zbuffer,所以不用进行任何操作:

class DepthFragmentShader : public FragmentShader
{
public:virtual bool fragment(FragmentInfo& info) override{return false;}
};

我们定一个新的 zbuffer 数组专门用来存储深度值:

float* depthMapBuffer;
depthMapBuffer = new float[width * height];
std::fill(depthMapBuffer, depthMapBuffer + width * height, std::numeric_limits<float>::max());

渲染完成之后,将 depthMapBuffer 的值输出到纹理中:

std::shared_ptr<TGAImage> depthMap = std::make_shared<TGAImage>(width, height, TGAImage::RGB);
for (int i = 0; i < width * height; i++)
{int x = i % width;int y = i / width;if (depthMapBuffer[i] >= 1){	depthMap->set(x, y, white);}else{unsigned int color = depthMapBuffer[i] * 255;depthMap->set(x, y, TGAColor(color, color, color, 255));}
}
depthMap->flip_vertically();
depthMap->write_tga_file("depthMap.tga");

image.png

可以观察到长方体的颜色更偏黑,代表深度值小,位于更前面。

正常渲染

第二步就是正常的渲染场景。在顶点着色器中,我们使用相同的 lightSpaceMatrix,将世界空间顶点位置转换为光空间。顶点着色器传递一个普通的经变换的世界空间顶点位置 worldPos 和一个光空间的 posForLightSpace 给片段着色器。

片段着色器使用 Blinn-Phong 光照模型渲染场景。我们会计算出一个 shadow 值,当片段在阴影中时是 1.0,在阴影外是 0.0。然后,diffusespecular 颜色会乘以这个阴影分量。由于散射(环境光照)的存在,模型不会完全黑暗,所以不会对 ambient 分量进行乘法。

Vec3f I = ambient + (1 - shadow) * (diffuse + specular);

剩下要做的就是计算阴影分量 shadow。要检查片段是否处于阴影中,首先要做的是将光空间片段位置转换为裁剪空间的标准化设备坐标(NDC),进行透视除法:

float shadow = 0.f;
Vec3f projCoords = posForLightSpace / posForLightSpace.w;

这会将坐标转换到 [ − 1 , 1 ] [-1,1] [1,1] 之间。当使用正交投影矩阵时,顶点 w 分量保持不变,所以这一步实际上毫无意义。可是,当使用透视投影的时候就是必须的了,所以为了保证在两种投影矩阵下都有效就得留着这行。

随后我们需要将坐标转化到 [ 0 , 1 ] [0,1] [0,1] 之间:

projCoords = projCoords * 0.5 + Vec3f(0.5, 0.5, 0.5);

然后就可以对 depth map 进行采样了,得到最近的深度值:

float closestDepth = depthMap->texture(int(projCoords.x), int(projCoords.y)).r / 255.f;

然后将最近深度值与当前深度值进行比较,判断当前片段是否位于阴影之中:

float currentDepth = projCoords.z;
shadow = (currentDepth > closestDepth) ? 1.f : 0.f;

image.png

渲染出来的效果还不错,但是仔细观察红圈地方,你会发现这里出现了阴影!为什么会这样呢?这个问题被称为:Shadow Acne。

由于 Depth Map 受到分辨率的限制,在距离光源较远的情况下,多个片段可能从 Depth Map 的同一个值中采样。图中的每个斜坡代表 Depth Map 的一个单独纹理像素。可以看到,多个片段用同一个深度值进行采样。

image.png

虽然很多时候没问题,但是当光源以一个角度朝向表面时就会出现问题,在这种情况下,Depth Map 也是从一个角度下渲染的。多个片段会从同一个斜坡的 Depth Map 像素中采样,图中黄色与黑色交界处为实际采样点,左边区域的像素与光源的距离小于采样点则是明亮的(表面上面),右边区域的像素与光源的距离大于采样点则存在阴影(表面下面);这样我们所得到的阴影就有了差异。因此,有些片段被认为是在阴影之中,有些不在,由此产生了图中的条纹样式。

我们可以用一个叫做阴影偏移(shadow bias)的技巧来解决这个问题,我们简单地对表面的深度(或 Depth Map)应用一个偏移量,这样片段就不会被错误地认为在表面之下了。

image.png

使用偏移量后,所有采样点都获得了比表面深度更小的深度值(相当于所有点都更靠近了光源一些,但是 Depth Map 中的深度值没变),这样整个表面就正确地被照亮,没有任何阴影。我们可以这样实现这个偏移:

float bias = 0.0155f;
shadow = (currentDepth > closestDepth + bias) ? 1.f : 0.f;

image.png

需要注意的是这个 bias 值的大小完全取决于你渲染表面的坡度大小,不同的模型可能需要不同的 bias,有一个更加可靠的办法是根据表面朝向光线的角度更改偏移量,使用点乘:

float bias = std::max(0.0155 * (1.0 - std::max(normal.dot(lightDir), 0.f)), 0.001);

在 tinyrenderer 原教程中,并没有对 depth map 采样来获得深度值,而是直接访问 depthMapBuffer 缓冲区内的深度值来得到最近深度值的,这样也是可行的:

// 计算阴影
float shadow = 0.f;
{Vec4f projCoords = posForLightSpace / posForLightSpace.w;//projCoords = projCoords * 0.5 + Vec3f(0.5, 0.5, 0.5);//float closestDepth = depthMap->texture(projCoords.x, projCoords.y).r / 255.f;projCoords = m_program->GetViewPort() * projCoords;float closestDepth = depthMapBuffer[int(projCoords.x) + int(projCoords.y) * width];float currentDepth = projCoords.z;float bias = std::max(0.015 * (1.0 - std::max(normal.dot(lightDir), 0.f)), 0.001);shadow = (currentDepth > closestDepth + bias) ? 1.f : 0.f;
}

请格外注意我这部分代码:int(projCoords.x) + int(projCoords.y) * width ,强制类型转换的地方,因为循环遍历像素的时候,我们相当于直接截断了小数部分。

这种实现方式也会出现相似的问题:Z-fighting,所以也需要 bias 来缓解。

image.png

归根结底,Z-fighting 与 Shadow Acne 都是精度不够而导致的深度采样竞争,bias 能缓解但不能根除,而且也会带来其它的问题。

本次代码提交记录:

image.png

参考

  • tinyrenderer
  • 阴影映射 - LearnOpenGL CN

others

tinyrenderer 的工作暂时告一段落,还剩下最后一篇环境光遮蔽(Ambient Occlusion,AO)没有实现,当然重点是 SSAO——屏幕空间环境光遮蔽。SSAO 的理论其实并不复杂,但受限于我目前实现的框架,真正做的时候束手束脚的。现在缺少哪些必备功能呢?

TGAImage 能够存储 float 类型的数据 ——> Multiple Render Target 多重渲染目标 ——> 延迟着色 ——> SSAO

上面是我的理想工作流程,但这样一看花费的时间就有点多了。现在还有很多东西没学,所以暂告一段落吧。后面在推进这个软光栅项目时,肯定会进行大范围重构的。

最后感叹一句,有些东西看起来很简单,但是有很多坑,手动实现光栅化流程确实受益匪浅!

相关文章:

tinyrenderer笔记(Shadow Mapping)

tinyrenderer个人代码仓库&#xff1a;tinyrenderer个人练习代码 前言 阴影是光线被阻挡的结果&#xff1b;当光源的光线由于其他物体的阻挡而无法到达物体表面时&#xff0c;该物体就会产生阴影。阴影能使场景看起来更真实&#xff0c;并让观察者获得物体之间的空间位置关系。…...

【quantity】1 SI Prefixes 实现解析(prefix.rs)

一、源码 // prefix.rs //! SI Prefixes (国际单位制词头) //! //! 提供所有标准SI词头用于单位转换&#xff0c;仅处理10的幂次 //! //! Provides all standard SI prefixes for unit conversion, handling only powers of 10.use typenum::{Z0, P1, P2, P3, P6, P9, P12, …...

如何开发一个笑话管理小工具

前言 笔者曾经开发过一个可以对笑话浏览、收藏、分类、编辑上传的小工具&#xff08;笔者开发后台&#xff0c;另外一个朋友负责小程序前台开发&#xff09;&#xff0c;如今所租用的服务器到期了&#xff0c;特此记录一下。 数据层 部署数据库 # 拉取Mysql镜像 docker pull…...

Transformer-LSTM混合模型在时序回归中的完整流程研究

Transformer-LSTM混合模型在时序回归中的完整流程研究 引言与背景 深度学习中的长期依赖建模一直是时序预测的核心问题。长短期记忆网络&#xff08;LSTM&#xff09;作为一种循环神经网络&#xff0c;因其特殊的门控结构能够有效捕捉序列的历史信息&#xff0c;并在时序预测…...

深入浅出iOS性能优化:打造极致用户体验的实战指南

前言 在当今移动应用竞争激烈的时代&#xff0c;性能优化已经成为iOS开发中不可或缺的重要环节。一个性能优秀的应用不仅能给用户带来流畅的使用体验&#xff0c;还能减少设备资源消耗&#xff0c;延长电池寿命&#xff0c;提高用户留存率。本文将深入探讨iOS性能优化的各个方…...

Spring AI 与大语言模型工具调用机制详细笔记

一、基本概念 大语言模型&#xff08;LLM&#xff09;工具调用机制是一种允许AI模型与外部系统交互的技术框架&#xff0c;它使模型能够在对话过程中请求调用预定义的函数或服务。这种机制极大地扩展了大模型的能力边界&#xff0c;使其不再局限于静态知识&#xff0c;而是能够…...

数据清洗-电商双11美妆数据分析

1.数据读取&#xff08;前八行&#xff09; 2.数据清洗 2.1 因为数据中存在重复跟空值&#xff0c;将数据进行重复值处理 &#xff08;删除重复值&#xff09; 2.2 缺失值处理 存在的缺失值很可能意味着售出的数量为0或者评论的数量为0&#xff0c;所以我们用0来填补缺失值 2…...

公司项目架构搭建者

公司项目架构搭建者分析 项目架构搭建的核心角色 #mermaid-svg-FzOOhBwW3tctx2AR {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FzOOhBwW3tctx2AR .error-icon{fill:#552222;}#mermaid-svg-FzOOhBwW3tctx2AR .err…...

广告场景下的检索平台技术

检索方向概述 数据检索领域技术选型大体分为SQL事务数据库、NoSQL数据库、分析型数据库三个类型。 SQL数据库的设计思路是采用关系模型组织数据&#xff0c;注重读写操作的一致性&#xff0c;注重数据的绝对安全。为了实现这一思路&#xff0c;SQL数据库往往会牺牲部分性能&…...

LintCode407-加一,LintCode第479题-数组第二大数

第407题: 描述 给定一个非负数&#xff0c;表示一个数字数组&#xff0c;在该数的基础上1&#xff0c;返回一个新的数组。 该数字按照数位高低进行排列&#xff0c;最高位的数在列表的最前面. 样例 1&#xff1a; 输入&#xff1a;[1,2,3] 输出&#xff1a;[1,2,4] 样例 …...

网络安全的范式革命:从被动防御到 AI 驱动的主动对抗

当黑客利用生成式 AI 在 30 秒内生成 10 万组钓鱼邮件&#xff0c;当恶意代码学会根据网络环境自主进化&#xff0c;传统网络安全防线正面临前所未有的挑战。2025 年&#xff0c;全球网络安全领域正在经历一场从 “被动挨打” 到 “主动出击” 的革命性转变&#xff0c;AI 与量…...

内网im软件,支持企业云盘的协同办公软件推荐

BeeWorks不仅是一个即时通讯工具&#xff0c;更是一个综合性的企业管理平台。其云盘功能支持大容量文件存储&#xff0c;便企业集中管理文件。并且具备在线协同编辑的能力&#xff0c;这使得企业在文件管理和团队协作方面更加高效和便捷。以下是BeeWorks在企业云盘和在线协同编…...

JAVA SE(9)——多态

1.多态的概念&作用 多态(Polymorphism)是面向对象编程的三大基本特性之一&#xff08;封装和继承已经讲过了&#xff09;&#xff0c;它允许不同类的对象对同一消息做出不同的响应。具体来说&#xff0c;多态允许基类/父类的引用指向派生类/子类的对象&#xff08;向上转型…...

单调栈算法精解(Java实现):从原理到高频面试题

在算法与数据结构的领域中&#xff0c;单调栈&#xff08;Monotonic Stack&#xff09;凭借其独特的设计和高效的求解能力&#xff0c;成为解决特定类型问题的神兵利器。它通过维护栈内元素的单调性&#xff0c;能将许多问题的时间复杂度从暴力解法的\(O(n)\)优化至\(O(n)\)&am…...

密码工具类-生成随机密码校验密码强度是否满足要求

生成随机密码 符合密码强度的密码要求&#xff1a; 至少有一个大写字母至少有一个小写字母至少有一个数字至少有一个特殊字符长度满足要求&#xff08;通常为8-16位&#xff09; // 大写字母private static final String UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ";…...

什么是进程,如何管理进程

基本概念&#xff08;什么是进程&#xff1f;&#xff09; 课本概念&#xff1a;程序的一个执行实例&#xff0c;正在执行的程序等内核观点&#xff1a;担当分配系统资源&#xff08;CPU时间&#xff0c;内存&#xff09;的实体。 描述进程-PCB 进程信息被放在一个叫做进程控…...

小刚说C语言刷题—1044 -找出最经济型的包装箱型号

1.题目描述 已知有 A&#xff0c;B&#xff0c;C&#xff0c;D&#xff0c;E五种包装箱&#xff0c;为了不浪费材料&#xff0c;小于 10公斤的用 A型&#xff0c;大于等于 10公斤小于 20 公斤的用 B型&#xff0c;大于等于 20公斤小于 40 公斤的用 C型&#xff0c;大于等于 40…...

用 GRPO 魔法点亮Text2SQL 的推理之路:让模型“思考”得更像人类

推理能力&#xff08;Chain of Thought, CoT&#xff09;可以帮助模型逐步解释其思考过程&#xff0c;从而提高Text-to-SQL 生成的准确性和可解释性。本文探讨了如何将一个标准的 7B 参数的大型语言模型&#xff08;Qwen2.5-Coder-7B-Instruct&#xff09;转变为一个能够为Text…...

k8s service的类型

service和Pods service通过使用labels指向pods,而不是指向deployments或者replicasets。这种设计的灵活性极高&#xff0c;因为创建pods的方式有很多&#xff0c;而Service不需要关心pods通过那种方式创建 不使用service&#xff08;首先看不使用service的情况&#xff09; 如下…...

机器学习 day6 -线性回归练习

题目‌&#xff1a; 从Kaggle的“House Prices - Advanced Regression Techniques”数据集使用Pandas读取数据&#xff0c;并查看数据的基本信息。选择一些你认为对房屋价格有重要影响的特征&#xff0c;并进行数据预处理&#xff08;如缺失值处理、异常值处理等&#xff09;。…...

机器学习-简要与数据集加载

一.机器学习简要 1.1 概念 机器学习即计算机在数据中总结规律并预测未来结果&#xff0c;这一过程仿照人类的学习过程进行。 深度学习是机器学习中的重要算法的其中之一&#xff0c;是一种偏近现代的算法。 1.2 机器学习发展历史 从上世纪50年代的图灵测试提出、塞缪尔开发…...

HTTP请求与前端资源未优化的系统性风险与高性能优化方案

目录 前言一、未合并静态资源&#xff1a;HTTP请求的隐形杀手1.1 多文件拆分的代价1.2 合并策略与工具链实践 二、未启用GZIP压缩&#xff1a;传输流量的浪费2.1 文本资源的压缩潜力2.2 服务端配置与压缩算法选择 三、未配置浏览器缓存&#xff1a;重复请求的根源3.1 缓存失效的…...

黑马点评day04(分布式锁-setnx)

4、分布式锁 4.1 、基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程并行&#x…...

哈尔滨服务器租用

选择一家正规的本地服务商&#xff0c;能够直接促进您网站今后的发展、确保您企业的信息化进程安全、高效。擦亮您的慧眼&#xff0c;用我的经验告诉您该怎么选择服务商。。。。。。。。综合我们为数据客户服务的经验&#xff0c;选择服务器租用、服务提供商客户所需要关注的主…...

企业级RAG架构设计:从FAISS索引到HyDE优化的全链路拆解,金融/医疗领域RAG落地案例与避坑指南(附架构图)

本文较长&#xff0c;纯干货&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习内容&#xff0c;尽在聚客AI学院。 一. RAG技术概述 1.1 什么是RAG&#xff1f; RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09; 是…...

js获取uniapp获取webview内容高度

js获取uniapp获取webview内容高度 在uni-app中&#xff0c;如果你想要获取webview的内容高度&#xff0c;可以使用uni-app提供的bindload事件来监听webview的加载&#xff0c;然后通过调用webview的invokeMethod方法来获取内容的高度。 以下是一个示例代码&#xff1a; <te…...

AI量化解析:从暴跌5%到飙涨3%—非线性动力学模型重构黄金极端波动预测框架

AI分析&#xff1a;假期效应褪去&#xff0c;金价回调背后的市场逻辑 五一假期期间&#xff0c;全球贵金属市场经历显著波动。5月1日&#xff0c;现货黄金单日跌幅达5.06%&#xff0c;价格从历史高位回落至3200美元/盎司附近&#xff0c;国内金饰价格同步回调&#xff0c;主流…...

Python之pip图形化(GUI界面)辅助管理工具

Python之pip图形化&#xff08;GUI界面&#xff09;辅助管理工具 pip 是 Python 的包管理工具&#xff0c;用于安装、管理、更新和卸载 Python 包&#xff08;模块&#xff09;。用于第三方库的安装和管理过程&#xff0c;是 Python 开发中不可或缺的工具。 包的安装、更新、…...

数字传播生态中开源链动模式与智能技术协同驱动的品牌认知重构研究——基于“开源链动2+1模式+AI智能名片+S2B2C商城小程序”的场景化传播实践

摘要&#xff1a;在数字传播碎片化与用户注意力稀缺的双重挑战下&#xff0c;传统品牌认知构建模式面临效率衰减与情感黏性缺失的困境。本文以“开源链动21模式AI智能名片S2B2C商城小程序”的协同创新为切入点&#xff0c;构建“技术赋能-场景重构-认知强化”的分析框架。通过对…...

小芯片大战略:Chiplet技术如何重构全球半导体竞争格局?

在科技飞速发展的今天&#xff0c;半导体行业作为信息技术的核心领域之一&#xff0c;其发展速度和创新水平对全球经济的发展具有举足轻重的影响。然而&#xff0c;随着芯片制造工艺的不断进步&#xff0c;传统的单片集成方式逐渐遇到了技术瓶颈&#xff0c;如摩尔定律逐渐逼近…...

链表的面试题3找出中间节点

来来来&#xff0c;接着继续我们的第三道题 。 解法 暴力求解 快慢指针 https://leetcode.cn/problems/middle-of-the-linked-list/submissions/ 这道题的话&#xff0c;思路是非常明确的&#xff0c;就是让你找出我们这个所谓的中间节点并且输出。 那这道题我们就需要注意…...

Java泛型深度解析与电商场景应用

学海无涯&#xff0c;志当存远。燃心砺志&#xff0c;奋进不辍。 愿诸君得此鸡汤&#xff0c;如沐春风&#xff0c;事业有成。 若觉此言甚善&#xff0c;烦请赐赞一枚&#xff0c;共励学途&#xff0c;同铸辉煌&#xff01; 泛型的工作原理可能包括类型擦除、参数化类型、类型边…...

C语言 指针(7)

目录 1.函数指针变量 2.函数指针数组 3.转移表 1.函数指针变量 1.1函数指针变量的创建 什么是函数指针变量呢&#xff1f; 根据前面学习整型指针&#xff0c;数组指针的时候&#xff0c;我们的类比关系&#xff0c;我们不难得出结论&#xff1a; 函数指针变量应该是用来…...

go 编译报错:build constraints exclude all Go files

报错信息&#xff1a; package command-line-arguments imports github.com/amikos-tech/chroma-go imports github.com/amikos-tech/chroma-go/pkg/embeddings/default_ef imports github.com/amikos-tech/chroma-go/pkg/tokenizers/libtokenizers: …...

Android Service 从 1.0 到 16 的演进史

一、Android 1.0&#xff08;API 1&#xff09; - 服务的诞生 核心特性&#xff1a; 基础服务组件&#xff1a;作为四大组件之一&#xff0c;Service 用于在后台执行长时间运行的任务&#xff0c;不提供 UI 界面。 启动方式&#xff1a;通过 startService() 启动独立运行的服…...

如何保障服务器租用中的数据安全?

网络科技和互联网的飞速发展&#xff0c;让用户越来越依赖与网络业务&#xff0c;各个行业开展了不同的线上服务&#xff0c;租用服务器已经成为必不可少的组成部分&#xff0c;能够为企业带来便捷&#xff0c;但是数据安全也是不可忽视的&#xff0c;为了能够保护服务器中数据…...

python校园二手交易管理系统-闲置物品交易系统

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xf…...

消除AttributeError: module ‘ttsfrd‘ has no attribute ‘TtsFrontendEngine‘报错输出的记录

#工作记录 尝试消除 消除“模块ttsfrd没有属性ttsfrontendengine”的错误的记录 报错摘录&#xff1a; Traceback (most recent call last): File "F:\PythonProjects\CosyVoice\webui.py", line 188, in <module> cosyvoice CosyVoice(args.model_di…...

MD2card + Deepseek 王炸组合 一键制作小红书知识卡片

本文目录 MD2Card介绍使用示例deepseek 提示词输出结果MD2Card 制作小红书卡片 MD2Card介绍 MD2Card 是一个免费的 Markdown 转知识卡片工具&#xff0c;支持一键生成小红书风格海报、社交媒体文案排版&#xff0c;让创作者轻松制作精美的图文内容。支持多种主题风格、长文自动…...

Relay算子注册(在pytorch.py端调用)

1. Relay算子注册 (C层) (a) 算子属性注册 路径: src/relay/op/nn/nn.cc RELAY_REGISTER_OP("hardswish").set_num_inputs(1).add_argument("data", "Tensor", "Input tensor.").set_support_level(3).add_type_rel("Identity…...

基于RT-Thread的STM32F4开发第二讲第一篇——ADC

文章目录 前言一、RT-Thread工程创建二、ADC工程创建三、ADC功能实现1.ADC.c2.ADC.h3.mian.c 四、效果展示和工程分享总结 前言 ADC是什么不多讲了&#xff0c;前面裸机操作部分有很多讲述。我要说的是RT-Thread对STM32的ADC外设的适配极其不好&#xff0c;特别是STM32G4系类&…...

py实现win自动化自动登陆qq

系列文章目录 py实现win自动化自动登陆qq 文章目录 系列文章目录前言一、上代码&#xff1f;总结 前言 之前都是网页自动化感觉太容易了&#xff0c;就来尝尝win自动化&#xff0c;就先写了一个qq登陆的&#xff0c;这个是拿到className 然后进行点击等。 一、上代码&#xf…...

Axure : 列表分页、 列表翻页

文章目录 引言I 列表分页操作说明II 列表翻页操作说明引言 列表分页实现思路:局部变量、 中继器设置每页项目数 I 列表分页 操作说明 在列表元件底部添加一个分页下拉控件,分别为10,20,30,40,50; 将列表转换为动态面板,将设置面板大小勾选取消 给分页大小下拉控件添加…...

大学之大:隆德大学2025.5.6

隆德大学&#xff1a;北欧学术明珠的八百年传承与创新 一.前身历史&#xff1a;从中世纪神学院到现代综合大学的蜕变 隆德大学的历史可追溯至1425年&#xff0c;由丹麦国王埃里克七世在瑞典南部城市隆德创立的“神学与教会法研究院”。这所中世纪学府最初以培养天主教神职人员…...

每日算法-250506

每日算法学习记录 - 250506 今天记录了三道算法题的解题过程和思路&#xff0c;分享给大家。 3192. 使二进制数组全部等于 1 的最少操作次数 II 题目 思路 贪心 解题过程 我们从左到右遍历数组。使用一个变量 ret 来记录已经执行的操作次数。 对于当前元素 nums[i]&#x…...

【免费试用】LattePanda Mu x86 计算模块套件,专为嵌入式开发、边缘计算与 AI 模型部署设计

本次活动为载板设计挑战&#xff0c;旨在激发创意与技术实践能力&#xff0c;鼓励电子工程师、创客、学生及开发者围绕指定LattePanda Mu进行功能丰富、应用多样的载板开发设计。参赛用户将有机会展示硬件设计能力&#xff0c;并通过作品解决实际问题或构建创新项目。本次挑战活…...

用于备份的git版本管理指令

一、先下载一个git服务器软件并安装&#xff0c;创建一个git服务器进行备份的版本管理。 下列指令用于git常用备份&#xff1a; 1、强制覆盖远程仓库&#xff1a; git push --force origin master 2、重新指向新仓库&#xff1a; git remote set-url origin http://192.168.1.2…...

STM32H743单片机实现ADC+DMA多通道检测

在stm32cubeMX上配置ADCDMA实现多通道检测功能 DMA配置 生成代码&#xff0c;HAL_ADC_Start_DMA开始DMA读取ADC值&#xff0c;HAL_ADC_Stop_DMA关闭DMA读取 void Start_ADC2_DMA(void) {/* 初始化后校准ADC */HAL_ADCEx_Calibration_Start(&hadc2, ADC_CALIB_OFFSET, ADC_…...

(提升)媒体投稿技能

1\前期策划与准备 精准定位目标受众&#xff0c;分析内容偏好与活跃媒体基于目标受众&#xff0c;确定发稿核心主题与内容方向 2\内容创作与素材 撰写稿件注重标题吸引力&#xff0c;确保内容逻辑清晰价值突出。素材整合准备高质量的配图、视频 3\内部审核与优化 对稿件内…...

2025年提交App到Appstore从审核被拒到通过的经历

今年3月份提交一个App到Appstore&#xff0c;感觉比以前要严格了很多&#xff0c;被拒了多次才通过。 如果周末提交审核会非常非常&#xff0c;所以最好选择周一之周四的中午提交。 第一次提交被拒&#xff0c;原因为 Guideline 2.1 - Performance - App Completeness Guidel…...