news 2026/10/14 1:17:56

DirectX 12光线追踪实战:DXR加速结构与降噪优化指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DirectX 12光线追踪实战:DXR加速结构与降噪优化指南

1. 光线追踪到底解决了渲染里的哪些老问题

1.1 从光栅化到光线追踪的底层逻辑切换

做实时渲染的人都知道,过去二十多年我们一直在用光栅化管线。它的核心思路是把三维场景里的三角形投影到屏幕上,然后逐像素判断覆盖关系、采样纹理、计算光照。这套流程快是真快,但它有个天生的短板:它只关心“这个像素被哪个三角形盖住了”,而不关心“这个像素背后的世界长什么样”。所以反射、折射、全局光照这些效果,传统上只能靠各种贴图技巧去“骗”眼睛。

我举个最典型的例子。你在一个光滑地板上放一面镜子,光栅化管线要表现镜面反射,通常得额外渲染一遍场景,把摄像机镜像到地板下方,再把结果当作纹理贴回地板表面。这个做法叫平面反射,效果凑合,但有几个硬伤:只能处理平面,曲面反射基本没戏;每多一个反射面就多一遍场景渲染,开销线性增长;反射里看不到反射,也就是没有多次弹射。

光线追踪换了个思路。它从摄像机出发,对每个像素发射一条光线,去和场景里的几何体求交。命中之后,根据材质决定是继续弹射、还是采样光源、还是终止。这个“继续弹射”的能力,就是它能天然处理多次反射、折射、软阴影的原因。DirectX Raytracing(DXR)把这套能力做进了GPU管线里,让我们可以在实时帧率下跑光线追踪。

1.2 DXR在DirectX 12里的定位与能力边界

DXR不是另起炉灶的一套API,它是DirectX 12的一个扩展。你可以把它理解成在原有的命令列表、资源绑定、管线状态体系里,新增了几种专门给光线追踪用的对象和着色器阶段。核心新增的东西包括:加速结构(Acceleration Structure)、光线生成着色器、命中着色器、未命中着色器、以及光线追踪管线状态对象。

这里要特别说清楚一个边界:DXR本身不负责“把画面变好看”,它只负责“高效地做光线和几何体的求交”。画面好不好看,取决于你在命中着色器里写了什么光照模型、弹射几次、怎么采样。很多人第一次接触DXR会误以为开了它画面就自动变电影级,实际上你如果命中着色器里只返回一个固定颜色,那画面就是一片纯色。所以真正的工作量在着色器逻辑和降噪上,API只是提供了基础设施。

1.3 为什么现在值得投入精力做DXR

从硬件层面看,支持DXR的显卡已经覆盖了相当广的价位段,不再是旗舰专属。从工具链层面看,PIX、RenderDoc这些调试工具对光线追踪的支持也越来越成熟,排查问题比早期舒服太多。从内容层面看,玩家对画面质量的预期在提升,纯光栅化的画面在反射和阴影上已经很难再有质的突破。

我个人的判断是:如果你在做写实风格的实时渲染项目,光线追踪带来的反射和阴影质量提升,是那种“一眼就能看出来”的差异。尤其是室内场景、水面、金属材质密集的场景,收益非常明显。而如果你做的是卡通渲染或者低多边形风格,那DXR的优先级可以往后放,因为这类风格本身就不追求物理正确的光照。

2. 加速结构:光线追踪的性能地基

2.1 两级加速结构的设计意图

光线追踪最怕的就是“每条光线都和所有三角形求交”。一个中等规模的场景几十万三角形,一帧几百万像素,每条光线都遍历一遍,算力再强也扛不住。加速结构就是用来做空间划分、快速剔除的。DXR用的是两级结构:底层加速结构(BLAS)和顶层加速结构(TLAS)。

BLAS描述的是单个模型内部的三角形分布。比如一个角色模型、一把椅子、一块地形,各自构建一个BLAS。BLAS构建完之后是可以复用的,只要模型本身的顶点数据不变,就不需要重建。TLAS描述的是场景里所有实例的摆放关系,每个实例引用一个BLAS,并带一个变换矩阵。你移动一个物体,只需要更新TLAS里对应实例的变换,不用动BLAS。

这个设计的好处非常直接:场景里如果有大量重复物体,比如一片森林里的树、一条街上的路灯,它们的BLAS只需要建一次,TLAS里放多个实例引用同一个BLAS就行。显存占用和构建时间都能省下来。

2.2 构建参数怎么选:性能与质量的权衡

构建加速结构的时候,有几个参数需要你根据场景特点去调。第一个是构建方式,分快速构建和快速追踪两种。快速构建优先保证构建速度,生成的树质量一般,追踪时剔除效率低一些;快速追踪优先保证树的质量,构建慢但追踪快。怎么选?看你的场景是静态为主还是动态为主。

静态场景,比如建筑漫游、固定关卡,用快速追踪,一次构建多次受益。动态场景,比如每帧都在变形的角色、破碎的物体,用快速构建,因为你要频繁重建BLAS,构建速度比追踪效率更重要。我实测过一个场景,快速追踪相比快速构建,追踪性能大概能提升百分之十五到二十,但构建时间可能翻倍。所以这个取舍要看你的帧预算花在哪。

第二个参数是允许的三角形数量上限和是否允许更新。如果你的模型顶点数会变,比如布料模拟、顶点动画,那BLAS需要允许更新,构建时要带上相应的标志位。但更新比重建慢,如果变化幅度大,不如直接重建。

2.3 实例化与变换更新的实操细节

TLAS的实例更新是每帧都可能发生的操作。这里有个容易踩的坑:很多人以为更新TLAS实例就是改个矩阵,实际上你需要重新构建TLAS,只是构建时引用的BLAS不用重建。DXR提供了更新实例的接口,但底层仍然是一次TLAS的构建过程,只是比全量重建快。

另一个细节是实例的可见性掩码和命中组。可见性掩码可以控制某条光线是否与某个实例求交,这在做阴影光线时特别有用。比如你想让某些物体不投射阴影,就可以在阴影光线的实例掩码里把它排除掉,省去求交开销。命中组则决定了命中这个实例时调用哪个命中着色器,不同材质走不同的着色逻辑。

注意:BLAS和TLAS的构建都需要在GPU上分配显存,而且对对齐有要求。构建前一定要查清楚当前硬件对加速结构缓冲区对齐的具体数值,对齐错了会直接导致构建失败或者追踪结果异常,这个坑我踩过,排查了大半天。

3. 着色器体系:光线在管线里怎么流动

3.1 光线生成、命中、未命中三者的协作

DXR的着色器执行模型和传统光栅化很不一样。传统管线是顶点着色器、像素着色器这样按阶段顺序走。光线追踪这边,入口是光线生成着色器,它负责决定“从哪发射光线、往哪个方向发射、携带什么载荷数据”。你可以把它理解成光栅化里的像素着色器,只不过它每个线程处理的是一个像素或者一个采样点。

光线发射出去之后,会和加速结构求交。如果命中了某个三角形,就调用该三角形所属实例绑定的命中着色器。命中着色器里你能拿到命中距离、重心坐标、三角形索引、实例索引这些信息,然后根据材质计算颜色、决定是否继续弹射。如果没有命中任何东西,就调用未命中着色器,通常用来采样环境贴图或者返回天空颜色。

这里的关键点是:光线弹射不是自动的,是你在命中着色器里手动调用TraceRay发出去的。这意味着你可以精确控制弹射次数、弹射方向、以及每次弹射携带什么数据。这个灵活性是DXR强大的地方,也是容易写错的地方。弹射次数控制不好,要么画面噪点多,要么性能崩掉。

3.2 载荷与属性的数据传递

光线在弹射过程中需要携带数据,比如当前累积的颜色、剩余的弹射次数、随机种子等。这些数据通过载荷(Payload)结构传递。载荷是在光线生成着色器里定义并初始化的,命中着色器和未命中着色器都可以读写它。属性(Attribute)结构则是命中着色器特有的输入,包含重心坐标和三角形几何信息。

我建议把载荷结构设计得尽量紧凑。因为载荷是在寄存器里传递的,太大就会溢出到本地内存,性能下降明显。我见过有人把整个材质参数结构体塞进载荷,结果追踪性能直接腰斩。正确的做法是载荷里只放必要的累积量,材质参数通过实例索引去常量缓冲区或者描述符表里查。

3.3 递归深度与性能的平衡

光线弹射深度是影响画质和性能最直接的参数。弹射一次,只能处理直接光照和一次反射。弹射两次,能处理反射里的反射。弹射三次以上,全局光照的间接效果才比较完整。但每多一次弹射,光线数量是指数级增长的。

我的经验是:实时渲染里,反射弹射两到三次基本够用,再往上收益递减得厉害。阴影光线通常只弹射一次,因为阴影不需要多次弹射。全局光照的间接光弹射,可以配合降噪器,用较低弹射次数加时间累积来逼近高质量结果。不要盲目追求高弹射次数,先把降噪做好,低弹射次数也能出好画面。

4. 降噪:让噪点画面变得可用的关键一环

4.1 为什么光线追踪必须配降噪

光线追踪出来的原始画面,如果每像素只发射少量光线,噪点会非常严重。这是因为蒙特卡洛积分本身是随机采样,采样数不够的时候方差很大。你不可能在实时帧率下每像素发射几百条光线,所以必须用降噪算法把少量光线的结果“猜”成干净画面。

降噪的本质是利用空间邻域和时间历史信息,对当前帧的噪声结果做滤波。空间降噪看相邻像素,时间降噪看前一帧的对应像素。两者结合,就能用每像素一两条光线的成本,得到接近每像素几十条光线的画质。这个技术路线在实时渲染里已经是标配了。

4.2 时间累积与运动向量的配合

时间降噪的核心是运动向量。你需要知道当前帧的像素在上一帧对应哪个位置,才能把历史信息正确地重投影过来。运动向量通常由引擎在渲染几何体时生成,包含摄像机和物体的运动。如果运动向量不对,时间降噪就会产生拖影或者闪烁。

我踩过的一个坑是:透明物体和粒子的运动向量处理。这些物体往往不写入运动向量缓冲区,导致降噪时历史信息对不上,画面出现鬼影。解决办法是在降噪的拒绝逻辑里,对这类区域加大空间降噪的权重,减少时间历史的依赖。或者干脆给它们单独做一套降噪参数。

4.3 降噪器的选型与参数调优

降噪器可以自己写,也可以用现成的方案。自己写的话,通常基于联合双边滤波或者SVGF(Spatio-Temporal Variance Guided Filter)这类算法。用现成方案的话,各家硬件厂商和引擎都有提供。选型的时候重点看几个指标:对运动向量的依赖程度、对透明物体的处理、以及性能开销。

参数调优方面,最关键的几个是:空间滤波核大小、时间累积帧数上限、以及方差引导的阈值。空间核太大画面会糊,太小噪点去不干净。时间累积帧数太多,运动物体会有拖影。方差引导阈值决定了滤波在哪些区域更激进,通常平坦区域可以激进一些,边缘区域要保守。这些参数没有万能值,得根据你的场景和光照条件去试。

5. 混合渲染:光栅化与光线追踪怎么分工

5.1 哪些效果交给光线追踪最划算

全场景光线追踪在实时渲染里仍然太贵,所以主流做法是混合渲染:光栅化负责主要可见性,光线追踪负责那些光栅化做不好的效果。那哪些效果最值得交给光线追踪?我的排序是:反射、阴影、环境光遮蔽、全局光照。

反射排第一,因为光栅化的反射方案限制太多,而光线追踪反射质量提升最直观。阴影排第二,尤其是大面积软阴影和接触阴影,光线追踪能做出非常自然的效果。环境光遮蔽排第三,它本质上是短距离的光线追踪,开销相对小。全局光照排最后,因为它的弹射次数要求高,降噪压力大,通常放在管线最后做补充。

5.2 光线追踪反射的实现要点

光线追踪反射的实现思路是:在光栅化渲染完G-Buffer之后,对需要反射的像素发射反射光线。反射方向由摄像机到像素的向量和像素法线计算得出。光线命中场景后,取命中点的着色结果作为反射颜色。如果反射光线没命中,就采样环境贴图。

这里有个细节:反射光线的起点要沿法线偏移一点,避免自相交。偏移量太小会出噪点,太大会漏掉近距离的反射细节。我一般用像素世界坐标加上法线乘以一个跟距离相关的系数,近距离偏移小,远距离偏移大。另外,粗糙表面的反射需要发射多条光线做抖动,然后降噪,否则反射会像镜子一样锐利,不符合材质表现。

5.3 光线追踪阴影的优化技巧

阴影光线的做法是:从着色点向光源发射一条光线,如果中途被遮挡就是阴影,否则就是受光。听起来简单,但优化空间很大。第一个技巧是阴影光线的起点也要偏移,避免自阴影噪点。第二个技巧是使用任意命中着色器,阴影光线只关心“有没有命中”,不关心“命中了什么”,所以可以用更轻量的任意命中着色器,命中即返回,不执行完整着色逻辑。

第三个技巧是光源采样。对于面光源,阴影光线要射向光源上的随机点,这样出来的阴影才有半影效果。采样点数量决定了阴影的软硬程度和噪点水平。我通常用两到四个采样点配合时间降噪,效果和性能比较平衡。第四个技巧是阴影光线的距离限制,超过光源距离的命中直接忽略,减少无效求交。

6. 性能分析与常见瓶颈排查

6.1 用调试工具定位光线追踪开销

光线追踪的性能问题比光栅化更难定位,因为它涉及加速结构遍历、着色器执行、降噪等多个环节。我常用的工具是PIX和RenderDoc。PIX对DXR的支持比较完整,能看到每条光线的弹射次数分布、加速结构遍历的耗时、以及各个着色器的执行时间。

一个实用的排查思路是:先把降噪关掉,看原始光线追踪的耗时。如果原始耗时就已经很高,那问题在加速结构或者着色器逻辑。如果原始耗时正常,开了降噪才卡,那问题在降噪器。然后再细分,把反射、阴影、全局光照逐个关掉,看哪个环节是大头。这个二分排查法能快速缩小范围。

6.2 加速结构构建与更新的开销控制

加速结构构建是容易被忽视的性能黑洞。尤其是动态场景,如果每帧重建所有BLAS,开销会非常大。优化方向有几个:一是尽量复用BLAS,只有顶点数据变了才重建;二是对于形变物体,考虑用允许更新的BLAS而不是重建;三是控制TLAS的实例数量,太多实例会让TLAS构建和遍历都变慢。

还有一个细节是构建的时机。加速结构构建是GPU操作,可以和渲染命令并行。如果你能在上一帧的末尾就把下一帧需要的加速结构构建好,就能隐藏掉这部分开销。这需要你对场景的更新频率有清晰的规划,不是所有物体都需要每帧更新。

6.3 光线发散与缓存效率

光线发散是指同一组光线方向差异很大,导致GPU的缓存命中率下降。在光线追踪里,光线发散会显著影响性能,因为加速结构遍历和纹理采样都依赖缓存。减少发散的方法包括:按材质或者按区域对光线排序、使用更紧凑的加速结构、以及控制弹射方向的范围。

我实测过一个场景,把反射光线的方向按半球区域做排序后再发射,追踪性能提升了大概百分之十。这个优化在反射粗糙度低的时候效果更明显,因为方向集中。粗糙度高的时候光线本来就散,排序收益有限。所以这个优化要看你的材质分布,不是万能药。

7. 实操落地:一个最小可用的DXR集成流程

7.1 环境准备与设备能力查询

动手之前,先确认你的开发环境和目标硬件支持DXR。在代码里,你需要查询设备的DXR支持等级,通常分一级和二级。一级支持基本的加速结构和光线追踪管线,二级增加了允许更新的BLAS等特性。查询到支持之后,才能创建DXR相关的设备和命令队列。

开发环境方面,Windows SDK和显卡驱动都要更新到较新的版本。调试工具建议装PIX,它对DXR的调试支持最完善。编译的时候注意着色器模型要选6.3以上,因为DXR的着色器阶段是6.3引入的。如果你的项目还在用更老的着色器模型,需要先升级。

7.2 加速结构构建的代码骨架

构建BLAS的大致流程是:准备三角形的顶点缓冲和索引缓冲,填写构建描述结构,指定三角形数量、顶点格式、是否允许更新等参数,然后调用构建接口。构建是异步的,需要配合围栏或者事件来同步。构建完成后,BLAS就可以被TLAS引用了。

TLAS的构建类似,但描述的是实例。每个实例包含一个变换矩阵、一个BLAS引用、一个实例ID、一个命中组索引、以及一个可见性掩码。把所有实例填进一个数组,然后调用TLAS构建接口。TLAS构建完之后,就可以在光线追踪管线里使用了。这里要注意,TLAS构建时引用的BLAS必须已经构建完成,否则会报错。

7.3 光线追踪管线状态对象的创建

管线状态对象(PSO)在DXR里叫光线追踪管线状态对象,它比光栅化的PSO复杂一些。你需要指定光线生成着色器、命中着色器组、未命中着色器、以及弹射深度、载荷大小等参数。命中着色器组可以包含多个命中着色器,通过命中组索引来区分。

创建PSO的时候,弹射深度和载荷大小这两个参数要特别小心。弹射深度设小了,光线弹到一半就停了,画面会缺效果。设大了,性能浪费。载荷大小设小了,数据放不下会出错。设大了,寄存器压力大性能下降。我的建议是先用一个保守的值跑通,然后再根据实际需求逐步调整。

7.4 每帧的调度与同步

每帧的调度顺序大致是:更新场景数据、更新TLAS实例、构建或更新加速结构、设置光线追踪管线状态、发射光线、降噪、合成到最终画面。这里面加速结构的更新和光线追踪的发射之间需要同步,确保光线追踪用的是最新的加速结构。

同步用围栏或者事件都可以,关键是不要过度同步导致GPU流水线停顿。我一般把加速结构构建放在帧的开头,和上一帧的降噪、合成并行,这样能隐藏一部分构建开销。光线追踪的发射放在G-Buffer渲染之后,因为反射和阴影需要G-Buffer里的法线和深度信息。

8. 常见问题速查与避坑经验

8.1 画面异常类问题排查

现象可能原因排查方向
画面全黑光线生成着色器没发射光线,或载荷没写回检查TraceRay调用和载荷赋值
反射错位反射方向计算错误,或法线空间不对确认法线在世界空间还是切线空间
阴影噪点严重阴影光线起点偏移不足,或采样数太少加大偏移量,增加光源采样
画面闪烁时间降噪历史信息对不上检查运动向量和拒绝逻辑
加速结构构建失败对齐错误或参数越界核对对齐要求和三角形数量

8.2 性能类问题排查

性能问题我总结了一个排查顺序:先看加速结构构建耗时,再看光线追踪着色器耗时,最后看降噪耗时。加速结构构建如果超过帧预算的百分之十,就要考虑优化构建策略。光线追踪着色器耗时高,通常是弹射次数太多或者着色逻辑太重。降噪耗时高,可能是滤波核太大或者分辨率太高。

还有一个容易被忽视的点是描述符表的绑定。光线追踪需要绑定加速结构、纹理、常量缓冲区等资源,如果描述符表切换频繁,会有额外开销。尽量把同一批次的光线追踪用到的资源放在同一个描述符表里,减少切换。

8.3 画质类问题调优

画质问题主要集中在噪点、拖影、和细节丢失上。噪点问题优先加采样数和优化降噪参数。拖影问题检查运动向量和降噪的时间累积权重。细节丢失通常是降噪太激进,把高频信息滤掉了,需要调小空间滤波核或者提高方差引导阈值。

我个人的经验是:画质调优不要一次调多个参数,每次只动一个,观察变化。因为降噪参数之间会相互影响,一起调很容易迷失方向。另外,准备几个典型场景做对比测试,比如静态室内、动态角色、水面反射,每个场景关注不同的画质维度。

提示:光线追踪的画质和性能是强耦合的,调画质的时候一定要盯着帧时间。有时候画质提升一点点,性能掉一大截,这种参数就要慎重。

9. 后续可扩展的方向

9.1 路径追踪的渐进式引入

如果你已经把反射和阴影的光线追踪跑通了,下一步可以考虑引入路径追踪来做全局光照。路径追踪的思路是让光线在场景里多次弹射,每次弹射都根据材质做随机采样,最终累积出全局光照效果。实时路径追踪通常配合时间累积和降噪,用较低的每帧采样数逐步逼近收敛结果。

引入路径追踪的时候,建议先从漫反射全局光照做起,因为漫反射的弹射方向分布比较均匀,噪点相对好处理。镜面反射的路径追踪噪点更集中,降噪难度更大。另外,路径追踪对加速结构的要求更高,因为光线弹射次数多了,遍历次数也多了,加速结构的质量直接影响性能。

9.2 硬件光追单元的深度利用

新一代硬件在光线求交上有专门的加速单元,这些单元对特定类型的操作有优化。比如有些硬件对轴对齐包围盒的遍历有加速,有些对三角形求交有加速。了解你目标硬件的特性,在加速结构构建和着色器逻辑上做针对性优化,能拿到额外的性能收益。

一个具体的做法是:根据硬件特性调整加速结构的构建参数。比如某些硬件对宽树的遍历效率更高,那构建时就可以调整分支因子。这些参数通常不在文档的显眼位置,需要你去查硬件厂商的开发者文档或者做实验测试。

9.3 与内容创作管线的配合

光线追踪的落地不只是渲染端的事,内容创作管线也要配合。比如材质的粗糙度、金属度、法线这些参数,在光线追踪下的表现和光栅化下不一样,美术需要重新校准。加速结构的构建也影响资产的组织方式,比如哪些物体应该合并成一个BLAS,哪些应该独立。

我建议在项目早期就让美术和渲染程序一起定一套光线追踪友好的资产规范。比如限制单个BLAS的三角形数量、规范实例的命名和分组、统一材质的参数范围。这些规范定得越早,后期返工越少。等到项目后期再改,成本会高很多。

最后分享一个我在实际项目里的小技巧:光线追踪的调试不要只盯着最终画面,要学会看中间缓冲。把反射缓冲、阴影缓冲、降噪前的原始缓冲都可视化出来,很多问题一眼就能看出来。比如反射缓冲里如果有明显的块状伪影,那多半是加速结构或者光线发散的问题,而不是降噪的问题。这个习惯帮我省了很多排查时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 1:17:46

C#线程安全集合深度解析:从ConcurrentDictionary到Channel的选型与避坑

/* 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:17:31

MMX在现代x86_64 Linux中的实战价值与调试方法

/* 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:17:28

小程序数据导入实战:云端数据库批量写入与失败重试全解析

/* 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:17:02

STM32纯软件仿真入门:不买开发板也能跑通GPIO、串口与ADC

/* 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:16:51

Sentinel规则持久化实战:Pull、Push与Ahas方案选型指南

/* 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:16:48

AMD NPU驱动NPU-RAI1.2安装与验证:Windows AI推理调用NPU的完整指南

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

作者头像 李华