自由学习记录(56)
从贴图空间(texture space)将值还原到切线空间(tangent space)向量
tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
背后的知识点:法线贴图中的 RGB 是在 0~1 范围内编码的向量
所以贴图法线是怎么“压缩”和“解压”的?
💾 存储时:
-
法线向量
float3(n.x, n.y, n.z)
∈ [-1, 1] -
存入贴图时要转为 0~1 范围:
读取后要“反解码”回来:
fixed3 packedNormal = tex2D(_BumpMap, i.normalUv);读到的是范围是 [0,1]
所以:tangentNormal.xy = packedNormal.xy * 2 - 1;
-
这一步将
0~1
解码为-1~1
-
_BumpScale
是调节法线扰动强度的乘子(可以人为增强凹凸感)
法线贴图中,我们通常只显式存储了 x
和 y
分量(映射到 R 和 G 通道),而 z
是通过单位向量长度约束反推的。
RGB 贴图中 xyz 的意义是什么?
通道 | 存储的是什么 | 映射回 tangent space 的含义 |
---|---|---|
R(x) | 切线方向上的扰动(沿 tangent 轴) | normal 在 tangent 方向上的偏移量 |
G(y) | 副切线方向上的扰动(沿 bitangent 轴) | normal 在 bitangent 方向上的偏移量 |
B(z) | 法线方向(沿 normal 轴) | 通常通过反推计算,表示“凸出/凹陷”程度 |
这些值共同构成了贴图中每个点的扰动法线向量(在 tangent space 中)。
法线贴图并不改变顶点的 normal,而是在片元级别提供比顶点插值更精细的扰动方向
-
顶点 normal 是由模型网格决定的(每个顶点一个方向)
-
法线贴图在片元级别提供 “在当前表面方向上的微扰”
-
Shader 会把贴图中的
(x,y,z)
作为一个 Tangent Space 中的扰动法线 -
然后用 TBN 把这个扰动变换到世界空间,再用于光照计算
z 分量作用 保证法线是单位向量,体现凹凸“凸起”的程度
Unity UnpackNormal(tex2D(...)) is used to decode the normal
Normal map is saved as an RGB texture with values in 0–1.
_BumpMap ("Normal Map", 2D) = "bump" {}
Unity 会自动:
把这个属性在材质面板中以 “Normal Map 类型”显示
在材质面板中选择贴图时,Unity 会验证它是否是 正常格式的法线贴图
有些内置函数(如 UnpackNormal)也会默认适配
这是一种 语义提示 + 编辑器行为绑定机制。
值 | 含义解释 |
---|---|
"white" | 默认使用 Unity 的纯白纹理(全通、全亮),通常用于颜色贴图、控制图等 |
"black" | 默认使用纯黑纹理 |
"gray" | 使用灰色纹理,常用于金属度、AO、Roughness 等 |
"bump" | ✅ 使用 Unity 内置的法线贴图格式(编码好的 normal map),不会当成颜色贴图 |
"red" , "green" , "blue" | 分别用纯红、绿、蓝纹理测试 |
预处理判断语句 并不是为了“再计算值”,而是为了根据光源类型(点光、方向光、多光源)不同,决定如何使用这个值。
#ifndef USING_LIGHT_MULTI_COMPILE
如果你没有启用多光源(不是 forward 渲染 path)
return objSpaceLightPos - v.xyz * _WorldSpaceLightPos0.w;
这段非常关键,它根据 _WorldSpaceLightPos0.w 来判断当前是:
_WorldSpaceLightPos0.w 值 | 光源类型 | 表达形式 |
---|---|---|
1 | 点光源 | L = lightPos - vertexPos |
0 | 方向光 | L = -lightDir |
#ifndef USING_DIRECTIONAL_LIGHT
return objSpaceLightPos.xyz - v.xyz;
表示点光源:
方向 = 光源位置 - 当前顶点位置
#else
return objSpaceLightPos.xyz;
表示方向光:
方向 = 光线方向(由 _WorldSpaceLightPos0
直接编码)
tex2D方法传入的Sample2D和uv类型,这里的uv是经过了TRANSFORM_TEX的变换的,所以对应关系没有出现问题
tex2D()
返回的是 RGBA 值,对应纹理的 4 个通道:
分量 | 意义(默认) |
---|---|
x / .r | Red 通道 → 通常表示 normal.x |
y / .g | Green 通道 → 通常表示 normal.y |
z / .b | Blue 通道 → 通常表示 normal.z(或重构) |
w / .a | Alpha 通道 → 通常无意义,默认值为 1,除非你主动使用它 |
“返回的是四维矩阵?w 是什么?”
-
❌ 它不是矩阵,是一个四维向量:
float4
-
✅
.w
是采样纹理像素的 Alpha 通道 -
✅ 在法线贴图中,w/alpha 一般 没有特别作用,除非你专门用它存东西
📌 例外情况:
有时你可能会看到设计得比较高级的 Packed Texture,用 RGBA 四个通道存多个信息,比如:
通道 | 内容(例) |
---|---|
R | 法线 x 分量 |
G | 法线 y 分量 |
B | AO or 金属度 |
A | 透明度 or 粗糙度 |
在这种情况下,.w
(或 .a
)是你主动使用的额外通道。
常见的指令说明:
指令 | 含义 |
---|---|
#if defined(MACRO) | 如果宏 MACRO 被定义,执行下面的代码块 |
#elif defined(...) | 否则如果另一个宏被定义,执行这一块 |
#else | 如果以上都不满足,则执行这一块 |
#endif | 结束 #if 条件块 |
#define MACRO | 定义一个宏(一般由 Unity 自动定义) |
#undef MACRO | 取消一个宏定义 |
法线贴图需要::编码/解码
贴图压缩技术::DXT5nm、BC5、ASTC 等压缩格式对 normal map 做特定方式编码
(解码).x *= .w 在 RGorAG 模式中,w(即 alpha 通道)保存了缩放系数,乘上修正 x
宏分支控制,,,让同一 Shader 能适配不同贴图格式和硬件压缩能力
从数学上讲,xy 变大,z 的确必须变小,否则法线向量就不再是单位长度了。我们来精确、严谨地拆解这一点,并澄清“为什么我们不能随意直接乘 z”。
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
-
xy 扰动越大 → z 越低 → 表示“偏离表面方向更多”
-
xy 越小 → z 越接近 1 → 趋近于原始平面法线(表面更平)
packedNormal.x *= packedNormal.w;
✅ 这是 Unity 在处理 DXT5nm 格式(或 RG/AG 编码)法线贴图时,
为了还原正确的 X 分量方向所做的“签名恢复”操作(sign restoration)。
这句代码的意义就是:
恢复 DXT5nm 格式贴图中被拆开的 X 分量的符号信息。
使用情况 | 是否需要 x *= w ? |
---|---|
RGB 正常法线贴图 | ❌ 不需要,RGB 已存完整 xyz |
DXT5nm (RGorAG) 格式 | ✅ 需要,从 R+A 通道还原 x |
ASTC RG 格式 | ✅ 一样道理,可能用 .g *= .a |
“tangentNormal.xy *= _BumpScale
是在 z
已经被 sqrt()
计算之后进行的,那这时法线向量还是单位长度吗?还合理吗?”
❗这样写是不严谨的,结果法线向量将不再是单位向量,可能会影响光照准确性,
但实际在某些低精度光照模型中可能“看起来”没太大问题,所以有时会被这么写。
正确的写法应该是:先 scale,再重建 z
但也可以tangentNormal = normalize(tangentNormal);这虽然也能修复向量长度,但会引入不必要的性能消耗,并且对于低精度法线会有误差积累。
如果这里没设置成Normal map,是default,那就按注释的那一块来
如果在 Unity 中导入贴图时 没有把它设置为 “Normal map” 类型,而是保留为
Default
,那就不能直接用UnpackNormal()
,而要自己手动解码贴图数据(如*2 - 1
等)——对不对?✔ 是的,这个判断逻辑是完全正确的。
所以注意::cross(N, T ) * w ✅ 是 Unity 推荐的构造 binormal 方法
由于 HLSL 是“列向量 × 行矩阵”的表达顺序 —— 不能直接这么写!
也就是你写的这个形式:
float3 worldNormal = float3(
dot(i.TtiW0.xyz, tangentNormal),
dot(i.TtiW1.xyz, tangentNormal),
dot(i.TtiW2.xyz, tangentNormal)
);
为什么不需要逆矩阵?
这是一个关键误区 —— 很多书说“方向向量要乘以逆转置矩阵”,那是对 非正交矩阵 或 非单位正交基 的情况。
但是在这里:
-
T, B, N 都是单位向量
-
三者彼此正交
-
所以 TBN 是正交矩阵
对于正交矩阵:
tex2D(_RampTex, fixed2(halfLambert, halfLambert))
如果你贴的是一张黑 → 白的横向渐变图,那它就像是:
-
漫反射强 → 用白
-
光线垂直 → 用灰
-
背面 → 用黑(因为 dot = 0)
Half-Lambert 是为了让漫反射“更柔和、避免背光发黑”而人为做的函数变换;而 Blinn-Phong 的 Half Vector 是光和视线之间的“几何中间方向”,是完全不同的概念。
渲染操作 | 是否进行 | 说明 |
---|---|---|
深度测试 | ✅ 默认开启,所有物体都可以参与 | 无论透明还是不透明,片元都可能接受深度测试 |
深度写入 | ⛔ 透明物体通常默认关闭 | 因为深度写入会导致后绘制的透明物体被挡住 |
为什么有这些不同混合因子
艺术风格与公式的“对应性”
很多时候,所谓“自然的过渡”、“漂亮的发光”,其实是:
-
物理叠加模拟真实世界光照(如 Linear Add)
-
用 Cutoff + Lambert 模拟日光与背光
-
用 Soft Add 表现氛围感或光晕扩散
这些都是人类在长期调试中找到的:
✅ “某些数学形式 → 在显示器上经过 gamma → 在人眼中感觉刚刚好”
自然感 = 光 + 人眼 + 数学的共同结果
你看到的那种:
“怎么一个冷冰冰的
Blend One OneMinusSrcAlpha
,出来就能让玻璃有那种淡淡的透光感?”
那是因为它刚好:
-
用透明度控制了透光量(alpha)
-
保留了背景(OneMinusSrcAlpha)
-
而你屏幕发光、你眼睛接收,刚好构成了感知的闭环
当你在片元着色器(frag()
)里写了 discard;
,
GPU 会把这个 当前正准备写入屏幕的像素,彻底丢弃,不再处理它。
举个实际例子:一张带 alpha 的叶子贴图
比如:
-
树叶是绿色,alpha = 1
-
叶子空隙是透明,alpha = 0
if (texColor.a < 0.5) discard;
那结果就是:
✅ 树叶的绿色区域 → 保留渲染
❌ 空隙区域 → 这个片元直接 被丢弃,什么都不写,像没来过一样
图形管线视角下的“丢”
整个流程简化如下(以一个像素为例):
-
顶点着色器执行完 → 光栅化 → 生成这个片元
-
进入片元着色器执行 → 执行到你写的
discard;
-
GPU 立刻中止这个片元的写入流程
-
不写入颜色缓冲区,不写入深度,不触发混合,不投影阴影
-
后面的像素该怎么画还怎么画,就像这个像素从没存在过
return 的颜色,还要经过 后处理通道
在 Unity 的标准 Shader 中,开启 Blend
通常会:
🔹默认搭配
ZWrite Off
,否则效果可能异常(如透明遮挡错误)。
Tags { "Queue" = "Transparent" } ✅ Unity 内置 Shader 会自动设为 ZWrite Off
- To define Pass tags, place the
Tags
block inside aPass
block. - To define SubShader tags, place the
Tags
block inside aSubShader
block but outside aPass
block.
Common built-in RenderType
values Unity recognizes:
What does “replaced” mean in RenderType?
In this context, “replaced” refers to a feature called:
🎯 Camera.SetReplacementShader(...)
It means Unity can:
👉 Replace the shaders of everything in the scene at runtime,
BUT only for objects with a certainRenderType
.
This is what RenderType is actually used for in this case.
Aspect | Defined in Shader Language? | Used internally by Unity systems? | Example |
---|---|---|---|
"Transparent" | ❌ No | ✅ Yes | Replacement shaders, depth prepass |
"Opaque" | ❌ No | ✅ Yes | G-buffer, deferred passes |
"TransparentCutout" | ❌ No | ✅ Yes | Cutout lighting, shadows |
"MyCustomType" | ❌ No | ❌ Only if you use it | Custom pipeline or editor tools |
使用多个 Pass
的确是为了让 一个 Shader 能够对同一个材质进行多次渲染处理。
👉 每一个 Pass 相当于 GPU 要渲染这个物体一次。
所以多个 Pass 就是 同一个物体重复渲染多遍(不代表重复 draw call,但有性能代价)。
Pass {
ZWrite On
ColorMask 0
}
的作用是:
✅ 提前写入深度值,但不渲染任何颜色,
这样后续的透明 Pass 就可以正确地进行基于深度的遮挡排序,避免穿模。
为什么透明物体会错位?
原因是:
-
透明物体一般不开 ZWrite(ZWrite Off)
-
Unity 渲染透明物体时默认按 背面→前面的顺序(靠排序,不靠深度)
-
如果模型像“绳结”那样自相交(自己遮挡自己),
👉 你就无法靠“渲染顺序”判断哪个面该挡住谁了
👉 导致“后面的反而盖住了前面”
ZWrite 的深度信息只要写入,就会保留在深度缓冲区中,直到下一帧/下一次清除,
✅ 所以:只要材质没有 ZWrite Off
,无论是否最终画出来,只要通过深度测试,它就确实会写入深度。
三种法线外拓的shader写法
法线向量不能被平移 法线必须保持和切平面垂直(几何关系) 非均匀缩放会破坏这种垂直性
法线向量是一个协变向量(co-vector),
变换它需要使用原始变换矩阵的逆的转置(inverse transpose)
左乘和右乘有明确、统一、严谨的数学定义,
它们的本质是取决于你采用的是**“列向量”还是“行向量”表示法”**。
一旦你选定了其中一种方式 —— 所有矩阵乘法的方向、变换链的顺序、转置等操作都有一致的逻辑。
常见约定:列向量形式是计算机图形学中最常用的表示
使用方式 | 表示法 | 矩阵乘法顺序 | 向量在右边 or 左边 |
---|---|---|---|
✅ 列向量约定 | v∈Rnv \in \mathbb{R}^nv∈Rn 是列 | MvMvMv | 向量在右边(标准) |
⛔ 行向量约定(不常用) | v⊤v^\topv⊤ 是行向量 | v⊤Mv^\top Mv⊤M | 向量在左边 |
图形学、Unity、OpenGL、HLSL 统一采用列向量规范:
✅ 你常写的这些:
float4 worldPos = mul ( unity_ObjectToWorld , v.vertex );
本质就是:
向量是列向量,矩阵在左边,左乘矩阵,右乘向量。
「左乘矩阵、右乘向量」这类说法,确实是在强调“向量在右边”的这种形式。
它告诉你的是向量和矩阵在表达式中的排列关系,是列/行向量系统的一部分语义。
🎯 换句话说:
“左乘矩阵”,是说矩阵写在左边,向量在右边
→ 即:Mv
(向量被左边的矩阵变换)
→ 默认你在使用 列向量系统---------------Unity巴拉巴拉
“右乘矩阵”,是说向量写在左边,矩阵在右边
→ 即:vM
(向量右乘矩阵)
→ 默认你在使用 行向量系统
所以,是不是「只有这两种说法」?
是的,从向量和矩阵相乘的定义角度来说,就这两种约定,没有第三种本质上不同的方式。
表示方式 | 写法 | 适配场景 |
---|---|---|
列向量 | Mv | ✅ 图形学、Unity、OpenGL、HLSL、GLSL |
行向量 | vM | ⛔ 一些数学教材、少部分线性代数风格(但不常用于图形变换) |
---------
可是我写成的形式只是fixed3(x,y,z),,实际上存储数据的是不关注行列的,,,那么是不是意味着这里的tanspose同样是只是逻辑上的提示,并不代表里面实际的数据处理方式真的有转置?
🎯 简要回答你:
✅ 是的,你是对的 ——
实际上fixed3(x, y, z)
或float3
本质上只是 一组线性数据,GPU 或 CPU 并不会自动知道它是“行向量”还是“列向量”,这是数学意义上的区别,不是内存物理结构。
而像你提到的
transpose
,更多是对数学逻辑的一种语义提示或约定,并不意味着内存中数据真的被 rearrange(重新排布)了。
Pass{Name "Outline"Cull FrontCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"float _Outline;fixed4 _OutlineColor;struct v2f{float4 vertex :SV_POSITION;};v2f vert (appdata_base v){v2f o;//物体空间法线外拓//v.vertex.xyz += v.normal * _Outline;//o.vertex = UnityObjectToClipPos(v.vertex);//视角空间法线外拓//float4 pos = mul(UNITY_MATRIX_V, mul(unity_ObjectToWorld, v.vertex));//float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));//pos = pos + float4(normal,0) * _Outline;//o.vertex = mul(UNITY_MATRIX_P, pos);//裁剪空间法线外拓o.vertex = UnityObjectToClipPos(v.vertex);float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));float2 viewNoraml = TransformViewToProjection(normal.xy);o.vertex.xy += viewNoraml * _Outline;return o;}float4 frag(v2f i):SV_Target{return _OutlineColor;}ENDCG}
把 UNITY_MATRIX_IT_MV
转成 3x3 ((float3x3)UNITY_MATRIX_IT_MV
) 是合理的,因为我们处理的是法线方向向量,不需要考虑位移分量
-
对于方向向量如
v.normal
,这段3x3
就足够完成旋转和缩放变换。 -
如果你传入的是位置
v.vertex
,那才需要用float4x4
,因为要考虑位移部分。
为什么只用 normal.xy
?
1. 目标是对 o.vertex.xy
做偏移
-
o.vertex
是裁剪空间(clip space)下的坐标,作用于屏幕上的 2D 位置。 -
所以我们只需要在 X 和 Y 平面内偏移这个像素点,制造出轮廓线的“外拓”效果。
-
z
分量在这里并不会影响你看到的轮廓(不会显著影响屏幕上的位置,只影响深度)
TransformViewToProjection()
是做什么的?
-
这个函数将 观察空间(View Space) 中的向量转换为 投影空间(Projection Space)。
-
它用于将法线的方向也带入透视变形中,避免轮廓线在不同视角下厚度不一致。
-
但它的输入是
float2
,意味着只能转换 2D 屏幕平面上的分量。
o.vertex.xy += viewNormal * _Outline;
明明 vertex
是 clip space 的 xyzw,
只改 xy
会不会出问题?
不会出问题,反而这是合理做法,因为这里我们就是要在屏幕空间做一个 2D 偏移效果,而不破坏 z
和 w
o.vertex = UnityObjectToClipPos(v.vertex);
它是 Clip Space(裁剪空间)坐标,四维向量 (x, y, z, w)。
后续会自动进入 NDC(x/w, y/w, z/w),再映射到屏幕。
o.vertex.xy += viewNormal * _Outline;
因为:
你只想让模型边缘沿屏幕方向“鼓出来”,产生描边效果。
所以只改 xy,让它在画面中“挤出一点”。
改 z 会造成深度变化,可能导致遮挡错误。
改 w 会影响投影缩放关系,造成透视失真。
是否破坏了 clip space 的投影?
不会。
-
Clip space 是一个过渡空间,最终 GPU 会做
o.vertex.xyz / o.vertex.w
投影。 -
xy
改变后,在屏幕上会位移。 -
z
和w
不变,意味着你不会影响遮挡关系或投影缩放。
假设你有一张纸(屏幕),你把一个点轻轻沿着横纵方向推开一点,但不把它抬起来(不改 z),也不改变它离你的“距离比例”(w)。那么它的视觉位置偏移了,但它的深度和透视仍然是对的。
smoothstep()
是一个在图形编程中非常常用的函数,用于生成**平滑过渡(soft transition)**效果
smoothstep(edge0, edge1, x)
其作用是:
当 x 在 edge0 和 edge1 之间时,输出一个从 0 到 1 的平滑插值值。
当 x <= edge0,返回 0;
当 x >= edge1,返回 1;
中间用一个平滑三次曲线过渡。
float t = saturate((x - edge0) / (edge1 - edge0));
return t * t * (3 - 2 * t);
这个公式创造了一个 S型曲线,即缓入缓出(ease in/out)的效果。
difLight = smoothstep(0, 1, difLight);
这一步是:
对 dot(worldLightDir, i.worldNormal) * 0.5 + 0.5 这个光照强度进行 平滑映射;
避免原始 dot 值带来的硬切换或强烈对比;
实际上就是给卡通风格加一点“柔化的边缘过渡”。
Shader 中前面那一堆 ShaderLab 语言(也就是 Pass
里的非 HLSL 部分),它们是用来控制渲染行为的,并不是计算颜色或坐标,而是告诉 GPU 在“怎么渲染”这段片元
Pass
{
Name "XRay"
Tags { "ForceNoShadowCasting" = "true" }
Blend SrcAlpha One
ZWrite Off
ZTest Greater
Name "XRay"
-
给这个
Pass
起个名字,方便调试或多通道控制时引用。 -
不影响渲染行为。
Tags { "ForceNoShadowCasting" = "true" }
-
让这个 Pass 不参与阴影投射。
-
对于半透明或特殊渲染(如 X-Ray)是有意义的:不需要参与阴影。
ZWrite Off
-
关闭深度写入,也就是说这个 Pass 不会写入 Z 缓冲区。
-
原因是:XRay 要“穿透”,你不希望它挡住别的物体。
ZTest Greater
-
表示只有在片元比当前深度更远(Z更大)时才绘制。
-
非常关键!这让这个 XRay Pass 只在物体背面被遮挡时才显示。
-
也就是:只有当它“在其他物体后面”才会出现,模拟透视内部结构。
-
-
相比常用的
ZTest LEqual
或ZTest Always
,这是一种隐藏条件渲染。
fixed4 frag(v2f i) : SV_Target
{
float3 normal = normalize(i.normal);
float3 viewDir = normalize(i.viewDir);
float rim = 1 - dot(normal, viewDir);
return _XRayColor * pow(rim, 1 / _XRayPower);
}
Shader 计算了一个 Rim(边缘高光)值 来模拟透视轮廓。
和前面的 ZTest Greater 联合使用后,只有被挡住的背面边缘才出现 XRay 效果,很自然地做出内透或透视显示。
-
同一个模型的不同三角面片之间,只要一个片元比另一个更靠前,就会记录它的深度。
-
所以模型自己也会遮挡自己。
-
这就是为什么你可以做 XRay 效果:它会在“模型自己后面的像素”上画出边缘线或高光。
切线空间(Tangent Space)转世界空间(World Space)
fixed3 worldNormal = normalize(float3(
dot(i.TtoW0.xyz, tangentNormal),
dot(i.TtoW1.xyz, tangentNormal),
dot(i.TtoW2.xyz, tangentNormal)
));
TBN矩阵 = [Tangent, Binormal, Normal] (每一列是一个向量)
由于你无法直接在 Unity 的 fragment shader 中使用 mul(float3x3, float3)
(尤其是当 TBN
来自顶点插值的时候),Unity 中很多时候会“展开”这个乘法写成:
顶点插值不支持结构体或矩阵传递
相关文章:
自由学习记录(56)
从贴图空间(texture space)将值还原到切线空间(tangent space)向量 tangentNormal.xy (packedNormal.xy * 2 - 1) * _BumpScale; 背后的知识点:法线贴图中的 RGB 是在 0~1 范围内编码的向量 所以贴图法线是怎么“压…...
计算机网络八股——HTTP协议与HTTPS协议
前言: 到时候我想要写一篇文章就是:在浏览器中输入URL并按下回车会发生什么? 然后将几篇文章全部串联到一起,现在几天的任务就是将这里的每个小部分进行一个详细的介绍 HTTP1.1简述与特性 Web 上的通信都是建⽴在 HTTP 协议上的…...
JAVAEE(网络原理—UDP报头结构)
我们本篇文章要讲的是UDP的报头结构以及注意事项。 下面呢,我先说一下UDP是什么? 1.UDP是什么? UDP是一种网络协议。网络协议是计算机网络中,为了使不同设备之间能够准确、高效地进行数据交换和通信,而预先制定的一…...
Redis-分布式锁
Redis-分布式锁 文章目录 Redis-分布式锁1.基本原理和不同方式实现方式对比2.Redis分布式锁的基本实现思路3.分布式锁误删问题一4.分布式锁误删问题二5.Redission1.功能介绍2.快速入门3.可重入锁原理4.锁重试和WatchDog机制1.锁重试2. WatchDog 机制(锁自动续期&…...
如何优雅地为 Axios 配置失败重试与最大尝试次数
在 Vue 3 中,除了使用自定义的 useRequest 钩子函数外,还可以通过 axios 的拦截器 或 axios-retry 插件实现接口请求失败后的重试逻辑。以下是两种具体方案的实现方式: 方案一:使用 axios 拦截器实现重试 实现步骤: 通…...
Windows使用SonarQube时启动脚本自动关闭
一、解决的问题 Windows使用SonarQube时启动脚本自动关闭,并发生报错: ERROR: Elasticsearch did not exit normally - check the logs at E:\Inori_Code\Year3\SE\sonarqube-25.2.0.102705\sonarqube-25.2.0.102705\logs\sonarqube.log ERROR: Elastic…...
MYSQL初阶(暂为自用草稿)
目录 基本操作 database操作 table操作 数据类型 INT类型 bit类型 FLOAT类型 CHAR类型 DATE类型 SEL类型 表的约束 列约束 NULL DEFAULT PRIMARY KEY UNIQUE KEY 表约束 PRIMARY KEY FOREIGN KEY 其他补充 AUTO_INCREMENT COMMENT ZEROFILL 表的CRUD …...
交换排序——快速排序
交换排序的基本思路:把序列中的两个元素进行比较,根据需求对两个元素进行交换。特点是较大的元素向序列的尾部移动,较小的元素向序列的前部移动。 hoare法 在序列中任取一个元素作为基准值,一趟排序完成之后,以基准值为…...
资源-又在网上淘到金了
前言: 本期再分享网上冲浪发现的特效/动画/视频资源网站。 一、基本介绍: mantissa.xyz,about作者介绍为:Midge “Mantissa” Sinnaeve (米奇辛纳夫)是一位屡获殊荣的艺术家和导演,提供动画、…...
CSS中的`transform-style`属性:3D变换的秘密武器
在CSS中,当我们尝试创建复杂的3D场景时,transform-style属性变得尤为重要。它决定了子元素是在3D空间中呈现还是被展平到2D平面中。本文将深入探讨transform-style的用法,并通过具体的代码示例来展示如何利用这个属性来增强你的网页设计。 什…...
Step文件无法编辑怎么办?
Step文件无法编辑怎么办? 这里介绍两种方法, 1、 直接导入 准备step文件,solidworks导入后是这样,不能在上面直接编辑 图 1 点击右键,选择解除特征(不同版本的可能不太一样,这里是solidworks2…...
从 LabelImg 到 Label Studio!AI 数据标注神器升级,Web 版真香
视频讲解: 从 LabelImg 到 Label Studio!AI 数据标注神器升级,Web 版真香 Label Studio 支持图像、文本、音频、视频、时间序列等多类型数据标注,覆盖计算机视觉(目标检测、语义分割)、自然语言处理&#x…...
纯FPGA实现驱动AD9361配置的思路和实现之一 概述
我们在做ZYNQ系统开发时候做的IP基本都是AXI_LITE_SLAVE,是SLAVE,从设备。就是提供了若干寄存器接口供MASTER进行读写。SLAVE里面的逻辑通过读写动作或者读写的数据进行响应的动作。这种方式的好处是硬件层面可以访问寄存器,软件层面是可以实…...
Nacos配置中心服务端源码解析
文章目录 概述一、配置持久化到数据库二、发布事件2.1、事件发布者端2.1.1、DefaultPublisher#publish2.1.2、DefaultPublisher#run2.1.3、DefaultPublisher#receiveEvent 2.2、事件订阅者端2.2.1、Subscriber#onEvent2.2.2、ConfigCacheService#dump 总结:Nacos 配…...
SAP系统工艺路线的分配物料出现旧版包材
问题:工艺路线的物料错了 这是3月份技术部发现的问题,10000209这个成品有两个版本的BOM, 在创建新版的工艺路线里,发现分配的物料仍然是旧版的物料. 原因排查: 1 BOM中物料错误? 2 选错了生产版本,选了版本1? 3 生产版本设置中的可选BOM错误? 解决:把可选的BOM…...
JVM虚拟机--JVM的组成
(一)JVM的组成 一、JVM介绍 (1)JVM的作用 我们知道,Java代码要想在计算机中正常运行,就需要经过编译为class二进制字节码文件,而JVM就提供了class二进制字节码的运行环境。 一次编写,到处运行 因为JVM是…...
科学研究:怎么做
科研(科学研究) 是指通过系统化的方法,探索自然、社会或人文领域的未知问题,以发现新知识、验证理论或解决实际问题的活动。它的核心是基于证据的探索与创新,旨在推动人类认知和技术的进步。 科研的核心要…...
PyTorch数据操作基础教程:从张量创建到高级运算
本文通过示例代码全面讲解PyTorch中张量的基本操作,包含创建、运算、广播机制、索引切片等核心功能,并提供完整的代码和输出结果。 1. 张量创建与基本属性 import torch# 创建连续数值张量 x torch.arange(12, dtypetorch.float32) print("原始张…...
微服务治理与可观测性
服务注册与发现 核心功能 服务实例动态变化:实例可能因扩缩容、故障或迁移导致IP变动。服务依赖解耦:调用方无需硬编码服务地址,降低耦合度。负载均衡:自动选择健康实例,提升系统可用性。 核心组件 服务注册中心&am…...
如何对docker镜像存在的gosu安全漏洞进行修复——筑梦之路
这里以mysql的官方镜像为例进行说明,主要流程为: 1. 分析镜像存在的安全漏洞具体是什么 2. 根据分析结果有针对性地进行修复处理 3. 基于当前镜像进行修复安全漏洞并复核验证 # 镜像地址mysql:8.0.42 安全漏洞现状分析 dockerhub网站上获取该镜像的…...
OpenCV 04.19 练习
1. 创建一个 PyQt 应用程序,该应用程序能够: 1.使用 OpenCV 加载一张图像。 2.在 PyQt 的窗口中显示这张图像。 3.提供四个按钮(QPushButton): - 一个用于将图像转换为灰度图 - 一个用于将图像恢复为原始彩色图 - 一个…...
uv:重新定义Python开发效率的下一代工具链
在Python生态系统中,包管理和项目工具链的复杂性一直是开发者面临的一大挑战。从依赖管理、虚拟环境创建到多版本Python切换,传统的工具链(如pip、virtualenv、poetry等)虽然功能强大,但操作繁琐、性能不足的问题长期存在。而uv的出现,以颠覆性的速度和功能集成,为Pytho…...
【Easylive】Gateway模块 bootstrap.yml 解析
【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版 Gateway模块 bootstrap.yml 常规解析 该配置文件定义了 Spring Cloud Gateway 的核心配置,包括 环境配置、服务注册、动态路由规则 等。以下是逐项解析: 1. 基础配…...
Warcraft Logs [Classic] [WCL] Usage Wizard <HTOC>
HTOC(十字军的试炼)副本中各个BOSS的ID如下: 629 - 诺森德野兽 633 - 加拉克苏斯大王 637 - 派系冠军 641 - 瓦格里双子 645 - 阿努巴拉克 encounterID!637 and encounterID!641 encounterID NOT IN (637,641) 伤害 …...
多模态大语言模型arxiv论文略读(二十八)
MM-SAP: A Comprehensive Benchmark for Assessing Self-Awareness of Multimodal Large Language Models in Perception ➡️ 论文标题:MM-SAP: A Comprehensive Benchmark for Assessing Self-Awareness of Multimodal Large Language Models in Perception ➡️…...
JavaScript数据类型简介
在JavaScript中,理解不同的数据类型是掌握这门语言的基础。数据类型决定了变量可以存储什么样的值以及这些值能够执行的操作。JavaScript支持多种数据类型,每种都有其特定的用途和特点。本文将详细介绍JavaScript中的主要数据类型,并提供一些…...
CasualLanguage Model和Seq2Seq模型的区别
**问题1:**Causal Language Modeling 和 Conditional Generation 、Sequence Classification 的区别是什么? 因果语言模型(Causal Language Model): 预测给定文本序列中的下一个字符,一般用于文本生成、补全句子等,模型…...
在Qt和OSG中动态改变部分3D模型数据
要在Qt和OSG环境中导入3D模型并只对部分数据进行动态改变,你可以采用以下方法: 基本实现步骤 加载模型:使用OSG的读取器加载3D模型文件 访问特定部分:识别并获取模型中需要修改的部分 动态修改:在Qt界面或逻辑中设置修改这些部分的机制 更新显示:确保修改后的模型能够实…...
命令update-alternatives
❯ which pip /home/ying/anaconda3/bin/pipying192 ~ [2]> which pip /usr/bin/pip使用update-alternatives对他们进行管理和切换 快捷方式 和 实际路径不可以相同 所以我这边选择了/usr/local/bin目录作为介质存储快捷方式,另外该快捷方式会自己创建我们只需选…...
10.thinkphp的响应
响应输出 响应操作 1. 响应输出,有好几种:包括return、json()和view()等等; 2. 默认输出方式是以html格式输出,如果你发起json请求,则输出json; 3. 而背后是response对象,可以用response()输…...
【技术派后端篇】技术派中的白名单机制:基于Redis的Set实现
在技术派社区中,为了保证文章的质量和社区的良性发展,所有发布的文章都需要经过审核。然而,并非所有作者的文章都需要审核,我们通过白名单机制来优化这一流程。本文将详细介绍技术派中白名单的实现方式,以及如何利用Re…...
Keil A51汇编伪指令
以下是 Keil A51 汇编器支持的常用伪指令 及其详细说明,涵盖代码结构、数据定义、条件编译等关键功能,结合实际应用场景进行分类和示例: 一、程序结构与地址控制 伪指令功能语法示例说明ORG设置代码/数据起始地址ORG 0000H后续代码从指定地址…...
Windows上安装FFmpeg的详细指南
1.下载FFmpeg 访问FFmpeg官方下载页面:https://ffmpeg.org/download.html 点击"Windows builds from gyan.dev"或"Windows builds by BtbN" gyan.dev版本:https://www.gyan.dev/ffmpeg/builds/ BtbN版本:https://githu…...
jmeter利用csv进行参数化和自动断言
1.测试数据 csv测试数据如下(以注册接口为例) 2.jemer参数化csv设置 打开 jmeter,添加好线程组、HTTP信息头管理器、CSV 数据文件设置、注册请求、响应断言、查看结果树 1) CSV 数据文件设置 若 CSV 中数据包含中文,…...
《Android 应用开发基础教程》——第二章:Activity 与生命周期详解
目录 第二章:Activity 与生命周期详解 2.1 什么是 Activity? 作用: 2.2 创建一个 Activity 示例代码 Manifest 注册: 2.3 Activity 生命周期(Life Cycle) 生命周期图解: 2.4 生命周期代…...
[Java]反射、String类补充
目录 1、反射定义 2、用途(了解) 3、反射相关的类 4、Class类(反射机制的起源) 4.1、相关方法 5、反射示例 5.1、获取Class对象 5.2、反射的使用 6、反射优点和缺点 7、String类补充 7.1、创建对象的思考 8、字符串常量池 9、再谈String对象创建 10、intern方法 …...
word表格批量操作——宏
word中所有表格代码 这个是表格的模板代码 Sub 表格通用代码() For i ActiveDocument.Tables.Count To 1 Step -1ActiveDocument.Tables (i) Next End Sub1、根据内容自动调整表格 Sub 表格适用内容() For i ActiveDocument.Tables.Count To 1 Step -1ActiveDocument.Tabl…...
eSTK.me
eSTK.me 调用 USIM 卡的 Applet 实现在 iOS 内自助切换 Profile。写卡仍然需要借助硬件读卡器或者兼容 eSIM 的 Android 手机。支持国行 iPhone。 eSTK.me(固件 v2.x 及以后的版本)基于 ETSI 的 Bearer Independent Protocol (BIP) 协议,使…...
四级英语备考指南
一、引言 大学英语四级考试是对大学生英语综合能力的一次重要检验。无论是为了学业要求,还是提升自身竞争力,顺利通过四级考试都有着重要意义。本文将为大家详细介绍四级英语的备考策略,帮助大家高效备考,取得理想成绩。 二、了…...
Java Web 之 互联网协议 100问
HTTP是什么? HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最广泛的协议之一,用于在客户端(如浏览器)和服务器之间传输数据。它是 Web 通信的基础,支持浏览器访…...
【单倍型理解及计算系列之二】单倍型基本概念以及其与遗传定位中Bin的定义区别
问题:如何理解单倍型,与遗传定位中Bin的定义区别: 简而言之:就是单倍型是基于LD,通常为连锁不平衡(LD)较高的区域形成。但bin是人为划分的,如以固定SNP数量/固定长度设置࿰…...
ArcPy Mapping 模块基础
在地理信息系统(GIS)的应用中,地图制作是一个非常重要的环节。无论是生成用于展示的静态地图,还是动态更新的地图服务,都需要高效且灵活的工具来实现。ArcPy 提供了强大的mapping模块,可以帮助用户自动化地…...
AcWing 11:背包问题求方案数 ← 0-1背包
【题目来源】 https://www.acwing.com/problem/content/11/ 【题目描述】 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总…...
Redis增删改查
### 进入redis控制台 redis-cli --raw #加上raw,防止中文乱码### 增 127.0.0.1:6379> LPUSH list0 "hello" #增加一个list 1 127.0.0.1:6379> LRANGE list0 0 -1 #查看list hello### 删 127.0.0.1:6379> DEL list0 #删除list 1 127.0.0.1:6379> LRANG…...
多道程序和多任务操作系统区别
多道程序 vs. 多道任务:对比分析 ✅ 共同点 方面共同特征核心机制都依赖于进程/任务切换执行需求实现多个程序或任务"并发"执行系统支持都需要操作系统的支持(如调度算法、内存管理)本质目标提高资源利用率(CPU不空转…...
【MySQL】MySQL建立索引不知道注意什么?
基本原则: 1.选择性原则: 选择高选择性的列建立索引(该列有大量不同的值) 2.适度原则:不是越多越好,每个索引都会增加写入开销 列选择注意事项: 1.常用查询条件列:WHERE字句中频繁使用的列 2.连接操作列…...
区块链木材业务服务平台:商贸物流新变革
区块链木材业务服务平台:商贸物流新变革 在全球商贸物流行业不断发展的当下,木材贸易作为其中重要的一环,面临着诸多挑战。区块链木材业务服务平台的出现,为木材商贸物流领域带来了全新的解决方案,正逐步引领行业走向…...
【AI提示词】经济学家
提示说明 经济学家致力于提供深入的经济分析和预测,帮助用户理解经济趋势、政策影响以及市场动态。他们通过专业的经济模型和数据分析,为用户在投资、决策等方面提供指导。 提示词 # 角色 经济学家## 注意 1. 经济学家专家需要具备深入分析经济现象的…...
C++用于保留浮点数的两位小数,使用宏定义方法(可兼容低版本Visual Studio)
文章目录 一、 描述二、 样例二、 结果输出 一、 描述 这个宏定义(可放入.h头文件里)使用基本的数学运算,几乎兼容所有版本的VS,以下可对正数做四舍五入: #define ROUND_TO_TWO(x) ( (floor((x) * 100 0.5) / 100) …...
kimi+deepseek制作PPT
文章目录 KIMI简介一、基本信息二、核心特点三、服务理念 Deepseek简介PPT关键词提示 KIMI简介 KIMI官网:Kimi - 会推理解析,能深度思考的AI助手 一、基本信息 名称 :KIMI开发团队 :月之暗面科技有限公司上线时间 :…...