news 2026/9/18 16:18:24

Unity快速次表面散射SSS:基于曲率与厚度的移动端皮肤渲染

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity快速次表面散射SSS:基于曲率与厚度的移动端皮肤渲染

做了几年Unity渲染,皮肤材质一直是个绕不开的坎。角色模型明明做得挺精细,一打光就像上了层清漆的塑料,尤其是耳廓、鼻翼、指缝这些透着薄肉的部位,黑得死死的,怎么调都缺那股“活人气”。其实问题不在贴图,而在光照模型没处理次表面散射(Subsurface Scattering,SSS)。这篇文章我想聊聊一种性价比极高的快速SSS方案,它不用屏幕空间卷积也不需要深度贴图,靠着顶点曲率和厚度信息就能在移动端把皮肤、蜡烛、树叶这类材质做出相当能打的效果。整套逻辑跑通之后,你再看任何“透光”材质,思路都会开阔很多。

适合看这篇东西的,主要是Unity开发的TA、图形程序,以及那些被“皮肤塑料感”困扰的美术同学。我会把原理、选型思路、完整Shader代码、踩坑记录一次性写完,保证你照着抄完能跑出效果,也能自己调出想要的味道。

1. 次表面散射是什么:为什么皮肤、蜡烛、树叶都自带“柔光”

1.1 一句话说清SSS的物理本质

光打到不透明物体表面,要么被反射,要么被吸收。但皮肤、蜡烛、树叶、玉石这类材质不一样,一部分光会从表面进入物体内部,在材质里连续散射、折来折去,最后从离入射点有一定距离的位置重新出射。这个过程就是次表面散射。

拿手电筒照自己的手指,你会发现除了被照亮的那面,手指背面也泛着红光。光线从手电侧射入,在皮肤组织里经历多次散射,一部分绕过了指骨区域透到另一侧出射。这就是SSS最直观的现象。落在渲染层面,SSS会带来两个典型视觉特征:一是轮廓边缘发亮甚至泛红,二是暗部不会死黑,总有一种“血液在皮下流动”的暖通感。

1.2 为什么SSS是实时渲染的“老大难”

理论很美好,实现很骨感。光的散射路径在物理上是无限次的,真正的模拟要依赖光线追踪里的BSSRDF(双向散射表面反射分布函数)模型,看看次表面散射游戏行业的标杆做法就知道了——顽皮狗在《最后生还者》里的皮肤方案,用到了屏幕空间散射和高斯模糊,画质好,但计算量也大得多。对于移动端和中小团队来说,光是拿到一张深度纹理再跑卷积,性价比就极低。

SSS的难点说白了就是:你要回答“光从哪进来、在内部跑了多远、从哪出去”这三个问题,全算出来几乎等于做一次子表面模拟。实时的思路都是想办法跳过这些精确计算,用近似值演戏。

1.3 常见SSS实现方案横向对比

主流的实时SSS方案就三种,各有各的适用场景。

方案原理画质性能适用场景
屏幕空间散射(SSSSS)在屏幕空间对高光/边缘做模糊扩散中高主机/PC第三人称游戏
深度贴图透射从光源方向渲染场景深度,计算光穿透厚度较高PC/中高端移动平台,刻薄材质
厚度/曲率近似(快速SSS)用模型表面厚度或曲率模拟光衰减中高极低移动端/低端平台,全类型材质

快速SSS的优势很明确:它不需要额外Pass,不需要深度相机,更不需要全屏后处理,一个Shader就能搞定。对正在做移动端项目、又不想在皮肤表现上妥协的团队,这是最务实的切入点。

2. 快速SSS的方案选型:为什么用厚度和曲率而不是屏幕空间卷积

2.1 “快速”的定位:想清楚你的平台和目标

很多团队一上来就说我要做次表面散射,然后去查论文、抄屏幕空间方案,最后发现手机跑不动,PC上效果也一般。我建议先问自己三个问题:目标平台是什么,角色在镜头里的占比有多大,项目里有多少角色需要套这个材质。

如果是写实手游、锁视角、角色大头照频出的项目,屏幕空间方案是可以考虑的。但如果是策略游戏、塔防、或者角色的脸部只在中距离出现,快速SSS的低成本优势就非常明显。方案选型最大的坑不是你选错了算法,而是你为了一个低频画面效果背上了全屏后处理的性能债。

2.2 厚度/曲率近似:为什么它能以假乱真

快速SSS的核心假设是:模型表面的凸起程度能间接反映透光强度。耳朵、鼻翼、手指缝隙这些凸起尖锐的区域,物理上对应着更薄的体积,光更容易穿透,散射也更强烈;而脸颊、额头、肚子这些平滑饱满的区域,体积厚,光穿透困难,散射微弱。

所以我们的目标就变成了:如何用现有数据表示“这个顶点所在位置的薄厚程度”。有两种常用方案:

  • 曲率烘焙:通过顶点法线在相邻顶点之间的变化速率计算曲率,凸起尖锐的位置曲率大,存进顶点色。
  • 厚度贴图:美术手动用灰度图绘制,白表示薄,黑表示厚。几乎全手绘,但是可控性极强,同一套网格要做出角色和怪物的差异,直接换贴图就行。

我个人的项目经验是,曲率烘焙适合做通用材质,速度快、自动生成,但精度一般;厚度贴图效果可控,特别适合主角脸模这种需要精雕细琢的部位。实际项目中可以把两个数据一起用:曲率负责边缘散射的自动分布,厚度图负责主要块面的透射强度。

2.3 光照模型设计思路

拿到厚度信息之后,下一步就是把它塞进光照模型里。快速SSS的光照模型一般拆成三个部分:

  • 漫反射:用Wrap Lighting让明暗过渡更柔和,避免皮肤上出现生硬的明暗分界线。Wrap Lighting的本质是把NdotL的值域重新映射,让光线在光照方向变化时不是突然断开,而是慢慢衰减。
  • 透射光:根据视角方向和光线方向的点积,模拟背光时“看穿”薄区域的透射效果。
  • 边缘散射:类似边缘光(Rim Light),但是用厚度信息做权重,只有薄的地方才泛光。

三个部分叠加之后,材质就有了“透”的层级感,而不是全物体均匀发光。重点在于:散射的强度必须和厚度挂钩,否则效果就变成了跑马灯。

3. 实操配件准备:贴图通道、顶点曲率与Shader框架

3.1 贴图与模型准备

动手写Shader之前,先整理一下需要的资产。不用太复杂,四张贴图就够起步:

  • 漫反射贴图(Albedo):给出皮肤的基础颜色,这个不用多说。
  • 法线贴图:提供表面凹凸细节,让高光和散射都有细节可以抓。
  • 高光贴图(Gloss Map):控制局部的光滑度,嘴唇、鼻子区域高光更强。
  • 厚度信息:可以直接用顶点色R通道,或者单独的一张灰度贴图。白色代表薄、红色代表薄(顶点色)这种约定你自己定,但写Shader的时候要跟烘焙逻辑对上。

厚度的取值建议在0到1之间线性映射。0代表完全实心,光完全穿不过去;1代表薄如蝉翼,光直接穿透。做怪物皮肤时,可以让美术在耳部、翼膜、尾巴尖这些位置把厚度的值拉高,增强透光诡异感。

3.2 顶点曲率烘焙脚本:复制就能用

如果你不打算手绘厚度贴图,那么一个自动曲率烘焙工具能省不少事。思路很简单:遍历每个顶点,取它相邻顶点的法线,计算法线夹角平均值。夹角越大,说明该位置弯折越剧烈,曲率越高,对应的薄度越高。

下面这个C#脚本我放在项目里很久了,简单粗暴但够用。把它挂到带MeshFilter的物体上,勾选Bake On Start,运行一次就能把结果写进顶点色。注意它直接修改了sharedMesh,编辑时建议复制一份Mesh再烘焙。

using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(MeshFilter))] public class CurvatureBaker : MonoBehaviour { public bool bakeOnStart = true; [Range(0f, 1f)] public float blendAmount = 0.8f; void Start() { if (bakeOnStart) Bake(); } public void Bake() { Mesh mesh = GetComponent<MeshFilter>().sharedMesh; if (mesh == null) return; if (!mesh.isReadable) { Debug.LogError("Mesh must be set to readable before baking curvature."); return; } Vector3[] vertices = mesh.vertices; Vector3[] normals = mesh.normals; // 顶点色不够长就重新建一个 Color[] colors = mesh.colors; if (colors == null || colors.Length != vertices.Length) colors = new Color[vertices.Length]; else // 保留原顶点色的RGB,只把R通道覆盖为曲率 for (int i = 0; i < colors.Length; i++) colors[i] = new Color(0, colors[i].g, colors[i].b, colors[i].a); // 邻接表:每个顶点连接到的顶点索引列表 List<int>[] adjacency = new List<int>[vertices.Length]; for (int i = 0; i < vertices.Length; i++) adjacency[i] = new List<int>(); int[] tris = mesh.triangles; for (int i = 0; i < tris.Length; i += 3) { AddEdge(adjacency, tris[i], tris[i + 1]); AddEdge(adjacency, tris[i + 1], tris[i + 2]); AddEdge(adjacency, tris[i + 2], tris[i]); } for (int i = 0; i < vertices.Length; i++) { float sumDot = 0f; int count = 0; for (int j = 0; j < adjacency[i].Count; j++) { int n = adjacency[i][j]; sumDot += Vector3.Dot(normals[i], normals[n]); count++; } float avgDot = count > 0 ? sumDot / count : 1f; // 法线越不平行,avgDot 越小,曲率越大 float curvature = Mathf.Clamp01(1f - avgDot); colors[i].r = Mathf.Lerp(colors[i].r, curvature, blendAmount); colors[i].g = colors[i].r; colors[i].b = colors[i].r; } mesh.colors = colors; } void AddEdge(List<int>[] adj, int a, int b) { if (!adj[a].Contains(b)) adj[a].Add(b); } }

脚本里的邻接表用List.Contains判断重复,在几万顶点的网格上跑会有性能浪费,但烘焙是一次性的,没必要优化到底。如果你在脚本上遇到过UV被冲掉的问题,注意赋值colors之前先把旧顶点色备份,或者直接把R、G、B都覆盖成曲率,低频信息足够用了。

3.3 Shader框架搭建

接下来就是Shader部分。我选择用Surface Shader来写,理由很简单:Unity内置管线里Surface Shader能快速接入各种光源类型、阴影和全局光照,我们只需要实现一个光照模型函数,省去大量手写顶点/片元Shader的样板代码。

先声明Properties块,把需要用到的变量都暴露出来:

Shader "Custom/FastSSS" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _NormalMap ("Normal Map", 2D) = "bump" {} _GlossMap ("Gloss (R)", 2D) = "white" {} _ScatterColor ("Scatter Color", Color) = (1.0, 0.4, 0.3, 1.0) _ScatterStrength ("Scatter Strength", Range(0, 3)) = 1.2 _TransmissionColor ("Transmission Color", Color) = (1.0, 0.3, 0.25, 1.0) _TransmissionStrength ("Transmission Strength", Range(0, 3)) = 1.5 _TransmissionPower ("Transmission Power", Range(1, 8)) = 4.0 _WrapAmount ("Wrap Amount", Range(0, 1)) = 0.5 _Gloss ("Gloss Power", Range(0.5, 8)) = 2.5 _SpecStrength ("Spec Strength", Range(0, 2)) = 0.8 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf FastSSS fullforwardshadows #pragma target 3.0 ... } }

这里我把散射颜色和透射颜色分开,一个是边缘泛光色,一个是背光透射色。对于皮肤来说两者相近,都是偏红偏暖;如果是做树叶,散射色可以偏黄绿,透射色偏亮绿,效果会很有生命力。

4. 核心Shader实现:快速SSS完整代码与逐段拆解

4.1 核心光模型代码

下面这段是完整的Surface Shader光照模型部分,逻辑全写在LightingFastSSS函数里。我先把完整的代码放出来,再逐段讲为什么这么写。

sampler2D _MainTex; sampler2D _NormalMap; sampler2D _GlossMap; fixed4 _ScatterColor; half _ScatterStrength; fixed4 _TransmissionColor; half _TransmissionStrength; half _TransmissionPower; half _WrapAmount; half _Gloss; half _SpecStrength; struct Input { float2 uv_MainTex; float2 uv_NormalMap; float2 uv_GlossMap; float4 vertexColor : COLOR; float3 viewDir; }; void surf(Input IN, inout SurfaceOutput o) { fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = albedo.rgb; fixed3 normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)); o.Normal = normal; o.Gloss = tex2D(_GlossMap, IN.uv_GlossMap).r; // 顶点色的R通道作为“薄度”,0为厚,1为薄 float thinness = saturate(IN.vertexColor.r); o.Alpha = thinness; } inline half4 LightingFastSSS(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) { half3 normal = normalize(s.Normal); half3 lightColor = _LightColor0.rgb; // 1. Wrap漫反射:让明暗过渡更柔和 half ndl = saturate(dot(normal, lightDir)); half wrap = saturate((ndl + _WrapAmount) / (1.0 + _WrapAmount)); half diff = wrap * wrap; // 二次映射让衰减更自然 // 2. Blinn-Phong高光 half3 halfDir = normalize(lightDir + viewDir); half ndh = saturate(dot(normal, halfDir)); half spec = pow(ndh, _Gloss * 32.0) * _SpecStrength * s.Gloss; // 3. 透射光:背光穿透过薄区域 half transDot = saturate(dot(viewDir, -lightDir)); half trans = pow(transDot, _TransmissionPower) * _TransmissionStrength; half3 transCol = _TransmissionColor.rgb * trans * s.Alpha; // 4. 边缘散射:视角与法线方向垂直时,薄区域泛光 half rim = pow(1.0 - saturate(dot(viewDir, normal)), 3.0); half3 scatterCol = _ScatterColor.rgb * rim * s.Alpha * _ScatterStrength; half3 color = lightColor * (s.Albedo * diff + spec) + transCol + scatterCol; return half4(color * atten, 1.0); } ENDCG } FallBack "Diffuse" }

4.2 关键公式逐个讲透

先看Wrap漫反射。普通的lambert漫反射是NdotL,光照方向一转,明暗切换特别生硬。Wrap Lighting的思路是把NdotL从原来的映射范围扩展一下,让光照在物体转向背面时仍然保留一小段衰减,而不是瞬间跌到零。公式里的_WrapAmount控制在0到1之间,_WrapAmount越大,暗部越亮,皮肤就越“透”。

再看透射光。dot(viewDir, -lightDir)这个式子理解起来有点绕,我拆开说。viewDir是视线方向,-lightDir是光线反方向。当视线方向正好和光线反方向重合时,你看到的是光线穿过物体朝镜头射来的效果,也就是“对着光源看透光”;当两者垂直时,透射几乎不可见。用这个点积作为透射的依据,再叠加厚度信息s.Alpha,就实现了“薄的地方才透光”的效果。

边缘散射部分,我用的是1 - dot(viewDir, normal),这是经典Rim Light公式。视角与法线越接近90度,边缘越亮,再乘上薄度因子s.Alpha,让散射主要集中在凸起边缘。这个公式跟透射光有区别:透射光依赖光源方向,主要用于背光场景;边缘散射依赖视角方向,无论光从哪来,轮廓处都会有点泛光,更接近皮肤在环境光下的“通透感”。

4.3 如何把材质调到像皮肤

代码写完后,材质面板上会有十几个参数。我一般按顺序调,别一上来就乱扭:

  1. 先把_WrapAmount调到0.5左右,看整体明暗过渡是否柔和。再往大调,模型会像在冒浓雾,所以要适量。
  2. 调_TransmissionStrength。背光下耳廓和鼻翼应当出现透光,亮度比周围肤色明显高一个层级。TransmissionPower控制过渡的锐利程度,数值越低,透光范围越大。
  3. 调_ScatterStrength,让轮廓和曲面转折的地方微微泛红。皮肤散射色用(1.0, 0.4, 0.3),树叶可以换成(0.3, 0.8, 0.3)。
  4. 最后调高光。皮肤的Gloss值不要调太高,嘴唇、鼻子尖可以有比较强的点高光,但两颊的高光要弱。Gloss通道贴图就是干这个用的。

注意,透射光和散射光的位置可能会重叠,导致某些区域偏亮。这种情况下我会把Transmission Strength稍微降一点,同时调小ScatterStrength的Rim Power,让它更集中地锁在轮廓线附近。

5. 参数调优与移动端性能注意事项

5.1 参数速查表

材质调参是我见过美术新手翻车最多的地方。整理了一份速查表,照着调基本能稳住:

参数皮肤推荐值蜡烛推荐值树叶推荐值效果说明
Wrap Amount0.4-0.60.6-0.80.4-0.5越大暗部越亮,过大会发灰
Transmission Strength1.0-1.82.0-3.01.5-2.5背光透射的整体强度
Transmission Power3-52-32-4越小透光扩散范围越大
Scatter Strength0.8-1.51.0-2.01.2-2.0边缘泛光强度
Gloss Power2-41-21-2高光聚集程度,皮肤不要太高
Spec Strength0.5-1.00.3-0.80.3-0.6镜面高光的整体强度

做蜡烛的话可以让TransmissionStrength拉到2以上,蜡烛在背光下几乎是整根透亮,再配合漫反射暗部偏暖色,效果很有氛围感。做树叶可以调低WrapAmount,让叶脉区域保留更多明暗对比,散射色选亮柠檬黄,颜色会很清新。

5.2 移动端性能优化

这套方案在移动端之所以友好,是因为它把所有散射计算都放在一个光照模型函数里,没有额外Pass,没有全屏后处理,没有深度相机。Shader的指令数取决于你开了多少特性,就这套基础版而言,跑在骁龙7系和天玑8000级别的设备上毫无压力,真机Profile基本可以忽略不计。

不过有几个优化习惯要保持:

  • 目标平台不用OpenGL ES 2.0的话,target 3.0是稳妥的选择。
  • 高光计算里的pow(ndh, _Gloss * 32.0),如果_Gloss很高,低端GPU可能有一点点压力。实际项目里把_Gloss限制在10以内,就用32倍乘数,高光范围足够细腻。
  • 如果在URP管线里用这套方案,注意LightingFastSSS函数是内置管线的写法,URP需要改成LightingFastSSS_float并配合HLSL。如果你是用Shader Graph,可以直接用Custom Function节点封装核心公式。

5.3 和阴影系统配合的坑

快速SSS最容易出问题的是和阴影搭配。默认Surface Shader已经支持fullforwardshadows,所以阴影本身没有问题。问题出在透射光上:主光源被挡住时,背面产生的透射光会让角色像“漏光”,因为我们的透射项不感知阴影。

解决方案有两种。一种是在Lighting函数里用atten乘掉透射光中的主要光源贡献,但这会把背光透射也削弱,因为主光源被挡住时其实背光强了。另一种更实用的做法是给透射项增加一个独立的距离衰减,让近距离的透射更明显,远距离时减小强度,模拟光强随距离衰减的直觉。

我个人推荐的做法是保留atten对透射光的影响但不做全乘,而是乘一个0.5的权重,再叠加一个环境光带来的微弱透射。这样主光源被遮挡时依然有一层淡淡的透射,既不会死黑,也不会漏光。

6. 常见问题与排查技巧实录

6.1 问题速查表

实际项目里总会碰到各种画面问题,我把高频问题整理成了表格,方便你遇到时直接对号入座:

现象可能原因解决方案
整个模型像在发光散射强度和透射强度太高降低Scatter Strength和Transmission Strength,检查是否顶点色全白
透射光只在背光时出现,但效果很生硬TransmissionPower太高降低TransmissionPower,从6降到3会有明显改善
边缘没有泛红顶点色Curvature烘焙失败或没有导入检查Mesh是否Readable,确认Shade模式顶点色面板有数据
高光像金属GlossPower过高或Gloss图过白把GlossPower降到3以下,用Gloss图控制局部高光
背光时暗部一片死黑没启用透射,或者透射Power过高确认Lighting函数里trans项存在,适当调低Power
URP下材质完全变紫Surface Shader在URP下不直接兼容改用URP的Custom Function或者Shader Graph复刻

6.2 我踩过的三个坑

第一个坑是顶点色烘焙后模型变紫色。原因是我在C#脚本里重建colors数组时,直接把Color数组new出来,因为未赋值的Color默认为0,0,0,0,在Shader里如果错误地用顶点色R当Alpha混色,就可能导致半透明黑。后来我把顶点色初始化改成先赋值白色,再覆盖R通道,问题立刻消失。

第二个坑是背光透射把整根脖子变成一个亮条。头颈部网格曲率高,顶点色R会偏白,套用透射公式后整个脖子都发光。解决办法是在模型上限制曲率的作用范围:要么在顶点色R通道上限值,要么在Shader里用saturate缩紧权重。我现在习惯在脚本里对curvature做一个pow映射,让大多数区域保持暗色,只有真正尖锐的凸起才高亮。

第三个坑是插件材质和顶点色不兼容。项目用了一些第三方着色器,不支持顶点色,导致同一个模型换材质后皮肤又变回塑料。这时需要统一约定:SSS材质必须用顶点色R通道,普通材质直接忽略。最好在材质命名和检入流程上做好规范,否则团队协作时容易互相覆盖。

6.3 拓展:从皮肤到蜡烛、玉石、树叶

快速SSS的价值在于同一套思路可以覆盖多种材质。做蜡烛时,把漫反射贴图换成一盏半透明白的纹理,TransmissionStrength拉到2.5,TransmissionPower调成2.5,再把手绘厚度图弄成整体偏白,只在烛芯周围留一点黑,点燃状态下那圈内透光非常迷人。

做玉石,关键是把WrapAmount调高到0.7左右,让暗部保持足够的亮度,同时传射Colors偏青绿,透射Power降到2,这样翻转玉石时你会看到一种“光在里面游”的通透感,比纯Lambert散射真实很多。

做树叶,重点在双面渲染和透射。叶子一般只用两个面,把材质设置里Cull Off打开,让叶片内部也能接受到散射光。散射色用亮黄绿,TransmissionStrength中等,配合Wind动画,阳光下树叶整体会有种嫩生生的鲜活感。

做怪物皮肤是这套方案最过瘾的场景。你可以在厚度图上画上筋脉、伤疤、辐射纹理,把这些部位画成白色,受伤区域会透着光,配合动画和粒子特效,压迫感直接拉满。

在实际项目中最后的几点体会

这套快速SSS我前后用了三四个项目,从二次元角色到写实怪物都试过。最大的感受是:它不是一个以假乱真的物理方案,而是一个性价比极高的“视觉方案”。阈值把握好,观众根本不会注意到你的散射是假的,也不会管你的透射是不是严格符合光学定律。反而那些追求物理正确但性能撑不住的项目,最后都得在参数和平台之间做大量妥协,反而更难看。

如果你想在现有基础上继续深挖,我建议下一步可以尝试把这套光照模型接入自定义阴影,比如用两层的Shadow Map实现次表面遮挡,或者加一个视差厚度贴图来增强轮廓感。最后再分享一个小技巧:所有散射相关的参数,都尽量做进材质属性里,千万别写死。因为同一套Shader要服务截然不同的美术资产,写死一个参数等于堵死了很多效果的可能性。

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

用Python解析docx题库:从三支一扶试题到结构化数据

简介&#xff1a;2019年广西柳州市三支一扶考试补录试题及答案解析&#xff0c;是一份面向三支一扶备考群体的真题演练资料&#xff0c;尤其适合报考广西柳州地区岗位、需要熟悉当地笔试题型和难度的考生。资源包内共1个docx文档&#xff0c;文档约46页&#xff0c;整体大小仅6…

作者头像 李华
网站建设 2026/9/18 16:06:50

ResNet残差结构实战解析:从退化问题到工业级微调

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

作者头像 李华
网站建设 2026/9/18 16:05:42

Claude Code 跨会话又“失忆”?TaoToken 供 Key 后 Memory 索引照旧跑

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

作者头像 李华
网站建设 2026/9/18 16:04:51

AT89C51密码锁:矩阵键盘与AT24C02掉电存储设计

简介&#xff1a;这份面向单片机课程设计与电子制作入门的 Word 文档&#xff0c;围绕 AT89C51 单片机电子密码锁展开&#xff0c;适合电子信息、自动化等专业学生及嵌入式初学者参考。内容以 AT89C51 最小系统为核心&#xff0c;串联 44 矩阵键盘、LCD1602 显示与报警模块&…

作者头像 李华
网站建设 2026/9/18 16:04:25

电力系统优化:蒙特卡洛与Copula在可再生能源调度中的应用

1. 项目背景与核心价值这个项目本质上是在解决一个现代电力系统面临的复杂优化问题&#xff1a;如何在高比例可再生能源接入和电动汽车大规模普及的背景下&#xff0c;实现电网的经济高效运行。我去年参与过某省级电网的类似项目&#xff0c;深刻体会到这类问题的挑战性——你不…

作者头像 李华