Unity URP 曲面细分学习笔记
学百人时遇到了曲面着色器的内容,有点糊里糊涂,于是上知乎找到了两篇大佬的文章 Unity URP 曲面细分 和 Unity曲面细分笔记,本文只是自己做学习记录使用
1.曲面细分与镶嵌
曲面细分或细分曲面(Subdivision surface)是指一种通过递归算法将一个粗糙的几何网格细化的技术。镶嵌(Tessellation)则是实现曲面细分的具体手段,它能将场景中的几何物体顶点集划分为合适的渲染结构。
曲面细分分为三个阶段:外壳着色器(Hull Shader)、镶嵌器(Tessellator)、域着色器(Domain Shader)。
1.1 外壳着色器 Hull Shader
Hull shader实际上是两个阶段(Phase)组成:常量外壳着色器(Constant Hull Shader)和 控制点外壳着色器(Control point hull shader),两个阶段并行运行。
- Constant Hull Shader 会对每一个面片进行处理,主要任务是输出网格的曲面细分因子(Tessellation Factor),曲面细分因子用于指导面片细分数。
- 假如要传入的面片是三角形,那么对于三角形就会有三个边曲面细分因子,所以
edgeFactor[3],对于内部曲面细分因子,因为三角形是最小的图元所以内部是一个因子(个人理解)insideFactor,如果是矩形的话就是四个边因子和两个内部因子 - 常量外壳着色器会以面片的所有顶点(或控制点)为输入,所以有三个顶点
InputPatch<VertexOut, 3> patch,数字为3 - SV_PrimitiveID 提供传入面片的ID值,可以用来区分不同的Patch,这样你就可以根据Patch的ID来为每个Patch设置不同的细分因子,或者执行其他依赖于Patch ID的操作。它对于每个图元都是不同的
- 假如要传入的面片是三角形,那么对于三角形就会有三个边曲面细分因子,所以
struct PatchTess{float edgeFactor[3];float insideFactor;
};PatchTess PatchConstant(InputPatch<VertexOut, 3> patch, uint patchID : SV_PrimitiveID)
{PatchTess o;o.edgeFactor[0] = 4;o.edgeFactor[1] = 4; o.edgeFactor[2] = 4;o.insideFactor = 4;return o;
}
- Control Point Hull Shader 用来改变每个输出顶点的位置信息,例如将一个三角形变为三次贝塞尔三角面片
- domain:面片类型,参数有三角面tri、四角面片quad、等值线isoline
- partitioning:曲面细分方式,参数有integer、fractional_even、fractional_odd
- integer,指新顶点的增加指取决于细分的整数部分(等分),图形可能会出现图片
- fractional_even,向上取最近的偶数n,将整段切割为n-2个相等长度的部分,和两端较短的部分
- fractional_odd,向上取最近的奇数n,将整段切割为n-2个相等长度的部分,和两端较短的部分
- outputtopology:细分创建的三角形面片的绕序,参数有顺时针triangle_cw、逆时针triangle_ccw
- patchconstantfunc:常量外壳着色器的函数名
- outputcontrolpoints:外壳着色器的执行次数,即生成的控制点个数
- maxtessfactor:程序会使用到最大的细分因子
- SV_OutputControlPointID:当前正在操作的控制点索引ID
[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[patchconstantfunc("PatchConstant")]
[outputcontrolpoints(3)]
[maxtessfactor(64.0f)]
HullOut ControlPoint (InputPatch<VertexOut,3> patch,uint id : SV_OutputControlPointID){ HullOut o;o.positionOS = patch[id].positionOS;o.texcoord = patch[id].texcoord; return o;
}
常量外壳着色器对每个片元执行一次,输出细分因子等信息;控制点外壳着色器对每个控制点执行一次,并输出对应或衍生的控制点。两个阶段并行运行。
1.2 镶嵌器阶段 Tessellator
这一阶段我们无法对其做出任何控制,全程由硬件控制。在这一阶段,硬件根据之前曲面细分因子对面片做出细分操作。
1.3 域着色器阶段 Domain Shader
就和普通的顶点着色器要做的差不多,我们需要计算每一个控制点的顶点位置等信息。
- 功能
- 生成细分顶点,这些顶点是由外壳着色器确定的细分因子和细分模式所生成的
- 插值属性,根据控制点的属性进行插值,得到细分顶点的属性
- 输出顶点,域着色器生成最终的顶点数据,并传递给后面的着色器

struct HullOut{float3 positionOS : INTERNALTESSPOS; float2 texcoord : TEXCOORD0;
};struct DomainOut
{float4 positionCS : SV_POSITION;float2 texcoord : TEXCOORD0;
};[domain("tri")]
DomainOut FlatTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION)
{ float3 positionOS = patch[0].positionOS * bary.x + patch[1].positionOS * bary.y + patch[2].positionOS * bary.z; float2 texcoord = patch[0].texcoord * bary.x + patch[1].texcoord * bary.y + patch[2].texcoord * bary.z;DomainOut output;output.positionCS = TransformObjectToHClip(positionOS);output.texcoord = texcoord;return output;
}
- 域着色器输入:接受一个域(Domain)输入,代表了细分后顶点在原始补丁内的位置,这个位置通常用参数空间坐标表示,例如重心坐标(Barycentric Coordinates)(这里的重心坐标,是由硬件在细分过程的一个中间阶段 Tessellator 自动计算得出的)
- 属性插值:域着色器使用这些参数空间坐标来插值原始控制点的属性。
- 顶点输出:域着色器输出一个顶点,这个顶点包含计算后的位置和其他属性(如颜色、纹理坐标、法线等)
2.具体实现
2.1 不同的细分策略
2.1.1 平面镶嵌 Flat Tessellation
平面镶嵌只是线性插值位置信息,细分后的图案只比之前多了一些三角面片,单独使用并不能平滑模型。通常和置换贴图配合使用,创建凹凸不平的平面。
Shader "Tessellation/Flat Tessellation"
{Properties{[NoScaleOffset]_BaseMap ("Base Map", 2D) = "white" {} [Header(Tess)][Space][KeywordEnum(integer, fractional_even, fractional_odd)]_Partitioning ("Partitioning Mode", Float) = 0[KeywordEnum(triangle_cw, triangle_ccw)]_Outputtopology ("Outputtopology Mode", Float) = 0_EdgeFactor ("EdgeFactor", Range(1,8)) = 4 _InsideFactor ("InsideFactor", Range(1,8)) = 4 }SubShader{Tags { "RenderType"="Opaque" }Pass{ HLSLPROGRAM#pragma target 4.6 #pragma vertex FlatTessVert#pragma fragment FlatTessFrag #pragma hull FlatTessControlPoint#pragma domain FlatTessDomain#pragma multi_compile _PARTITIONING_INTEGER _PARTITIONING_FRACTIONAL_EVEN _PARTITIONING_FRACTIONAL_ODD #pragma multi_compile _OUTPUTTOPOLOGY_TRIANGLE_CW _OUTPUTTOPOLOGY_TRIANGLE_CCW #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" CBUFFER_START(UnityPerMaterial) float _EdgeFactor; float _InsideFactor; CBUFFER_ENDTEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); struct Attributes{float3 positionOS : POSITION; float2 texcoord : TEXCOORD0;};struct VertexOut{float3 positionOS : INTERNALTESSPOS; float2 texcoord : TEXCOORD0;};struct PatchTess { float edgeFactor[3] : SV_TESSFACTOR;float insideFactor : SV_INSIDETESSFACTOR;};struct HullOut{float3 positionOS : INTERNALTESSPOS; float2 texcoord : TEXCOORD0;};struct DomainOut{float4 positionCS : SV_POSITION;float2 texcoord : TEXCOORD0; };VertexOut FlatTessVert(Attributes input){ VertexOut o;o.positionOS = input.positionOS; o.texcoord = input.texcoord;return o;}PatchTess PatchConstant (InputPatch<VertexOut,3> patch, uint patchID : SV_PrimitiveID){ PatchTess o;o.edgeFactor[0] = _EdgeFactor;o.edgeFactor[1] = _EdgeFactor; o.edgeFactor[2] = _EdgeFactor;o.insideFactor = _InsideFactor;return o;}[domain("tri")] #if _PARTITIONING_INTEGER[partitioning("integer")] #elif _PARTITIONING_FRACTIONAL_EVEN[partitioning("fractional_even")] #elif _PARTITIONING_FRACTIONAL_ODD[partitioning("fractional_odd")] #endif #if _OUTPUTTOPOLOGY_TRIANGLE_CW[outputtopology("triangle_cw")] #elif _OUTPUTTOPOLOGY_TRIANGLE_CCW[outputtopology("triangle_ccw")] #endif[patchconstantfunc("PatchConstant")] [outputcontrolpoints(3)] [maxtessfactor(64.0f)] HullOut FlatTessControlPoint (InputPatch<VertexOut,3> patch,uint id : SV_OutputControlPointID){ HullOut o;o.positionOS = patch[id].positionOS;o.texcoord = patch[id].texcoord; return o;}[domain("tri")] DomainOut FlatTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION){ float3 positionOS = patch[0].positionOS * bary.x + patch[1].positionOS * bary.y + patch[2].positionOS * bary.z; float2 texcoord = patch[0].texcoord * bary.x + patch[1].texcoord * bary.y + patch[2].texcoord * bary.z;DomainOut output;output.positionCS = TransformObjectToHClip(positionOS);output.texcoord = texcoord;return output; }half4 FlatTessFrag(DomainOut input) : SV_Target{ half3 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.texcoord).rgb;return half4(color, 1.0); }ENDHLSL}}
}

2.1.2 PN Tessellation(Per-Node Tessellation)
论文学习 Curved PN Triangles(Paper)
网站学习 OpenGL tutorial
在外壳着色器阶段,把一个三角面片(3个控制点)转换为一个3次贝塞尔三角面片(Cubic Bezier Triangle Patch,一种具有10个控制点的面片),这种策略称为 Curved Point-Normal Triangles(PN triangles),不同于Flat Tessellation,即使没有置换贴图,也能实现改变模型形状,平滑轮廓的作用。满足了资源有限以及环境限制的需求。
我们将使用一个Bezier曲面,Bezier三角形,形式如下

uvw代表的是质心坐标(u + v + w = 1),10个 b u v w b_{uvw} buvw是CPs,CPs形似如下图,类似于三角形顶部有一个点膨胀的表面
在镶嵌流水线中,
- 外壳着色器:我们将生成10个控制点并确定细分因子
- 如何生成控制点?
- 1.三角形原始顶点B003、B030和B300保持不变
- 2.两个中点B012和B021在1/3和2/3的位置
- 3.将中点投影在初始顶点的切平面上
- 4.对于B111点,我们从原始的三角形中心(三个初始顶点取平均值)到6个中点的平均值(投影后的)取一个矢量

- 如何生成控制点?
- Tesellation Primitive Generator:PG中再根据细分因子对三角形域进行细分,并对每个新点执行域着色器;
- 域着色器:把来自PG的质心坐标和来自外壳着色器的10个控制点插入到贝塞尔三角形多项式中,得到的结果是膨胀表面上的坐标。
每个控制点的生成:


float3 ComputeCP(float3 pA, float3 pB, float3 nA){return (2 * pA + pB - dot((pB - pA), nA) * nA) / 3.0f;
}

由于控制点的增多,在Hull Shader输出时每个顶点需要多携带两个顶点信息(中心控制点b111可以直接推算出来),例如:b030 可能需要携带b021和b012的顶点信息。修改一下控制点外壳着色器。
- 用TEXCOORD1和TEXCOORD2来存储额外的两个顶点信息
- 在PNTessControlPoint控制点着色器中,使用三元运算符来对nextID进行赋值,实现对每个新生成的控制点进行计算。
struct HullOut{float3 positionOS : INTERNALTESSPOS;float3 normalOS : NORMAL;float2 texcoord : TEXCOORD0;float3 positionOS1 : TEXCOORD1; // 三角片元每个顶点多携带两个顶点信息float3 positionOS2 : TEXCOORD2;
}; float3 ComputeCP(float3 pA, float3 pB, float3 nA){return (2 * pA + pB - dot((pB - pA), nA) * nA) / 3.0f;
}[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[patchconstantfunc("PatchConstant")]
[outputcontrolpoints(3)]
[maxtessfactor(64.0f)]
HullOut PNTessControlPoint(InputPatch<VertexOut,3> patch,uint id : SV_OutputControlPointID){ HullOut output;const uint nextCPID = id < 2 ? id + 1 : 0;output.positionOS = patch[id].positionOS;output.normalOS = patch[id].normalOS;output.texcoord = patch[id].texcoord;output.positionOS1 = ComputeCP(patch[id].positionOS, patch[nextCPID].positionOS, patch[id].normalOS);output.positionOS2 = ComputeCP(patch[nextCPID].positionOS, patch[id].positionOS, patch[nextCPID].normalOS);return output;
}
域着色器负责“Bezier三角形的实现”,根据上面Bezier三角形的表达式
- 首先计算系数u,v,w(即质心坐标,上面已经介绍了,是PG阶段自动生成的)
- 把外壳着色器中得到的控制点赋值给变量,并计算E和V,得到b111点
- 此处对于法线的计算就简化为“最简单的三个控制点插值获取的方式”
[domain("tri")]
DomainOut PNTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION)
{ float u = bary.x;float v = bary.y;float w = bary.z;float uu = u * u;float vv = v * v;float ww = w * w;float uu3 = 3 * uu;float vv3 = 3 * vv;float ww3 = 3 * ww;float3 b300 = patch[0].positionOS;float3 b210 = patch[0].positionOS1;float3 b120 = patch[0].positionOS2;float3 b030 = patch[1].positionOS;float3 b021 = patch[1].positionOS1;float3 b012 = patch[1].positionOS2;float3 b003 = patch[2].positionOS;float3 b102 = patch[2].positionOS1;float3 b201 = patch[2].positionOS2; float3 E = (b210 + b120 + b021 + b012 + b102 + b201) / 6.0;float3 V = (b003 + b030 + b300) / 3.0; float3 b111 = E + (E - V) / 2.0f; // 插值获得细分后的顶点位置float3 positionOS = b300 * ww * w + b030 * uu * u + b003 * vv * v+ b210 * ww3 * u + b120 * uu3 * w + b021 * uu3 * v + b012 * vv3 * u+ b102 * vv3 * w + b201 * ww3 * v+ b111 * 6.0 * w * u * v;// 此处简化了法线的计算float3 normalOS = patch[0].normalOS * u + patch[1].normalOS * v+ patch[2].normalOS * w;normalOS = normalize(normalOS);float2 texcoord = patch[0].texcoord * u+ patch[1].texcoord * v+ patch[2].texcoord * w;DomainOut output; output.positionCS = TransformObjectToHClip(positionOS); output.normalWS = TransformObjectToWorldNormal(normalOS);output.uv = texcoord;return output;
}
然而,目前的做法是有缺陷的,在面对一些相同位置有不同法线的模型时,细分后会造成模型边缘的不连续,形成裂缝(Crack)。
2.1.3 Phong Tessellation
Phong着色应该很熟悉,是一种利用法向量线性差值得到平滑的着色的技术。Phong细分的灵感来自Phong着色,将Phong着色这一概念扩展到空间域。
核心思想:是利用三角形每个角的顶点法线来影响细分过程中新顶点的位置,从而创造出曲面而非平面。

三角形原始顶点的切平面上, P ′ = P − ( ( P − V ) ⋅ N ) N P' = P - ((P-V)\cdot N)N P′=P−((P−V)⋅N)N
- P P P 是最初插值的平面位置
- V V V 是平面上的一个顶点位置
- N N N 是顶点 V V V 处的法线
- P ′ P' P′是 P P P 在平面上的投影。
float3 ProjectPointOnPlane(float3 flatPositionWS, float3 cornerPositionWS, float3 normalWS)
{return flatPositionWS - dot(flatPositionWS - cornerPositionWS, normalWS) * normalWS;
}

投影在三个切平面的三个点重新组成一个新的三角形,再用当前顶点的重心坐标插值计算出新的点。
real3 PhongTessellation(real3 positionWS, real3 p0, real3 p1, real3 p2, real3 n0, real3 n1, real3 n2, real3 baryCoords, real shape){// 分别计算三个切平面的投影点real3 c0 = ProjectPointOnPlane(positionWS, p0, n0);real3 c1 = ProjectPointOnPlane(positionWS, p1, n1);real3 c2 = ProjectPointOnPlane(positionWS, p2, n2);// 利用质心坐标插值得到最终顶点位置real3 phongPositionWS = baryCoords.x * c0 + baryCoords.y * c1 + baryCoords.z * c2;// 通过shape 控制平滑程度return lerp(positionWS, phongPositionWS, shape);}
域着色器修改为
DomainOut PhongTriTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION)
{ float3 positionWS = patch[0].positionWS * bary.x + patch[1].positionWS * bary.y + patch[2].positionWS * bary.z; positionWS =PhongTessellation(positionWS, patch[0].positionWS, patch[1].positionWS, patch[2].positionWS, patch[0].normalWS, patch[1].normalWS, patch[2].normalWS, bary, _PhongShape);float2 texcoord = patch[0].texcoord * bary.x + patch[1].texcoord * bary.y + patch[2].texcoord * bary.z;DomainOut output;output.positionCS = TransformObjectToHClip(positionWS);output.texcoord = texcoord;return output;
}
2.2 不同的细分因子
2.2.1 基于相机距离
为了让距离相机近的位置细分程度高一点,所以我们要先获取相机的位置,并且得到片元距相机的距离(三角形边缘中点到相机的距离),以此距离来调整细分因子
real3 GetDistanceBasedTessFactor(real3 p0, real3 p1, real3 p2, real3 cameraPosWS, real tessMinDist, real tessMaxDist)
{real3 edgePosition0 = 0.5 * (p1 + p2);real3 edgePosition1 = 0.5 * (p0 + p2);real3 edgePosition2 = 0.5 * (p0 + p1);// In case camera-relative rendering is enabled, 'cameraPosWS' is statically known to be 0,// so the compiler will be able to optimize distance() to length().real dist0 = distance(edgePosition0, cameraPosWS);real dist1 = distance(edgePosition1, cameraPosWS);real dist2 = distance(edgePosition2, cameraPosWS);// The saturate will handle the produced NaN in case min == maxreal fadeDist = tessMaxDist - tessMinDist;real3 tessFactor;tessFactor.x = saturate(1.0 - (dist0 - tessMinDist) / fadeDist);tessFactor.y = saturate(1.0 - (dist1 - tessMinDist) / fadeDist);tessFactor.z = saturate(1.0 - (dist2 - tessMinDist) / fadeDist);return tessFactor;
}
- real可以根据编译时的设置或宏定义来决定它具体代表哪种浮点数类型
- cameraPosWS变量不需要在Shader中声明,因为它是由Unity渲染管线自动提供的
- fadeDist是最大距离tessMaxDist和最小距离tessMinDist之间的差值,这个值定义了距离影响细分因子的范围
- 对于每个边缘,细分因子是通过将距离与tessMinDist相减,然后除以fadeDist(这个比例表示了当前距离相对于细分开始和结束的距离范围的位置),再用1减去该值(将比例翻转,即摄像机靠近相机时,细分因子接近1),最后使用saturate函数来限制结果在0到1之间(细分因子通常是介于0-1之间的值,0表示没有细分,1表示最大细分级别)。这样,当边缘中点距离相机较近时,细分因子接近1,表示细分程度较高;当边缘中点距离相机较远时,细分因子接近0,表示细分程度较低。
通过上述返回的细分因子,给三角形的边缘细分因子和内部细分因子赋值(内部细分因子设为三个边缘的平均)
real4 CalcTriTessFactorsFromEdgeTessFactors(real3 triVertexFactors)
{real4 tess;tess.x = triVertexFactors.x;tess.y = triVertexFactors.y;tess.z = triVertexFactors.z;tess.w = (triVertexFactors.x + triVertexFactors.y + triVertexFactors.z) / 3.0;return tess;
}
现将常量外壳着色器的代码调整如下
PatchTess PatchConstant (InputPatch<VertexOut,3> patch, uint patchID : SV_PrimitiveID){ PatchTess o;float3 cameraPosWS = GetCameraPositionWS();real3 triVectexFactors = GetDistanceBasedTessFactor(patch[0].positionWS, patch[1].positionWS, patch[2].positionWS, cameraPosWS, _TessMinDist, _TessMinDist + _FadeDist);float4 tessFactors = _EdgeFactor * CalcTriTessFactorsFromEdgeTessFactors(triVectexFactors);o.edgeFactor[0] = max(1.0, tessFactors.x);o.edgeFactor[1] = max(1.0, tessFactors.y);o.edgeFactor[2] = max(1.0, tessFactors.z);o.insideFactor = max(1.0, tessFactors.w);return o;
}
相关文章:
Unity URP 曲面细分学习笔记
学百人时遇到了曲面着色器的内容,有点糊里糊涂,于是上知乎找到了两篇大佬的文章 Unity URP 曲面细分 和 Unity曲面细分笔记,本文只是自己做学习记录使用 1.曲面细分与镶嵌 曲面细分或细分曲面(Subdivision surface)是…...
每天五分钟深度学习pytorch:训练神经网络模型的基本步骤
本文重点 本文个人认为是本专栏最重要的章节内容之一,前面我们学习了pytorch中的基本数据tensor,后面我们就将学学习深度学习模型的内容了,在学习之前,我们先来看一下我们使用pytorch框架训练神经网络模型的基本步骤,然后我们下面就将这些步骤分解开来,详细学习。 代码…...
【langchain学习】使用缓存优化langchain中的LLM调用性能:内存、SQLite与Redis的对比
在处理语言模型(LLM)调用时,特别是在需要多次执行相同请求的情况下,缓存机制能够显著提升系统的性能。本文通过对比内存缓存(InMemoryCache)、SQLite缓存(SQLiteCache)和Redis缓存(RedisCache),探讨了如何在Langchain中使用这些缓存机制来优化LLM调用的性能。 代码…...
spring boot 集成EasyExcel
EasyExcel 是一个基于 Java 的快速、简洁的 Excel 处理工具,它能够在不用考虑性能和内存等因素的情况下,快速完成 Excel 的读写功能。 首先,需要在 Spring Boot 项目中引入 EasyExcel 依赖。在 pom.xml 文件中添加以下依赖: <d…...
获取对象中第一个存在的值
在JavaScript中,要从一个对象中获取第一个存在的(非undefined、非null、非空数组等)值,你可以使用Object.values()方法结合Array.prototype.find()方法。以下是一个示例代码,演示如何实现这一点: const ob…...
Python学习笔记----集合与字典
1. 字符串、列表和元组的元素都是按下标顺序排列,可通过下 标直接访问,这样的数据类型统称为序列。 其中,字符串和元组中的元素不能修改,而列表中的元素可以修改。 集合 1. 与元组和列表类似,Set (集合&a…...
c# 排序、强转枚举
List<Tuple<double,int>> mm中doble从小到大排序 mm本身排序 在C#中,如果你有一个List<Tuple<double, int>>类型的集合mm,并且你想要根据Tuple中的double值(即第一个元素)从小到大进行排序,同…...
“华为杯”第十六届中国研究生数学建模竞赛-C题:视觉情报信息分析
目录 摘 要: 一、问题重述 二、模型假设 三、符号说明 四、问题一分析与求解 4.1 问题一分析 4.2 模型建立 4.2.1 位置变换模型建立 4.2.4 多平面转换模型建立 4.3 模型求解 4.3.1 问题一图 1 结果 4.3.2 问题一图 2 结果 4.3.3 问题一图 3 结果 4.3.4 问题一图 4 结果 4.4 模…...
html+css+js网页设计 找法网2个页面(带js)ui还原度百分之90
htmlcssjs网页设计 找法网2个页面(带js)ui还原度百分之90 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑…...
018 | backtrader回测反转策略
什么是反转策略? 反转策略(Reversal Strategy)是一种试图捕捉市场价格趋势逆转的交易策略。与趋势跟随策略不同,反转策略的核心理念是“物极必反”,即价格在经过一段时间的单边趋势后,往往会出现逆转的机会…...
《图解HTTP》全篇目录
前言 目前,国内讲解 HTTP 协议的书实在太少了。在我的印象中,讲解网络协议的书仅有两本。一本是《HTTP 权威指南》,但其厚度令人望而生畏;另一本是《TCP/IP 详解,卷 1》,内容艰涩难懂,学习难度…...
基于VS2019(Release_x64)+Qt的软件开发—环境配置
前置博客: 基于C高级编程语言的软件开发随记——环境变量-CSDN博客 (一)一种避免设置大量环境变量的VS2019环境配置方法 Ⅰ 解决方案资源管理器->VC目录->在包含目录/库目录中添加对应的include/lib文件夹($(So…...
【书生大模型实战营(暑假场)闯关材料】入门岛:第1关 Linux 基础知识
【书生大模型实战营(暑假场)闯关材料】入门岛:第1关 Linux 基础知识 1. 使用VScode进行SSH远程连接服务器2. 端口映射及实例参考文献 这一博客主要介绍使用VScode进行服务器远程连接及端口映射。 1. 使用VScode进行SSH远程连接服务器 安装V…...
240810-Gradio通过HTML组件打开本地文件+防止网页跳转到about:blank
A. 最终效果 B. 可通过鼠标点击打开文件,但会跳转到about:blank import gradio as gr import subprocessdef open_pptx():pptx_path /Users/liuguokai/Downloads/240528-工业大模型1.pptxtry:subprocess.Popen([open, pptx_path])return "PPTX file opened s…...
go在linux上安装
1.首先要确定Linux架构 uname -m如果你的系统是 armv7l(32-bit ARM),你需要下载 armv6l 版的Go语言。 如果你的系统是 aarch64(64-bit ARM),你需要下载 arm64 版的Go语言。 如果你的系统是 x86_64…...
算法日记day 35(动归之分割等和子集|最后一块石头的重量2)
一、分割等和子集 题目: 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 1: 输入:nums [1,5,11,5] 输出:true 解释:数组可以分…...
FPGA使用sv生成虚拟单音数据
FPGA使用sv生成虚拟单音数据 之前一直使用matlab生成虚拟的数据,导出到txt或是coe文件中,再导入到fpga中进行仿真测试。 复杂的数据这样操作自然是必要的,但是平日使用正弦数据进行测试的话,这样的操作不免复杂,今日…...
Linux shell编程:监控进程CPU使用率并使用 perf 抓取高CPU进程信息
0. 概要 本文将介绍一个用于监控一组进程CPU使用率的Shell脚本,,当检测到某进程的CPU使用率超出阈值时,使用 perf 工具抓取该进程的详细信息。 本shell脚本为了能在普通嵌入式系统上运行做了妥协和优化。 1. shell脚本流程的简要图示&#…...
Linux网络编程的套接字分析(其一,基本知识)
文章目录 套接字的类型流套接字数据报套接字原始套接字 套接字地址获取套接字地址 协议族和地址族 套接字的类型 Linux系统的套接字有三类:流套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAM)。 流套接字 用于面向连接…...
后端Web开发之Maven
1.java项目构建工具maven介绍 Maven是apache旗下的一个开源项目。Apache软件基金会,成立于1999年7月,是目前世界上最大的最受欢迎的开源(源代码开放)软件基金会也是一一个专门为支持开源项目而生的非盈利性组织。 apache开源项目…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
