tinyrenderer笔记(透视矫正)
- tinyrenderer
- 个人代码仓库:tinyrenderer个人练习代码
引言
还要从上一节知识说起,在上一节中我为了调试代码,换了一个很简单的正方形 obj 模型,配上纹理贴图与法线贴图进行渲染,得了下面的结果:
what?这是啥,为什么正方形中间纹理出现了扭曲,当我调大旋转的角度,这个扭曲越发明显:
很明显这是纹理坐标出了问题,这就引出了另外一个知识:透视矫正插值,也就是 tinyrenderer 的这篇文章:Technical difficulties: linear interpolation with perspective deformations
透视矫正插值
首先思考我们现在的插值是怎么做的?
如上图所示,假设我们渲染一个 △ A B C \bigtriangleup ABC △ABC 内的 P P P 点,经透视投影后,被投影为 △ a b c \bigtriangleup abc △abc 内的 p p p 点。我们是如何计算重心坐标的?
Vec3f bc_screen = glm::barycentric(viewport_coords[0], viewport_coords[1], viewport_coords[2], P);
上述代码中,我们拿的是屏幕空间的坐标去计算重心坐标。思考这样一个问题:假设观察空间下 △ A B C \bigtriangleup ABC △ABC 内 P P P 点的重心坐标为 ( α , β , γ ) (\alpha,\beta,\gamma) (α,β,γ), △ a b c \bigtriangleup abc △abc 内 p p p 点为 ( α ′ , β ′ , γ ′ ) (\alpha^\prime,\beta^\prime,\gamma^\prime) (α′,β′,γ′)。你觉得 ( α , β , γ ) = ( α ′ , β ′ , γ ′ ) (\alpha,\beta,\gamma)=(\alpha^\prime,\beta^\prime,\gamma^\prime) (α,β,γ)=(α′,β′,γ′) 吗?
答案为否,因为透视投影是一种非线性变换, △ A B C \bigtriangleup ABC △ABC 经过透视投影之后会发生畸变,整体比例会发生变化。但我们却用 ( α ′ , β ′ , γ ′ ) (\alpha^\prime,\beta^\prime,\gamma^\prime) (α′,β′,γ′) 去插值,希望得到 △ A B C \bigtriangleup ABC △ABC 内点 P P P 的属性,这肯定会造成误差!
可能有同学很快会想出一种思路:既然计算 △ a b c \bigtriangleup abc △abc 内 p p p 点的重心坐标去插值不准确,那么我是否可以将屏幕空间下的 p p p 点经过逆变换得到 P P P 点,然后计算 P P P 点关于 △ A B C \bigtriangleup ABC △ABC 的重心坐标呢?
理论上没有问题,但是思考一下我们是如何获得 p p p 点的 z z z (深度值)的,我们是通过插值获得的(这不套娃吗?),所以屏幕空间下 p p p 点的 z z z 坐标是无法准确得到的。同时,在经过透视投影后,我们进行了透视除法, w w w 分量也被丢弃了。所以,我们将 p p p 逆变换成 P P P 困难重重。
虽然 p p p 点的信息我们无法确定,但是三角形三个顶点的所有信息我们是能够知道的,那么我们是否能够通过已知变量来建立 ( α ′ , β ′ , γ ′ ) (\alpha^\prime,\beta^\prime,\gamma^\prime) (α′,β′,γ′) 到 ( α , β , γ ) (\alpha,\beta,\gamma) (α,β,γ) 的映射关系呢?前辈们已经为我们做好了这个工作,下面将开始推导。
推导
推导过程来自:perspective-correct-interpolation.dvi
下图展示了透视投影在视图空间形成的视锥体,相机位于原点:
设点 A , B , C , P A,B,C,P A,B,C,P 经过齐次矩阵 M M M 转化为了 A ′ , B ′ , C ′ , P ′ A^\prime,B^\prime,C^\prime,P^\prime A′,B′,C′,P′(经过透视投影与透视除法的坐标):
( A ′ w a w a ) = M ( A 1 ) ( B ′ w b w b ) = M ( B 1 ) ( C ′ w c w c ) = M ( C 1 ) ( P ′ w p w p ) = M ( P 1 ) \begin{align*} \begin{pmatrix} A'w_a \\ w_a \end{pmatrix} &= \mathbf{M} \begin{pmatrix} A \\ 1 \end{pmatrix}\\ \begin{pmatrix} B'w_b \\ w_b \end{pmatrix} &= \mathbf{M} \begin{pmatrix} B \\ 1 \end{pmatrix}\\ \begin{pmatrix} C'w_c \\ w_c \end{pmatrix} &= \mathbf{M} \begin{pmatrix} C \\ 1 \end{pmatrix}\\ \begin{pmatrix} P'w_p \\ w_p \end{pmatrix} &= \mathbf{M} \begin{pmatrix} P \\ 1 \end{pmatrix} \end{align*} (A′wawa)(B′wbwb)(C′wcwc)(P′wpwp)=M(A1)=M(B1)=M(C1)=M(P1)
真正的重心坐标权重为 α , β , γ \alpha,\beta,\gamma α,β,γ,经过透视投影变为了 α ′ , β ′ , γ ′ \alpha^\prime,\beta^\prime,\gamma^\prime α′,β′,γ′。
P = α A + β B + γ C P ′ = α ′ A ′ + β ′ B ′ + γ ′ C ′ \begin{align*} P &= \alpha A + \beta B + \gamma C\\ P' &= \alpha' A' + \beta' B' + \gamma' C' \end{align*} PP′=αA+βB+γC=α′A′+β′B′+γ′C′
在光栅化阶段我们可以直接计算 α ′ , β ′ , γ ′ \alpha^\prime,\beta^\prime,\gamma^\prime α′,β′,γ′,但我们需要 α , β , γ \alpha,\beta,\gamma α,β,γ 来正确的插值顶点的属性。
( P 1 ) = α ( A 1 ) + β ( B 1 ) + γ ( C 1 ) M ( P 1 ) = α M ( A 1 ) + β M ( B 1 ) + γ M ( C 1 ) ( P ′ w p w p ) = α ( A ′ w a w a ) + β ( B ′ w b w b ) + γ ( C ′ w c w c ) \begin{align*} \begin{pmatrix} P \\ 1 \end{pmatrix} &= \alpha \begin{pmatrix} A \\ 1 \end{pmatrix} + \beta \begin{pmatrix} B \\ 1 \end{pmatrix} + \gamma \begin{pmatrix} C \\ 1 \end{pmatrix}\\ \mathbf{M} \begin{pmatrix} P \\ 1 \end{pmatrix} &= \alpha \mathbf{M} \begin{pmatrix} A \\ 1 \end{pmatrix} + \beta \mathbf{M} \begin{pmatrix} B \\ 1 \end{pmatrix} + \gamma \mathbf{M} \begin{pmatrix} C \\ 1 \end{pmatrix}\\ \begin{pmatrix} P'w_p \\ w_p \end{pmatrix} &= \alpha \begin{pmatrix} A'w_a \\ w_a \end{pmatrix} + \beta \begin{pmatrix} B'w_b \\ w_b \end{pmatrix} + \gamma \begin{pmatrix} C'w_c \\ w_c \end{pmatrix} \tag{1} \end{align*} (P1)M(P1)(P′wpwp)=α(A1)+β(B1)+γ(C1)=αM(A1)+βM(B1)+γM(C1)=α(A′wawa)+β(B′wbwb)+γ(C′wcwc)(1)
将 1 式分解则有:
P ′ w p = α A ′ w a + β B ′ w b + γ C ′ w c w p = α w a + β w b + γ w c \begin{align*} P' w_p &= \alpha A' w_a + \beta B' w_b + \gamma C' w_c \tag{2}\\ w_p &= \alpha w_a + \beta w_b + \gamma w_c \tag{3}\\ \end{align*} P′wpwp=αA′wa+βB′wb+γC′wc=αwa+βwb+γwc(2)(3)
将 3 式带入 2 式可得:
P ′ = α A ′ w a + β B ′ w b + γ C ′ w c α w a + β w b + γ w c P ′ = α w a α w a + β w b + γ w c A ′ + β w b α w a + β w b + γ w c B ′ + γ w c α w a + β w b + γ w c C ′ \begin{align*} P' &= \frac{\alpha A' w_a + \beta B' w_b + \gamma C' w_c}{\alpha w_a + \beta w_b + \gamma w_c}\\ P' &= \frac{\alpha w_a}{\alpha w_a + \beta w_b + \gamma w_c} A' + \frac{\beta w_b}{\alpha w_a + \beta w_b + \gamma w_c} B' + \frac{\gamma w_c}{\alpha w_a + \beta w_b + \gamma w_c} C' \end{align*} P′P′=αwa+βwb+γwcαA′wa+βB′wb+γC′wc=αwa+βwb+γwcαwaA′+αwa+βwb+γwcβwbB′+αwa+βwb+γwcγwcC′
所以有:
{ α ′ = α w a α w a + β w b + γ w c β ′ = β w b α w a + β w b + γ w c γ ′ = γ w c α w a + β w b + γ w c \begin{cases} \begin{align*} \alpha' &= \frac{\alpha w_a}{\alpha w_a + \beta w_b + \gamma w_c}\\ \beta' &= \frac{\beta w_b}{\alpha w_a + \beta w_b + \gamma w_c}\\ \gamma' &= \frac{\gamma w_c}{\alpha w_a + \beta w_b + \gamma w_c} \end{align*} \end{cases} ⎩ ⎨ ⎧α′β′γ′=αwa+βwb+γwcαwa=αwa+βwb+γwcβwb=αwa+βwb+γwcγwc
但我们希望用 α ′ , β ′ , γ ′ \alpha',\beta',\gamma' α′,β′,γ′ 来表示 α , β , γ \alpha,\beta,\gamma α,β,γ,设分母为 k k k
k = 1 α w a + β w b + γ w c k=\frac{1}{\alpha w_a + \beta w_b + \gamma w_c} k=αwa+βwb+γwc1
则有:
{ α ′ = α w a k β ′ = β w b k γ ′ = γ w c k \begin{cases} \begin{align*} \alpha' &= \alpha w_ak\\ \beta' &= \beta w_bk\\ \gamma' &= \gamma w_ck \end{align*} \end{cases} ⎩ ⎨ ⎧α′β′γ′=αwak=βwbk=γwck
{ α = α ′ w a k β = β ′ w b k γ = γ ′ w c k \begin{cases} \begin{align*} \alpha &= \frac{\alpha'}{w_ak} \tag{4}\\ \beta &= \frac{\beta'}{w_bk}\tag{5}\\ \gamma &= \frac{\gamma'}{w_ck}\tag{6} \end{align*} \end{cases} ⎩ ⎨ ⎧αβγ=wakα′=wbkβ′=wckγ′(4)(5)(6)
又因为 α + β + γ = 1 \alpha+\beta+\gamma=1 α+β+γ=1,
1 = α + β + γ = α ′ w a k + β ′ w b k + γ ′ w c k k = α ′ w a + β ′ w b + γ ′ w c \begin{align*} 1&=\alpha+\beta+\gamma=\frac{\alpha'}{w_ak}+\frac{\beta'}{w_bk}+\frac{\gamma'}{w_ck}\\ k&=\frac{\alpha'}{w_a}+\frac{\beta'}{w_b}+\frac{\gamma'}{w_c} \tag{7} \end{align*} 1k=α+β+γ=wakα′+wbkβ′+wckγ′=waα′+wbβ′+wcγ′(7)
将 7 式带入 4、5、6 式即可得到:
{ α = α ′ w a α ′ w a + β ′ w b + γ ′ w c β = β ′ w b α ′ w a + β ′ w b + γ ′ w c γ = γ ′ w c α ′ w a + β ′ w b + γ ′ w c \begin{cases} \begin{align*} \alpha &= \frac{\frac{\alpha'}{w_a}}{\frac{\alpha'}{w_a}+\frac{\beta'}{w_b}+\frac{\gamma'}{w_c}}\\ \beta &= \frac{\frac{\beta'}{w_b}}{\frac{\alpha'}{w_a}+\frac{\beta'}{w_b}+\frac{\gamma'}{w_c}}\\ \gamma &= \frac{\frac{\gamma'}{w_c}}{\frac{\alpha'}{w_a}+\frac{\beta'}{w_b}+\frac{\gamma'}{w_c}} \end{align*} \end{cases} ⎩ ⎨ ⎧αβγ=waα′+wbβ′+wcγ′waα′=waα′+wbβ′+wcγ′wbβ′=waα′+wbβ′+wcγ′wcγ′
现在,我们就可以使用 α , β , γ \alpha,\beta,\gamma α,β,γ 来正确插值顶点的属性了。
代码
来完善这个函数,barycentricCorrect
,它会返回经过透视矫正后的重心坐标。这是在二位平面上的插值,A、B、C 的 z 分量会存储它们经过透视投影后的 w 分量。
Vec3f glm::barycentricCorrect(const Vec3f& A, const Vec3f& B, const Vec3f& C, const Vec3f& P)
{if (IsNearlyZero(A.z) && IsNearlyZero(B.z) && IsNearlyZero(C.z)){std::cout << "glm::barycentricCorrect A, B, C w is zero!" << std::endl;return barycentric(A, B, C, P);}Vec3f bc = barycentric(A, B, C, P);float det = bc.x/A.z + bc.y/B.z + bc.z/C.z;if (IsNearlyZero(det)){std::cout << "glm::barycentricCorrect det is zero" << std::endl;return bc;}bc.x = bc.x / A.z / det;bc.y = bc.y / B.z / det;bc.z = bc.z / C.z / det;return bc;
}
剩下要做的就只有在 glProgram::Draw
函数内,把对 barycentric
改为 barycentricCorrect
:
Vec3f bc_screen = glm::barycentricCorrect(Vec3f(viewport_coords[0].x, viewport_coords[0].y, vertexs_w[0]),Vec3f(viewport_coords[1].x, viewport_coords[1].y, vertexs_w[1]),Vec3f(viewport_coords[2].x, viewport_coords[2].y, vertexs_w[2]), P);
结果:
最后还需要提一点,本文提到的透视矫正只针对透视投影来说,正交投影不存在这个问题。
本次代码提交记录:
这个版本的
LookAt
函数存在错误!2025-4-29 16.23 提交修复
参考
- Technical difficulties: linear interpolation with perspective deformations
- perspective-correct-interpolation.dvi
相关文章:
tinyrenderer笔记(透视矫正)
tinyrenderer个人代码仓库:tinyrenderer个人练习代码 引言 还要从上一节知识说起,在上一节中我为了调试代码,换了一个很简单的正方形 obj 模型,配上纹理贴图与法线贴图进行渲染,得了下面的结果: what&…...
c++类【发展】
类的静态成员(用static声明的成员),在声明之外用例单独的语句进行初始化,初始化时,不再需要用static进行限定。在方法文件中初始化。以防重复。 特殊成员函数 复制构造函数: 当使用一个对象来初始化另一个对象…...
玛格丽特鸡尾酒评鉴,玛格丽特酒的寓意和象征
玛格丽特鸡尾酒会有独特的风味,而且还会有一个比较吸引人的背后故事。在目前的鸡尾酒界就会占据着很重要的地位,不仅是味蕾的盛宴,同样也会拥有深厚的情感。 玛格丽特由龙舌兰酒、柠檬汁和君度橙酒调制而成,将三者巧妙地结合在一起…...
关于Java多态简单讲解
面向对象程序设计有三大特征,分别是封装,继承和多态。 这三大特性相辅相成,可以使程序员更容易用编程语言描述现实对象。 其中多态 多态是方法的多态,是通过子类通过对父类的重写,实现不同子类对同一方法有不同的实现…...
SecureCrt设置显示区域横列数
1. Logical rows //逻辑行调显示区域高度的 一般超过50就全屏了 2. Logical columns //逻辑列调显示区域宽度的 3. Scrollback buffer //缓冲区大小...
【PhysUnits】1 SI Prefixes 实现解析(prefix.rs)
一、源码 // prefix.rs //! SI Prefixes (国际单位制词头) //! //! 提供所有标准SI词头用于单位转换,仅处理10的幂次 //! //! Provides all standard SI prefixes for unit conversion, handling only powers of 10.use typenum::{Z0, P1, P2, P3, P6, P9, P12, …...
【Python】--实现多进程
import multiprocessing import time # 1.定义好函数 # codeing def coding():for i in range(10):print(f正在编写第{i}行代码)time.sleep(0.2)# music def music():for i in range(10):print(f正在听第{i}首歌曲)time.sleep(0.2)单任务 # 单任务--时间为4s多 if __name__ _…...
计算机视觉与深度学习 | 基于数字图像处理的裂缝检测与识别系统(matlab代码)
🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅 基于数字图像处理的裂缝检测与识别系统 🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦**系统架构设计****1. 图像预处理**目标:消除噪声+增强裂缝特征**2. 图像分割**目标:提取裂缝区域**3. 特征…...
嵌入式MCU语音识别算法及实现方案
在嵌入式MCU(微控制器单元)中实现语音识别,由于资源限制(如处理能力、内存、功耗等),通常需要轻量级算法和优化技术。以下是常见的语音识别算法及实现方案: 一、传统语音识别算法 动态时间规整&…...
【C++核心技术深度解析:从继承多态到STL容器 】
一、C继承机制:代码复用与层次设计 1. 继承基础概念 什么是继承? 继承是面向对象编程的核心机制,通过class Derived : public Base让子类(派生类)复用父类(基类)的属性和方法,同时…...
【C/C++】new关键字解析
📘 C 中 new 关键字详解笔记 🔹 什么是 new? new 是 C 中用于动态内存分配的关键字,它在堆内存中为对象或变量分配空间,并返回对应类型的指针。 与 C 语言中的 malloc 相比,new 更安全、更方便ÿ…...
C++高性能内存池
目录 1. 项目介绍 1. 这个项目做的是什么? 2. 该项目要求的知识储备 2. 什么是内存池 1. 池化技术 2. 内存池 3. 内存池主要解决的问题 4.malloc 3. 先设计一个定长的内存池 4.高并发内存池 -- 整体框架设计 5. 高并发内存池 -- thread cache 6. 高并发内存池 -- …...
chili3d调试笔记12 deepwiki viewport
xiangechen/chili3d | DeepWiki viewport阅读 🧠deep 我要把模型投影成dxf导出有什么办法 引用lookat 截图是如何实现的 明天接着搞 ----------------------------------------------------------------...
前端取经路——JavaScript修炼:悟空的九大心法
大家好,我是老十三,一名前端开发工程师。JavaScript如同孙悟空的七十二变,变化多端却又充满威力。本篇文章我将带你攻克JS中最令人头疼的九大难题,从闭包陷阱到原型链继承,从异步编程到性能优化。每个难题都配有实战代…...
从零实战:在Xilinx Zynq PS端移植VxWorks 6.9系统
一、环境准备与工具链搭建 1.1 硬件配置清单 开发板: Zynq-7000系列(推荐ZedBoard或ZCU102)调试工具: USB-JTAG调试器(如Xilinx Platform Cable USB II)存储介质: SD卡(建议Class 10以上)1.2 软件环境 工具版本作用Vivado2022.1FPGA硬件设计Vitis2022.1系统集成开发Wind…...
网工实验——RIP配置
网络拓扑图 配置 1.为每台设备配置ip地址 AR4 <Huawei>u t m <Huawei>sys [Huawei]sysname AR4 [AR4]int g0/0/0 [AR4-GigabitEthernet0/0/0]ip address 172.16.1.1 24 [AR4-GigabitEthernet0/0/0]q#下面配置换回口,模拟网 [AR4]int LoopBack 0 [AR4…...
前端流行框架Vue3教程:14. 组件传递Props效验
(4) 组件传递Props效验 Vue组件可以更细致地声明对传入的props的校验要求 ComponentA.vue <script> import ComponentB from ./ComponentB.vue; export default {components: {ComponentB},data() {return {title: 标题}} } </script> <template><h3&g…...
电子电器架构 --- 网关ECU中采用多CPU解决方案来实现网关功能
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...
关于tftpboot的用法
TFTPBOOT 是一个常用于嵌入式系统或网络设备中的命令,用于通过 TFTP 协议从网络上启动操作系统镜像或引导文件。这个命令通常在设备启动时执行,允许设备通过网络从 TFTP 服务器下载启动镜像或其他必要的文件,而不需要从本地存储中启动。 一般…...
团队协作的润滑剂——GitHub与协作流程
各位代码界的社交恐惧症患者们,今天我们要聊的是如何假装自己很会团队协作——使用GitHub!这就像程序员版的"相亲平台",只不过在这里,你展示的不是自拍和收入,而是代码和commit记录(后者往往更令…...
数据库复习
DML操作包括: SELECT INSERT UPDATE DELETE MERGE 返回字符串长度:length() 查询记录:SELECT 增(INSERT)、删(DELETE)、改(UPDATE)、查(SELECT&#…...
AI与机器学习、深度学习在气候变化预测中的应用与实践
前言: 全球气候变化是现代社会面临的最重要的环境挑战之一,影响了气温、降水、海平面、农业、生态系统等多个方面。气候变化的驱动因素主要包括温室气体排放、气溶胶浓度、火灾频发、海冰融化、叶绿素变化、农业变化和生态环境变化等。这些因素在全球范围…...
Laravel 12 基于 EMQX 实现 MQTT 消息发送与接收
Laravel 12 基于 EMQX 实现 MQTT 消息发送与接收 要在 Laravel 12 中实现基于 EMQX 的 MQTT 消息发送与接收,你可以按照以下步骤操作: 1. 安装必要的依赖包 首先安装 MQTT 客户端库: composer require php-mqtt/client2. 配置 EMQX 连接 …...
论广告系统对存算分离架构的应用
辅助论点 辅助论点一:存算分离架构起源于数据库领域,并不是在线系统。 存算分离的架构源于Google的Spanner数据库,这个数据库采用了KV做存储层,OLAP做计算层的分离式设计,其目的是能快速伸缩计算资源,且节…...
create-vue搭建Vue3项目(Vue3学习2)
一、认识create-vue image.png 二、create-vue搭建Vue3项目 image.png image.png 依次执行npm install 和npm run dev即可运行项目 image.png image.png © 著作权归作者所有,转载或内容合作请联系作者 喜欢的朋友记得点赞、收藏、关注哦!!ÿ…...
NHDEEP档案管理系统功能介绍
NHDEEP档案管理系统单机版专注于提高档案管理效率,无需网络连接即可独立运作,确保数据的安全与私密性。无论是机关单位的常规档案工作,还是工程、基建项目的特殊档案管理需求,系统都能提供全面的解决方案。系统支持信创环境。 核心…...
【C++】C++中的命名/名字/名称空间 namespace
C中的命名/名字/名称空间 namespace 1、问题引入2、概念3、作用4、格式5、使用命名空间中的成员5.1 using编译指令( 引进整个命名空间) ---将这个盒子全部打开5.2 using声明使特定的标识符可用(引进命名空间的某个成员) ---将这个盒子中某个成员的位置打…...
游戏引擎学习第260天:在性能分析器中实现钻取功能
昨天那个帧内存满之后触发段错误实在没找到什么原因导致的 继续研究一下为什么导致的 内存不够进来释放frame 释放frame 应该会给DebugState->FirstFreeStoredEvent 赋值吧 这段宏定义: #define FREELIST_DEALLOCATE(Pointer, FreeListPointer) \if(Pointer) {…...
人工智能100问☞第15问:人工智能的常见分类方式有哪些?
目录 一、通俗解释 二、专业解析 三、权威参考 人工智能的常见分类方式包括:按智能水平(弱人工智能、通用人工智能、超级人工智能)、按技术原理(生成式AI、判别式AI、强化学习)、按功能目标(生成内容、优化决策)、按应用领域(自然语…...
JavaSE核心知识点01基础语法01-04(数组)
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 JavaSE核心知识点01基础语法01-04࿰…...
抖音代播领航者——品融电商(PINKROON)的运营实力与服务解析
抖音代播领航者——品融电商(PINKROON)的运营实力与服务解析 在兴趣电商高速发展的背景下,杭州品融品牌管理有限公司(PINKROON)凭借其全域增长方法论与抖音生态的深度布局,成为众多品牌首选的抖音代播服务商…...
LeetCode 790 多米诺和托米诺平铺 题解
对于本题不去看LeetCode的评论区和题解很难想到如何去dp,毕竟就算再怎么枚举也很难找到适用于面向结果的规律。所以对于题解我建议大家还是去看一下灵神给的题解,以下是灵神汇总的图,如果能看懂的话,对于解决题目有很大的帮助。 根…...
力扣-hot100 (缺失的第一个正数)
41. 缺失的第一个正数 困难 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1: 输入:nums [1,2,0] 输出:3 解释ÿ…...
Electrolink信息泄露(CVE-2025-28228)
免责声明 本文档所述漏洞详情及复现方法仅限用于合法授权的安全研究和学术教育用途。任何个人或组织不得利用本文内容从事未经许可的渗透测试、网络攻击或其他违法行为。使用者应确保其行为符合相关法律法规,并取得目标系统的明确授权。 对于因不当使用本文信息而造成的任何直…...
Leetcode Hot 100 三数之和
思路 对数组先排序,然后使用双指针法进行,并且整个过程需要把握去重的逻辑 代码 class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:if not nums:return []nums.sort() #去重都需要排序res[]for i in range(len(nums)):if i…...
5月6日日记
一点心得是 看通知要仔细认真,自己想问的问题要先看看通知或者文件中说了没有,如果没说再去问相关负责人。 上课的教室一定要看好,看准了再去。别像今天一样先去了科技楼又去了工学馆。 线代开课了。感觉总体还行,并不是很难。…...
巧记英语四级单词 Unit7-中【晓艳老师版】
collapse v.倒塌,坍塌 都(col)来扑(lap)噻immune a.不受影响的,免疫的 我im 木讷mune,人应该木讷一点yard n.院子 鸭的,在哪养;backyard 后院backward a.往后的 ward表示方向 profile n.外形,轮廓 从前面看…...
Windows系统修改Docker Desktop(WSL2)内存分配
# Windows查看docker信息 docker info 新增wsl全局配置文件(.wslconfig文件):windows路径栏输入:%UserProfile%,找到目录C:\Users\Administrator,默认是没有这个配置文件的,可以自己新增 # 设置在wsl2上运行 [wsl2] # …...
Oracle02-安装
零、文章目录 Oracle02-安装 1、Windows Server2022安装Oracle11g (1)下载 百度网盘地址: https://pan.baidu.com/s/15MBkMt1ldbSFm4L74h7Myg?pwd8888下载完成两个压缩包解压放在一起 (2)安装 双击 setup 文件安…...
Linux[Makefile]
Makefile基础结构 规则语法 target: prerequisitescommandtarget:生成的目标(如可执行文件、.o文件) prerequisites:依赖项(源码、头文件等) command:构建命令(必须用Tab缩进&am…...
相同的数(简单)
深度优先搜索 如果两个二叉树都为空,则两个二叉树相同。如果两个二叉树中有且只有一个为空,则两个二叉树一定不相同。 如果两个二叉树都不为空,那么首先判断它们的根节点的值是否相同,若不相同则两个二叉树一定不同,…...
「Mac畅玩AIGC与多模态22」开发篇18 - 多段输出拼接与格式化展现工作流示例
一、概述 本篇以已有多字段输出为基础,介绍如何通过执行 LLM 节点对多个上游字段进行统一拼接与格式化处理。开发人员将学习如何从多个节点输出中提取数据字段,并组合为结构清晰、风格统一的最终输出,提升用户阅读体验。 二、环境准备 mac…...
餐饮部绩效考核管理制度与综合评估方法
在竞争激烈的餐饮行业中,标准化与数据驱动的管理手段正成为提升服务质量与运营效率的关键。绩效考核不仅关乎员工奖惩,更直接影响顾客体验、成本控制与营收水平。构建一套科学有效的绩效体系,是餐饮部精细化运营的起点。 本文围绕餐饮部绩效…...
conda虚拟环境相关操作
查看当前存在哪些虚拟环境 conda env list conda info --env创建虚拟环境conda create -n env_name pythonX.X删除虚拟环境conda remove -n env_name --all查看安装了哪些包conda list下载/删除环境中的某个包conda install package_nameconda uninstall package_name删除所有未…...
达梦DM数据库安装步骤
文章目录 1、下载并解压缩2、安装DM数据库2.1 运行安装程序2.2 选择语言与时区2.3 安装向导2.4 许可证协议2.5 Key文件2.6 选择组件2.7 安装位置2.8 安装前小结2.9 安装过程2.10 已完成2.11 初始化 3、配置实例3.1选择操作方式3.2创建数据库模版3.3指定数据库目录3.4数据库标识…...
vue3在使用@import “./index.scss“报错
Deprecation Warning: Sass import rules are deprecated and will be removed in Dart Sass 3.0.0. More info and automated migrator: https://sass-lang.com/d/import 2 │ import "./index.scss"; 在 Sass 3.0.0.之后 导入样式使用 “use” ,不在使…...
对标研华ECU-461,搭载飞腾4核/8核国产处理器, 提供8网 8串B码对时 双显 无风扇的ARM通信管理平台
飞腾 FT-2000/4 和 D2000/8 主控制器,主频 2.3~2.6GHz 8 个千兆网口 , 8 个全功能隔离串口 HDMIVGA 双显示接口 3 个 USB2.0, 2 个 USB3.0 支持 2 组 SATA 硬盘存储 支持 CAN 通讯 ( 替换 4 路或 8 路 COM) 整机无风扇散热设计 …...
如何将C#程序打包成软件绿色包
文章目录 前言步骤如下:总结 前言 在实际工作中,很多时候会开发一些特别小的工具,当这些工具需要发给别人用时,不值当的打个安装包,最适合做一个绿色包,别人拿到后,直接双击exe就可以用。 步骤…...
实验三 数据查询
一、【实验教学 1、掌握单表查询。 2、掌握多表查询。 二、【实验教学的基本要求】 1、掌握SQL程序设计基本规范; 2、熟练运用SQL实现数据基本查询,包括单表查询、分组统计查询和连接查询; 3、理解和掌握SQL查询语句中各个子句的特点和…...
关于串口读写NAND闪存的用法
在嵌入式系统中,nand 命令通常用于操作和管理 NAND 闪存子系统,特别是在引导加载程序(如 U-Boot)中。NAND 闪存是一种非易失性存储设备,广泛用于嵌入式设备中,用于存储操作系统、应用程序、配置文件等数据。…...