基于链表的OIT实现:每像素透明片元排序与合成
简介OpenGL基于链表的无序透明度渲染OIT实现资料面向计算机图形学学习者和游戏渲染开发者解决传统画家算法必须按深度排序绘制透明物体、在动态场景中效率低下且难以正确叠加的痛点。压缩包共458个文件整体约846KB主要由头文件、内联模板文件、C源文件与多重着色器构成同时包含常用数学库、图像加载库和少量示例模型与效果图。已有137人浏览学习。借助主程序以及渲染、合成、光照三类着色器的完整代码读者可以搭建可运行的工程逐步理解每个像素如何维护透明片段链表、如何调整深度测试与混合状态、如何在光照计算后遍历链表完成颜色累加合并并通过自带的模型和效果图对比透明物体多层交叠、阴影和折射的渲染差异掌握从理论机制到实际应用的完整路径。1. 基于链表的OIT为什么值得把每个像素的透明片元串成链表OpenGL基于链表的OIT是我在多个透明渲染项目里踩出来的一个判断只要场景里出现两层以上互相穿插的半透明物体常规的“先画不透明、再对透明物体按距离从远到近排序”就会开始翻车玻璃杯、粒子、纱网叠在一起时排序顺序会互相矛盾怎么调都不对。链表式OIT的思路反直觉但有效不光在像素上留一个颜色而是给每个像素挂一串“片元链表”把深度、颜色、下一个节点位置都存下来最后再统一排序合成。适合做实时渲染、粒子特效、室内外可视化的人员也适合刚接触透明渲染但想绕过深度排序噩梦的入门者。这个资源的核心价值就是让你从零实现一套不依赖物体排序的透明管线同时看懂它为什么会比深度剥离和加权混合更省事。2. 为什么选链表数据结构、原子操作与内存开销的权衡2.1 三种OIT方案的取舍链表、深度剥离与加权混合先交代选型背景。OITOrder Independent Transparency在图形学里不是新话题但每一代硬件都会让解法变一次。最经典的是深度剥离把透明物体按深度一层层渲染每一层只取出最近的那一个片元渲染几十遍才够把五层以上的透明物体剥干净画质好内存可控但Pass太多场景复杂到十层时性能已经不能看。加权混合则是把颜色和深度都加权累积成一个值占用极小性能极好但它输出的是统计意义上的近似结果边缘和遮挡关系都会出现一定误差做阴影和多层玻璃时尤其明显。基于链表的OIT刚好站在两者中间它不重排整个场景的三角形而是在屏幕空间里为每个像素维护一份“透明片元清单”最后只需要对这些片元排序。它避免了深度剥离的多Pass开销也避免了加权混合的近似误差。这个资源选择链表结构而不是数组或者向量列表关键原因是粒度和内存分配方式每个片元只分配一个节点插入链表只做一次原子替换不要求预先知道某个像素会产生多少片元这非常适合透明片段数量分布不均的场景——有些像素只有两层有些像素可能叠了二十层。2.2 每像素链表的数据结构节点字段与SSBO布局链表式OIT的核心数据结构可以拆成三块头指针图像Head Pointer Image、节点缓冲区Node Buffer、原子计数器Atomic Counter。头指针用一张R32UI格式的2D纹理保存纹理上每个像素对应屏幕上的一个像素存储的是该像素链表的第一个节点索引。节点缓冲区用SSBO实现每个节点记录一份颜色、一个深度、一个next指针。原子计数器负责分配节点每写入一个透明片元就递增一次返回的值就是新节点的数组下标。我用GLSL描述一个最常见的节点布局#version 430 core layout(std430, binding 2) buffer NodeBuffer { uvec4 nodes[]; }; // 节点内存布局定义 // nodes[].x 压缩后的RGBA颜色 // nodes[].y 压缩后的片元深度 // nodes[].z 下一个节点编号无后继时为 0xFFFFFFFF // nodes[].w 保留字段留给后续扩展示例中固定为 0实际使用中不需要定义单独的结构体数组用uvec4就能凑满16字节对齐方便又省事。颜色用packUnorm4x8压缩成32位深度用floatBitsToUint压进32位next占32位余下32位留给自定义标记。这样每个节点正好16字节SSBO对齐规则下不会有任何空洞读写性能也最好。如果追求更高精度可以直接在uvec4基础上改成vec4存颜色代价是节点变成32字节显存压力翻倍。2.3 原子操作与帧生命周期为什么每次要清空链表每像素链表的关键操作是“头插法”。当一个透明片元到达时先通过atomicCounterIncrement拿到一个新节点索引然后读出当前像素的头指针再做一次imageAtomicExchange把新节点写入头指针。这个过程必须是原子的否则两个片元同一帧同时写入同一个像素时链表会断开或者产生循环引用。帧开始时必须先清空所有缓冲常见做法有三步头指针纹理Clear成0xFFFFFFFF节点计数器重置为0节点缓冲区不需要清空数据因为所有有效节点都会从头指针重新遍历到。这里有一个很容易忽略的点渲染多帧时如果上一帧残留的节点索引没有清理第二帧的第一个透明片元会插入到一条脏链表里画面出现闪烁和残迹而且这种错误不是每次都能复现调试起来非常痛苦。我一般会在每帧开始用glClearTexImage清头指针再把原子计数器的缓冲区子数据置0顺序不能颠倒。3. Pass拆解从写链表到读链表的完整渲染流程3.1 Pass 0深度预渲染把透明片元分成前后两批链表式OIT并不是拿到所有片元就无脑插入透明片元与不透明场景混合时有一个顺序问题被不透明物体完全挡住的透明片元不应该参与排序否则会在半透明界面上形成错乱的颜色。所以标准做法是先渲染一遍不透明场景把深度信息存进深度缓冲区然后在透明Pass里只接受深度大于等于当前深度缓冲值的片元。这步不需要额外的帧缓冲在透明Pass的片段着色器里做一次深度比较即可。很多初学者会跳过这一步结果是不透明物体后面的透明三角形依然写入链表渲染出的画面里出现“穿透雾状色块”尤其是场景有大量地形和建筑时特别明显。预渲染不透明场景的代价是一次场景提交但对于透明物体多的项目这几乎省不掉。3.2 写入链表透明几何体Pass的顶点与片段工作下面的着色器是链表写入Pass的核心直接对应资源里的主Pass版本#version 430 core layout(location 0) out vec4 outColor; layout(binding 0, r32ui) uniform coherent uimage2D headPointerImage; layout(binding 1, offset 0) uniform atomic_uint nodeCounter; layout(std430, binding 2) buffer NodeBuffer { uvec4 nodes[]; }; void main() { ivec2 px ivec2(gl_FragCoord.xy); // 获取当前透明片元颜色alpha 低于阈值的片元直接丢弃 vec4 transparentColor outColor; if (transparentColor.a 0.05) { discard; } // 从原子计数器分配节点索引超过上限则丢弃当前片元 uint nodeIndex atomicCounterIncrement(nodeCounter); if (nodeIndex MAX_NODE_COUNT) { discard; } // 构造节点 uvec4 node; node.x packUnorm4x8(transparentColor); node.y floatBitsToUint(gl_FragCoord.z); // 把节点插入到当前像素链表头部 uint oldHead imageAtomicExchange(headPointerImage, px, nodeIndex); node.z oldHead; node.w 0u; nodes[nodeIndex] node; }这段代码有几个参数值得细说。MAX_NODE_COUNT是你在申请SSBO时定死的容量上限它决定了一帧内最多能写多少个透明片元。atomicCounterIncrement返回的是自增前的旧值所以第一次调用它返回0正好对应当前帧的第一个节点。imageAtomicExchange返回的是旧头指针可能来自当前帧也可能来自未清理的上帧因此帧清理必须严格。alpha阈值的含义是alpha为0的片元对画面没有任何贡献但它们依然占用节点槽位密集的粒子系统如果忽略这一步节点缓冲区会在半秒内被填满。3.3 读取链表在合成Pass中恢复并遍历节点写入成功后合成Pass要做的就是读取头指针遍历这条链表把所有节点收集到局部数组排序后合成颜色。最简单的读取版本如下#version 430 core layout(location 0, index 0) out vec4 finalColor; layout(binding 0, r32ui) uniform readonly uimage2D headPointerImage; layout(std430, binding 2) readonly buffer NodeBuffer { uvec4 nodes[]; }; #define LOCAL_STACK_SIZE 32 void main() { ivec2 px ivec2(gl_FragCoord.xy); uint head imageLoad(headPointerImage, px).r; uint stack[LOCAL_STACK_SIZE]; uint count 0u; uint current head; while (current ! 0xFFFFFFFFu count LOCAL_STACK_SIZE) { stack[count] current; current nodes[current].z; } // 后续排序与合成代码在下一章展开 }我在这里用固定长度数组做局部栈最大支持32个透明片元同屏叠加。这个数字是有讲究的超过32层透明物体叠在同一个像素时视觉差异基本只剩亮度变化绝大多数项目根本堆不满32层。局部栈的长度改小能减少寄存器占用但改大可能导致着色器占用过多寄存器而被迫降低occupancy实际性能不升反降。遍历结束后排序的真正难点不是遍历而是如何保证头指针指向的节点就是几何上正确的起点。注意如果链表在遍历过程中出现current等于已经访问过的节点就说明链表已经成环一般是上一帧节点复用没有清理导致的此时应该优先检查帧清理逻辑而不是继续调试排序。4. 排序与合成把链表里的片元变成正确的透明颜色4.1 排序算法的选择为什么局部插入排序比归并排序更好链表收集到的节点是无序的写入顺序取决于几何体提交顺序和光栅化顺序。排序目标是按深度从远到近排列合成时从最远的片元开始往前混合。对于深度排序理论上归并排序和快速排序都不差但在GPU着色器里实现它们有一个共同痛点动态数组和递归支持有限而且分支发散严重。局部插入排序虽然时间复杂度是O(n²)但n代表每个像素上的透明片元数绝大多数像素小于8在这么小的样本量下插入排序的常数小、无递归、无额外暂存反而是最稳的选择。在shader中实现插入排序通常把深度排序和颜色读取一起完成。下面这段代码接在读取链表之后// 从节点数组取出深度用于排序 // 使用插入排序按深度从近到远 for (uint i 1u; i count; i) { uint key stack[i]; float keyDepth uintBitsToFloat(nodes[key].y); int j int(i) - 1; while (j 0) { uint other stack[j]; float otherDepth uintBitsToFloat(nodes[other].y); // 深度更远的节点往后移近的往前 if (otherDepth keyDepth) { break; } stack[j 1] stack[j]; --j; } stack[j 1] key; }循环里反复调用uintBitsToFloat做深度还原稍微有一点重复计算但n小于32时完全可忽略。这个排序是按“从近到远”排列的也就是说stack[0]是最近的那个透明片元。如果你希望在合成Pass里避免深度比较也可以把排序方向反过来记住合成公式对应的是哪个方向就行。4.2 透明合成公式预乘Alpha与常规Alpha混色的区别透明度合成最常用的公式是source-over新片元颜色乘alpha后叠到背景上。但链表OIT中的片元彼此之间没有固定的绘制顺序必须自己控制合成方向。常见做法是从最近的片元开始把它当作背景然后依次用后面的片元混合它或者从最远的片元开始把它作为最底层逐层向观察者方向用source-over叠加。资源里采用的通用流程是从远到近合成合成公式可以写成vec3 compositeColor mix(vec3(0.0), vec3(unpackedColor), unpackedAlpha); float compositeAlpha 0.0;这里的预乘处理是为了避免半透明颜色出现“边缘白边”。如果你直接使用常规alpha混合公式来做多个透明片元的递归合成每个片元都乘一次alpha亮色叠加后会迅速变暗而且半透明物体边缘会因为像素覆盖率不同产生黑边。预乘Alpha对纹理贴图的RGBA也有额外要求美术给贴图时应该把RGB先乘过alpha或者在采样完成后手动处理。4.3 完整的合成Shader示例把所有环节拼起来合成Pass的完整逻辑如下#version 430 core layout(location 0, index 0) out vec4 finalColor; layout(binding 0, r32ui) uniform readonly uimage2D headPointerImage; layout(std430, binding 2) readonly buffer NodeBuffer { uvec4 nodes[]; }; #define LOCAL_STACK_SIZE 32 void main() { ivec2 px ivec2(gl_FragCoord.xy); uint head imageLoad(headPointerImage, px).r; uint stack[LOCAL_STACK_SIZE]; uint count 0u; uint current head; while (current ! 0xFFFFFFFFu count LOCAL_STACK_SIZE) { stack[count] current; current nodes[current].z; } // 按深度从近到远排序 for (uint i 1u; i count; i) { uint key stack[i]; float keyDepth uintBitsToFloat(nodes[key].y); int j int(i) - 1; while (j 0) { uint other stack[j]; float otherDepth uintBitsToFloat(nodes[other].y); if (otherDepth keyDepth) { break; } stack[j 1] stack[j]; --j; } stack[j 1] key; } // 从最远到最近合成 vec3 color vec3(0.0); float alpha 0.0; for (uint k 0u; k count; k) { uint id stack[k]; float nodeDepth uintBitsToFloat(nodes[id].y); vec4 nodeColor unpackUnorm4x8(nodes[id].x); // 预乘Alpha方式合成 color mix(color, nodeColor.rgb * nodeColor.a, nodeColor.a); alpha nodeColor.a * (1.0 - alpha); } // 叠加到当前像素已有颜色 ivec2 pxCoord px; // 透明像素背景就是黑色实际项目中需由外部传入不透明背景颜色 finalColor vec4(color, alpha); }这里还有一个细节链表节点中可能混入被遮挡片元它们深度值比当前像素的不透明背景更深。合成前要么在Pass 0时就丢弃这些片元要么在合成后把它们从结果中剔除。后者实现更简单在合成循环里加一个最大深度阈值深度值超过背景深度的节点直接跳过。我在资源给出的示例里是把这一步放在写入Pass中用不透明深度缓冲做提前剔除效果更好也省节点槽位。5. 避坑链表式OIT实现中四个让人头大的实际问题5.1 现象画面出现大面积黑色闪烁或者干脆整个屏幕黑掉原因最典型的是SSBO容量不够或者原子计数器溢出。当你写入的透明片元数量超过MAX_NODE_COUNT时着色器里把节点索引判断为无效并discard此时头指针仍然被更新但节点数据并没有写入下次遍历时拿到的深度全是0排序后混出一个极不稳定的结果。黑屏通常就是这个原因尤其在粒子系统全开时。解决排查分两步。第一步把MAX_NODE_COUNT调大看能否解决问题。第二步在合成Pass里把节点的深度、颜色值都可视化输出到屏幕上检查是哪一类片元给到错误数据。如果确认是计数器溢出建议在场景里记录一帧的原子计数器自增值并做成调试面板显示这样就不用肉眼猜上限该设多少。5.2 现象半透明物体边缘出现细密黑边跟贴图是否透明无关原因这是典型的非预乘Alpha导致的采集错误。写入链表时片元颜色被packUnorm4x8压缩如果纹理贴图本身就是半透明边缘颜色那么它在边缘处的RGB可能与alpha变化不同步。合成过程中用了两次alpha乘边缘透明度低的像素混合了两次产生黑边。解决把写入前的transparentColor改为预乘形式也就是要求Diffuse贴图在采样后执行rgb * a。如果美术资源不方便改就在写入Pass里用vec4(transparentColor.rgb * transparentColor.a, transparentColor.a)替换原色。改动之后合成循环里不再重复乘alpha边缘黑边通常会消失。5.3 现象窗口缩放后透明物体出现固定位置的断裂带原因链表头指针纹理的大小与窗口绑定窗口resize后纹理尺寸变了但SSBO和原子计数器没有跟着重置或者头指针纹理只用glTexImage2D指定过一次缩放后没重新分配。于是新旧两帧的像素坐标不一致链表的遍历会在某个位置读到旧数据形成一条撕裂线。解决在窗口resize回调里重新分配头指针纹理并重新做整帧清理。比较保险的顺序是先重新分配头指针纹理再渲染时用glClearTexImage清理头指针最后重置原子计数器。只要你把这三步放进一个叫resetOITState()的函数里每次resize和每帧开头都调用它就不会出现撕裂。5.4 现象性能突然下降一个两万三角面的场景居然跑不满60帧原因链表节点写入的原子操作很贵特别是在同一个像素上几十个片元同时写入时imageAtomicExchange竞争严重。不过更常见的原因是没有设置深度预渲染透明物体后面的不透明几何体产生大量不可见片元全部进入链表把这些无效节点全部排序和合成白白消耗带宽。解决先确认Pass 0是否存在。如果存在再看透明物体的包围盒是否裁剪正确。此时可以在合成Pass里加一个可视化模式把count值映射成颜色越红的像素说明链越长用这个热力图找热点区域。大部分情况下热点都在粒子系统中心或者玻璃幕墙重叠处热点区域节点的数量决定性能上限。设置节点MAX_NODE_COUNT时参考的是热点像素的节点数量而不是全局均值。注意原子计数器在部分驱动上不支持从着色器内读取调试时不要直接在片段里读它先用glGetBufferSubData把计数拿回CPU侧最稳。6. 进阶验证与调参如何判断这套OIT实现是否完全正确验证OIT效果不能只看一个角度。我习惯把相机设定在透明物体密集的位置缓慢旋转并录制视频然后逐帧检查两处一是透明物体互相穿插的边缘是否稳定二是移动相机时链表节点是否会出现“闪烁尾迹”。一旦出现尾迹说明节点缓冲区或者头指针纹理存在上一帧残留此时我会在Debug模式下把合成Pass输出改成块状色块用颜色标识当前像素的链长链长跳变的帧就是问题帧。为了量化排序质量我会在画面上放置一个半透明的阶梯测试模型各个台阶距离依次拉远并且在台阶后面放置一个表现明显的条纹贴图背景。如果排序正确条纹背景透过三个台阶后依然保持清晰边缘如果排序错乱条纹边缘会明显变糊。这个测试成本低而且不需要额外输出数据肉眼就能判断。下一步是调参。链表OIT最值得调的是三个变量MAX_NODE_COUNT、LOCAL_STACK_SIZE、alpha阈值。三者不是独立存在的。MAX_NODE_COUNT提高以后节点占用的显存变大LOCAL_STACK_SIZE如果没跟上多余节点会直接丢弃等于白扩容量alpha阈值调大能省节点但粒子边缘的细碎半透明效果也会被切掉。我曾在粒子系统上限开到10万粒子时把alpha阈值从0.02调到0.08节点容量直接砍掉30%画面观感几乎不变。另一个进阶技巧是输出链表节点深度图。把headPointerImage和nodes[]里存的深度值还原成灰度图再用伪彩色映射可以直观看清链表的空间分布。如果深度图出现明显不连续的点说明某些节点的深度没有正确写入这时不要去怀疑排序而应该回到写入Pass定位packUnorm4x8和floatBitsToUint这一对编码/解码过程。这资源里没有做自动的节点容量估算我在借鉴它做了两个项目后养成了一个固定习惯任何一次透明渲染改动结束都强制走一遍“统计热点链长、检查边缘伪影、验证预乘alpha”这三步。经验是绝大多数OIT问题都来自数据生命周期管理而不是排序算法本身。希望这份记录能帮你在折腾基于链表的OIT时少走一段弯路把精力集中在真正影响画质和性能的地方。本文还有配套的精品资源点击获取

相关新闻

STM32+云IoT+Qt跨平台智能家居系统实战:从传感器到控制界面全链路打通

STM32+云IoT+Qt跨平台智能家居系统实战:从传感器到控制界面全链路打通

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 1:36:09 阅读更多 →
STM32+云平台+Qt:从零搭建智能家居中控全链路实战

STM32+云平台+Qt:从零搭建智能家居中控全链路实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 1:36:09 阅读更多 →
Qt多线程互斥与同步:核心机制、选型与实战避坑指南

Qt多线程互斥与同步:核心机制、选型与实战避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 1:36:09 阅读更多 →

最新新闻

风电水电联合调度优化算法与工程实践

风电水电联合调度优化算法与工程实践

1. 项目背景与核心价值去年参与某省级电网清洁能源消纳项目时,我第一次接触到风-水联合调度这个课题。当时面对风电出力的强随机性和水电调节的滞后性,团队在制定优化运行方案时遇到了不少挑战。这个经历让我深刻认识到:在双碳目标下&#xf…

2026/10/14 2:21:24 阅读更多 →
从PowerDesigner迁移到PDMan:开源数据库建模与Git版本管理实践

从PowerDesigner迁移到PDMan:开源数据库建模与Git版本管理实践

1. 为什么我最终把数据库建模从商业软件迁到了PDMan做后端开发时间长了,数据库建模这件事几乎绕不开。早些年团队里清一色用PowerDesigner,画物理模型、生成DDL、导出字段文档,一套流程走下来确实顺手。但问题也很明显:正版授权费…

2026/10/14 2:21:24 阅读更多 →
微信小程序仿智行火车票开发实战:云开发+数据离线化方案

微信小程序仿智行火车票开发实战:云开发+数据离线化方案

简介:本资源是一套完整的微信小程序仿智行火车票12306系统实战项目,面向Java后端与小程序前端初学者及全栈学习者,聚焦铁路订票核心业务场景,涵盖用户查询、车次管理、订单生成等全流程功能实现。项目采用SSM(SpringSp…

2026/10/14 2:21:23 阅读更多 →
RTMP Flash播放插件替代方案:flv.js迁移实战与参数调优

RTMP Flash播放插件替代方案:flv.js迁移实战与参数调优

简介:这份资源是面向需要在网页中播放RTMP流媒体的开发者与运维人员的一套Flash播放插件实现方案,核心解决旧系统或特定场景下通过Flash技术播放RTMP直播、点播流的问题。压缩包共5个文件,约1.15MB,包含2个swf播放器与安装检测组件…

2026/10/14 2:21:23 阅读更多 →
AI编程助手代码质量救星:TypeScript静态类型约束实战

AI编程助手代码质量救星:TypeScript静态类型约束实战

AI 编程助手越帮越乱,这个话题我最近真的是感同身受。我们团队接入 AI 编程助手之后,合并请求里的代码量倒是上去了,可编译期错误非但没减少,反而冒出来一批"语法完全合法、类型对不上、一跑就崩"的代码。后来大家坐下来…

2026/10/14 2:21:23 阅读更多 →
LaTeX 2025环境配置:texlive + TeXstudio

LaTeX 2025环境配置:texlive + TeXstudio

文章目录Latex2025环境配置一、LaTeX 简介1.1 什么是 LaTeX?1.2 为什么使用 LaTeX?1.3 如何开始使用 LaTeX?二、texlive下载安装2.1 texlive下载2.2 texlive安装2.3 常见问题2.3.1 用管理员权限打开install-tl-windows.bat 报错2.3.2 Latex安…

2026/10/14 2:20:23 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →